Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
7.5 KiB

  1. /*
  2. ITEAD RF BRIDGE MODULE
  3. Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #ifdef SONOFF_RFBRIDGE
  6. // -----------------------------------------------------------------------------
  7. // DEFINITIONS
  8. // -----------------------------------------------------------------------------
  9. #define RF_MESSAGE_SIZE 9
  10. #define RF_CODE_START 0xAA
  11. #define RF_CODE_ACK 0xA0
  12. #define RF_CODE_LEARN 0xA1
  13. #define RF_CODE_LEARN_KO 0xA2
  14. #define RF_CODE_LEARN_OK 0xA3
  15. #define RF_CODE_RFIN 0xA4
  16. #define RF_CODE_RFOUT 0xA5
  17. #define RF_CODE_STOP 0x55
  18. // -----------------------------------------------------------------------------
  19. // GLOBALS TO THE MODULE
  20. // -----------------------------------------------------------------------------
  21. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  22. unsigned char _uartpos = 0;
  23. unsigned char _learnId = 0;
  24. bool _learnStatus = true;
  25. // -----------------------------------------------------------------------------
  26. // PRIVATES
  27. // -----------------------------------------------------------------------------
  28. void _rfbAck() {
  29. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
  30. Serial.println();
  31. Serial.write(RF_CODE_START);
  32. Serial.write(RF_CODE_ACK);
  33. Serial.write(RF_CODE_STOP);
  34. Serial.flush();
  35. Serial.println();
  36. }
  37. void _rfbLearn() {
  38. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
  39. Serial.println();
  40. Serial.write(RF_CODE_START);
  41. Serial.write(RF_CODE_LEARN);
  42. Serial.write(RF_CODE_STOP);
  43. Serial.flush();
  44. Serial.println();
  45. char wsb[100];
  46. sprintf_P(wsb, PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  47. wsSend(wsb);
  48. }
  49. void _rfbSend(byte * message) {
  50. Serial.println();
  51. Serial.write(RF_CODE_START);
  52. Serial.write(RF_CODE_RFOUT);
  53. for (unsigned char j=0; j<RF_MESSAGE_SIZE; j++) {
  54. Serial.write(message[j]);
  55. }
  56. Serial.write(RF_CODE_STOP);
  57. Serial.flush();
  58. Serial.println();
  59. }
  60. void _rfbSend(byte * message, int times) {
  61. char buffer[RF_MESSAGE_SIZE];
  62. _rfbToChar(message, buffer);
  63. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending MESSAGE '%s' %d time(s)\n"), buffer, times);
  64. for (int i=0; i<times; i++) {
  65. if (i>0) {
  66. unsigned long start = millis();
  67. while (millis() - start < RF_SEND_DELAY) delay(1);
  68. }
  69. _rfbSend(message);
  70. }
  71. }
  72. void _rfbDecode() {
  73. byte action = _uartbuf[0];
  74. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  75. DEBUG_MSG_P(PSTR("[RFBRIDGE] Action 0x%02X\n"), action);
  76. if (action == RF_CODE_LEARN_KO) {
  77. _rfbAck();
  78. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
  79. wsSend("{\"action\": \"rfbTimeout\"}");
  80. }
  81. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  82. _rfbToChar(&_uartbuf[1], buffer);
  83. mqttSend(MQTT_TOPIC_RFIN, buffer);
  84. _rfbAck();
  85. }
  86. if (action == RF_CODE_LEARN_OK) {
  87. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn success\n");
  88. rfbStore(_learnId, _learnStatus, buffer);
  89. // Websocket update
  90. char wsb[100];
  91. sprintf_P(wsb, PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), _learnId, _learnStatus ? 1 : 0, buffer);
  92. wsSend(wsb);
  93. }
  94. if (action == RF_CODE_RFIN) {
  95. DEBUG_MSG_P(PSTR("[RFBRIDGE] Forward message '%s'\n"), buffer);
  96. // Look for the code
  97. unsigned char id, status;
  98. bool found = false;
  99. for (id=0; id<relayCount(); id++) {
  100. for (status=0; status<2; status++) {
  101. String code = rfbRetrieve(id, status == 1);
  102. if (code.length()) {
  103. if (code.endsWith(&buffer[12])) {
  104. found = true;
  105. break;
  106. }
  107. }
  108. }
  109. if (found) break;
  110. }
  111. if (found) relayStatus(id, status == 1);
  112. }
  113. }
  114. void _rfbReceive() {
  115. static bool receiving = false;
  116. while (Serial.available()) {
  117. yield();
  118. byte c = Serial.read();
  119. //DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
  120. if (receiving) {
  121. if (c == RF_CODE_STOP) {
  122. _rfbDecode();
  123. receiving = false;
  124. } else {
  125. _uartbuf[_uartpos++] = c;
  126. }
  127. } else if (c == RF_CODE_START) {
  128. _uartpos = 0;
  129. receiving = true;
  130. }
  131. }
  132. }
  133. /*
  134. From an hexa char array ("A220EE...") to a byte array (half the size)
  135. */
  136. bool _rfbToArray(const char * in, byte * out) {
  137. if (strlen(in) != RF_MESSAGE_SIZE * 2) return false;
  138. char tmp[3] = {0};
  139. for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
  140. memcpy(tmp, &in[p*2], 2);
  141. out[p] = strtol(tmp, NULL, 16);
  142. }
  143. return true;
  144. }
  145. /*
  146. From a byte array to an hexa char array ("A220EE...", double the size)
  147. */
  148. bool _rfbToChar(byte * in, char * out) {
  149. for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
  150. sprintf(&out[p*2], "%02X", in[p]);
  151. }
  152. return true;
  153. }
  154. void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
  155. if (type == MQTT_CONNECT_EVENT) {
  156. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  157. sprintf(buffer, "%s/+", MQTT_TOPIC_RFLEARN);
  158. mqttSubscribe(buffer);
  159. mqttSubscribe(MQTT_TOPIC_RFOUT);
  160. }
  161. if (type == MQTT_MESSAGE_EVENT) {
  162. // Match topic
  163. String t = mqttSubtopic((char *) topic);
  164. // Check if should go into learn mode
  165. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  166. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  167. if (_learnId >= relayCount()) {
  168. DEBUG_MSG_P(PSTR("[RFBRIDGE] Wrong learnID (%d)\n"), _learnId);
  169. return;
  170. }
  171. _learnStatus = (char)payload[0] != '0';
  172. _rfbLearn();
  173. }
  174. if (t.equals(MQTT_TOPIC_RFOUT)) {
  175. byte message[RF_MESSAGE_SIZE];
  176. if (_rfbToArray(payload, message)) {
  177. _rfbSend(message, RF_SEND_TIMES);
  178. }
  179. }
  180. }
  181. }
  182. // -----------------------------------------------------------------------------
  183. // PUBLIC
  184. // -----------------------------------------------------------------------------
  185. void rfbStore(unsigned char id, bool status, const char * code) {
  186. DEBUG_MSG_P(PSTR("[RFBRIDGE] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  187. char key[8] = {0};
  188. sprintf(key, "rfb%d%s", id, status ? "on" : "off");
  189. setSetting(key, code);
  190. }
  191. String rfbRetrieve(unsigned char id, bool status) {
  192. char key[8] = {0};
  193. sprintf(key, "rfb%d%s", id, status ? "on" : "off");
  194. return getSetting(key);
  195. }
  196. void rfbStatus(unsigned char id, bool status) {
  197. String value = rfbRetrieve(id, status);
  198. if (value.length() > 0) {
  199. byte message[RF_MESSAGE_SIZE];
  200. _rfbToArray(value.c_str(), message);
  201. _rfbSend(message, RF_SEND_TIMES);
  202. }
  203. }
  204. void rfbLearn(unsigned char id, bool status) {
  205. _learnId = id;
  206. _learnStatus = status;
  207. _rfbLearn();
  208. }
  209. void rfbForget(unsigned char id, bool status) {
  210. char key[8] = {0};
  211. sprintf(key, "rfb%d%s", id, status ? "on" : "off");
  212. delSetting(key);
  213. // Websocket update
  214. char wsb[100];
  215. sprintf_P(wsb, PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  216. wsSend(wsb);
  217. }
  218. // -----------------------------------------------------------------------------
  219. // SETUP & LOOP
  220. // -----------------------------------------------------------------------------
  221. void rfbSetup() {
  222. mqttRegister(_rfbMqttCallback);
  223. }
  224. void rfbLoop() {
  225. _rfbReceive();
  226. }
  227. #endif