/* * Evil Copyright (c) 2015 Kevin Nygaard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Pinout for Stentura 200 (NOT FOR FLASH) * Ribbon * |||||| * |||||| * |||||| * xxxxxx (contacts facing up) * \\\\\\_ Power +5V * \\\\\_ 2 Test * \\\\_ 3 Shift load * \\\_ 4 Serial out * \\_ 5 Clock * \_ Ground */ /* IO Definitions STENTURA 200*/ /* #define CLK 5 #define DATA_IN 4 #define LATCH_EN 3 #define TEST_EN 2 #define BAUD 115200 */ /* * Pinout for ProCAT Flash * IO Board Pads * (NOTE: THIS IS NOT THE RIBBON CABLE, that is different) * * Pads are in two columns, 1,2,3 and 4,5,6. 1 being the square pad (visible from top). * * 1 ??? (Must be test. Connects to Resistors) * 2 PWR * 3 GND * * 4 Serial Out (Pin 9 of first IC) * 5 CLK (Clk rail) * 6 SH/LD (Shift Load rail) * */ /* IO Definitions ProCAT Flash*/ #define CLK 5 #define DATA_IN 4 #define LATCH_EN 6 #define TEST_EN 2 //Can't put TEST on 1, which would be UART TX #define BAUD 9600 #define DEBUG 0 //set to 1 when using Arduino to debug #define DEBOUNCE_PERIOD 10 #define ARRAY_SIZE(array) \ (sizeof(array) / sizeof(*array)) /* * Setup IO and serial connections */ void setup() { // Initialize serial Serial.begin(BAUD); if(DEBUG){ Serial.println("Hax Steno Begin"); } // Setup IOs pinMode(CLK, OUTPUT); pinMode(LATCH_EN, OUTPUT); pinMode(DATA_IN, INPUT); //pinMode(TEST_EN, OUTPUT); //Not needed // Drive known values to outputs digitalWrite(CLK, LOW); digitalWrite(LATCH_EN, LOW); //digitalWrite(TEST_EN, LOW); //Not needed } /* * Convert raw Stentura byte data into packed Gemini PR format * * Data that is seemingly skipped is rendundant, and as far as I can tell, * unnecessary * * Raw matrix mapping of Stentura 200 SRT from serial shift registers * 0: S- * 1: T- * 2: K- * 3: P- * 4: W- * 5: H- * 6: R- * 7: A- * 8: O- * 9: * * 10: -E * 11: -U * 12: -F * 13: -R * 14: -P * 15: -B * 16: -L * 17: -G * 18: -T * 19: -S * 20: -D * 21: -Z * 22: # * 23: N/A * * Gemini PR protocol (from plover source ./plover/machine/geminipr.py) * * In the Gemini PR protocol, each packet consists of exactly six bytes and the * most significant bit (MSB) of every byte is used exclusively to indicate * whether that byte is the first byte of the packet (MSB=1) or one of the * remaining five bytes of the packet (MSB=0). As such, there are really only * seven bits of steno data in each packet byte. This is why the STENO_KEY_CHART * below is visually presented as six rows of seven elements instead of six rows * of eight elements. * STENO_KEY_CHART = ("Fn" , "#" , "#" , "#" , "#" , "#" , "#" , * "S-" , "S-", "T-", "K-", "P-", "W-" , "H-" , * "R-" , "A-", "O-", "*" , "*" , "res", "res", * "pwr", "*" , "*" , "-E", "-U", "-F" , "-R" , * "-P" , "-B", "-L", "-G", "-T", "-S" , "-D" , * "#" , "#" , "#" , "#" , "#" , "#" , "-Z") * */ //void construct_data(char raw_data[], char packed_data[]) //{ // packed_data[0] = 0x80; // packed_data[1] = (raw_data[ 0] << 6) /* S- */ // | (raw_data[ 1] << 1) /* T- */ // | (raw_data[ 2] << 3) /* K- */ // | (raw_data[ 3] << 2) /* P- */ // | (raw_data[ 4] << 0) /* W- */ // | (raw_data[ 5] << 0); /* H- */ //packed_data[2] = (raw_data[ 6] << 6) /* R- */ // | (raw_data[ 7] << 5) /* A- */ // | (raw_data[ 8] << 4) /* O- */ // | (raw_data[ 9] << 3); /* * */ //packed_data[3] = (raw_data[10] << 3) /* -E */ // | (raw_data[11] << 2) /* -U */ // | (raw_data[12] << 1) /* -F */ // | (raw_data[13] << 0); /* -R */ //packed_data[4] = (raw_data[14] << 6) /* -P */ // | (raw_data[15] << 5) /* -B */ // | (raw_data[16] << 4) /* -L */ // | (raw_data[17] << 3) /* -G */ // | (raw_data[18] << 2) /* -T */ // | (raw_data[19] << 1) /* -S */ // | (raw_data[20] << 0); /* -D */ //packed_data[5] = (raw_data[22] << 6) /* # */ // | (raw_data[21] << 0); /* -Z */ //} /* ProCAT Flash Mapping * * The ProCAT Flash is somewhat different. The above Stentura 200 will not work. Instead we have: * * * Raw matrix mapping of ProCAT Flash from serial shift registers * * You can somewhat infer this from the PCB (cheat) by reading letter order, but not all are logical * 0: H- // * 1: W- // * 2: P- // * 3: K- // * 4: T- //changes marked with comments * 5: S- // * 6: # // //old was R- * * 7: A- * 8: -R // * 9: F // * 10: -U // * 11: -E // * 12: * // * 13: -R * * 14: -P * 15: R //was -B * 16: -Z // * 17: -D // * 18: -S // * 19: -T // * 20: -G // * * 21: -L ?? * 22: # * 23: ???? * * need -B */ void construct_data(char raw_data[], char packed_data[]) { packed_data[0] = 0x80; packed_data[1] = (raw_data[ 5] << 6) /* S- */ //Have to move arrays entries around here, if you are going to | (raw_data[ 4] << 4) /* T- */ //change values. Need all 24 bits. | (raw_data[ 3] << 3) /* K- */ | (raw_data[ 2] << 2) /* P- */ | (raw_data[ 1] << 1) /* W- */ | (raw_data[ 0] << 0); /* H- */ packed_data[2] = (raw_data[ 15] << 6) /* R- */ | (raw_data[ 14] << 5) /* A- */ | (raw_data[ 13] << 4) /* O- */ | (raw_data[ 12] << 3); /* * */ packed_data[3] = (raw_data[11] << 3) /* -E */ | (raw_data[10] << 2) /* -U */ | (raw_data[9] << 1) /* -F */ | (raw_data[8] << 0); /* -R */ packed_data[4] = (raw_data[23] << 6) /* -P */ // was 14 | (raw_data[22] << 5) /* -B */ // was dupe 0 | (raw_data[21] << 4) /* -L */ | (raw_data[20] << 3) /* -G */ | (raw_data[19] << 2) /* -T */ | (raw_data[18] << 1) /* -S */ | (raw_data[17] << 0); /* -D */ packed_data[5] = (raw_data[6] << 6) /* # */ // | (raw_data[16] << 0); /* -Z */ } /* * Read keystrokes and return them to the host */ void loop(){ // Byte array of depressed keys, following mapping above // 0 means not pressed, 1 means pressed static char pressed_keys[24]; // Keys packed in Gemini PR format, ready for transmission over serial static char packed_keys[6]; // Chord has been started, but is not complete (all keys released) static char in_progress; // Send data to host over serial static char send_data; // Debounce counter static int debounce_count; int i; char pressed; char keys_down; //If keys have already been recorded into pressed_keys array if (send_data) { construct_data(pressed_keys, packed_keys); Serial.write(packed_keys, ARRAY_SIZE(packed_keys)); /* * Data returned to host, reset stateful variables * Note that we don't need to do this in the setup, because they are all * static and guaranteed to be 0 by spec */ memset(pressed_keys, 0, sizeof(pressed_keys)); send_data = 0; in_progress = 0; // Else, record keys into pressed_keys array } else { // Latch current state of all keys digitalWrite(LATCH_EN, HIGH); // Read all latched data keys_down = 0; for (i = 0; i < ARRAY_SIZE(pressed_keys); i++){ /* * All inputs are pulled up. Pressing a key shorts the circuit to * ground. * * We invert the logic here to convert to more conventional positive * logic. */ pressed = !digitalRead(DATA_IN); //At this point, you are reading each individual bit in as a 1 if pressed. //debug if(pressed && DEBUG){ Serial.print(pressed, BIN); Serial.print("Pressed was high for i = "); Serial.println(i); } // Once a key is pressed, it stays pressed until the chord is over // OR result to array pressed_keys[i] |= pressed; if (pressed) { keys_down = 1; in_progress = 1; } /* * Toggle clock. Max frequency of shift register MM74HC165 is 30 * MHz, so it can be switched without any delay by Arduino. */ digitalWrite(CLK, HIGH); digitalWrite(CLK, LOW); } // Make latch transparent again to capture next set of keystrokes digitalWrite(LATCH_EN, LOW); if (keys_down) { debounce_count = 0; } // Return data to host when all keys have been released if (in_progress && !keys_down && (++debounce_count >= DEBOUNCE_PERIOD)) { send_data = 1; } } }