esp32 firmware for a toaster reflow oven WIP!!!!!

websrv.c 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #include <esp_log.h>
  2. #include <esp_err.h>
  3. #include <fileserver/token_subs.h>
  4. #include <httpd_utils/captive.h>
  5. #include <errno.h>
  6. #include "firehazard.h"
  7. #include "websrv.h"
  8. #include "esp_http_server.h"
  9. #include "utils.h"
  10. #include "files/files_enum.h"
  11. #include "version.h"
  12. #include "analog.h"
  13. static const char *TAG="websrv";
  14. static httpd_handle_t s_hServer = NULL;
  15. // Embedded files (must also be listed in CMakeLists.txt as COMPONENT_EMBED_TXTFILES)
  16. efile(index_file, "index_html");
  17. static struct tpl_kv_list build_index_replacements_kv(void)
  18. {
  19. // char name[TPL_KV_KEY_LEN];
  20. struct tpl_kv_list kv = tpl_kv_init();
  21. tpl_kv_add(&kv, "version", APP_VERSION);
  22. size_t pcap1 = 300;
  23. size_t pcap2 = 300;
  24. char *path1 = malloc(pcap1);
  25. char *path2 = malloc(pcap2);
  26. assert(path1);
  27. assert(path2);
  28. path1[0] = 0;
  29. path2[0] = 0;
  30. #define SCRATCH_SIZE 15
  31. char scratch1[SCRATCH_SIZE];
  32. char scratch2[SCRATCH_SIZE];
  33. bool last_empty = true; // first is move
  34. bool suc;
  35. for (int i = 0; i < REG_HISTORY_LEN; i++) {
  36. float x = i*2.5f;
  37. if (reg_meas_history[i] == 0) {
  38. snprintf(scratch1, SCRATCH_SIZE, "M%.1f,0", x);
  39. snprintf(scratch2, SCRATCH_SIZE, "M%.1f,0", x);
  40. last_empty = true;
  41. } else {
  42. snprintf(scratch1, SCRATCH_SIZE, "%c%.1f,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_meas_history[i]));
  43. snprintf(scratch2, SCRATCH_SIZE, "%c%.1f,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_tset_history[i]));
  44. last_empty = false;
  45. }
  46. suc = append_realloc(&path1, &pcap1, scratch1);
  47. assert(suc);
  48. suc = append_realloc(&path2, &pcap2, scratch2);
  49. assert(suc);
  50. }
  51. tpl_kv_add_heapstr(&kv, "ser-act", path1);
  52. tpl_kv_add_heapstr(&kv, "ser-set", path2);
  53. bool ena = fire_enabled();
  54. tpl_kv_add(&kv, "fire_dis_ck", ena ? "" : "selected");
  55. tpl_kv_add(&kv, "fire_en_ck", ena ? "selected" : "");
  56. tpl_kv_add_int(&kv, "timeshift", history_counter);
  57. snprintf(scratch1, SCRATCH_SIZE, "%.2f", analog_read());
  58. tpl_kv_add(&kv, "temp", scratch1);
  59. snprintf(scratch1, SCRATCH_SIZE, "%.0f", fire_get_setpoint(false));
  60. tpl_kv_add(&kv, "tset", scratch1);
  61. float kp, ki, kd;
  62. fire_get_tuning(&kp, &ki, &kd);
  63. snprintf(scratch1, SCRATCH_SIZE, "%.3f", kp);
  64. tpl_kv_add(&kv, "kp", scratch1);
  65. snprintf(scratch1, SCRATCH_SIZE, "%.3f", ki);
  66. tpl_kv_add(&kv, "ki", scratch1);
  67. snprintf(scratch1, SCRATCH_SIZE, "%.3f", kd);
  68. tpl_kv_add(&kv, "kd", scratch1);
  69. #undef SCRATCH_SIZE
  70. return kv;
  71. }
  72. /* Main page */
  73. static esp_err_t handler_index(httpd_req_t *req)
  74. {
  75. struct tpl_kv_list kv = build_index_replacements_kv();
  76. esp_err_t suc = httpd_send_template_file(req, FILE_INDEX_HTML, tpl_kv_replacer, &kv, 0);
  77. tpl_kv_free(&kv);
  78. return suc;
  79. }
  80. /* Update XHR for new index page data */
  81. static esp_err_t handler_update(httpd_req_t *req)
  82. {
  83. struct tpl_kv_list kv = build_index_replacements_kv();
  84. esp_err_t suc = tpl_kv_send_as_ascii_map(req, &kv);
  85. tpl_kv_free(&kv);
  86. return suc;
  87. }
  88. /* Set a param */
  89. static esp_err_t handler_set(httpd_req_t *req)
  90. {
  91. char buf[200];
  92. int n = httpd_req_recv(req, buf, 63);
  93. if (n < 0) {
  94. ESP_LOGW(TAG, "rx er");
  95. goto err;
  96. }
  97. buf[n]=0;
  98. char val[20];
  99. // select box 0/1
  100. esp_err_t rv = httpd_query_key_value(buf, "fire", val, 20);
  101. if (rv == ESP_OK) {
  102. fire_enable(val[0] == '1');
  103. }
  104. rv = httpd_query_key_value(buf, "tset", val, 20);
  105. if (rv == ESP_OK) {
  106. errno = 0;
  107. float f = atoff(val);
  108. if (!errno) {
  109. fire_setlevel(f);
  110. }
  111. }
  112. float kp, ki, kd;
  113. // sorry
  114. // Kp
  115. rv = httpd_query_key_value(buf, "kp", val, 20);
  116. if (rv == ESP_OK) {
  117. errno = 0;
  118. kp = atoff(val);
  119. if (!errno) {
  120. // Ki
  121. rv = httpd_query_key_value(buf, "ki", val, 20);
  122. if (rv == ESP_OK) {
  123. errno = 0;
  124. ki = atoff(val);
  125. if (!errno) {
  126. // Kd
  127. rv = httpd_query_key_value(buf, "kd", val, 20);
  128. if (rv == ESP_OK) {
  129. errno = 0;
  130. kd = atoff(val);
  131. if (!errno) {
  132. fire_set_tuning(kp, ki, kd);
  133. // save to NVS
  134. nvs_handle nvs;
  135. ESP_ERROR_CHECK(nvs_open("config", NVS_READWRITE, &nvs));
  136. union uf32 ukp, uki, ukd;
  137. ukp.f = kp;
  138. uki.f = ki;
  139. ukd.f = kd;
  140. nvs_set_u32(nvs, "kp", ukp.u);
  141. nvs_set_u32(nvs, "ki", uki.u);
  142. nvs_set_u32(nvs, "kd", ukd.u);
  143. nvs_commit(nvs);
  144. nvs_close(nvs);
  145. }
  146. }
  147. }
  148. }
  149. }
  150. }
  151. httpd_resp_set_hdr(req, "Location", "/");
  152. httpd_resp_set_status(req, "302 Found");
  153. return httpd_resp_send(req, NULL, 0);
  154. err:
  155. return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL);
  156. }
  157. /* Request emulator reboot */
  158. static esp_err_t handler_reboot(httpd_req_t *req)
  159. {
  160. httpd_resp_send(req, "<!DOCTYPE html><html><head>"
  161. "<meta http-equiv=\"refresh\" content=\"10; url=/\">"
  162. "</head>"
  163. "<body>"
  164. "Reboot requested. Reloading in 10 seconds.<br>"
  165. "<a href=\"/\">Try now.</a>", -1);
  166. ESP_LOGI(TAG, "Restarting ESP...");
  167. esp_restart();
  168. }
  169. /* An HTTP GET handler */
  170. static esp_err_t handler_staticfiles(httpd_req_t *r)
  171. {
  172. const struct embedded_file_info *file;
  173. const char *fname = r->user_ctx;
  174. enum file_access_level access = FILE_ACCESS_PROTECTED;
  175. // wildcard files must be public
  176. if (fname == NULL) {
  177. fname = r->uri + 1; // URI always starts with slash, but we dont want a slash in the file name
  178. access = FILE_ACCESS_PUBLIC;
  179. }
  180. #ifdef USE_CAPTIVE_PORTAL
  181. // First check if this is a phone taken here by the captive portal
  182. esp_err_t rv = httpd_captive_redirect(r);
  183. if (rv != ESP_ERR_NOT_FOUND) return rv;
  184. #endif
  185. if (ESP_OK != www_get_static_file(fname, access, &file)) {
  186. ESP_LOGW(TAG, "File not found: %s", fname);
  187. return httpd_resp_send_404(r);
  188. }
  189. else {
  190. if (streq(file->mime, "text/html")) {
  191. // using the template func to allow includes
  192. return httpd_send_template_file_struct(r, file, /*replacer*/NULL, /*ctx*/NULL, /*opts*/0);
  193. } else {
  194. return httpd_send_static_file_struct(r, file, TPL_ESCAPE_NONE, 0);
  195. }
  196. }
  197. }
  198. static const httpd_uri_t routes[] = {
  199. {
  200. .uri = "/",
  201. .method = HTTP_GET,
  202. .handler = handler_index,
  203. },
  204. {
  205. .uri = "/data",
  206. .method = HTTP_GET,
  207. .handler = handler_update,
  208. },
  209. {
  210. .uri = "/set",
  211. .method = HTTP_POST,
  212. .handler = handler_set,
  213. },
  214. {
  215. .uri = "/reboot",
  216. .method = HTTP_GET,
  217. .handler = handler_reboot,
  218. },
  219. {
  220. .uri = "*", // any file except protected (e.g. not HTML, PEM etc)
  221. .method = HTTP_GET,
  222. .handler = handler_staticfiles,
  223. },
  224. };
  225. esp_err_t websrv_init(void)
  226. {
  227. httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  228. config.stack_size = 6000;
  229. config.uri_match_fn = httpd_uri_match_wildcard;
  230. config.lru_purge_enable = true;
  231. ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port);
  232. esp_err_t suc = httpd_start(&s_hServer, &config);
  233. if (suc == ESP_OK) {
  234. ESP_LOGI(TAG, "HTTP server started");
  235. for (int i = 0; i < sizeof(routes) / sizeof(httpd_uri_t); i++) {
  236. httpd_register_uri_handler(s_hServer, &routes[i]);
  237. }
  238. return ESP_OK;
  239. }
  240. ESP_LOGE(TAG, "Error starting server!");
  241. return suc;
  242. }