improv.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #include "esp_system.h"
  2. #include "esp_log.h"
  3. #include "improv.h"
  4. #include "tools.h"
  5. #include "string.h"
  6. static ImprovCommandStruct_t last_command;
  7. static callback_table_t callbacks[] = {
  8. {IMPROV_CMD_UNKNOWN, NULL},
  9. {IMPROV_CMD_WIFI_SETTINGS, NULL},
  10. {IMPROV_CMD_GET_CURRENT_STATE, NULL},
  11. {IMPROV_CMD_GET_DEVICE_INFO, NULL},
  12. {IMPROV_CMD_GET_WIFI_NETWORKS, NULL},
  13. {IMPROV_CMD_BAD_CHECKSUM, NULL},
  14. {-1, NULL}};
  15. const char *improv_get_error_desc(ImprovError_t error)
  16. {
  17. switch (error)
  18. {
  19. ENUM_TO_STRING(IMPROV_ERROR_NONE)
  20. ENUM_TO_STRING(IMPROV_ERROR_INVALID_RPC)
  21. ENUM_TO_STRING(IMPROV_ERROR_UNKNOWN_RPC)
  22. ENUM_TO_STRING(IMPROV_ERROR_UNABLE_TO_CONNECT)
  23. ENUM_TO_STRING(IMPROV_ERROR_NOT_AUTHORIZED)
  24. ENUM_TO_STRING(IMPROV_ERROR_UNKNOWN)
  25. }
  26. return "";
  27. }
  28. const char *improv_get_command_desc(ImprovCommand_t command)
  29. {
  30. switch (command)
  31. {
  32. ENUM_TO_STRING(IMPROV_CMD_UNKNOWN)
  33. ENUM_TO_STRING(IMPROV_CMD_WIFI_SETTINGS)
  34. ENUM_TO_STRING(IMPROV_CMD_GET_CURRENT_STATE)
  35. ENUM_TO_STRING(IMPROV_CMD_GET_DEVICE_INFO)
  36. ENUM_TO_STRING(IMPROV_CMD_GET_WIFI_NETWORKS)
  37. ENUM_TO_STRING(IMPROV_CMD_BAD_CHECKSUM)
  38. }
  39. return "";
  40. };
  41. static improv_send_callback_t send_callback = NULL;
  42. const uint8_t improv_prefix[] = {'I', 'M', 'P', 'R', 'O', 'V', IMPROV_SERIAL_VERSION};
  43. typedef struct __attribute__((__packed__))
  44. {
  45. uint8_t prefix[6];
  46. uint8_t version;
  47. uint8_t packet_type;
  48. uint8_t data_len;
  49. } improv_packet_t;
  50. #define PACKET_CHECKSUM_SIZE sizeof(uint8_t)
  51. #define PACKET_PAYLOAD(packet) ((uint8_t *)packet) + sizeof(improv_packet_t)
  52. static ImprovAPListStruct_t *ap_list = NULL;
  53. static size_t ap_list_size = 0;
  54. static size_t ap_list_actual = 0;
  55. void improv_wifi_list_free()
  56. {
  57. ap_list_actual = 0;
  58. ImprovAPListStruct_t *current = ap_list;
  59. for (int i = 0; i < ap_list_actual; i++)
  60. {
  61. if (!current)
  62. {
  63. break;
  64. }
  65. FREE_AND_NULL(current->rssi);
  66. FREE_AND_NULL(current->ssid);
  67. FREE_AND_NULL(current->auth_req);
  68. current++;
  69. }
  70. FREE_AND_NULL(ap_list);
  71. }
  72. bool improv_wifi_list_allocate(size_t num_entries)
  73. {
  74. improv_wifi_list_free();
  75. ap_list = malloc_init_external(num_entries * sizeof(ImprovAPListStruct_t) + 1); // last byte will always be null
  76. ap_list_size = num_entries;
  77. ap_list_actual = 0;
  78. return ap_list != NULL;
  79. }
  80. bool improv_wifi_list_add(const char *ssid, int8_t rssi, bool auth_req)
  81. {
  82. const size_t yes_no_length = 4;
  83. ImprovAPListStruct_t *current = ap_list + ap_list_actual;
  84. if (ap_list_actual > ap_list_size || !current)
  85. {
  86. return false;
  87. }
  88. current->ssid = strdup_psram(ssid);
  89. size_t length = snprintf(NULL, 0, "%02d", rssi) + 1;
  90. current->auth_req = malloc_init_external(yes_no_length); // enough for YES/NO to fit
  91. current->rssi = (char *)malloc_init_external(length);
  92. if (!current->rssi || !current->auth_req)
  93. {
  94. return false;
  95. }
  96. snprintf(current->rssi, length, "%02d", rssi);
  97. snprintf(current->auth_req, yes_no_length, "%s", auth_req ? "YES" : "NO");
  98. ap_list_actual++;
  99. return true;
  100. }
  101. void improv_parse_data(ImprovCommandStruct_t *improv_command, const uint8_t *data, size_t length, bool check_checksum)
  102. {
  103. ImprovCommand_t command = (ImprovCommand_t)data[0];
  104. uint8_t data_length = data[1];
  105. if (data_length != length - 2 - check_checksum)
  106. {
  107. improv_command->command = IMPROV_CMD_UNKNOWN;
  108. return;
  109. }
  110. if (check_checksum)
  111. {
  112. uint8_t checksum = data[length - 1];
  113. uint32_t calculated_checksum = 0;
  114. for (uint8_t i = 0; i < length - 1; i++)
  115. {
  116. calculated_checksum += data[i];
  117. }
  118. if ((uint8_t)calculated_checksum != checksum)
  119. {
  120. improv_command->command = IMPROV_CMD_BAD_CHECKSUM;
  121. return;
  122. }
  123. }
  124. if (command == IMPROV_CMD_WIFI_SETTINGS)
  125. {
  126. uint8_t ssid_length = data[2];
  127. uint8_t ssid_start = 3;
  128. size_t ssid_end = ssid_start + ssid_length;
  129. uint8_t pass_length = data[ssid_end];
  130. size_t pass_start = ssid_end + 1;
  131. size_t pass_end = pass_start + pass_length;
  132. improv_command->ssid = malloc(ssid_length + 1);
  133. memset(improv_command->ssid, 0x00, ssid_length + 1);
  134. memcpy(improv_command->ssid, &data[ssid_start], ssid_length);
  135. improv_command->password = NULL;
  136. if (pass_length > 0)
  137. {
  138. improv_command->password = malloc(pass_length + 1);
  139. memset(improv_command->password, 0x00, pass_length + 1);
  140. memcpy(improv_command->password, &data[pass_start], pass_length);
  141. }
  142. }
  143. improv_command->command = command;
  144. }
  145. bool improv_parse_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,
  146. improv_command_callback_t callback, on_error_callback_t on_error)
  147. {
  148. ImprovCommandStruct_t command = {0};
  149. if (position < 7)
  150. return byte == improv_prefix[position];
  151. if (position <= 8)
  152. return true;
  153. uint8_t command_type = buffer[7];
  154. uint8_t data_len = buffer[8];
  155. if (position <= 8 + data_len)
  156. return true;
  157. if (position == 8 + data_len + 1)
  158. {
  159. uint8_t checksum = 0x00;
  160. for (size_t i = 0; i < position; i++)
  161. checksum += buffer[i];
  162. if (checksum != byte)
  163. {
  164. on_error(IMPROV_ERROR_INVALID_RPC);
  165. return false;
  166. }
  167. if (command_type == IMPROV_PACKET_TYPE_RPC)
  168. {
  169. improv_parse_data(&command, &buffer[9], data_len, false);
  170. callback(&command);
  171. }
  172. }
  173. return false;
  174. }
  175. void improv_set_send_callback(improv_send_callback_t callback)
  176. {
  177. send_callback = callback;
  178. }
  179. bool improv_set_callback(ImprovCommand_t command, improv_command_callback_t callback)
  180. {
  181. callback_table_t *pCt = &callbacks;
  182. while (pCt->index > -1)
  183. {
  184. if (pCt->index == command)
  185. {
  186. pCt->callback = callback;
  187. return true;
  188. }
  189. }
  190. return false;
  191. }
  192. bool improv_handle_callback(ImprovCommandStruct_t *command)
  193. {
  194. const callback_table_t *pCt = &callbacks;
  195. while (pCt->index > -1)
  196. {
  197. if (pCt->index == command->command)
  198. {
  199. return pCt->callback && pCt->callback(command);
  200. }
  201. }
  202. return false;
  203. }
  204. bool improv_send_packet(uint8_t *packet, size_t msg_len)
  205. {
  206. bool result = false;
  207. if (send_callback && packet && msg_len > 0)
  208. {
  209. result = send_callback(packet, msg_len);
  210. }
  211. return result;
  212. }
  213. bool improv_send_byte(ImprovSerialType_t packet_type, uint8_t data)
  214. {
  215. size_t msg_len;
  216. uint8_t *packet = improv_build_response(packet_type, (const char *)&data, 1, &msg_len);
  217. bool result = improv_send_packet(packet, msg_len);
  218. FREE_AND_NULL(packet);
  219. return result;
  220. }
  221. bool improv_send_current_state(ImprovState_t state)
  222. {
  223. return improv_send_byte(IMPROV_PACKET_TYPE_CURRENT_STATE, (uint8_t)state);
  224. }
  225. bool improv_send_error(ImprovError_t error)
  226. {
  227. return improv_send_byte(IMPROV_PACKET_TYPE_ERROR_STATE, (uint8_t)error);
  228. }
  229. size_t improv_wifi_get_wifi_list_count(){
  230. return ap_list_actual;
  231. }
  232. bool improv_wifi_list_send()
  233. {
  234. size_t msglen = 0;
  235. bool result = true;
  236. if (ap_list_actual == 0)
  237. {
  238. return false;
  239. }
  240. for (int i = 0; i < ap_list_actual && result; i++)
  241. {
  242. uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_WIFI_NETWORKS,(const char **) &ap_list[i], IMPROV_AP_STRUCT_NUM_STR, &msglen);
  243. result = improv_send_packet(packet, msglen);
  244. FREE_AND_NULL(packet);
  245. }
  246. uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_WIFI_NETWORKS, NULL, 0, &msglen);
  247. result = improv_send_packet(packet, msglen);
  248. FREE_AND_NULL(packet);
  249. return result;
  250. }
  251. bool improv_send_device_url(ImprovCommand_t from_command, const char *url)
  252. {
  253. size_t msglen = 0;
  254. uint8_t *packet = NULL;
  255. bool result = false;
  256. if (url && strlen(url))
  257. {
  258. packet = improv_build_rpc_response(from_command, &url, 1, &msglen);
  259. if (!packet)
  260. return false;
  261. result = improv_send_packet(packet, msglen);
  262. FREE_AND_NULL(packet);
  263. }
  264. packet = improv_build_rpc_response(from_command, "", 0, &msglen);
  265. if (!packet)
  266. return false;
  267. result = improv_send_packet(packet, msglen);
  268. return result;
  269. }
  270. bool improv_send_device_info(const char *firmware_name, const char *firmware_version, const char *hardware_chip_variant, const char *device_name)
  271. {
  272. ImprovDeviceInfoStruct_t device_info;
  273. size_t msglen = 0;
  274. device_info.device_name = device_name;
  275. device_info.firmware_name = firmware_name;
  276. device_info.firmware_version = firmware_version;
  277. device_info.hardware_chip_variant = hardware_chip_variant;
  278. device_info.nullptr = NULL;
  279. uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_DEVICE_INFO, &device_info, IMPROV_DEVICE_INFO_NUM_STRINGS, &msglen);
  280. if (!packet)
  281. return false;
  282. bool result = improv_send_packet(packet, msglen);
  283. FREE_AND_NULL(packet);
  284. return true;
  285. }
  286. bool parse_improv_serial_line(const uint8_t *buffer)
  287. {
  288. const uint8_t *b = buffer;
  289. const uint8_t *p = improv_prefix;
  290. const uint8_t *data = NULL;
  291. uint8_t checksum = 0x00;
  292. uint8_t rec_checksum = 0x00;
  293. while (*p != '\0' && *b != '\0')
  294. {
  295. // check if line prefix matches the standard
  296. if (*p++ != *b++)
  297. {
  298. return false;
  299. }
  300. }
  301. uint8_t command_type = *p++;
  302. if (command_type == 0)
  303. return false;
  304. uint8_t data_len = *p++;
  305. data = p;
  306. rec_checksum = buffer[sizeof(improv_prefix) + data_len];
  307. for (size_t i = 0; i < sizeof(improv_prefix) + data_len; i++)
  308. {
  309. checksum += buffer[i];
  310. }
  311. if (checksum != rec_checksum)
  312. {
  313. improv_send_error(IMPROV_ERROR_INVALID_RPC);
  314. return false;
  315. }
  316. if (command_type == IMPROV_PACKET_TYPE_RPC)
  317. {
  318. improv_parse_data(&last_command, &data, data_len, false);
  319. return improv_handle_callback(&last_command);
  320. }
  321. return false;
  322. }
  323. // Improv packet format
  324. // 1-6 Header will equal IMPROV
  325. // 7 Version CURRENT VERSION = 1
  326. // 8 Type (see below)
  327. // 9 Length
  328. // 10...X Data
  329. // X + 10 Checksum
  330. improv_packet_t *improv_alloc_prefix(size_t data_len, ImprovSerialType_t packet_type, size_t *out_len)
  331. {
  332. size_t buffer_len = sizeof(improv_packet_t) + data_len + 1; // one byte for checksum
  333. if (out_len)
  334. {
  335. *out_len = buffer_len;
  336. }
  337. improv_packet_t *out = (improv_packet_t *)malloc_init_external(buffer_len + 1);
  338. memcpy(out, improv_prefix, sizeof(improv_prefix));
  339. out->packet_type = (uint8_t)packet_type;
  340. out->data_len = (uint8_t)data_len;
  341. return out;
  342. }
  343. uint8_t improv_set_checksum(improv_packet_t *data, size_t buffer_len)
  344. {
  345. uint32_t calculated_checksum = 0;
  346. for (int b = 0; b < buffer_len - 1; b++)
  347. {
  348. calculated_checksum += ((uint8_t *)data)[b];
  349. }
  350. calculated_checksum = calculated_checksum & 0xFF;
  351. ((uint8_t *)data)[buffer_len - 1] = (uint8_t)calculated_checksum;
  352. return calculated_checksum;
  353. }
  354. uint8_t *improv_build_response(ImprovSerialType_t packet_type, const char *datum, size_t len, size_t *out_len)
  355. {
  356. size_t buffer_len = 0;
  357. improv_packet_t *improv_packet = improv_alloc_prefix(len, packet_type, &buffer_len);
  358. if (out_len)
  359. {
  360. *out_len = buffer_len;
  361. }
  362. uint8_t *p = PACKET_PAYLOAD(improv_packet);
  363. for (int i = 0; i < len; i++)
  364. {
  365. *p++ = datum[i]; // string 1
  366. }
  367. improv_set_checksum(improv_packet, buffer_len);
  368. return (uint8_t *)improv_packet;
  369. }
  370. uint8_t *improv_build_rpc_response(ImprovCommand_t command, const char **results, size_t num_strings, size_t *out_len)
  371. {
  372. size_t buffer_len = 0;
  373. size_t total_string_len = 0;
  374. size_t string_buffer_len = 0;
  375. for (int i = 0; i < num_strings && (results[i] && (results[i])[0] != '\0'); i++)
  376. {
  377. size_t l = strlen(results[i]);
  378. total_string_len += l;
  379. string_buffer_len += l + 1; // length of the string plus byte for length
  380. }
  381. improv_packet_t *improv_packet = improv_alloc_prefix(string_buffer_len + 2, IMPROV_PACKET_TYPE_RPC_RESPONSE, &buffer_len); // 2 bytes for command and length of all strings
  382. if (out_len)
  383. {
  384. *out_len = buffer_len;
  385. }
  386. uint8_t *p = PACKET_PAYLOAD(improv_packet);
  387. *p++ = (uint8_t)command; // command being responded to
  388. *p++ = (uint8_t)string_buffer_len; //
  389. for (int i = 0; i < num_strings && results[i]; i++)
  390. {
  391. uint8_t curlel = (uint8_t)strlen(results[i]);
  392. *p++ = curlel;
  393. memcpy(p, results[i], curlel);
  394. p += curlel;
  395. }
  396. improv_set_checksum(improv_packet, buffer_len);
  397. return (uint8_t *)improv_packet;
  398. }