├── README.md ├── arduinoProj └── USB2keybus │ ├── F7msg.h │ ├── KeypadSerial.cpp │ ├── KeypadSerial.h │ ├── Makefile │ ├── ModSoftwareSerial.cpp │ ├── ModSoftwareSerial.h │ ├── PiSerial.cpp │ ├── PiSerial.h │ ├── USB2keybus.ino │ ├── USBprotocol.cpp │ ├── USBprotocol.h │ ├── Volts.cpp │ └── Volts.h └── docs ├── Alarm_Power_Wiring.jpg ├── Arduino_Keypad_Wiring.jpg ├── Keybus Protocol.doc ├── Keybus Protocol.pdf ├── Perfboard.jpg ├── README.md ├── rpi_alarm-1.jpg └── rpi_alarm-2.jpg /README.md: -------------------------------------------------------------------------------- 1 | # Arduino2keypad 2 | This repository is the companion of https://github.com/TomVickers/RPIalarm 3 | 4 | This repository contains code to interface a 6160 alarm keypad with an Arduino. If you are interested in using a 6160 alarm keypad (commonly found connected to a variety of Honeywell alarm systems like the Vista-20p), this code may be useful to you. 5 | 6 | As with most things in life, my work on this is simply an extension of the work by others. I found Mark Kimsal's website (http://getatanc.com/) and github repository (https://github.com/TANC-security/keypad-firmware) very helpful. Most of what you will find here, I found first on his pages. I have extended it a bit, and worked out some details of communicating directly with the keypad (his task involved talking to the alarm as a keypad). 7 | 8 | The doc directory contains notes, circuit diagrams, etc. If you find something is wrong (or learn something new), please let me know and I will try to update them as we learn more about this device. 9 | 10 | The ArduinoProj directory contains the Arduino project named USB2keybus. I build it using Arduino software (version 1.8.5) on a Mega 2560. It will probably run on other Arduino processors with minor changes. I can also build it on my alarm Raspberry PI using the provided Makefile. There are some notes in the comments at the top of the Makefile that indicate which packages you must install to enable cross compiling for the Arduino. You will notice that the project uses a modified version of the SoftwareSerial lib. All the modifications in my ModSoftwareSerial files are marked with the comment NON_STANDARD (in case you want to port these changes to a different version of SoftwareSerial). 11 | 12 | --------------- NOTE: Beta code ------------------------ 13 | 14 | This code is a work in progress. A few features are not yet complete, but it appears to be stable. I am currently using it as a bi-directional parser between a Raspberry Pi 3 USB serial port at 115200 baud and a 6160 keypad. 15 | 16 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/F7msg.h: -------------------------------------------------------------------------------- 1 | // file F7msg.h - F7 message info 2 | 3 | #pragma once 4 | 5 | // F7 bit macros to simplify code 6 | #define SET_BIT(b,v,m) (((b) & ~(m)) | ((v) ? (m) : (0x00))) 7 | #define SET_READY(b,v) SET_BIT(b,v,0x10) // BYTE2 8 | #define SET_ARMED_STAY(b,v) SET_BIT(b,v,0x80) // BYTE2 9 | #define SET_ARMED_AWAY(b,v) SET_BIT(b,v,0x04) // BYTE3 10 | #define SET_CHIME(b,v) SET_BIT(b,v,0x20) // BYTE3 11 | #define SET_POWER(b,v) SET_BIT(b,v,0x08) // BYTE3 12 | 13 | #define GET_BIT(b,m) ((b) & (m)) 14 | #define GET_READY(b) GET_BIT(b,0x10) // BYTE2 15 | #define GET_ARMED_STAY(b) GET_BIT(b,0x80) // BYTE2 16 | #define GET_ARMED_AWAY(b) GET_BIT(b,0x04) // BYTE3 17 | #define GET_CHIME(b) GET_BIT(b,0x20) // BYTE3 18 | #define GET_POWER(b) GET_BIT(b,0x08) // BYTE3 19 | 20 | #define H2B(a) ((uint8_t)(((a) >= '0' && (a) <= '9') ? (a) - '0' : (a) - 'A' + 10)) 21 | #define GET_BYTE(c1,c2) ((uint8_t)((H2B(c1) << 4) | H2B(c2))) 22 | #define GET_NIBBLE(c) ((uint8_t)(H2B(c))) 23 | #define GET_BOOL(b) ((b) == '1' ? 1 : 0) 24 | 25 | #define LCD_LINE_LEN (16) 26 | #define F7_MSG_SIZE (48) 27 | 28 | #pragma pack(push,1) // mesg struct needs 1 byte packing 29 | 30 | typedef struct { 31 | uint8_t type; 32 | uint8_t addr1; 33 | uint8_t addr2; 34 | uint8_t keypads; 35 | uint8_t addr4; 36 | uint8_t zone; 37 | uint8_t byte1; 38 | uint8_t byte2; 39 | uint8_t byte3; 40 | uint8_t prog; 41 | uint8_t prompt; 42 | uint8_t pad1; 43 | char line1[LCD_LINE_LEN]; 44 | char line2[LCD_LINE_LEN]; 45 | uint8_t chksum; 46 | uint8_t unused[3]; 47 | } t_MesgF7; // F7 mesg is 48 bytes 48 | 49 | #pragma pack(pop) // restore default packing 50 | 51 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/KeypadSerial.cpp: -------------------------------------------------------------------------------- 1 | // file KeypadSerial.cpp - a class for handling com with alarm keypad 2 | 3 | #include "KeypadSerial.h" 4 | 5 | // Keypad communication appears to be mostly inverted 8E2@4800, but some special handling is required 6 | // Check the comments below for details. 7 | 8 | #define ONE_BIT_DELAY delay_us(208) // ~one bit delay @4800 baud 9 | #define ONE_BYTE_DELAY delay_us(2030) // ~one byte delay @4800 baud 10 | #define DELAY_BETWEEN_POLL_WRITES delay_us(1015) // measured delay between polling writes 11 | #define LOW_BEFORE_WRITE_DELAY delay_us(4060) // time to drop transmit before regular writes 12 | 13 | KeypadSerial * KeypadSerial::pKeypadSerial = NULL; // pointer to class for ISR 14 | 15 | // class constructor 16 | KeypadSerial::KeypadSerial(void) : softSerial(RX_PIN, TX_PIN, true) {} 17 | 18 | // init the class 19 | void KeypadSerial::init(void) 20 | { 21 | pKeypadSerial = this; // setup class pointer for ISR 22 | pollState = NOT_POLLING; // not currently polling 23 | softSerial.begin(KP_SERIAL_BAUD); // set baud rate 24 | softSerial.setParity(true); // enable even parity 25 | afterWrite(); // normal state of the transmit line should be high 26 | } 27 | 28 | // microsecond delay function that supports interrupts during the delay 29 | inline void KeypadSerial::delay_us(uint32_t us) 30 | { 31 | // _delay_loop_2 delays for about 0.25us at 16MHz. This code may work for slower 32 | // clock speeds, but the timing resolution will be less and that may affect your results 33 | 34 | for (uint32_t s = micros(); micros() - s < us; ) 35 | _delay_loop_2(1); 36 | } 37 | 38 | // The before/after write functions manage the state of the transmit line to keypad 39 | 40 | // normal state of the transmit line to the keypad is high, but moves low before 41 | // a write starts (high start bit). 42 | void KeypadSerial::beforeWrite(void) 43 | { 44 | softSerial.tx_pin_write(LOW); // set transmit low before we start writing 45 | LOW_BEFORE_WRITE_DELAY; // hold transmit low before write for ~4ms 46 | } 47 | 48 | // restore high transmit after write 49 | void KeypadSerial::afterWrite(void) 50 | { 51 | softSerial.tx_pin_write(HIGH); 52 | } 53 | 54 | // parse the keypad byte of the poll response to see which keypads responded 55 | // although unlikely, up to 8 keypads could respond with data in the same poll cycle 56 | bool KeypadSerial::parsePollResp(uint8_t resp) 57 | { 58 | numKeypads = 0; 59 | 60 | if (resp != 0xFF) // 0xFF indicates no keypad responded 61 | { 62 | for (uint8_t i=0; i < 8 && numKeypads < KP_SERIAL_MAX_KEYPADS; i++) 63 | { 64 | if (((resp >> i) & 0x01) == 0) // keypad 16+i responded 65 | { 66 | keypadAddr[numKeypads++] = 16 + i; 67 | } 68 | } 69 | return (numKeypads > 0); 70 | } 71 | return false; 72 | } 73 | 74 | // this func holds transmit high for one byte (a 0x00 written inverted) 75 | // but does not disable pin interrupts during the byte output 76 | // however, the recv called by pinChangeIsr during the 3rd write will extend 77 | // the length of the 3rd high transmit. This is ok as the keypad has already responded 78 | void KeypadSerial::write0(void) 79 | { 80 | softSerial.tx_pin_write(HIGH); // set transmit high 81 | ONE_BYTE_DELAY; // hold transmit high for 1 byte (10 bits: start + 8 bits + parity) 82 | softSerial.tx_pin_write(LOW); // set transmit low 83 | DELAY_BETWEEN_POLL_WRITES; // delay needed between polling writes 84 | } 85 | 86 | // Poll the keypad for data. Return: true if data provided 87 | bool KeypadSerial::poll(void) 88 | { 89 | // poll the keypads to see what addresses respond 90 | // - use transmit like a clock to signal keypads when to respond 91 | // - keep transmit low for greater than 10ms (my alarm uses 13ms) 92 | // - then high for one word (1st), and low for ~1ms 93 | // - then high for one word (2nd), and low for ~1ms 94 | // - then high for one word (3rd), and low for ~1ms 95 | // - should recv responses from keypads when transmit is high 96 | // - keypad responses during polling change pollState in pinChangeIsr 97 | 98 | softSerial.setParity(false); // turn off parity for this transaction 99 | uint8_t pollResp = 0xFF; // init poll response 100 | pollState = POLL_STATE_1; // set pollState to initial value 101 | 102 | softSerial.tx_pin_write(LOW); // set transmit low 103 | _delay_ms(13); // keep low for > 10 ms to signal keypad 104 | write0(); // after write, should be at POLL_STATE_2 if keypad responded 105 | write0(); // after write, should be at POLL_STATE_3 if keypad responded 106 | write0(); // after write, should be at POLL_STATE_4 if keypad responded 107 | afterWrite(); // restore transmit line level 108 | 109 | if (pollState == POLL_STATE_4) // should be at POLL_STATE_4 if keypad responded to each write0 110 | { 111 | read(&pollResp, 10); // which keypads replied? 112 | } 113 | pollState = NOT_POLLING; // done polling (response or no) 114 | softSerial.setParity(true); // restore parity after this transaction 115 | 116 | return parsePollResp(pollResp); // return true if we got a response from any keypads 117 | } 118 | 119 | // write sequence of bytes to keypad 120 | void KeypadSerial::write(const uint8_t * msg, const uint8_t size) 121 | { 122 | beforeWrite(); // set transmit low before we start writing (about 4ms) 123 | 124 | for (uint8_t i=0; i < size; i++) 125 | { 126 | softSerial.write(*(msg + i)); 127 | ONE_BIT_DELAY; // extra stop bit delay makes output 8E2 128 | } 129 | 130 | afterWrite(); // restore transmit line level 131 | } 132 | 133 | // return one char read. timeout is in milliseconds. for non-blocking read, give timeout of zero 134 | bool KeypadSerial::read(uint8_t * c, uint32_t timeout) 135 | { 136 | uint32_t start = millis(); 137 | do 138 | { 139 | if (softSerial.available()) 140 | { 141 | *c = softSerial.read(); 142 | return true; 143 | } 144 | } while (millis() - start < timeout); 145 | 146 | *c = 0; 147 | return false; // timeout, char not available 148 | } 149 | 150 | // send F6 message to keypad to request data. Return mesg type 151 | uint8_t KeypadSerial::requestData(uint8_t kp) 152 | { 153 | uint8_t msgType = NO_MESG; 154 | 155 | recvMsgLen = 0; 156 | 157 | if (kp >= numKeypads || kp >= KP_SERIAL_MAX_KEYPADS) 158 | { 159 | return NO_MESG; // invalid kp number 160 | } 161 | 162 | beforeWrite(); // set transmit low before we start writing 163 | softSerial.write(0xF6); // tell keypad to send data 164 | ONE_BIT_DELAY; // extra stop bit delay 165 | softSerial.write(keypadAddr[kp]); // address keypad we want to hear from 166 | ONE_BIT_DELAY; // extra stop bit delay 167 | afterWrite(); // restore transmit line level 168 | 169 | uint8_t calcChksum = 0; 170 | 171 | if (read(&readBuf[0], 10) && read(&readBuf[1], 10)) // if we recv a message 172 | { 173 | calcChksum = readBuf[0] + readBuf[1]; // add first two bytes to checksum 174 | 175 | // second byte of message is either the length (key message) or a message type 176 | 177 | if (readBuf[1] == 0x87) // msgType 0x87 unknown, sent on power-up, total length 9, 7 bytes after type 178 | { 179 | for (uint8_t i=2; i < 8; i++) // read bytes up to checksum 180 | { 181 | read(&readBuf[i], 10); 182 | calcChksum += readBuf[i]; 183 | } 184 | read(&readBuf[8], 10); // read last mesg byte (checksum) 185 | recvMsgLen = 9; 186 | msgType = readBuf[1]; 187 | } 188 | else if (readBuf[1] <= 16) // assume this is a key message 189 | { 190 | for (uint8_t i=0; i < readBuf[1]; i++) // 2nd byte of keys mesg is length 191 | { 192 | if (read(&readBuf[i+2], 10) && i < readBuf[1]-1) 193 | { 194 | calcChksum += readBuf[i+2]; 195 | } 196 | } 197 | recvMsgLen = readBuf[1]+2; // remain_bytes + header + length 198 | msgType = KEYS_MESG; 199 | } 200 | else // some other type of message, unknown length, read however many bytes are provided 201 | { 202 | // read input until we hit size of read buf or read times out 203 | for (recvMsgLen=2; recvMsgLen < KP_SERIAL_READ_BUF_SIZE && read(&readBuf[recvMsgLen], 10); recvMsgLen++) 204 | ; 205 | for (uint8_t i=2; i < recvMsgLen-1; i++) // assume last byte read is mesg checksum 206 | calcChksum += readBuf[i]; 207 | msgType = readBuf[1]; 208 | } 209 | 210 | calcChksum = 0x100 - calcChksum; // two's compliment 211 | 212 | ONE_BIT_DELAY; 213 | ONE_BIT_DELAY; // appears that a two bit delay is needed before dropping transmit for ack 214 | 215 | if ((readBuf[0] & 0x3F) == keypadAddr[kp] && // if correct keypad responded to our query 216 | calcChksum == readBuf[recvMsgLen-1]) // and the checksum is correct 217 | { 218 | beforeWrite(); // set transmit low before we start writing 219 | softSerial.write(readBuf[0]); // send keypad mesg ack 220 | ONE_BIT_DELAY; // extra stop bit 221 | afterWrite(); // restore transmit line level 222 | return msgType; 223 | } 224 | } 225 | return NO_MESG; // no message recv for this keypad, or bad checksum 226 | } 227 | 228 | // Pin Change INTerrupts --------------------------------------------------------------------------- 229 | 230 | // this pin change ISR replaces the one normally used by SoftwareSerial 231 | inline void KeypadSerial::pinChangeIsr(void) // declared static 232 | { 233 | if (pKeypadSerial->softSerial.rx_pin_read()) // low->high pin change (high start bit) 234 | { 235 | if (pKeypadSerial->pollState == NOT_POLLING || pKeypadSerial->pollState == POLL_STATE_3) 236 | { 237 | pKeypadSerial->softSerial.recv(); // start recv of byte 238 | } 239 | if (pKeypadSerial->pollState != NOT_POLLING) // we are currently polling keypad 240 | { 241 | pKeypadSerial->pollState++; // while polling, bump pollState when pin changes from low to high 242 | } 243 | } 244 | else 245 | { 246 | // do nothing when pin changes from high->low 247 | } 248 | } 249 | 250 | #if defined(PCINT0_vect) 251 | ISR(PCINT0_vect) // pin change on D8-D13 GPIO pins 252 | { 253 | KeypadSerial::pinChangeIsr(); 254 | } 255 | #endif 256 | 257 | #if defined(PCINT1_vect) 258 | ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect)); 259 | #endif 260 | 261 | #if defined(PCINT2_vect) 262 | ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect)); 263 | #endif 264 | 265 | #if defined(PCINT3_vect) 266 | ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect)); 267 | #endif 268 | 269 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/KeypadSerial.h: -------------------------------------------------------------------------------- 1 | // file KeypadSerial.h - a class for handling com with alarm keypad 2 | 3 | #pragma once 4 | 5 | #include 6 | #include "ModSoftwareSerial.h" 7 | 8 | // i/o pins for software serial 9 | #define RX_PIN (12) 10 | #define TX_PIN (11) 11 | 12 | // responses from requestData func 13 | #define NO_MESG (0) 14 | #define KEYS_MESG (1) 15 | 16 | #define KP_SERIAL_BAUD (4800) // baud rate for keypad communication 17 | #define KP_SERIAL_MAX_KEYPADS (8) // max number of keypads in alarm circuit 18 | #define KP_SERIAL_READ_BUF_SIZE (64) // size of read buffer 19 | 20 | // polling states during keypad polling 21 | enum { 22 | NOT_POLLING = 0, 23 | POLL_STATE_1 = 1, // sent first 0x00 24 | POLL_STATE_2 = 2, // send second 0x00 25 | POLL_STATE_3 = 3, // sent third 0x00 26 | POLL_STATE_4 = 4 // read bitmask from keypad 27 | }; 28 | 29 | class KeypadSerial 30 | { 31 | public: 32 | KeypadSerial(void); // Class constructor. Returns: none 33 | 34 | void init(void); // init the class 35 | bool poll(void); 36 | void write(const uint8_t * msg, const uint8_t size); 37 | bool read(uint8_t * c, uint32_t timeout); 38 | void getMsg(char * buf, uint8_t bufLen); 39 | uint8_t requestData(uint8_t kp); 40 | 41 | // return keypad address for keypad kp 42 | uint8_t getAddr(uint8_t kp) { return kp < numKeypads ? keypadAddr[kp] : 0; } 43 | 44 | // return the number of keypads that responded to the poll request 45 | uint8_t getNumKeypads(void) { return numKeypads; } 46 | 47 | // return the number of keys returned by keypad 48 | uint8_t getKeyCount(void) { return recvMsgLen > 3 ? recvMsgLen - 3 : 0; } 49 | 50 | // return pointer to array of keys returned by keypad 51 | uint8_t * getKeys(void) { return &readBuf[2]; } // keys start at byte 2 52 | 53 | // return length of data received from keypad 54 | uint8_t getRecvMsgLen(void) { return recvMsgLen; } 55 | 56 | // return pointer to array of data read from keypad 57 | uint8_t * getRecvMsg(void) { return readBuf; } 58 | 59 | static inline void pinChangeIsr(void) __attribute__((__always_inline__)); 60 | static KeypadSerial * pKeypadSerial; 61 | 62 | private: 63 | bool parsePollResp(uint8_t); 64 | void write0(void); 65 | void beforeWrite(void); 66 | void afterWrite(void); 67 | 68 | static inline void delay_us(uint32_t us) __attribute__((__always_inline__)); 69 | 70 | SoftwareSerial softSerial; 71 | 72 | uint8_t pollState; 73 | uint8_t numKeypads; 74 | uint8_t recvMsgLen; 75 | uint8_t keypadAddr[KP_SERIAL_MAX_KEYPADS]; 76 | uint8_t readBuf[KP_SERIAL_READ_BUF_SIZE]; 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for USB2keybus project 2 | # 3 | # To use the Makefile method for building the project: 4 | # sudo apt-get install gcc-avr avr-libc avrdude 5 | # wget https://github.com/arduino/Arduino/archive/master.zip 6 | # unzip archive and rename top dir /usr/share/Arduino 7 | 8 | # parameters for avrdude 9 | BAUD=115200 10 | AVR_TYPE=atmega2560 11 | AVR_FREQ=16000000L 12 | PROGRAM_DEV=/dev/ttyACM0 13 | PROGRAM_TYPE=wiring 14 | 15 | # path to Arduino lib source code 16 | ARDUINO_CORE_PATH=/usr/share/Arduino/hardware/arduino/avr/cores/arduino 17 | ARDUINO_VARIANT_PATH=/usr/share/Arduino/hardware/arduino/avr/variants/mega 18 | 19 | DEFINES=-DF_CPU=$(AVR_FREQ) -DARDUINO=10802 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR 20 | INCLUDES= -I$(ARDUINO_CORE_PATH) -I$(ARDUINO_VARIANT_PATH) 21 | DEF_FLAGS= $(DEFINES) $(INCLUDES) -mmcu=$(AVR_TYPE) -Wall -Os -ffunction-sections -fdata-sections 22 | CFLAGS=$(DEF_FLAGS) -fno-fat-lto-objects 23 | CPPFLAGS= $(DEF_FLAGS) -fno-exceptions -fpermissive -fno-exceptions -fno-threadsafe-statics 24 | LINK_FLAGS= -w -Os -flto -fuse-linker-plugin -Wl,--gc-sections,--relax -mmcu=$(AVR_TYPE) 25 | 26 | PROJ_SRCS= \ 27 | KeypadSerial.cpp \ 28 | ModSoftwareSerial.cpp \ 29 | PiSerial.cpp \ 30 | USB2keybus.cpp \ 31 | USBprotocol.cpp \ 32 | Volts.cpp 33 | 34 | # list of all the Arduino core .c & .cpp files 35 | CORE_C_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.c) 36 | CORE_CPP_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.cpp) 37 | 38 | # if you want to exclude any core files not needed for your build, list them here 39 | CORE_EXCLUDE= IPAddress.o PluggableUSB.o Tone.o 40 | 41 | # build the list of needed obj files from the source files 42 | OBJ_LIST1=$(patsubst %.cpp,%.o,$(PROJ_SRCS)) 43 | OBJ_LIST1+=$(notdir $(patsubst %.cpp,%.o,$(CORE_CPP_SRCS))) 44 | OBJ_LIST1+=$(notdir $(patsubst %.c,%.o,$(CORE_C_SRCS))) 45 | OBJ_LIST1+=wiring_pulse_asm.o 46 | 47 | # location for built objects 48 | OBJDIR=obj 49 | 50 | # the final obj list 51 | OBJS=$(addprefix $(OBJDIR)/,$(filter-out $(CORE_EXCLUDE),$(OBJ_LIST1))) 52 | 53 | .PHONY: flash clean 54 | 55 | all: main.hex 56 | @echo build complete 57 | 58 | USB2keybus.cpp: USB2keybus.ino 59 | @ln -sf USB2keybus.ino USB2keybus.cpp 60 | 61 | $(OBJDIR)/%.o: %.cpp 62 | @mkdir -p $(OBJDIR) 63 | avr-g++ $(CPPFLAGS) -c $< -o $@ 64 | 65 | $(OBJDIR)/%.o: $(ARDUINO_CORE_PATH)/%.cpp 66 | @mkdir -p $(OBJDIR) 67 | avr-g++ $(CPPFLAGS) -c $< -o $(OBJDIR)/$(notdir $@) 68 | 69 | $(OBJDIR)/%.o: $(ARDUINO_CORE_PATH)/%.c 70 | @mkdir -p $(OBJDIR) 71 | avr-gcc $(CFLAGS) -c $< -o $(OBJDIR)/$(notdir $@) 72 | 73 | $(OBJDIR)/%.o: $(ARDUINO_CORE_PATH)/wiring_pulse.S 74 | @mkdir -p $(OBJDIR) 75 | avr-gcc $(CFLAGS) -x assembler-with-cpp -c $< -o $(OBJDIR)/wiring_pulse_asm.o 76 | 77 | main.elf: $(OBJS) 78 | avr-gcc $(LINK_FLAGS) -o $@ $^ 79 | avr-size --mcu=$(AVR_TYPE) -C main.elf 80 | 81 | # save for later (use this instead?) 82 | # @avr-objcopy -j .text -j .data -O ihex $^ $@ 83 | main.hex: main.elf 84 | @avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings \ 85 | --change-section-lma .eeprom=0 main.elf main.eep 86 | @avr-objcopy -O ihex -R .eeprom main.elf main.hex 87 | 88 | flash: main.hex 89 | avrdude -v -p $(AVR_TYPE) -c $(PROGRAM_TYPE) -P $(PROGRAM_DEV) -b $(BAUD) -D -U flash:w:$<:i 90 | 91 | clean: 92 | rm -rf main.hex main.elf main.eep $(OBJDIR) 93 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/ModSoftwareSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | NOTE: Modified version of SoftwareSerial.cpp (formerly NewSoftSerial.cpp) 3 | NOTE: Modifications marked with NON_STANDARD (if you want to port them to a newer version of SoftwareSerial) 4 | 5 | Multi-instance software serial library for Arduino/Wiring 6 | -- Interrupt-driven receive and other improvements by ladyada 7 | (http://ladyada.net) 8 | -- Tuning, circular buffer, derivation from class Print/Stream, 9 | multi-instance support, porting to 8MHz processors, 10 | various optimizations, PROGMEM delay tables, inverse logic and 11 | direct port writing by Mikal Hart (http://www.arduiniana.org) 12 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 13 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 14 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | 30 | The latest version of this library can always be found at 31 | http://arduiniana.org. 32 | */ 33 | 34 | // When set, _DEBUG co-opts pins 11 and 13 for debugging with an 35 | // oscilloscope or logic analyzer. Beware: it also slightly modifies 36 | // the bit times, so don't rely on it too much at high baud rates 37 | #define _DEBUG 0 38 | #define _DEBUG_PIN1 8 39 | #define _DEBUG_PIN2 9 40 | // 41 | // Includes 42 | // 43 | #include 44 | #include 45 | #include 46 | #include "ModSoftwareSerial.h" // our custom version of standard SoftwareSerial 47 | #include 48 | 49 | // 50 | // Statics 51 | // 52 | SoftwareSerial *SoftwareSerial::active_object = 0; 53 | uint8_t SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; 54 | volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; 55 | volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; 56 | 57 | // 58 | // Debugging 59 | // 60 | // This function generates a brief pulse 61 | // for debugging or measuring on an oscilloscope. 62 | #if _DEBUG 63 | inline void DebugPulse(uint8_t pin, uint8_t count) 64 | { 65 | volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); 66 | 67 | uint8_t val = *pport; 68 | while (count--) 69 | { 70 | *pport = val | digitalPinToBitMask(pin); 71 | *pport = val; 72 | } 73 | } 74 | #else 75 | inline void DebugPulse(uint8_t, uint8_t) {} 76 | #endif 77 | 78 | // 79 | // Private methods 80 | // 81 | 82 | /* static */ 83 | inline void SoftwareSerial::tunedDelay(uint16_t delay) { 84 | _delay_loop_2(delay); 85 | } 86 | 87 | // This function sets the current object as the "listening" 88 | // one and returns true if it replaces another 89 | bool SoftwareSerial::listen() 90 | { 91 | if (!_rx_delay_stopbit) 92 | return false; 93 | 94 | if (active_object != this) 95 | { 96 | if (active_object) 97 | active_object->stopListening(); 98 | 99 | _buffer_overflow = false; 100 | _receive_buffer_head = _receive_buffer_tail = 0; 101 | active_object = this; 102 | 103 | setRxIntMsk(true); 104 | return true; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | // Stop listening. Returns true if we were actually listening. 111 | bool SoftwareSerial::stopListening() 112 | { 113 | if (active_object == this) 114 | { 115 | setRxIntMsk(false); 116 | active_object = NULL; 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | // 123 | // The receive routine called by the interrupt handler 124 | // 125 | void SoftwareSerial::recv() 126 | { 127 | 128 | #if GCC_VERSION < 40302 129 | // Work-around for avr-gcc 4.3.0 OSX version bug 130 | // Preserve the registers that the compiler misses 131 | // (courtesy of Arduino forum user *etracer*) 132 | asm volatile( 133 | "push r18 \n\t" 134 | "push r19 \n\t" 135 | "push r20 \n\t" 136 | "push r21 \n\t" 137 | "push r22 \n\t" 138 | "push r23 \n\t" 139 | "push r26 \n\t" 140 | "push r27 \n\t" 141 | ::); 142 | #endif 143 | 144 | uint8_t d = 0; 145 | 146 | // If RX line is high, then we don't see any start bit 147 | // so interrupt is probably not for us 148 | if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) 149 | { 150 | // Disable further interrupts during reception, this prevents 151 | // triggering another interrupt directly after we return, which can 152 | // cause problems at higher baudrates. 153 | setRxIntMsk(false); 154 | 155 | // Wait approximately 1/2 of a bit width to "center" the sample 156 | tunedDelay(_rx_delay_centering); 157 | DebugPulse(_DEBUG_PIN2, 1); 158 | 159 | // Read each of the 8 bits 160 | for (uint8_t i=8; i > 0; --i) 161 | { 162 | tunedDelay(_rx_delay_intrabit); 163 | d >>= 1; 164 | DebugPulse(_DEBUG_PIN2, 1); 165 | if (rx_pin_read()) 166 | d |= 0x80; 167 | } 168 | 169 | if (_inverse_logic) 170 | d = ~d; 171 | 172 | // if buffer full, set the overflow flag and return 173 | uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; 174 | if (next != _receive_buffer_head) 175 | { 176 | // save new data in buffer: tail points to where byte goes 177 | _receive_buffer[_receive_buffer_tail] = d; // save new byte 178 | _receive_buffer_tail = next; 179 | } 180 | else 181 | { 182 | DebugPulse(_DEBUG_PIN1, 1); 183 | _buffer_overflow = true; 184 | } 185 | 186 | // NON_STANDARD - support parity bit 187 | if (_parity) 188 | { 189 | tunedDelay(_rx_delay_stopbit); 190 | } 191 | 192 | // skip the stop bit 193 | tunedDelay(_rx_delay_stopbit); 194 | DebugPulse(_DEBUG_PIN1, 1); 195 | 196 | // Re-enable interrupts when we're sure to be inside the stop bit 197 | setRxIntMsk(true); 198 | 199 | } 200 | 201 | #if GCC_VERSION < 40302 202 | // Work-around for avr-gcc 4.3.0 OSX version bug 203 | // Restore the registers that the compiler misses 204 | asm volatile( 205 | "pop r27 \n\t" 206 | "pop r26 \n\t" 207 | "pop r23 \n\t" 208 | "pop r22 \n\t" 209 | "pop r21 \n\t" 210 | "pop r20 \n\t" 211 | "pop r19 \n\t" 212 | "pop r18 \n\t" 213 | ::); 214 | #endif 215 | } 216 | 217 | uint8_t SoftwareSerial::rx_pin_read() 218 | { 219 | return *_receivePortRegister & _receiveBitMask; 220 | } 221 | 222 | // NON_STANDARD - don't use standard interrupt handling, handle it outside of class 223 | #if 0 224 | // 225 | // Interrupt handling 226 | // 227 | 228 | /* static */ 229 | inline void SoftwareSerial::handle_interrupt() 230 | { 231 | if (active_object) 232 | { 233 | active_object->recv(); 234 | } 235 | } 236 | 237 | #if defined(PCINT0_vect) 238 | ISR(PCINT0_vect) 239 | { 240 | SoftwareSerial::handle_interrupt(); 241 | } 242 | #endif 243 | 244 | #if defined(PCINT1_vect) 245 | ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect)); 246 | #endif 247 | 248 | #if defined(PCINT2_vect) 249 | ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect)); 250 | #endif 251 | 252 | #if defined(PCINT3_vect) 253 | ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect)); 254 | #endif 255 | 256 | #endif // end of NON_STANDARD section 257 | 258 | // 259 | // Constructor 260 | // 261 | SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : 262 | _rx_delay_centering(0), 263 | _rx_delay_intrabit(0), 264 | _rx_delay_stopbit(0), 265 | _tx_delay(0), 266 | _buffer_overflow(false), 267 | _inverse_logic(inverse_logic), 268 | _parity(false) // NON_STANDARD - init parity mode to false 269 | { 270 | setTX(transmitPin); 271 | setRX(receivePin); 272 | } 273 | 274 | // 275 | // Destructor 276 | // 277 | SoftwareSerial::~SoftwareSerial() 278 | { 279 | end(); 280 | } 281 | 282 | void SoftwareSerial::setTX(uint8_t tx) 283 | { 284 | // First write, then set output. If we do this the other way around, 285 | // the pin would be output low for a short while before switching to 286 | // output high. Now, it is input with pullup for a short while, which 287 | // is fine. With inverse logic, either order is fine. 288 | digitalWrite(tx, _inverse_logic ? LOW : HIGH); 289 | pinMode(tx, OUTPUT); 290 | _transmitBitMask = digitalPinToBitMask(tx); 291 | uint8_t port = digitalPinToPort(tx); 292 | _transmitPortRegister = portOutputRegister(port); 293 | } 294 | 295 | void SoftwareSerial::setRX(uint8_t rx) 296 | { 297 | pinMode(rx, INPUT); 298 | if (!_inverse_logic) 299 | digitalWrite(rx, HIGH); // pullup for normal logic! 300 | _receivePin = rx; 301 | _receiveBitMask = digitalPinToBitMask(rx); 302 | uint8_t port = digitalPinToPort(rx); 303 | _receivePortRegister = portInputRegister(port); 304 | } 305 | 306 | uint16_t SoftwareSerial::subtract_cap(uint16_t num, uint16_t sub) { 307 | if (num > sub) 308 | return num - sub; 309 | else 310 | return 1; 311 | } 312 | 313 | // 314 | // Public methods 315 | // 316 | 317 | // NON_STANDARD (allow enabling/disabling even parity) 318 | void SoftwareSerial::setParity(bool parity) 319 | { 320 | _parity = parity; 321 | } 322 | 323 | void SoftwareSerial::begin(long speed) 324 | { 325 | _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; 326 | 327 | // Precalculate the various delays, in number of 4-cycle delays 328 | uint16_t bit_delay = (F_CPU / speed) / 4; 329 | 330 | // 12 (gcc 4.8.2) or 13 (gcc 4.3.2) cycles from start bit to first bit, 331 | // 15 (gcc 4.8.2) or 16 (gcc 4.3.2) cycles between bits, 332 | // 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit 333 | // These are all close enough to just use 15 cycles, since the inter-bit 334 | // timings are the most critical (deviations stack 8 times) 335 | _tx_delay = subtract_cap(bit_delay, 15 / 4); 336 | 337 | // Only setup rx when we have a valid PCINT for this pin 338 | if (digitalPinToPCICR(_receivePin)) { 339 | #if GCC_VERSION > 40800 340 | // Timings counted from gcc 4.8.2 output. This works up to 115200 on 341 | // 16Mhz and 57600 on 8Mhz. 342 | // 343 | // When the start bit occurs, there are 3 or 4 cycles before the 344 | // interrupt flag is set, 4 cycles before the PC is set to the right 345 | // interrupt vector address and the old PC is pushed on the stack, 346 | // and then 75 cycles of instructions (including the RJMP in the 347 | // ISR vector table) until the first delay. After the delay, there 348 | // are 17 more cycles until the pin value is read (excluding the 349 | // delay in the loop). 350 | // We want to have a total delay of 1.5 bit time. Inside the loop, 351 | // we already wait for 1 bit time - 23 cycles, so here we wait for 352 | // 0.5 bit time - (71 + 18 - 22) cycles. 353 | _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23) / 4); 354 | 355 | // There are 23 cycles in each loop iteration (excluding the delay) 356 | _rx_delay_intrabit = subtract_cap(bit_delay, 23 / 4); 357 | 358 | // There are 37 cycles from the last bit read to the start of 359 | // stopbit delay and 11 cycles from the delay until the interrupt 360 | // mask is enabled again (which _must_ happen during the stopbit). 361 | // This delay aims at 3/4 of a bit time, meaning the end of the 362 | // delay will be at 1/4th of the stopbit. This allows some extra 363 | // time for ISR cleanup, which makes 115200 baud at 16Mhz work more 364 | // reliably 365 | _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11) / 4); 366 | #else // Timings counted from gcc 4.3.2 output 367 | // Note that this code is a _lot_ slower, mostly due to bad register 368 | // allocation choices of gcc. This works up to 57600 on 16Mhz and 369 | // 38400 on 8Mhz. 370 | _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11) / 4); 371 | _rx_delay_intrabit = subtract_cap(bit_delay, 11 / 4); 372 | _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17) / 4); 373 | #endif 374 | 375 | 376 | // Enable the PCINT for the entire port here, but never disable it 377 | // (others might also need it, so we disable the interrupt by using 378 | // the per-pin PCMSK register). 379 | *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin)); 380 | // Precalculate the pcint mask register and value, so setRxIntMask 381 | // can be used inside the ISR without costing too much time. 382 | _pcint_maskreg = digitalPinToPCMSK(_receivePin); 383 | _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin)); 384 | 385 | tunedDelay(_tx_delay); // if we were low this establishes the end 386 | } 387 | 388 | #if _DEBUG 389 | pinMode(_DEBUG_PIN1, OUTPUT); 390 | pinMode(_DEBUG_PIN2, OUTPUT); 391 | #endif 392 | 393 | listen(); 394 | } 395 | 396 | void SoftwareSerial::setRxIntMsk(bool enable) 397 | { 398 | if (enable) 399 | *_pcint_maskreg |= _pcint_maskvalue; 400 | else 401 | *_pcint_maskreg &= ~_pcint_maskvalue; 402 | } 403 | 404 | void SoftwareSerial::end() 405 | { 406 | stopListening(); 407 | } 408 | 409 | 410 | // Read data from buffer 411 | int SoftwareSerial::read() 412 | { 413 | if (!isListening()) 414 | return -1; 415 | 416 | // Empty buffer? 417 | if (_receive_buffer_head == _receive_buffer_tail) 418 | return -1; 419 | 420 | // Read from "head" 421 | uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte 422 | _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; 423 | return d; 424 | } 425 | 426 | int SoftwareSerial::available() 427 | { 428 | if (!isListening()) 429 | return 0; 430 | 431 | return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF; 432 | } 433 | 434 | size_t SoftwareSerial::write(uint8_t b) 435 | { 436 | if (_tx_delay == 0) { 437 | setWriteError(); 438 | return 0; 439 | } 440 | 441 | // By declaring these as local variables, the compiler will put them 442 | // in registers _before_ disabling interrupts and entering the 443 | // critical timing sections below, which makes it a lot easier to 444 | // verify the cycle timings 445 | volatile uint8_t *reg = _transmitPortRegister; 446 | uint8_t reg_mask = _transmitBitMask; 447 | uint8_t inv_mask = ~_transmitBitMask; 448 | uint8_t oldSREG = SREG; 449 | bool inv = _inverse_logic; 450 | uint16_t delay = _tx_delay; 451 | 452 | uint8_t parity = 0; // NON_STANDARD - support even parity 453 | 454 | if (inv) 455 | b = ~b; 456 | 457 | cli(); // turn off interrupts for a clean txmit 458 | 459 | // Write the start bit 460 | if (inv) 461 | *reg |= reg_mask; 462 | else 463 | *reg &= inv_mask; 464 | 465 | tunedDelay(delay); 466 | 467 | // Write each of the 8 bits 468 | for (uint8_t i = 8; i > 0; --i) 469 | { 470 | if (b & 1) // choose bit 471 | { 472 | *reg |= reg_mask; // send 1 473 | parity ^= 0x01; // NON_STANDARD 474 | } 475 | else 476 | { 477 | *reg &= inv_mask; // send 0 478 | parity ^= 0x00; // NON_STANDARD 479 | } 480 | tunedDelay(delay); 481 | b >>= 1; 482 | } 483 | 484 | // NON_STANDARD - write even parity stop bit 485 | if (_parity) 486 | { 487 | if (parity == 0) // even number of 1s 488 | tx_pin_write(inv ? HIGH : LOW); // send 0 489 | else // odd number of 1s 490 | tx_pin_write(inv ? LOW : HIGH); // send 1 491 | tunedDelay(delay); 492 | } 493 | 494 | // restore pin to natural state 495 | if (inv) 496 | *reg &= inv_mask; 497 | else 498 | *reg |= reg_mask; 499 | 500 | SREG = oldSREG; // turn interrupts back on 501 | tunedDelay(_tx_delay); 502 | 503 | return 1; 504 | } 505 | 506 | void SoftwareSerial::flush() 507 | { 508 | // There is no tx buffering, simply return 509 | } 510 | 511 | int SoftwareSerial::peek() 512 | { 513 | if (!isListening()) 514 | return -1; 515 | 516 | // Empty buffer? 517 | if (_receive_buffer_head == _receive_buffer_tail) 518 | return -1; 519 | 520 | // Read from "head" 521 | return _receive_buffer[_receive_buffer_head]; 522 | } 523 | 524 | // NON_STANDARD 525 | void SoftwareSerial::tx_pin_write(uint8_t pin_state) 526 | { 527 | if (pin_state == LOW) 528 | *_transmitPortRegister &= ~_transmitBitMask; 529 | else 530 | *_transmitPortRegister |= _transmitBitMask; 531 | } 532 | 533 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/ModSoftwareSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | NOTE: Modified version of SoftwareSerial.h (formerly NewSoftSerial.h) 3 | NOTE: Modifications marked with NON_STANDARD (if you want to port them to a newer version of SoftwareSerial) 4 | 5 | Multi-instance software serial library for Arduino/Wiring 6 | -- Interrupt-driven receive and other improvements by ladyada 7 | (http://ladyada.net) 8 | -- Tuning, circular buffer, derivation from class Print/Stream, 9 | multi-instance support, porting to 8MHz processors, 10 | various optimizations, PROGMEM delay tables, inverse logic and 11 | direct port writing by Mikal Hart (http://www.arduiniana.org) 12 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 13 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 14 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | 30 | The latest version of this library can always be found at 31 | http://arduiniana.org. 32 | */ 33 | 34 | #ifndef ModSoftwareSerial_h 35 | #define ModSoftwareSerial_h 36 | 37 | #include 38 | #include 39 | 40 | /****************************************************************************** 41 | * Definitions 42 | ******************************************************************************/ 43 | 44 | #ifndef _SS_MAX_RX_BUFF 45 | #define _SS_MAX_RX_BUFF 64 // RX buffer size 46 | #endif 47 | 48 | #ifndef GCC_VERSION 49 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 50 | #endif 51 | 52 | class SoftwareSerial : public Stream 53 | { 54 | protected: // NON_STANDARD - changed from private to protected 55 | // per object data 56 | uint8_t _receivePin; 57 | uint8_t _receiveBitMask; 58 | volatile uint8_t *_receivePortRegister; 59 | uint8_t _transmitBitMask; 60 | volatile uint8_t *_transmitPortRegister; 61 | volatile uint8_t *_pcint_maskreg; 62 | uint8_t _pcint_maskvalue; 63 | 64 | // Expressed as 4-cycle delays (must never be 0!) 65 | uint16_t _rx_delay_centering; 66 | uint16_t _rx_delay_intrabit; 67 | uint16_t _rx_delay_stopbit; 68 | uint16_t _tx_delay; 69 | 70 | uint16_t _buffer_overflow:1; 71 | uint16_t _inverse_logic:1; 72 | uint16_t _parity:1; // NON_STANDARD 73 | 74 | // static data 75 | static uint8_t _receive_buffer[_SS_MAX_RX_BUFF]; 76 | static volatile uint8_t _receive_buffer_tail; 77 | static volatile uint8_t _receive_buffer_head; 78 | static SoftwareSerial *active_object; 79 | 80 | // private methods 81 | void setTX(uint8_t transmitPin); 82 | void setRX(uint8_t receivePin); 83 | inline void setRxIntMsk(bool enable) __attribute__((__always_inline__)); 84 | 85 | // Return num - sub, or 1 if the result would be < 1 86 | static uint16_t subtract_cap(uint16_t num, uint16_t sub); 87 | 88 | // private static method for timing 89 | static inline void tunedDelay(uint16_t delay); 90 | 91 | public: 92 | // public methods 93 | SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); 94 | ~SoftwareSerial(); 95 | void begin(long speed); 96 | bool listen(); 97 | void end(); 98 | bool isListening() { return this == active_object; } 99 | bool stopListening(); 100 | bool overflow() { bool ret = _buffer_overflow; if (ret) _buffer_overflow = false; return ret; } 101 | int peek(); 102 | 103 | // NON_STANDARD - move from private to public section 104 | void recv(); 105 | uint8_t rx_pin_read(); 106 | void tx_pin_write(uint8_t pin_state); 107 | // end NON_STANDARD 108 | 109 | virtual size_t write(uint8_t byte); 110 | virtual int read(); 111 | virtual int available(); 112 | virtual void flush(); 113 | virtual void setParity(bool parity=false); // NON_STANDARD 114 | operator bool() { return true; } 115 | 116 | using Print::write; 117 | }; 118 | 119 | // Arduino 0012 workaround 120 | #undef int 121 | #undef char 122 | #undef long 123 | #undef byte 124 | #undef float 125 | #undef abs 126 | #undef round 127 | 128 | #endif // ModSoftwareSerial_h 129 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/PiSerial.cpp: -------------------------------------------------------------------------------- 1 | // file PiSerial.cpp - Serial class for handling serial com with Raspberry PI 2 | 3 | #include "PiSerial.h" 4 | 5 | void PiSerial::clearCmd(void) 6 | { 7 | cmdRecvd = false; 8 | bufIdx = 0; 9 | msgBuf[0] = '\0'; 10 | } 11 | 12 | void PiSerial::init(void) 13 | { 14 | // init USB serial connection to Raspberry PI 15 | Serial.begin(PI_SERIAL_BAUD); 16 | sprintf(msgBuf, "\nUSB2keybus initialized, USB rx buf size %d\n", SERIAL_RX_BUFFER_SIZE); 17 | Serial.println(msgBuf); 18 | clearCmd(); 19 | } 20 | 21 | void PiSerial::write(const char * buf) 22 | { 23 | Serial.print(buf); 24 | } 25 | 26 | // read from the RPI serial port. Returns: true if complete command recvd 27 | bool PiSerial::read(void) 28 | { 29 | while (!cmdRecvd && Serial.available() && bufIdx < PI_SERIAL_MSG_BUF_SIZE-1) 30 | { 31 | char c = Serial.read(); 32 | if (c == '\n' || c == '\r') // strip either type of line termination 33 | { 34 | if (bufIdx > 0) // don't create zero length commands 35 | { 36 | if (msgBuf[0] == 'F') // commands always start with 'F' 37 | { 38 | cmdRecvd = true; 39 | } 40 | else 41 | { 42 | Serial.println("ERR_FMT: garbled command: "); 43 | Serial.println(msgBuf); 44 | Serial.println("\n"); 45 | clearCmd(); 46 | } 47 | } 48 | } 49 | else 50 | { 51 | msgBuf[bufIdx++] = c; 52 | } 53 | } 54 | 55 | if (bufIdx >= PI_SERIAL_MSG_BUF_SIZE-1) 56 | { 57 | Serial.println("ERR_OFL: buf overflow\n"); 58 | clearCmd(); 59 | } 60 | else 61 | { 62 | msgBuf[bufIdx] = '\0'; // keep current buffer null terminated 63 | } 64 | return cmdRecvd; 65 | } 66 | 67 | const char * PiSerial::getMsg(uint8_t * size) 68 | { 69 | *size = cmdRecvd ? bufIdx : 0; 70 | return (const char *)msgBuf; 71 | } 72 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/PiSerial.h: -------------------------------------------------------------------------------- 1 | // file PiSerial.h - Serial class for handling serial com with Raspberry PI 2 | 3 | #pragma once 4 | 5 | #define SERIAL_RX_BUFFER_SIZE 256 // increase default size for arduino serial recv buffer 6 | 7 | #include 8 | 9 | #define PI_SERIAL_BAUD 115200 // baud rate for USB serial port 10 | 11 | static const uint8_t PI_SERIAL_MSG_BUF_SIZE = 128; // max length of recv'd msg 12 | 13 | class PiSerial 14 | { 15 | public: 16 | PiSerial(void) {} // Class constructor. Returns: none 17 | 18 | void init(void); // init the PiSerial class 19 | bool read(void); // poll the Pi for serial input 20 | void write(const char * buf); // write buf to serial out 21 | void clearCmd(void); // clear the current command buf 22 | const char * getMsg(uint8_t * size); // get serial message (if any) 23 | 24 | private: 25 | char msgBuf[PI_SERIAL_MSG_BUF_SIZE]; 26 | 27 | uint8_t bufIdx; 28 | bool cmdRecvd; 29 | }; 30 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/USB2keybus.ino: -------------------------------------------------------------------------------- 1 | // USB2keybus.ino - implement adapter between alarm processor serial port and keybus protocol keypad 2 | 3 | #include "PiSerial.h" // declare first to define SERIAL_RX_BUFFER_SIZE 4 | #include 5 | #include "KeypadSerial.h" 6 | #include "USBprotocol.h" 7 | #include "Volts.h" 8 | 9 | #define PRINT_BUF_SIZE (128) 10 | static char pBuf[PRINT_BUF_SIZE]; // sprintf buffer 11 | 12 | static const uint32_t KP_POLL_PERIOD = 330; // how often to poll keypad (ms) 13 | static const uint32_t KP_F7_PERIOD = 4000; // how often to send F7 status message (ms) 14 | static const uint32_t VOLT_PERIOD = 5000; // how often to sample the voltage rails (ms) 15 | static const uint32_t MIN_TX_GAP = 50; // allow at least this many ms between transmits to keypads 16 | static const uint32_t READ_KEY_DELAY = 40; // delay between keypad poll response and keypad read 17 | 18 | PiSerial piSerial; // piSerial class 19 | KeypadSerial kpSerial; // keypadSerial class 20 | USBprotocol usbProtocol; // protocol class for converting msgs to/from USB serial 21 | Volts volts; // voltage monitoring class 22 | 23 | uint32_t kpF7time; // global, last time F7 message sent 24 | uint32_t kpPollTime; // global, last time keypad was polled 25 | uint32_t voltTime; // global, last time volt message sent 26 | uint32_t lastSendTime; // global, last time message sent to keypad 27 | 28 | bool keyPadRead; // if true, in keypad read mode 29 | uint8_t keyPad; // next keypad to read 30 | uint8_t numKeyPads; // number of keypads that responded to poll 31 | 32 | // ------------------------------------------ setup ----------------------------------------- 33 | 34 | void setup(void) 35 | { 36 | usbProtocol.init(); // init class 37 | piSerial.init(); // init class 38 | kpSerial.init(); // init class 39 | volts.init(); // init class 40 | 41 | uint32_t ms = millis(); 42 | 43 | kpF7time = ms; 44 | kpPollTime = ms; 45 | voltTime = ms; 46 | lastSendTime = ms; 47 | 48 | keyPadRead = false; 49 | keyPad = 0; 50 | numKeyPads = 0; 51 | } 52 | 53 | // ---------------------------------------- main loop --------------------------------------- 54 | 55 | void loop(void) 56 | { 57 | uint8_t k = 0; 58 | 59 | if (kpSerial.read(&k, 0)) // if we have unhandled chars from keypad, consume them 60 | { 61 | sprintf(pBuf, "WARN: unhandled keypad char %02x\n", k); 62 | piSerial.write(pBuf); 63 | } 64 | 65 | if (piSerial.read()) // read any data available from console serial port 66 | { 67 | uint8_t piMsgSize = 0; 68 | const char * piMsg = piSerial.getMsg(&piMsgSize); 69 | 70 | uint8_t msgType = usbProtocol.parseRecv(piMsg, piMsgSize); 71 | 72 | if (msgType == 0xF7) 73 | { 74 | kpF7time = 0; // always update keypad as soon as new F7 message arrives 75 | } 76 | else if (msgType == 0) 77 | { 78 | // unknown console message 79 | sprintf(pBuf, "ERR_FMT: garble/bad msg format '%s'\n", piMsg); 80 | piSerial.write(pBuf); 81 | } 82 | piSerial.clearCmd(); // mark command as processed 83 | } 84 | 85 | uint32_t ms = millis(); // milliseconds since start of run 86 | 87 | if (keyPadRead) // we are in keypad read mode 88 | { 89 | if (ms - kpPollTime > READ_KEY_DELAY) // after waiting the appropriate time after polling, read the keypad data 90 | { 91 | // read the next keypad, send message to USB serial 92 | kpPollTime = ms; 93 | 94 | uint8_t msgType = kpSerial.requestData(keyPad); 95 | lastSendTime = millis(); 96 | 97 | if (msgType == KEYS_MESG) // if true, key presses were returned for this keypad 98 | { 99 | piSerial.write(usbProtocol.keyMsg(pBuf, PRINT_BUF_SIZE, 100 | kpSerial.getAddr(keyPad), kpSerial.getKeyCount(), kpSerial.getKeys(), msgType)); 101 | } 102 | else if (msgType != NO_MESG) // we received some other type of message 103 | { 104 | piSerial.write(usbProtocol.keyMsg(pBuf, PRINT_BUF_SIZE, 105 | kpSerial.getAddr(keyPad), kpSerial.getRecvMsgLen(), kpSerial.getRecvMsg(), msgType)); 106 | } 107 | 108 | if (++keyPad >= numKeyPads) // this was the last keypad with data 109 | { 110 | keyPad = numKeyPads = 0; 111 | keyPadRead = false; // end keypad read mode 112 | } 113 | else 114 | { 115 | // still in keyPadRead mode 116 | } 117 | } 118 | } 119 | else // not in a keypad read cycle, check if time to poll keypad, send F7 msg or send volt msg 120 | { 121 | if (ms - lastSendTime > MIN_TX_GAP) // min time gap between any type of msg pushed to keypads 122 | { 123 | // there is an implied priority scheme here. Once the min time between transmits 124 | // expires, look for the next thing to do in this order: 125 | // 1. push out a recv'd F7 msg 126 | // 2. poll the keypad (so key presses are responsive) 127 | // 3. push out a periodic F7 msg (no new F7 msg from RPi, just time to send one) 128 | // 4. sample the system voltage levels 129 | 130 | if (kpF7time == 0) // just received an F7 message from RPi, push it out 131 | { 132 | kpF7time = ms; 133 | kpSerial.write(usbProtocol.getF7(), usbProtocol.getF7size()); 134 | lastSendTime = millis(); 135 | } 136 | else if (ms - kpPollTime > KP_POLL_PERIOD) // time to poll keypad 137 | { 138 | kpPollTime = ms; 139 | if (kpSerial.poll()) 140 | { 141 | keyPadRead = true; 142 | keyPad = 0; // start with first keypad that responded 143 | numKeyPads = kpSerial.getNumKeypads(); 144 | } 145 | lastSendTime = millis(); 146 | } 147 | else if (ms - kpF7time > KP_F7_PERIOD) // time to send periodic F7 msg 148 | { 149 | kpF7time = ms; 150 | kpSerial.write(usbProtocol.getF7(), usbProtocol.getF7size()); 151 | lastSendTime = millis(); 152 | } 153 | else if (ms - voltTime > VOLT_PERIOD) // if time to sample voltage rails 154 | { 155 | voltTime = ms; 156 | #if 0 157 | volts.read(); 158 | volts.getMsg(pBuf, PRINT_BUF_SIZE); // generate volts msg 159 | piSerial.write(pBuf); 160 | #endif 161 | lastSendTime = millis(); 162 | } 163 | } 164 | } 165 | } 166 | 167 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/USBprotocol.cpp: -------------------------------------------------------------------------------- 1 | // file USBprotocol.cpp - methods for converting data into plain text strings transferred over USB serial 2 | 3 | // if you want to change the format of the messages exchanged with your CPU via the USB serial port, update 4 | // this class 5 | 6 | #include "USBprotocol.h" 7 | #include "KeypadSerial.h" 8 | 9 | // when arduino code inits, use these initial keypad values 10 | #define INIT_MSG "F7 z=00 t=0 c=1 r=1 a=1 s=0 p=0 b=1 1=Arduino Init 2=Completed v1.01" 11 | 12 | // macros to determine if command starts with 'F7' or 'F7A' 13 | #define F7_MSG_ALT(s) (*((s)+0) == 'F' && *((s)+1) == '7' && *((s)+2) == 'A') 14 | #define F7_MSG(s) (*((s)+0) == 'F' && *((s)+1) == '7') 15 | 16 | // init class 17 | void USBprotocol::init(void) 18 | { 19 | count = 0; 20 | altMsgActive = false; 21 | 22 | // init F7 message structs 23 | initF7(&msgF7[0]); 24 | initF7(&msgF7[1]); 25 | 26 | parseRecv(INIT_MSG, strlen(INIT_MSG)); 27 | } 28 | 29 | // initialize F7 message struct 30 | void USBprotocol::initF7(t_MesgF7 * pMsgF7) 31 | { 32 | memset((void *)pMsgF7, 0, sizeof(t_MesgF7)); 33 | 34 | // constant values 35 | pMsgF7->type = 0xF7; 36 | pMsgF7->keypads = 0xFF; // send to all keypads 37 | pMsgF7->addr4 = 0x00; // unknown value, my alarm panel is observed to send 0x10, but zero works 38 | pMsgF7->prog = 0x00; // programming mode (not used) 39 | pMsgF7->zone = 0xFC; // my keypad may need this to prevent fast beep problem? 40 | } 41 | 42 | // parse received command string 43 | uint8_t USBprotocol::parseRecv(const char * msg, const uint8_t len) 44 | { 45 | if (len > 4 && F7_MSG_ALT(msg)) // an alt F7 command only updates the secondary F7 message 46 | { 47 | altMsgActive = true; 48 | return parseF7(msg+4, len-4, &msgF7[1]); // parse the command after 'F7A ' 49 | } 50 | else if (len > 4 && F7_MSG(msg)) // a primary F7 command sets altMsg false, so send F7A msg second if needed 51 | { 52 | count = 0; // zero count so primary F7 msg is the next one displayed 53 | altMsgActive = false; 54 | return parseF7(msg+3, len-3, &msgF7[0]); // parse the command after 'F7 ' 55 | } 56 | return 0x0; // received unknown command 57 | } 58 | 59 | // generate message from data received from keypad 60 | const char * USBprotocol::keyMsg(char * buf, uint8_t bufLen, uint8_t addr, uint8_t len, uint8_t * pData, uint8_t type) 61 | { 62 | // format of message is KEYS_XX[N] key0 key1 ... keyN-1, where XX is keypad number, N is key count 63 | // or UNK__XX[N] byte0 byte1 .. byteN-1 for unknown message from keypad XX with N bytes 64 | 65 | uint8_t idx = 0; 66 | idx += sprintf(buf+idx, "%s_%2d[%02d] ", type == KEYS_MESG ? "KEYS" : "UNK_", addr, len); 67 | for (uint8_t i=0; i < len && bufLen - idx > 6; i++) 68 | { 69 | idx += sprintf(buf+idx, "0x%02x ", *(pData+i)); 70 | } 71 | sprintf(buf+idx-1, "\n"); 72 | return (const char *)buf; 73 | } 74 | 75 | // parse F7 command, form is F7[A] z=FC t=0 c=1 r=0 a=0 s=0 p=1 b=1 1=1234567890123456 2=ABCDEFGHIJKLMNOP 76 | // z - zone (byte arg) 77 | // t - tone (nibble arg) 78 | // c - chime (bool arg) 79 | // r - ready (bool arg) 80 | // a - arm-away (bool arg) 81 | // s - arm-stay (bool arg) 82 | // p - power-on (bool arg) 83 | // b - lcd-backlight-on (bool arg) 84 | // 1 - line1 text (16-chars) 85 | // 2 - line2 text (16-chars) 86 | 87 | // BYTE1 tone notes 88 | // 00-03 - low two bits define chime count for each F7 msg (0 none, 1,2,3 chime count per msg) 89 | // 04 - fast pulsing tone (like there is an error, or timeout almost done) 90 | // 05-06 - slow pulsing tone (like when alarm is in arm-delay and it is time to leave) 91 | // 07 - continous tone (not pulsing) 92 | // bits above bottom 3 don't do anything, 0x40 bit causes incompat. con. error 93 | 94 | // BYTE2 notes: bit(0x80) 1 -> ARMED-STAY, bit(0x10) 1 -> READY (1 when ok, 0 when exit delay) 95 | // BYTE3 notes: bit(0x20) 1 -> chime on, bit(0x08) 1 -> ac power ok, bit(0x04) 1 -> ARMED_AWAY 96 | 97 | uint8_t USBprotocol::parseF7(const char * msg, uint8_t len, t_MesgF7 * pMsgF7) 98 | { 99 | bool success = true; 100 | bool lcd_backlight = false; 101 | 102 | t_MesgF7 newF7; 103 | t_MesgF7 * pNewF7 = &newF7; 104 | memcpy(pNewF7, pMsgF7, sizeof(t_MesgF7)); // copy existing F7 mesg struct 105 | 106 | for (uint8_t i=0; i < len && *(msg+i) != '\0'; i++) // msg pointer starts after 'F7 ' or 'F7A ' 107 | { 108 | if (*(msg+i) != ' ') // skip over spaces 109 | { 110 | char parm = *(msg+i); 111 | i += 2; // move past parm and '=', msg+i now points at arg 112 | 113 | switch (parm) 114 | { 115 | case 'z': 116 | pNewF7->zone = GET_BYTE(*(msg+i), *(msg+i+1)); i += 2; 117 | break; 118 | case 't': 119 | pNewF7->byte1 = GET_NIBBLE(*(msg+i)); i++; 120 | break; 121 | case 'c': 122 | pNewF7->byte3 = SET_CHIME(pNewF7->byte3, GET_BOOL(*(msg+i))); i++; 123 | break; 124 | case 'r': 125 | pNewF7->byte2 = SET_READY(pNewF7->byte2, GET_BOOL(*(msg+i))); i++; 126 | break; 127 | case 'a': 128 | pNewF7->byte3 = SET_ARMED_AWAY(pNewF7->byte3, GET_BOOL(*(msg+i))); i++; 129 | break; 130 | case 's': 131 | pNewF7->byte2 = SET_ARMED_STAY(pNewF7->byte2, GET_BOOL(*(msg+i))); i++; 132 | break; 133 | case 'p': 134 | pNewF7->byte3 = SET_POWER(pNewF7->byte3, GET_BOOL(*(msg+i))); i++; 135 | break; 136 | case 'b': 137 | lcd_backlight = GET_BOOL(*(msg+i)); i++; 138 | break; 139 | case '1': // line1 arg must occur after 'b' parameter for this code to work 140 | memset(pNewF7->line1, 0, LCD_LINE_LEN); 141 | for (uint8_t j=0; j < LCD_LINE_LEN && i < len; j++) 142 | { 143 | pNewF7->line1[j] = *(msg+i) & 0x7f; 144 | i++; 145 | } 146 | pNewF7->line1[0] |= lcd_backlight ? 0x80 : 0x00; // or in backlight bit 147 | break; 148 | case '2': 149 | memset(pNewF7->line2, 0, LCD_LINE_LEN); 150 | for (uint8_t j=0; j < LCD_LINE_LEN && i < len; j++) 151 | { 152 | pNewF7->line2[j] = *(msg+i) & 0x7f; 153 | i++; 154 | } 155 | break; 156 | default: 157 | success = false; 158 | break; 159 | } 160 | } 161 | } 162 | 163 | if (success) 164 | { 165 | memcpy(pMsgF7, pNewF7, sizeof(t_MesgF7)); // replace existing F7 mesg with updated version 166 | 167 | pMsgF7->chksum = 0; 168 | 169 | for (uint8_t i=0; i < 44; i++) 170 | { 171 | pMsgF7->chksum += *(((uint8_t *)pMsgF7) + i); 172 | } 173 | 174 | pMsgF7->chksum = 0x100 - pMsgF7->chksum; // two's compliment 175 | 176 | return 0xF7; 177 | } 178 | return 0; // failed to parse message 179 | } 180 | 181 | // returned mesg always alternates between 2 stored messages (which may be the same) 182 | const uint8_t * USBprotocol::getF7(void) 183 | { 184 | if (altMsgActive) 185 | return (const uint8_t *)&(msgF7[count++ & 0x1]); 186 | else 187 | return (const uint8_t *)&(msgF7[0]); 188 | } 189 | 190 | 191 | #if 0 192 | // debug: print message struct into buf (broken, needs to be updated) 193 | const char * USBprotocol::printF7(char * buf) 194 | { 195 | uint8_t idx = 0; 196 | idx += sprintf(buf+idx, "%02x msg -> kp[%02x]\n", pMsgF7->type, pMsgF7->keypads); 197 | idx += sprintf(buf+idx, " zone=%02x, tone=%1x, chime=%c, power=%c\n", pMsgF7->zone, pMsgF7->byte1, 198 | GET_CHIME(pMsgF7->byte3) ? '1' : '0', GET_POWER(pMsgF7->byte3) ? '1' : '0'); 199 | idx += sprintf(buf+idx, " ready=%c, armed-away=%c, armed-stay=%c\n", GET_READY(pMsgF7->byte2) ? '1' : '0', 200 | GET_ARMED_AWAY(pMsgF7->byte3) ? '1' : '0', GET_ARMED_STAY(pMsgF7->byte2) ? '1' : '0'); 201 | idx += sprintf(buf+idx, " line1='"); 202 | for (uint8_t i=0; i < 16; i++) 203 | *(buf+idx+i) = *(pMsgF7->line1+i) & 0x7F; 204 | idx += 16; 205 | idx += sprintf(buf+idx, "'\n"); 206 | idx += sprintf(buf+idx, " line2='"); 207 | for (uint8_t i=0; i < 16; i++) 208 | *(buf+idx+i) = *(pMsgF7->line2+i) & 0x7F; 209 | idx += 16; 210 | idx += sprintf(buf+idx, "'\n"); 211 | idx += sprintf(buf+idx, "checksum %02x\n", pMsgF7->chksum); 212 | return (const char *)buf; 213 | } 214 | #endif 215 | 216 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/USBprotocol.h: -------------------------------------------------------------------------------- 1 | // file USBprotocol.h - class describing serial protocol over USB 2 | 3 | // if you want to change the format of the messages exchanged with your CPU via the USB serial port, update this class 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "F7msg.h" 9 | 10 | class USBprotocol 11 | { 12 | public: 13 | USBprotocol(void) {} // Class constructor. Returns: none 14 | 15 | void init(void); // init the class 16 | 17 | const char * keyMsg(char * buf, uint8_t bufLen, uint8_t addr, uint8_t len, uint8_t * pData, uint8_t type); 18 | uint8_t parseRecv(const char * msg, const uint8_t len); 19 | //const char * printF7(char * buf); 20 | 21 | const uint8_t * getF7(void); 22 | const uint8_t getF7size(void) { return (const uint8_t)F7_MSG_SIZE; } 23 | 24 | private: 25 | bool altMsgActive; // if true, altenate between primary and alternate messages 26 | uint8_t count; // incremented each time getF7 is called 27 | t_MesgF7 msgF7[2]; // 2 F7 mesgs, primary and alternate 28 | 29 | void initF7(t_MesgF7 * pMsgF7); 30 | uint8_t parseF7(const char * msg, uint8_t len, t_MesgF7 * pMsgF7); 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/Volts.cpp: -------------------------------------------------------------------------------- 1 | // file Volts.cpp - class for handling reading and outputing project voltages 2 | 3 | #include "Volts.h" 4 | 5 | // init 6 | void Volts::init(void) 7 | { 8 | for (uint8_t i=0; i < NUM_VOLTS; i++) 9 | { 10 | rail[i] = 0; 11 | } 12 | } 13 | 14 | // read the voltage rails. Returns: none 15 | void Volts::read(void) 16 | { 17 | int32_t v; 18 | 19 | for (uint8_t i=0; i < NUM_VOLTS; i++) 20 | { 21 | v = analogRead(pin[i]) * scale[i]; 22 | rail[i] = (v >> 10) & 0x0FFFF; // rail value is in 100ths of volts 23 | } 24 | } 25 | 26 | // generate voltages mesg in text string for sending to USB serial 27 | // FIXME - part of this function should be moved to the USBprotocol class 28 | void Volts::getMsg(char * buf, uint8_t bufLen) 29 | { 30 | uint8_t idx = 0; 31 | idx += sprintf(buf+idx, "VOLTS[%02d] ", 3); 32 | for (uint8_t i=0; i < NUM_VOLTS && bufLen - idx > 6; i++) 33 | { 34 | idx += sprintf(buf+idx, "0x%04x ", rail[i]); 35 | } 36 | sprintf(buf+idx-1, "\n"); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /arduinoProj/USB2keybus/Volts.h: -------------------------------------------------------------------------------- 1 | // file Volts.h - class for handling reading and outputing project voltages 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #define NUM_VOLTS (3) // number of voltages to monitor 8 | 9 | // voltages v0-v2 are passed through a voltage divider with a scaling of 0.175 (47k-10k) 10 | // that results in full scale input of (5/0.175) 28.5v. 11 | // Using a scale factor of 2850/1024 will give correct number of 100ths of volts 12 | 13 | static const uint16_t scale[NUM_VOLTS] = { 2850, 2850, 2850 }; 14 | static const uint8_t pin[NUM_VOLTS] = { A0, A1, A2 }; // analog input pins monitoring rails 15 | 16 | class Volts 17 | { 18 | public: 19 | Volts (void) {} // Class constructor. Returns: none 20 | 21 | void init(void); // init the class 22 | void read(void); // read the voltages 23 | void getMsg(char * buf, uint8_t bufLen); // write the voltage message into the provided buf 24 | 25 | private: 26 | uint16_t rail[NUM_VOLTS]; // voltage rails (in 100ths of volts) 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /docs/Alarm_Power_Wiring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/Alarm_Power_Wiring.jpg -------------------------------------------------------------------------------- /docs/Arduino_Keypad_Wiring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/Arduino_Keypad_Wiring.jpg -------------------------------------------------------------------------------- /docs/Keybus Protocol.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/Keybus Protocol.doc -------------------------------------------------------------------------------- /docs/Keybus Protocol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/Keybus Protocol.pdf -------------------------------------------------------------------------------- /docs/Perfboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/Perfboard.jpg -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Notes on Wiring diagrams 2 | 3 | Arduino_Keypad_Wiring.jpg 4 | 5 | This image is very similar to a drawing posted on Mark Kimsal's site. My components are sized for the 6 | 5 volt signaling used by the Arduino Mega. I also added a 1k pull down resistor on the transmit line 7 | to the keypad (my circuit would not work without it). 8 | 9 | Alarm_Power_Wiring.jpg 10 | 11 | This image documents the circuit I am using to power my alarm project. I am using a Dell laptop power 12 | brick (19v output) and a couple of large diodes (to drop the voltage a bit) to drive a picoUPS-100 module. 13 | The picoUPS will trickle charge a connected 12v sealed lead-acid (SLA) battery and continue to provide DC output if 14 | the primary power is removed. The output of the picoUPS drives a DC-DC converter set for 5 volt output. 15 | This will power the Raspberry PI as well as the connected Arduino. As a future mod, I plan to monitor the input and output 16 | power of the picoUPS as well as the battery voltage via analog inputs on the Arduino. This should allow 17 | for a clean shutdown of the Raspberry PI if I detect that the input power is off and the battery voltage 18 | is getting low. 19 | 20 | perfboard.jpg 21 | 22 | This image shows how I arranged the components to build the hat for the Arudino. 23 | 24 | rpi-alarm-1.jpg 25 | 26 | This image shows the 6160 Honeywell keypad connected the the Arduino and RPi running my custom code. 27 | 28 | rpi_alarm-2.jpg 29 | 30 | This image documents the full alarm setup. The RPi is at the top with a GPIO break-out board hat installed. My home's sense loops connect to the RPi GPIO pins via the screw terminals on the break-out board. You will also notice I am using a TP-Link USB wifi adapter with an external antenna. The Arduino 2560 mega is connected vi a short USB cable. The Arduino has a protoboard hat with circuit shown in Arduino_Keypad_Wiring.jpg. The keyboards are connected to this hat via a screw terminal block. The power to the RPi and Arduino is provided by a DC-DC converter with its output adjusted to 5V. The DC-DC converter's input is provided by a picoUPS card which is connected to the input DC adapter and a 12V SLA battery. 31 | 32 | Links to some of parts mentioned above: 33 | - DC-DC converter https://www.amazon.com/gp/product/B009HPB1OI 34 | - picoUPS: https://www.amazon.com/gp/product/B005TWE4GU 35 | - battery: https://www.amazon.com/PowerStar3-Warranty-Security-System-Battery/dp/B00G045G1I 36 | -------------------------------------------------------------------------------- /docs/rpi_alarm-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/rpi_alarm-1.jpg -------------------------------------------------------------------------------- /docs/rpi_alarm-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomVickers/Arduino2keypad/381e4834e17447f62fe5741bd59709589e2c6958/docs/rpi_alarm-2.jpg --------------------------------------------------------------------------------