tools.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. * (c) Philippe G. 20201, philippe_44@outlook.com
  3. * see other copyrights below
  4. *
  5. * This software is released under the MIT License.
  6. * https://opensource.org/licenses/MIT
  7. *
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <stdint.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14. #include "esp_task.h"
  15. #include "esp_tls.h"
  16. #include "esp_http_client.h"
  17. #include "esp_heap_caps.h"
  18. #include "esp_log.h"
  19. #include "tools.h"
  20. #if CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS < 2
  21. #error CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS must be at least 2
  22. #endif
  23. const static char TAG[] = "tools";
  24. /****************************************************************************************
  25. * UTF-8 tools
  26. */
  27. // Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
  28. // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
  29. // Copyright (c) 2017 ZephRay <zephray@outlook.com>
  30. //
  31. // utf8to1252 - almost equivalent to iconv -f utf-8 -t windows-1252, but better
  32. #define UTF8_ACCEPT 0
  33. #define UTF8_REJECT 1
  34. static const uint8_t utf8d[] = {
  35. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
  36. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
  37. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
  38. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
  39. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
  40. 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
  41. 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
  42. 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
  43. 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
  44. 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
  45. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
  46. 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
  47. 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
  48. 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
  49. };
  50. static uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
  51. uint32_t type = utf8d[byte];
  52. *codep = (*state != UTF8_ACCEPT) ?
  53. (byte & 0x3fu) | (*codep << 6) :
  54. (0xff >> type) & (byte);
  55. *state = utf8d[256 + *state*16 + type];
  56. return *state;
  57. }
  58. static uint8_t UNICODEtoCP1252(uint16_t chr) {
  59. if (chr <= 0xff)
  60. return (chr&0xff);
  61. else {
  62. ESP_LOGI(TAG, "some multi-byte %hx", chr);
  63. switch(chr) {
  64. case 0x20ac: return 0x80; break;
  65. case 0x201a: return 0x82; break;
  66. case 0x0192: return 0x83; break;
  67. case 0x201e: return 0x84; break;
  68. case 0x2026: return 0x85; break;
  69. case 0x2020: return 0x86; break;
  70. case 0x2021: return 0x87; break;
  71. case 0x02c6: return 0x88; break;
  72. case 0x2030: return 0x89; break;
  73. case 0x0160: return 0x8a; break;
  74. case 0x2039: return 0x8b; break;
  75. case 0x0152: return 0x8c; break;
  76. case 0x017d: return 0x8e; break;
  77. case 0x2018: return 0x91; break;
  78. case 0x2019: return 0x92; break;
  79. case 0x201c: return 0x93; break;
  80. case 0x201d: return 0x94; break;
  81. case 0x2022: return 0x95; break;
  82. case 0x2013: return 0x96; break;
  83. case 0x2014: return 0x97; break;
  84. case 0x02dc: return 0x98; break;
  85. case 0x2122: return 0x99; break;
  86. case 0x0161: return 0x9a; break;
  87. case 0x203a: return 0x9b; break;
  88. case 0x0153: return 0x9c; break;
  89. case 0x017e: return 0x9e; break;
  90. case 0x0178: return 0x9f; break;
  91. default: return 0x00; break;
  92. }
  93. }
  94. }
  95. void utf8_decode(char *src) {
  96. uint32_t codep = 0, state = UTF8_ACCEPT;
  97. char *dst = src;
  98. while (src && *src) {
  99. if (!decode(&state, &codep, *src++)) *dst++ = UNICODEtoCP1252(codep);
  100. }
  101. *dst = '\0';
  102. }
  103. /****************************************************************************************
  104. * URL tools
  105. */
  106. static inline char from_hex(char ch) {
  107. return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
  108. }
  109. void url_decode(char *url) {
  110. char *p, *src = strdup(url);
  111. for (p = src; *src; url++) {
  112. *url = *src++;
  113. if (*url == '%') {
  114. *url = from_hex(*src++) << 4;
  115. *url |= from_hex(*src++);
  116. } else if (*url == '+') {
  117. *url = ' ';
  118. }
  119. }
  120. *url = '\0';
  121. free(p);
  122. }
  123. /****************************************************************************************
  124. * Memory tools
  125. */
  126. void * malloc_init_external(size_t sz){
  127. void * ptr=NULL;
  128. ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
  129. if(ptr==NULL){
  130. ESP_LOGE(TAG,"malloc_init_external: unable to allocate %d bytes of PSRAM!",sz);
  131. }
  132. else {
  133. memset(ptr,0x00,sz);
  134. }
  135. return ptr;
  136. }
  137. void * clone_obj_psram(void * source, size_t source_sz){
  138. void * ptr=NULL;
  139. ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
  140. if(ptr==NULL){
  141. ESP_LOGE(TAG,"clone_obj_psram: unable to allocate %d bytes of PSRAM!",source_sz);
  142. }
  143. else {
  144. memcpy(ptr,source,source_sz);
  145. }
  146. return ptr;
  147. }
  148. char * strdup_psram(const char * source){
  149. void * ptr=NULL;
  150. size_t source_sz = strlen(source)+1;
  151. ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
  152. if(ptr==NULL){
  153. ESP_LOGE(TAG,"strdup_psram: unable to allocate %d bytes of PSRAM! Cannot clone string %s",source_sz,source);
  154. }
  155. else {
  156. memset(ptr,0x00,source_sz);
  157. strcpy(ptr,source);
  158. }
  159. return ptr;
  160. }
  161. /****************************************************************************************
  162. * Task manager
  163. */
  164. #define TASK_TLS_INDEX 1
  165. typedef struct {
  166. StaticTask_t *xTaskBuffer;
  167. StackType_t *xStack;
  168. } task_context_t;
  169. static void task_cleanup(int index, task_context_t *context) {
  170. free(context->xTaskBuffer);
  171. free(context->xStack);
  172. free(context);
  173. }
  174. BaseType_t xTaskCreateEXTRAM( TaskFunction_t pvTaskCode,
  175. const char * const pcName,
  176. configSTACK_DEPTH_TYPE usStackDepth,
  177. void *pvParameters,
  178. UBaseType_t uxPriority,
  179. TaskHandle_t *pxCreatedTask) {
  180. // create the worker task as a static
  181. task_context_t *context = calloc(1, sizeof(task_context_t));
  182. context->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
  183. context->xStack = heap_caps_malloc(usStackDepth,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT));
  184. TaskHandle_t handle = xTaskCreateStatic(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, context->xStack, context->xTaskBuffer);
  185. // store context in TCB or free everything in case of failure
  186. if (!handle) {
  187. free(context->xTaskBuffer);
  188. free(context->xStack);
  189. free(context);
  190. } else {
  191. vTaskSetThreadLocalStoragePointerAndDelCallback( handle, TASK_TLS_INDEX, context, (TlsDeleteCallbackFunction_t) task_cleanup);
  192. }
  193. if (pxCreatedTask) *pxCreatedTask = handle;
  194. return handle != NULL ? pdPASS : pdFAIL;
  195. }
  196. void vTaskDeleteEXTRAM(TaskHandle_t xTask) {
  197. /* At this point we leverage FreeRTOS extension to have callbacks on task deletion.
  198. * If not, we need to have here our own deletion implementation that include delayed
  199. * free for when this is called with NULL (self-deletion)
  200. */
  201. vTaskDelete(xTask);
  202. }
  203. /****************************************************************************************
  204. * URL download
  205. */
  206. typedef struct {
  207. void *user_context;
  208. http_download_cb_t callback;
  209. size_t max, bytes;
  210. bool abort;
  211. uint8_t *data;
  212. esp_http_client_handle_t client;
  213. } http_context_t;
  214. static void http_downloader(void *arg);
  215. static esp_err_t http_event_handler(esp_http_client_event_t *evt);
  216. void http_download(char *url, size_t max, http_download_cb_t callback, void *context) {
  217. http_context_t *http_context = (http_context_t*) heap_caps_calloc(sizeof(http_context_t), 1, MALLOC_CAP_SPIRAM);
  218. esp_http_client_config_t config = {
  219. .url = url,
  220. .event_handler = http_event_handler,
  221. .user_data = http_context,
  222. };
  223. http_context->callback = callback;
  224. http_context->user_context = context;
  225. http_context->max = max;
  226. http_context->client = esp_http_client_init(&config);
  227. xTaskCreateEXTRAM(http_downloader, "downloader", 8*1024, http_context, ESP_TASK_PRIO_MIN + 1, NULL);
  228. }
  229. static void http_downloader(void *arg) {
  230. http_context_t *http_context = (http_context_t*) arg;
  231. esp_http_client_perform(http_context->client);
  232. esp_http_client_cleanup(http_context->client);
  233. free(http_context);
  234. vTaskDeleteEXTRAM(NULL);
  235. }
  236. static esp_err_t http_event_handler(esp_http_client_event_t *evt) {
  237. http_context_t *http_context = (http_context_t*) evt->user_data;
  238. if (http_context->abort) return ESP_FAIL;
  239. switch(evt->event_id) {
  240. case HTTP_EVENT_ERROR:
  241. http_context->callback(NULL, 0, http_context->user_context);
  242. http_context->abort = true;
  243. break;
  244. case HTTP_EVENT_ON_HEADER:
  245. if (!strcasecmp(evt->header_key, "Content-Length")) {
  246. size_t len = atoi(evt->header_value);
  247. if (!len || len > http_context->max) {
  248. ESP_LOGI(TAG, "content-length null or too large %zu / %zu", len, http_context->max);
  249. http_context->abort = true;
  250. }
  251. }
  252. break;
  253. case HTTP_EVENT_ON_DATA: {
  254. size_t len = esp_http_client_get_content_length(evt->client);
  255. if (!http_context->data) {
  256. if ((http_context->data = (uint8_t*) malloc(len)) == NULL) {
  257. http_context->abort = true;
  258. ESP_LOGE(TAG, "failed to allocate memory for output buffer %zu", len);
  259. return ESP_FAIL;
  260. }
  261. }
  262. memcpy(http_context->data + http_context->bytes, evt->data, evt->data_len);
  263. http_context->bytes += evt->data_len;
  264. break;
  265. }
  266. case HTTP_EVENT_ON_FINISH:
  267. http_context->callback(http_context->data, http_context->bytes, http_context->user_context);
  268. break;
  269. case HTTP_EVENT_DISCONNECTED: {
  270. int mbedtls_err = 0;
  271. esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
  272. if (err != ESP_OK) {
  273. ESP_LOGE(TAG, "HTTP download disconnect %d", err);
  274. if (http_context->data) free(http_context->data);
  275. http_context->callback(NULL, 0, http_context->user_context);
  276. return ESP_FAIL;
  277. }
  278. break;
  279. }
  280. default:
  281. break;
  282. }
  283. return ESP_OK;
  284. }
  285. void dump_json_content(const char* prefix, cJSON* json, int level) {
  286. if (!json) {
  287. ESP_LOG_LEVEL(level,TAG, "%s: empty!", prefix);
  288. return;
  289. }
  290. char* output = cJSON_Print(json);
  291. if (output) {
  292. ESP_LOG_LEVEL(level,TAG, "%s: \n%s", prefix, output);
  293. }
  294. FREE_AND_NULL(output);
  295. }