Support entering either keycode or passphrase per button. master
authorPat Thoyts <patthoyts@users.sourceforge.net>
Sat, 9 Nov 2013 15:08:11 +0000 (15:08 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Sat, 9 Nov 2013 15:11:16 +0000 (15:11 +0000)
All the configuration data is now held in eeprom and for each button either
a multi-character alphanumeric phrase or a single keycode can be configured.
Added a python script to configure the eeprom image (either taken from the
device or generated by the compiler).

Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
Makefile
config_eeprom.py [new file with mode: 0755]
main.c

index 06df2560fe18d0d4db48cf9afb358fa0cb91b41e..747aa4fc50111033f78ab8e0bbef930540c8db60 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -91,12 +91,14 @@ main.hex: main.bin
 # do the checksize script as our last action to allow successful compilation
 # on Windows with WinAVR where the Unix commands will fail.
 
-serial: main.bin
+eeprom: main.eep.hex
+main.eep.hex: main.bin
        @avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
          --change-section-lma .eeprom=0 -O ihex main.bin main.eep.hex
-       @python setserial.py main.eep.hex
+       @echo call configure_eeprom.py to set eeprom values
+       #@python setserial.py main.eep.hex
 
-flash_serial:
+flash_eeprom:
        $(AVRDUDE) -U eeprom:w:main.eep.hex:i
 
 disasm:        main.bin
diff --git a/config_eeprom.py b/config_eeprom.py
new file mode 100755 (executable)
index 0000000..4311a83
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+
+import sys,os
+import argparse
+from intelhex import IntelHex
+
+def main(argv = None):
+    """ Configure the eeprom image for the hid keyboard device
+    --serialnum 8 --phraseA 'something' -keycodeB 4
+
+    0: calibration byte
+    1: calibration write count
+    3: 6 byte serial number
+    9: init delay value (ms/8)
+    10: key A code
+    11: phrase len for A
+    12: key B code
+    13: phrase len for B
+    14..: phrase data
+    """
+
+    if (argv is None):
+        argv = sys.argv
+
+    parser = argparse.ArgumentParser(description=
+                                     r'configure keyboard device eeprom data')
+    parser.add_argument('filename', type=file,
+                        help='eeprom data file in intel hex format')
+    parser.add_argument('--serial', type=int, help='set device serial number')
+    parser.add_argument('--phraseA', help='set phrase emitted by key A')
+    parser.add_argument('--keycodeA', type=int, help='set keycode for key A')
+    parser.add_argument('--phraseB', help='set phrase emitted by key B')
+    parser.add_argument('--keycodeB', type=int, help='set keycode for key B')
+    parser.add_argument('--init_delay', type=int,
+                        help='set initialization delay in milliseconds')
+    parser.add_argument('--debug', action='store_true',
+                        help='dump modified hex to stdout for review')
+    args = parser.parse_args(argv[1:])
+
+    lenA = 0 if args.phraseA is None else len(args.phraseA)
+    lenB = 0 if args.phraseB is None else len(args.phraseB)
+    if 14 + lenA + lenB > 255:
+        print("error: over 256 bytes of eeprom data")
+        return 1
+
+    data = IntelHex(args.filename)
+
+    if args.serial is not None:
+        data.puts(3, 'K%05d' % args.serial)
+    if args.init_delay is not None:
+        data[9] = delay / 8
+
+    if args.phraseA is not None:
+        data[11] = len(args.phraseA)
+        data.puts(14, args.phraseA)
+    else:
+        if args.keycodeA is not None:
+            data[10] = args.keycodeA
+
+    if args.phraseB is not None:
+        data[13] = len(args.phraseB)
+        data.puts(14 + lenA, args.phraseB)
+    else:
+        if args.keycodeB is not None:
+            data[12] = args.keycodeB
+
+    if args.debug:
+        data.dump()
+    else:
+        with open(argv[1], 'w') as f:
+            data.write_hex_file(f)
+
+    return 0
+
+if __name__=='__main__':
+    sys.exit(main())
diff --git a/main.c b/main.c
index 5d19854b56add4f7b2d61fe053d7b92279671130..0ac5ea8a2424f7da762f6cec407ca734d095a8d4 100644 (file)
--- a/main.c
+++ b/main.c
@@ -35,9 +35,6 @@
  */
 #define LED_PIN PB1
 
-char EEMEM ee_serial[USB_CFG_SERIAL_NUMBER_LEN] = { USB_CFG_SERIAL_NUMBER };
-uint8_t EEMEM ee_calibration = 0xff;
-
 /* Declare space in RAM for the default serial number. This should
  * be overwrittern from eeprom before USB gets initialized
  */
@@ -48,9 +45,14 @@ int usbDescriptorStringSerialNumber[USB_CFG_SERIAL_NUMBER_LEN + 2] = {
 };
 
 enum {
-    Modifier_NumLock = 1,
-    Modifier_CapsLock = 2,
-    Modifier_ScrollLock = 4,
+    Modifier_LeftControl = (1<<0),
+    Modifier_LeftShift = (1<<1),
+    Modifier_LeftAlt = (1<<2),
+    Modifier_LeftGUI = (1<<3), /* Windows/Compose/Apple/Meta */
+    Modifier_RightControl = (1<<4),
+    Modifier_RightShift = (1<<5),
+    Modifier_RightAlt = (1<<6),
+    Modifier_RightGUI = (1<<7),
 };
 
 enum {
@@ -61,6 +63,7 @@ enum {
     Led_Compose = 4,
     Led_Kana = 5,
     Led_Power = 6,
+    Led_Shift = 7,
 
     Led_Glacial = 65500,
     Led_Slow = 48000,
@@ -71,6 +74,11 @@ enum {
 enum {
     Key_A = 4,
     Key_B = 5,
+    Key_X = 27,
+    Key_Y = 28,
+    Key_Z = 29,
+    Key_1 = 30,
+    Key_Return = 40,
     Key_CapsLock = 57,
     Key_NumLock = 83,
     Key_Execute = 116,
@@ -99,12 +107,30 @@ typedef struct Global_t {
     uchar LED_state;
     uchar idle_rate;
     uint8_t calibrationValue;
+    uchar code_A;
+    uchar phraselen_A;
+    uchar code_B;
+    uchar phraselen_B;
+    uchar current_index; /* index of char to send or 0xff for not sending */
+    uchar current_pin;
     keyboard_report_t keyboard_report;
 } Global_t;
 static Global_t Global = {
-    0, Led_Slow, 0, 0, 0xff, 0, 0xff, {0, 0, {0, 0, 0, 0, 0, 0}}
+    0, Led_Slow, 0, 0, 0xff, 0, 0xff,
+    Key_A, 0, Key_B, 0, 0xff, 0,
+    {0, 0, {0, 0, 0, 0, 0, 0}}
 };
 
+EEMEM uchar ee_phrases; /* start of phrase storage */
+EEMEM uint8_t ee_phraselenB = 0; /* length of stored phrase B*/
+EEMEM uchar ee_code_B = Key_Y; /* key value for button B */
+EEMEM uint8_t ee_phraselenA = 0; /* length of stored phrase A */
+EEMEM uchar ee_code_A = Key_X; /* key value for button A */
+EEMEM uint8_t ee_init_delay = 50; /* delay on init (ms/8) */
+EEMEM char ee_serial[USB_CFG_SERIAL_NUMBER_LEN] = { USB_CFG_SERIAL_NUMBER };
+EEMEM uint16_t ee_cal_write_count = 0; /* record number of calibration writes */
+EEMEM uint8_t ee_calibration = 0xff;
+
 static void
 setLEDspeed()
 {
@@ -174,17 +200,65 @@ appInit(void)
     DDRB &= ~(_BV(DDB0) | _BV(DDB2));
 }
 
+/* convert an ascii char into a keyboar report - only does alphanum */
 static void
-handleKeyPress(uint8_t modifier, uint8_t keycode)
+set_report(uchar c, keyboard_report_t *report)
+{
+    report->modifier = 0;
+    if (c >= '0' && c <= '9') {
+        report->keycode[0] = (c == '0') ? 39 : 30 + (c - '1');
+    } else if (c >= 'a' && c <= 'z') {
+        report->keycode[0] = 4 + (c - 'a');
+    } else if (c >= 'A' && c <= 'Z') {
+        report->modifier = Modifier_LeftShift;
+        report->keycode[0] = 4 + (c - 'a');
+    } else {
+        report->keycode[0] = 85; /* KP_* */
+    }
+}
+
+static void
+queueNextChar()
+{
+    const uint8_t *p = (const uint8_t *)&ee_phrases;
+    uchar len = (Global.current_pin == PB3) ? Global.phraselen_A : Global.phraselen_B;
+    if (Global.current_index == len) {
+        Global.current_index = 0xff;
+    } else {
+        uchar c;
+        if (Global.current_pin == PB4)
+            p += Global.phraselen_A;
+        c = eeprom_read_byte(p + Global.current_index);
+        if (c == 0xff) {
+            Global.keyboard_report.modifier = 0;
+            Global.keyboard_report.keycode[0] = 85;
+        } else {
+            set_report(c, &Global.keyboard_report);
+        }
+        ++Global.current_index;
+        Global.state = State_KeyReady;
+    }
+}
+
+static void
+handleKeyPress(uint8_t pin)
 {
     if (Global.state == State_Default) {
         Global.led_limit = Led_Quick;
         ++Global.key_down_counter;
 
         if (Global.key_down_counter > 8000) {
-            Global.state = State_KeyReady;
-            Global.keyboard_report.modifier = modifier;
-            Global.keyboard_report.keycode[0] = keycode;
+            uchar len = (pin == PB3) ? Global.phraselen_A : Global.phraselen_B;
+            if (len == 0) {
+                uchar code = (pin == PB3) ? Global.code_A : Global.code_B;
+                Global.keyboard_report.modifier = 0;
+                Global.keyboard_report.keycode[0] = code;
+                Global.state = State_KeyReady;
+            } else {
+                Global.current_index = 0;
+                Global.current_pin = pin;
+                queueNextChar();
+            }
             Global.key_down_counter = 0;
         }
     }
@@ -201,23 +275,28 @@ appPoll(void)
         Global.led_counter = 0;
     }
 
-    /* key pressed (pin driven low) */
-    if (!(pin_state & _BV(PB3))) {
-        handleKeyPress(0, Key_A);
-    }
-    if (!(pin_state & _BV(PB4))) {
-        handleKeyPress(0, Key_CapsLock);
-    }
-    /* when a key is released, reset the LED flash speed */
-    if (pin_state & (_BV(PB3)|_BV(PB4)) && Global.state == State_Default) {
-        setLEDspeed();
+    if (Global.state == State_Default && Global.current_index != 0xff) {
+        queueNextChar();
+    } else {
+        /* key pressed (pin driven low) */
+        if (!(pin_state & _BV(PB3))) {
+            handleKeyPress(PB3);
+        }
+        if (!(pin_state & _BV(PB4))) {
+            handleKeyPress(PB4);
+        }
+        /* when a key is released, reset the LED flash speed */
+        if (pin_state & (_BV(PB3)|_BV(PB4)) && Global.state == State_Default) {
+            setLEDspeed();
+        }
     }
+
     /* called after every poll of the interrupt endpoint */
     if (usbInterruptIsReady() && Global.LED_state != 0xff) {
         if (Global.state != State_Default) {
             switch (Global.state) {
                 case State_KeyReady:
-                    Global.state = State_Pending;
+                    Global.state = State_KeyRelease;
                     break;
                 case State_Pending:
                     Global.state = State_KeyRelease;
@@ -242,8 +321,12 @@ on_usb_reset(void)
 #if F_CPU==16500000
     /* store the calibrated value in EEPROM only if changed */
     if (Global.calibrationValue != OSCCAL) {
+        uint16_t count = 0;
         eeprom_write_byte(&ee_calibration, OSCCAL);
         Global.calibrationValue = OSCCAL;
+        count = eeprom_read_word(&ee_cal_write_count);
+        ++count;
+        eeprom_write_word(&ee_cal_write_count, count);
     }
 #endif
 }
@@ -251,12 +334,14 @@ on_usb_reset(void)
 /* load serial number from eeprom into RAM. The library is
  * configured to return the string descriptor from this
  * RAM buffer.
+ *
+ * Also loads the configured keycodes.
  */
 
 static void
-load_serial_eeprom()
+load_eeprom_data()
 {
-    uchar serial[USB_CFG_SERIAL_NUMBER_LEN];
+    uchar c, serial[USB_CFG_SERIAL_NUMBER_LEN];
     serial[0] = 0xff;
     eeprom_read_block(&serial, &ee_serial, USB_CFG_SERIAL_NUMBER_LEN);
     if (serial[0] != 0xff) {
@@ -265,6 +350,22 @@ load_serial_eeprom()
             usbDescriptorStringSerialNumber[1 + n] = (int)(serial[n]);
         }
     }
+
+    c = eeprom_read_byte(&ee_code_A);
+    if (c != 0xff)
+        Global.code_A = c;
+
+    c = eeprom_read_byte(&ee_phraselenA);
+    if (c != 0xff)
+        Global.phraselen_A = c;
+
+    c = eeprom_read_byte(&ee_code_B);
+    if (c != 0xff)
+        Global.code_B = c;
+
+    c = eeprom_read_byte(&ee_phraselenB);
+    if (c != 0xff)
+        Global.phraselen_B = c;
 }
 
 int __attribute__((noreturn))
@@ -278,13 +379,20 @@ main(void)
     }
 #endif
 
-    load_serial_eeprom();  /* load the serial number from eeprom[1..7] */
+    load_eeprom_data();    /* load the serial number from eeprom[1..7] */
     wdt_enable(WDTO_1S);   /* setup a 1 second watchdog */
     odDebugInit();
     appInit();             /* app specific initialization */
     usbInit();             /* usb library initialization */
     usbDeviceDisconnect(); /* enforce re-enumeration */
-    _delay_ms(400);
+    {
+        uint16_t n, delay = (uint16_t)eeprom_read_byte(&ee_init_delay);
+        if (delay == 0x00ff || delay < 5) delay = 50;
+        for (n = 0; n < delay; ++n) {
+            wdt_reset();
+            _delay_ms(8);
+        }
+    }
     usbDeviceConnect();
     sei();
     for(;;) {