Guest User

P1632 virtual keyboard

a guest
Jul 1st, 2020
207
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.22 KB | None | 0 0
  1. #include <ESP8266WiFi.h>
  2. #include <ESP8266mDNS.h>
  3. #include <WiFiUdp.h>
  4. #include <ArduinoOTA.h>
  5. #include <ESP8266WebServer.h>
  6. #include <SoftwareSerial.h>
  7.  
  8. ESP8266WebServer http(80);
  9.  
  10. SoftwareSerial softSerial;
  11.  
  12. #define SERIAL softSerial
  13.  
  14. #define HOSTNAME "alarmkbd"
  15. #define WIFI_NAME "xxx"
  16. #define WIFI_PW "yyy"
  17. #define OTA_PW "test"
  18. #define KBD_ADDR 0x22
  19.  
  20. #define MODE_IDLE 'I'
  21. #define MODE_BF_WAIT_PING 'P'
  22. #define MODE_BF_SEND_CODE 'S'
  23. #define MODE_BF_WAIT_CMDRECV 'C'
  24. #define MODE_BF_SEND_RESET 'R'
  25.  
  26. #define LOG_LINES 15
  27. #define LOG_BUF_SIZE 80
  28. #define RX_BUF_LEN 27
  29.  
  30. #define PAK_RECV_ALL 0x31
  31. #define PAK_RECV_KBD KBD_ADDR
  32.  
  33. #define LED_BUILTIN 2
  34. #define LED_ON digitalWrite(LED_BUILTIN, LOW)
  35. #define LED_OFF digitalWrite(LED_BUILTIN, HIGH);
  36. char ledState = 0;
  37. #define TOGGLE_LED { ledState = !ledState; if (ledState) LED_ON; else LED_OFF; }
  38.  
  39. char connectionConfigured = 0;
  40.  
  41. typedef void (*PacketHandlerFunc) (const char len);
  42.  
  43. struct PacketHandler {
  44.   char* name;
  45.   char addr; // byte 1 in pak
  46.   char cmd; // byte 2 in pak
  47.   char len;  
  48.   PacketHandlerFunc func;
  49. };
  50.  
  51. struct State {
  52.   char busMode;
  53.   char busError;
  54.   char led;
  55.   char alarm;
  56.   char txtMode;
  57.   char line1[17];
  58.   char line2[17];
  59. } state;
  60.  
  61. char readRxBuffer(int i, char pakLen);
  62. void writeTxBuffer(char *buf, char pakLen) {
  63.   delay(10); // TODO: Use a timer instead?
  64.   SERIAL.write(buf, pakLen);
  65. }
  66.  
  67. #define TXTMODE_SHOW_TEXT 0x01
  68. #define TXTMODE_ADMIN 0x40
  69. #define TXTMODE_FUNC_MENU 0x80
  70.  
  71. #define TXTINDEX_LINE   0x20
  72. #define TXTINDEX_LINE2  0x40
  73.  
  74. #define LED_NONE 0x00
  75. #define LED_GREEN 0x02
  76. #define LED_GREENB 0x04
  77. #define LED_RED 0x80
  78. #define LED_REDB 0x81
  79. #define LED_BEEPER 0x40
  80.  
  81. #define KEY_ONOFF 0x80
  82. #define KEY_RESET 0x81
  83. #define KEY_INTERIOR 0x82
  84. #define KEY_BYPASS 0x84
  85. #define KEY_FUNC 0x88
  86.  
  87. #define ALARM_OFF 0x00
  88. #define ALARM_ARMED 0x80
  89.  
  90. #define PING_BUS_IDLE       0b00000001 // bitflags
  91. #define PING_BUS_ADMIN      0b01000000
  92. #define PING_BUS_FUNC_MODE  0b10000000
  93.  
  94. #define PING_ERROR_NONE 0x01
  95. #define PING_ERROR_WRONG_CODE 0x31
  96. #define PING_ERROR_ARM_ZN_FAULT 0x61
  97.  
  98. #define PONG_STATE_OUT_OF_SYSTEM 0x20
  99. #define PONG_STATE_LOCKED_OUT 0x21
  100. #define PONG_STATE_IDLE 0x01
  101.  
  102. char emptyTxBufferSize;
  103. char pakBuffer[RX_BUF_LEN]; // Ringbuffer
  104. char pakBufferTxIndex = 0;
  105. uint32_t paksReceivedById[256];
  106. char slowUpdates = 0;
  107. char logBuffer[LOG_LINES][LOG_BUF_SIZE];
  108. char logWriteIndex = 0;
  109. char currentAction[80] = "Idle";
  110. char mode = MODE_IDLE;
  111. char *lastPacket = NULL;
  112. uint32_t bfCode = 000000;
  113.  
  114.  
  115. char ledStrBuf[4];
  116. char * ledStr(char led) {
  117.   strcpy(ledStrBuf, "___");
  118.   if (led & LED_GREEN) ledStrBuf[0] = 'g';
  119.   if (led & LED_GREENB) ledStrBuf[0] = 'G';
  120.   if (led & LED_RED) ledStrBuf[1] = 'r';
  121.   if (led & LED_REDB) ledStrBuf[1] = 'R';
  122.   if (led & LED_BEEPER) ledStrBuf[2] = 'B';
  123.   return ledStrBuf;
  124. }
  125.  
  126. char * alarmStr(char alarm) {
  127.   if (alarm & ALARM_ARMED) return "armed";
  128.   else return "off";
  129. }
  130.  
  131. char textModeStrBuf[4];
  132. char * txtModeStr(char txtMode) {
  133.   textModeStrBuf[0] = (txtMode & TXTMODE_SHOW_TEXT) ? 'T' : '_';
  134.   textModeStrBuf[1] = (txtMode & TXTMODE_FUNC_MENU) ? 'F' : '_';
  135.   textModeStrBuf[2] = (txtMode & TXTMODE_ADMIN) ? 'A' : '_';
  136.   textModeStrBuf[3] = 0;
  137.   return textModeStrBuf;
  138. }
  139.  
  140. char busModeStrBuf[4];
  141. char * busModeStr(char busMode) {
  142.   busModeStrBuf[0] = busMode & PING_BUS_IDLE ? 'I' : '_';
  143.   busModeStrBuf[1] = busMode & PING_BUS_FUNC_MODE ? 'F' : '_';
  144.   busModeStrBuf[2] = busMode & PING_BUS_ADMIN ? 'A' : '_';
  145.   busModeStrBuf[3] = 0;
  146.   return busModeStrBuf;
  147. }
  148.  
  149. char * busErrorStr(char err) {
  150.   switch (err) {
  151.     default: return "?";
  152.     case PING_ERROR_NONE: return "none";
  153.     case PING_ERROR_WRONG_CODE: return "wrongCode";
  154.     case PING_ERROR_ARM_ZN_FAULT: return "armZnFault";
  155.   }
  156. }
  157.  
  158. String printState() {
  159.   String str = "state {";
  160.   str += "\n  busState: " + String(state.busMode, HEX)  + " -> " + busModeStr(state.busMode);
  161.   str += "\n  busError: " + String(state.busError, HEX) + " -> " + busErrorStr(state.busError);
  162.   str += "\n  led:      " + String(state.led, HEX)      + " -> " + ledStr(state.led);
  163.   str += "\n  alarm:    " + String(state.alarm, HEX)    + " -> " + alarmStr(state.led);
  164.   str += "\n  txtMode:  " + String(state.txtMode, HEX)    + " -> " + txtModeStr(state.txtMode);
  165.   str += "\n  line1:   \"" + String(state.line1) + "\"";
  166.   str += "\n  line2:   \"" + String(state.line2) + "\"";
  167.   str += "\n}";
  168.   return str;
  169. }
  170.  
  171. String printPakStats() {
  172.   String str = "pakStats {";
  173.   for (int i = 0; i <= 255; i++) {
  174.     if (paksReceivedById[i] > 0) str += "\n  0x" + String(i, HEX) + " = " + String(paksReceivedById[i], DEC);
  175.   }
  176.   str += "\n}";
  177.   return str;
  178. }
  179.  
  180. void sendPong(char state) {
  181.   log("tx Pong state=%02X", state);
  182.   char pak[4];
  183.   pak[0] = KBD_ADDR;
  184.   pak[1] = 0x44;
  185.   pak[2] = state;
  186.   pak[3] = pak[0] + pak[1] + pak[2];
  187.   writeTxBuffer(pak, sizeof(pak));
  188. }
  189.  
  190. void sendKeyCode6(uint32_t code, char key) {
  191.   log("tx KeyCode6 code=%d key=%02X", code, key);
  192.   // KBD   1x l=11 | K1->P Key&Code6 |  21 2B 01 04 05 06 07 08 09 88 FC                                                 | !+........ü                    |  code6=456789 key=Func  
  193.   char pak[11];
  194.   pak[0] = KBD_ADDR;
  195.   pak[1] = 0x2B;
  196.   pak[2] = 0x01; // 0x21 = reset
  197.   for (int i = 1; i <= 6; i++) {
  198.     char digit = code % 10;
  199.     pak[3 + (6 - i)] = (digit == 0x00 ? 0x0A : digit);
  200.     code /= 10;
  201.   }
  202.   pak[9] = key;
  203.  
  204.   char chksum = 0;
  205.   for (int i = 0; i < sizeof(pak)-1; i++) chksum += pak[i];
  206.   pak[sizeof(pak)-1] = chksum;
  207.   writeTxBuffer(pak, sizeof(pak));
  208. }
  209.  
  210. void sendReset() {
  211.   log("tx Reset");
  212.  
  213.   // s=7821ms t=8ms   Async Serial   1x l= 5 | K1->P Key&Code0 |  21 25 21 91 F8                                                                   | !%!.o                          |  add=RESET key=No
  214.   char pak[5];
  215.   pak[0] = KBD_ADDR;
  216.   pak[1] = 0x25;
  217.   pak[2] = 0x21; // add=RESET  
  218.   pak[3] = KEY_RESET;
  219.  
  220.   char chksum = 0;
  221.   for (int i = 0; i < sizeof(pak)-1; i++) chksum += pak[i];
  222.   pak[sizeof(pak)-1] = chksum;
  223.   writeTxBuffer(pak, sizeof(pak));
  224. }
  225.  
  226. void handleExtStatus(char len) {
  227.   if (readRxBuffer(3, len) == 0x04) return; // inv-checksum format; ignore
  228.   state.txtMode = readRxBuffer(4, len);
  229.   char txtIndex = readRxBuffer(5, len);
  230.   state.led = readRxBuffer(8, len);
  231.   state.alarm = readRxBuffer(9, len);
  232.   char *txtTarget = NULL;
  233.   if (txtIndex & TXTINDEX_LINE2) txtTarget = &state.line2[0];
  234.   else if (txtIndex & TXTINDEX_LINE) txtTarget = &state.line1[0];
  235.   if (state.txtMode & TXTMODE_SHOW_TEXT && txtTarget != NULL) {
  236.     for (int i = 0; i < 16; i++)
  237.       txtTarget[i] = readRxBuffer(10+i, len);
  238.     txtTarget[16] = 0;
  239.   }
  240.   log("rx ExtStatus txtMode=%02X txtIndex=%02X led=%02X alarm=%02X txt=%s", state.txtMode, txtIndex, state.led, state.alarm, txtTarget);
  241. }
  242.  
  243. void handleConf(char len) {
  244.   log("rx Conf");
  245.   sendPong(PONG_STATE_OUT_OF_SYSTEM);
  246. }
  247.  
  248. void handleCmdRecv(char len) {
  249.   log("rx CmdRecv %02X", readRxBuffer(1, len));
  250.   if (mode == MODE_BF_WAIT_CMDRECV) {
  251.     mode = MODE_BF_WAIT_PING;
  252.   }
  253. }
  254.  
  255. void bfSendNextCode() {
  256.   if (bfCode == 999999) {
  257.     log("bfSendNextCode reached end");
  258.     strcpy(currentAction, "Reached end :(");
  259.     mode = MODE_IDLE;
  260.     return; // nothing found, I guess
  261.   }
  262.   strcpy(currentAction, "Attempting code..");
  263.   bfCode = bfCode + 1;
  264.   log("bfSendNextCode %d", bfCode);
  265.   sendKeyCode6(bfCode, KEY_FUNC);
  266.   mode = MODE_BF_WAIT_CMDRECV; // Panel should ACK this cmd with a CMDRECV (doesn't matter if code valid or not valid)
  267. }
  268.  
  269. void bfCorrectCode() {
  270.   log("bfCorrectCode");
  271.   strcpy(currentAction, "Found code!");
  272.   sendReset(); // Exit Func mode
  273.   mode = MODE_IDLE;
  274. }
  275.  
  276. void handlePing(char len) {
  277.   state.busMode = readRxBuffer(4, len); // 0x01 idle, 0xC1 adminFuncMode, 0x81 userFuncMode
  278.   state.busError = readRxBuffer(5, len); // 0x01 none, 0x31 wrongCode (respond with 0x21 wrongCodeLockout), 0x61 armZnFault
  279.   log("rx Ping busState=%s (%02X), error=%s (%02X)",
  280.       busModeStr(state.busMode), state.busMode,
  281.       busErrorStr(state.busError), state.busError);
  282.   if (state.busError == PING_ERROR_WRONG_CODE) sendPong(PONG_STATE_LOCKED_OUT);
  283.   else if (state.busMode & PING_BUS_FUNC_MODE) bfCorrectCode();
  284.   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
  285.   else {
  286.     sendPong(PONG_STATE_IDLE);
  287.     if (mode == MODE_BF_WAIT_PING) { // Wait for at least one, fully healty Ping<>Pong cycle
  288.       mode = MODE_BF_SEND_CODE;
  289.     }
  290.   }
  291. }
  292.  
  293. const PacketHandler PACKET_HANDLERS[] = {
  294.   { "ExtStatus", /*addr=*/ PAK_RECV_ALL, /*cmd=*/ 0x3B, /*len=*/ 27, /*func=*/ handleExtStatus },
  295.   { "Status",    /*addr=*/ PAK_RECV_ALL, /*cmd=*/ 0x31, /*len=*/ 17, /*func=*/ NULL },
  296.   { "Ping",      /*addr=*/ PAK_RECV_KBD, /*cmd=*/ 0x4D, /*len=*/ 13, /*func=*/ handlePing },
  297.   { "CmdRecv",   /*addr=*/ PAK_RECV_KBD, /*cmd=*/ 0xC5, /*len=*/ 5, /*func=*/ handleCmdRecv },
  298.   { "Conf",      /*addr=*/ PAK_RECV_KBD, /*cmd=*/ 0x85, /*len=*/ 5, /*func=*/ handleConf },
  299. };
  300.  
  301. void setup() {
  302.   WiFi.mode(WIFI_STA);
  303.   WiFi.hostname(HOSTNAME);
  304.   WiFi.begin(WIFI_NAME, WIFI_PW);
  305.  
  306.   http.on("/", handleRoot);
  307.   http.on("/idle", handleIdle);
  308.   http.on("/bf", handleBf);
  309.   http.begin();
  310.  
  311.   //Serial.begin(5208, SERIAL_8E1); // 8 bits, even parity, 5208 baud
  312.   softSerial.begin(5208, SWSERIAL_8E1, D1, D2);
  313.   emptyTxBufferSize = SERIAL.availableForWrite();
  314.   pinMode(LED_BUILTIN, OUTPUT);
  315. }
  316.  
  317. String readLog() {
  318.   String logStr = "";
  319.   for (int i = 0; i < LOG_LINES; i++) {
  320.     logStr += "" + String(i) + ". " + String(logBuffer[(i + logWriteIndex) % LOG_LINES]) + "\n";
  321.   }
  322.   return logStr;
  323. }
  324.  
  325. void log(const char* strTemplate, ...) {
  326.   va_list args;
  327.   va_start(args, strTemplate);
  328.   vsnprintf(&logBuffer[logWriteIndex][0], LOG_BUF_SIZE, strTemplate, args);
  329.   va_end(args);
  330.   logWriteIndex = (logWriteIndex+1) % LOG_LINES;
  331. }
  332.  
  333. /** Reads one byte from the serial port, and places it in the RX buffer. */
  334. void readByteIntoRxBuffer() {
  335.   pakBuffer[pakBufferTxIndex] = SERIAL.read();
  336.   pakBufferTxIndex = (pakBufferTxIndex+1) % RX_BUF_LEN;
  337. }
  338. /** Calculates the backwards-in-rx-buffer going index for the given packet. */
  339. char calculateRxBufferIndex(int i, char pakLen) {
  340.   int arrayIdx = pakBufferTxIndex - pakLen + i;
  341.   while (arrayIdx < 0) arrayIdx += RX_BUF_LEN;
  342.   return arrayIdx;
  343. }
  344.  
  345. /** Reads the prior {pakLen} bytes, starting at {txIndex-pakLen} */
  346. char readRxBuffer(int i, char pakLen) {
  347.   return pakBuffer[calculateRxBufferIndex(i, pakLen)];
  348. }
  349.  
  350. /** Erases an handled packet from the RX buffer, preventing it from accidentally matching a second packet handler. */
  351. void eraseRxBuffer(char pakLen) {
  352.   for (int i = 0; i < pakLen; i++) {
  353.     pakBuffer[calculateRxBufferIndex(i, pakLen)] = 0;
  354.   }
  355. }
  356. /** Calculates the 8-bit check-sum() */
  357. char calculateChecksum(char pakLen) {
  358.   char sum = 0;
  359.   for (int i = 0; i < pakLen - 1; i++) {
  360.     sum += readRxBuffer(i, pakLen);
  361.   }
  362.   return sum;
  363. }
  364. /** Checks if the RX ring buffer just received a valid packet for the specified handler. */
  365. bool isValidPacketFor(PacketHandler packetHandler) {
  366.   if (readRxBuffer(0, packetHandler.len) != packetHandler.addr)
  367.     return false;
  368.   if (readRxBuffer(1, packetHandler.len) != packetHandler.cmd)
  369.     return false;
  370.   char checksum = calculateChecksum(packetHandler.len);
  371.   // TODO: Maybe support inverse checksum?
  372.   if (checksum != readRxBuffer(packetHandler.len - 1, packetHandler.len))
  373.     return false;
  374.   return true;
  375. }
  376. /** Returns the first matching packet handler, or NULL. */
  377. const PacketHandler* findMatchingPacketHandler() {
  378.   for (int i = 0; i < sizeof(PACKET_HANDLERS) / sizeof(PacketHandler); i++) {
  379.     if (isValidPacketFor(PACKET_HANDLERS[i]))
  380.       return &PACKET_HANDLERS[i];
  381.   }
  382.   return NULL;
  383. }
  384. /** Tries finding a matching packet handler and executing it. Returns true if a packetHandler was matched. */
  385. bool tryHandleValidPacket() {
  386.   const PacketHandler* pakHandler = findMatchingPacketHandler();
  387.   if (pakHandler == NULL) return false;
  388.  
  389.   // Record packetHandler match
  390.   paksReceivedById[pakHandler->cmd]++;
  391.   TOGGLE_LED;
  392.   lastPacket = pakHandler->name;
  393.   if (pakHandler->func != NULL) {
  394.     pakHandler->func(pakHandler->len);
  395.   } else log("rx  %s (n/a)", pakHandler->name);
  396.   eraseRxBuffer(pakHandler->len);
  397. }
  398.  
  399. /** Receives data from the serial port; waiting for a packet to be found. */
  400. void rx() {
  401.   while (SERIAL.available() > 0) {
  402.     readByteIntoRxBuffer();
  403.     if (tryHandleValidPacket()) break;
  404.   }
  405. }
  406.  
  407. void handleLogic() {
  408.   rx();
  409. }
  410.  
  411. void handleRoot() {
  412.   String str = String(HOSTNAME);
  413.   str += "\nmillis()=" + String(millis(), DEC) + ", free_heap=" + String(system_get_free_heap_size(), DEC) + ", slowUpdates=" + String(slowUpdates, DEC);
  414.   str += "\naddr=" + String(KBD_ADDR, HEX) + " pakBuffer:";
  415.   for (int i = 0; i < sizeof(pakBuffer); i++) str += String(pakBufferTxIndex == i ? '>' : ' ') + String(pakBuffer[i], HEX);
  416.   str += "\nlastPak: " + String(lastPacket);
  417.   str += "\nmode=" + String(mode) + " code=" + String(bfCode, DEC) + " action=" + String(currentAction);
  418.  
  419.   str += "\n\n";
  420.   str += printState();
  421.  
  422.   str += "\n\n";
  423.   str += printPakStats();
  424.  
  425.   str += "\n\nlatest log:\n" + readLog();
  426.   http.sendHeader("X-Content-Type-Options", "nosniff");
  427.   http.send(200, "text/plain", str);
  428. }
  429.  
  430. void handleIdle() {
  431.   http.send(200, "text/plain", "ok, idle.");
  432.   mode = MODE_IDLE;
  433. }
  434.  
  435. void handleBf() {
  436.   http.send(200, "text/plain", "will do.");
  437.   String codeArg = http.arg("code");
  438.   if (codeArg != "") {
  439.     uint32_t code = 0;
  440.     for (int i = 0; i < codeArg.length(); i++) {
  441.       char digit = codeArg.charAt(i) - '0';
  442.       code = code * 10;
  443.       code += digit;
  444.     }
  445.     bfCode = code - 1;  
  446.   }
  447.   mode = MODE_BF_WAIT_PING;
  448. }
  449.  
  450. void loop() {
  451.   unsigned long now = millis();
  452.   if (WiFi.isConnected()) {
  453.     if (!connectionConfigured) {
  454.       connectionConfigured = true;
  455.       ArduinoOTA.setHostname(HOSTNAME);
  456.       ArduinoOTA.setPassword(OTA_PW);
  457.       ArduinoOTA.begin();
  458.     }
  459.     ArduinoOTA.handle();
  460.     http.handleClient();
  461.   }
  462.   handleLogic();
  463.   unsigned long elapsed = millis() - now;
  464.   if (elapsed > 50) {
  465.     slowUpdates++;
  466.     log("Slow Update: %d ms", elapsed);
  467.   }
  468. }
Add Comment
Please, Sign In to add comment