V6MiLightUdpServer.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #include <V6MiLightUdpServer.h>
  2. #include <ESP8266WiFi.h>
  3. #include <Arduino.h>
  4. #include <Size.h>
  5. #include <V6CommandHandler.h>
  6. #define MATCHES_PACKET(packet1) ( \
  7. matchesPacket(packet1, size(packet1), packet, packetSize) \
  8. )
  9. V6CommandDemuxer V6MiLightUdpServer::COMMAND_DEMUXER = V6CommandDemuxer(
  10. V6CommandHandler::ALL_HANDLERS,
  11. V6CommandHandler::NUM_HANDLERS
  12. );
  13. uint8_t V6MiLightUdpServer::START_SESSION_COMMAND[] = {
  14. 0x20, 0x00, 0x00, 0x00, 0x16, 0x02, 0x62, 0x3A, 0xD5, 0xED, 0xA3, 0x01, 0xAE,
  15. 0x08, 0x2D, 0x46, 0x61, 0x41, 0xA7, 0xF6, 0xDC, 0xAF
  16. };
  17. uint8_t V6MiLightUdpServer::START_SESSION_RESPONSE[] = {
  18. 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02,
  19. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // should be replaced with hw addr
  20. 0x69, 0xF0, 0x3C, 0x23, 0x00, 0x01,
  21. 0xFF, 0xFF, // should be replaced with a session ID
  22. 0x00
  23. };
  24. uint8_t V6MiLightUdpServer::COMMAND_HEADER[] = {
  25. 0x80, 0x00, 0x00, 0x00
  26. };
  27. uint8_t V6MiLightUdpServer::HEARTBEAT_HEADER[] = {
  28. 0xD0, 0x00, 0x00, 0x00, 0x02
  29. };
  30. uint8_t V6MiLightUdpServer::HEARTBEAT_HEADER2[] = {
  31. 0x30, 0x00, 0x00, 0x00, 0x03
  32. };
  33. uint8_t V6MiLightUdpServer::COMMAND_RESPONSE[] = {
  34. 0x88, 0x00, 0x00, 0x00, 0x03, 0x00, 0xFF, 0x00
  35. };
  36. uint8_t V6MiLightUdpServer::SEARCH_COMMAND[] = {
  37. 0x10, 0x00, 0x00, 0x00
  38. //, 0x24, 0x02
  39. //, 0xAE, 0x65, 0x02, 0x39, 0x38, 0x35, 0x62
  40. };
  41. uint8_t V6MiLightUdpServer::SEARCH_RESPONSE[] = {
  42. 0x18, 0x00, 0x00, 0x00, 0x40, 0x02,
  43. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // mac address
  44. 0x00, 0x20, 0x39, 0x38, 0x35, 0x62,
  45. 0x31, 0x35, 0x37, 0x62, 0x66, 0x36,
  46. 0x66, 0x63, 0x34, 0x33, 0x33, 0x36,
  47. 0x38, 0x61, 0x36, 0x33, 0x34, 0x36,
  48. 0x37, 0x65, 0x61, 0x33, 0x62, 0x31,
  49. 0x39, 0x64, 0x30, 0x64, 0x01, 0x00,
  50. 0x01,
  51. 0x17, 0x63, // this is 5987 in hex. specifying a different value seems to
  52. // cause client to connect on a different port for some commands
  53. 0x00, 0x00, 0x05, 0x00, 0x09, 0x78,
  54. 0x6C, 0x69, 0x6E, 0x6B, 0x5F, 0x64,
  55. 0x65, 0x76, 0x07, 0x5B, 0xCD, 0x15
  56. };
  57. uint8_t V6MiLightUdpServer::OPEN_COMMAND_RESPONSE[] = {
  58. 0x80, 0x00, 0x00, 0x00, 0x15,
  59. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // mac address
  60. 0x05, 0x02, 0x00, 0x34, 0x00, 0x00,
  61. 0x00, 0x00 ,0x00 ,0x00, 0x00, 0x00,
  62. 0x00, 0x00, 0x34
  63. };
  64. V6MiLightUdpServer::~V6MiLightUdpServer() {
  65. V6Session* cur = firstSession;
  66. while (cur != NULL) {
  67. V6Session* next = cur->next;
  68. delete cur;
  69. cur = next;
  70. }
  71. }
  72. template <typename T>
  73. T V6MiLightUdpServer::readInt(uint8_t* packet) {
  74. size_t numBytes = sizeof(T);
  75. T value = 0;
  76. for (size_t i = 0; i < numBytes; i++) {
  77. value |= packet[i] << (8 * (numBytes - i - 1));
  78. }
  79. return value;
  80. }
  81. template <typename T>
  82. uint8_t* V6MiLightUdpServer::writeInt(const T& value, uint8_t* packet) {
  83. size_t numBytes = sizeof(T);
  84. for (size_t i = 0; i < numBytes; i++) {
  85. packet[i] = (value >> (8 * (numBytes - i - 1))) & 0xFF;
  86. }
  87. return packet + numBytes;
  88. }
  89. uint16_t V6MiLightUdpServer::beginSession() {
  90. const uint16_t id = sessionId++;
  91. V6Session* session = new V6Session(socket.remoteIP(), socket.remotePort(), id);
  92. session->next = firstSession;
  93. firstSession = session;
  94. if (numSessions >= V6_MAX_SESSIONS) {
  95. V6Session* cur = firstSession;
  96. for (size_t i = 1; i < V6_MAX_SESSIONS; i++) {
  97. cur = cur->next;
  98. }
  99. delete cur->next;
  100. cur->next = NULL;
  101. } else {
  102. numSessions++;
  103. }
  104. return id;
  105. }
  106. void V6MiLightUdpServer::handleSearch() {
  107. const size_t packetLen = size(SEARCH_RESPONSE);
  108. uint8_t response[packetLen];
  109. memcpy(response, SEARCH_RESPONSE, packetLen);
  110. WiFi.macAddress(response + 6);
  111. socket.beginPacket(socket.remoteIP(), socket.remotePort());
  112. socket.write(response, packetLen);
  113. socket.endPacket();
  114. }
  115. void V6MiLightUdpServer::handleStartSession() {
  116. size_t len = size(START_SESSION_RESPONSE);
  117. uint8_t response[len];
  118. uint16_t sessionId = beginSession();
  119. memcpy(response, START_SESSION_RESPONSE, len);
  120. WiFi.macAddress(response + 7);
  121. response[19] = sessionId >> 8;
  122. response[20] = sessionId & 0xFF;
  123. sendResponse(sessionId, response, len);
  124. }
  125. bool V6MiLightUdpServer::sendResponse(uint16_t sessionId, uint8_t* responseBuffer, size_t responseSize) {
  126. V6Session* session = firstSession;
  127. while (session != NULL) {
  128. if (session->sessionId == sessionId) {
  129. break;
  130. }
  131. session = session->next;
  132. }
  133. if (session == NULL || session->sessionId != sessionId) {
  134. Serial.print("Received request with untracked session ID: ");
  135. Serial.println(sessionId);
  136. return false;
  137. }
  138. #ifdef MILIGHT_UDP_DEBUG
  139. printf_P("Sending response to %s:%d\n", session->ipAddr.toString().c_str(), session->port);
  140. #endif
  141. socket.beginPacket(session->ipAddr, session->port);
  142. socket.write(responseBuffer, responseSize);
  143. socket.endPacket();
  144. return true;
  145. }
  146. bool V6MiLightUdpServer::handleOpenCommand(uint16_t sessionId) {
  147. size_t len = size(OPEN_COMMAND_RESPONSE);
  148. uint8_t response[len];
  149. memcpy(response, OPEN_COMMAND_RESPONSE, len);
  150. WiFi.macAddress(response + 5);
  151. return sendResponse(sessionId, response, len);
  152. }
  153. void V6MiLightUdpServer::handleCommand(
  154. uint16_t sessionId,
  155. uint8_t sequenceNum,
  156. uint8_t* cmd,
  157. uint8_t group,
  158. uint8_t checksum
  159. ) {
  160. uint8_t cmdType = readInt<uint8_t>(cmd);
  161. uint32_t cmdHeader = readInt<uint32_t>(cmd+1);
  162. uint32_t cmdArg = readInt<uint32_t>(cmd+5);
  163. #ifdef MILIGHT_UDP_DEBUG
  164. printf_P("Command cmdType: %02X, cmdHeader: %08X, cmdArg: %08X\n", cmdType, cmdHeader, cmdArg);
  165. #endif
  166. bool handled = false;
  167. if (cmdHeader == 0) {
  168. handled = handleOpenCommand(sessionId);
  169. } else {
  170. handled = COMMAND_DEMUXER.handleCommand(
  171. client,
  172. deviceId,
  173. group,
  174. cmdType,
  175. cmdHeader,
  176. cmdArg
  177. );
  178. }
  179. if (handled) {
  180. size_t len = size(COMMAND_RESPONSE);
  181. memcpy(responseBuffer, COMMAND_RESPONSE, len);
  182. responseBuffer[6] = sequenceNum;
  183. sendResponse(sessionId, responseBuffer, len);
  184. return;
  185. }
  186. #ifdef MILIGHT_UDP_DEBUG
  187. printf_P("V6MiLightUdpServer - Unhandled command: ");
  188. for (size_t i = 0; i < V6_COMMAND_LEN; i++) {
  189. printf_P("%02X ", cmd[i]);
  190. }
  191. printf_P("\n");
  192. #endif
  193. }
  194. void V6MiLightUdpServer::handleHeartbeat(uint16_t sessionId) {
  195. char header[] = { 0xD8, 0x00, 0x00, 0x00, 0x07 };
  196. memcpy(responseBuffer, header, size(header));
  197. WiFi.macAddress(responseBuffer+5);
  198. responseBuffer[11] = 0;
  199. sendResponse(sessionId, responseBuffer, 12);
  200. }
  201. bool V6MiLightUdpServer::matchesPacket(uint8_t* packet1, size_t packet1Len, uint8_t* packet2, size_t packet2Len) {
  202. return packet2Len >= packet1Len && memcmp(packet1, packet2, packet1Len) == 0;
  203. }
  204. void V6MiLightUdpServer::handlePacket(uint8_t* packet, size_t packetSize) {
  205. #ifdef MILIGHT_UDP_DEBUG
  206. printf_P("Packet size: %d\n", packetSize);
  207. #endif
  208. if (MATCHES_PACKET(START_SESSION_COMMAND)) {
  209. handleStartSession();
  210. } else if (MATCHES_PACKET(HEARTBEAT_HEADER) || MATCHES_PACKET(HEARTBEAT_HEADER2)) {
  211. uint16_t sessionId = readInt<uint16_t>(packet+5);
  212. handleHeartbeat(sessionId);
  213. } else if (MATCHES_PACKET(SEARCH_COMMAND)) {
  214. handleSearch();
  215. } else if (packetSize == 22 && MATCHES_PACKET(COMMAND_HEADER)) {
  216. uint16_t sessionId = readInt<uint16_t>(packet+5);
  217. uint8_t sequenceNum = packet[8];
  218. uint8_t* cmd = packet+10;
  219. uint8_t group = packet[19];
  220. uint8_t checksum = packet[21];
  221. #ifdef MILIGHT_UDP_DEBUG
  222. printf_P("session: %04X, sequence: %d, group: %d, checksum: %d\n", sessionId, sequenceNum, group, checksum);
  223. #endif
  224. handleCommand(sessionId, sequenceNum, cmd, group, checksum);
  225. } else {
  226. Serial.println(F("Unhandled V6 packet"));
  227. }
  228. }