main.c (12464B)
1#include "esp_netif_ip_addr.h" 2#include "esp_wifi_types_generic.h" 3#include "freertos/idf_additions.h" 4#include "ssd1306.h" 5 6#include "freertos/FreeRTOS.h" 7#include "freertos/task.h" 8#include "portmacro.h" 9#include "driver/gpio.h" 10#include "hal/gpio_types.h" 11#include "esp_wifi.h" 12#include "esp_http_server.h" 13#include "esp_log.h" 14#include "nvs_flash.h" 15 16#include <string.h> 17#include <stdio.h> 18 19#define TICKS_MS(x) ((x) / portTICK_PERIOD_MS) 20#define LOG(...) ESP_LOGI("desk-andon", __VA_ARGS__) 21#define PANIC(...) lcd_panic(__VA_ARGS__); 22 23/* The event group allows multiple bits for each event, but we only care about two events: 24 * - we are connected to the AP with an IP 25 * - we failed to connect after the maximum amount of retries */ 26#define WIFI_CONNECTED_BIT BIT0 27 28#define WIFI_ATTEMPT_MAX 5 29 30#define WIFI_SSID "andon-net" 31#define WIFI_PASS "uzw00ystu1" 32 33enum Lane { 34 RED, 35 ORANGE, 36 GREEN, 37 BLUE, 38 WHITE, 39 SPEAKER, 40}; 41 42static const uint8_t bitmap_frame[] = { 430xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 440xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 450xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 460xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 470xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 480xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 490xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 500xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 510xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 520xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 530xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 540xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 550xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 560xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 570xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 580xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 590xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 600xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 610xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 620xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 630xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 640xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 650xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 660xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 670xc0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 680xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 690xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 700xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 71}; 72 73static const uint8_t bitmap_light1[] = { 740x07, 0x00, 0x0e, 0x0e, 0x00, 0x1c, 0x1c, 0x00, 0x38, 0x38, 0x00, 0x70, 0x70, 0x00, 0xe0, 0xe0, 750x01, 0xc0, 0xc0, 0x03, 0x80, 0x80, 0x07, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x38, 760x00, 0x00, 0x70, 0x00, 0x00, 0xe0, 0x01, 0x01, 0xc0, 0x03, 0x03, 0x80, 0x07, 0x07, 0x00, 0x0e, 770x0e, 0x00, 0x1c, 0x1c, 0x00, 0x38, 0x38, 0x00, 0x70, 0x70, 0x00, 0xe0, 0xe0, 0x01, 0xc0, 0xc0, 780x03, 0x80 79}; 80 81static const uint8_t bitmap_light2[] = { 820x00, 0x1c, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xe0, 0x00, 0x01, 0xc0, 0x00, 0x03, 830x80, 0x00, 0x07, 0x00, 0x01, 0x0e, 0x00, 0x03, 0x1c, 0x00, 0x07, 0x38, 0x00, 0x0e, 0x70, 0x00, 840x1c, 0xe0, 0x00, 0x38, 0xc0, 0x00, 0x70, 0x80, 0x00, 0xe0, 0x00, 0x01, 0xc0, 0x00, 0x03, 0x80, 850x00, 0x07, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 860xe0, 0x00 87}; 88 89static const int ANDON_PINS[SPEAKER+1] = { 10, 6, 3, 5, 7, 4 }; 90static const char *ANDON_NAMES[SPEAKER+1] = { 91 [RED] = "red", 92 [ORANGE] = "orange", 93 [GREEN] = "green", 94 [BLUE] = "blue", 95 [WHITE] = "white", 96 [SPEAKER] = "speaker", 97}; 98 99static uint8_t state = 0; 100 101static SSD1306_t display = { 0 }; 102static bool lcd_init = false; 103static TaskHandle_t lcd_task = NULL; 104 105static char mac_str[16] = { 0 }; 106static char ip_str[17] = { 0 }; 107static bool wifi_connected = false; 108static int wifi_connect_attempt = 0; 109static EventGroupHandle_t wifi_event_group; 110 111static esp_err_t http_get_handler(httpd_req_t *req); 112static esp_err_t http_post_handler(httpd_req_t *req); 113static const httpd_uri_t http_get = { 114 .uri = "/", 115 .method = HTTP_GET, 116 .handler = http_get_handler, 117 .user_ctx = NULL 118}; 119static const httpd_uri_t http_post = { 120 .uri = "/", 121 .method = HTTP_POST, 122 .handler = http_post_handler, 123 .user_ctx = NULL 124}; 125 126static void 127lcd_task_main(void *_p) 128{ 129 bool frame_state[SPEAKER] = { 0 }; 130 char mac_str_old[16] = { 0 }; 131 char mac_str_new[16] = { 0 }; 132 char ip_str_old[17] = { 0 }; 133 char ip_str_new[17] = { 0 }; 134 135 TickType_t last = 0; 136 while (1) { 137 TickType_t now = xTaskGetTickCount(); 138 if (!last || (now - last) * portTICK_PERIOD_MS >= 500) { 139 uint8_t current = state; 140 memcpy(ip_str_new, ip_str, sizeof(ip_str)); 141 memcpy(mac_str_new, mac_str, sizeof(mac_str)); 142 bool connected = wifi_connected; 143 144 for (int i = 0; i < SPEAKER; i++) { 145 if (current & (1 << i)) { 146 frame_state[i] ^= true; 147 } 148 } 149 last = now; 150 151 if (!lcd_init) { 152 _ssd1306_clear(&display, false); 153 _ssd1306_bitmaps(&display, 0, 0, bitmap_frame, 128, 28, false); 154 lcd_init = true; 155 } 156 157 const int xoff = (128 - 24 * 5 - 4) / 2; 158 for (int i = 0; i < SPEAKER; i++) { 159 if (current & (1 << i)) { 160 _ssd1306_bitmaps(&display, xoff + i * 25, 3, 161 frame_state[i] ? bitmap_light2 : bitmap_light1, 162 24, 22, false); 163 } else { 164 _ssd1306_clear_rect(&display, xoff + i * 25, 3, 24, 22, false); 165 } 166 } 167 168 169 if (strcmp(mac_str_old, mac_str_new)) { 170 _ssd1306_display_text(&display, 4, 40, 171 mac_str_new, strlen(mac_str_new), false); 172 strncpy(mac_str_old, mac_str_new, sizeof(mac_str)); 173 } 174 175 if (strcmp(ip_str_old, ip_str_new)) { 176 _ssd1306_display_text(&display, connected ? 4 : 0, 52, 177 ip_str_new, strlen(ip_str_new), false); 178 strncpy(ip_str_old, ip_str_new, sizeof(ip_str)); 179 } 180 181 ssd1306_next_frame(&display); 182 } 183 184 vTaskDelay(TICKS_MS(50)); 185 } 186} 187 188static void 189lcd_panic(const char *msg) 190{ 191 vTaskSuspend(lcd_task); 192 vTaskDelete(lcd_task); 193 ssd1306_clear_screen(&display, false); 194 ssd1306_display_text(&display, 20, 42, "PANIC", 10, false); 195 while (1) { 196 ESP_LOGE("desk-andon", "panic: %s", msg); 197 vTaskDelay(TICKS_MS(1000)); 198 } 199} 200 201static void 202set_output(uint8_t bv) 203{ 204 state = bv; 205 for (int i = 0; i <= SPEAKER; i++) { 206 gpio_set_level(ANDON_PINS[i], !(state & (1 << i))); 207 vTaskDelay(10); 208 } 209} 210 211static void 212wifi_event_handler(void *arg, esp_event_base_t event_base, 213 int32_t event_id, void *event_data) 214{ 215 if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 216 snprintf(ip_str, sizeof(ip_str), " connecting.. "); 217 vTaskDelay(1); 218 wifi_connected = false; 219 esp_wifi_connect(); 220 } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 221 wifi_connected = false; 222 esp_wifi_connect(); 223 wifi_connect_attempt++; 224 LOG("retry to connect to the AP"); 225 snprintf(ip_str, sizeof(ip_str), " connecting..%c ", 226 wifi_connect_attempt % 2 == 0 ? '.' : ' '); 227 LOG("connect to the AP fail"); 228 } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 229 ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 230 LOG("got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 231 char tmp[32]; 232 snprintf(tmp, sizeof(tmp), IPSTR, IP2STR(&event->ip_info.ip)); 233 snprintf(ip_str, sizeof(ip_str), "%-16s", tmp); 234 wifi_connect_attempt = 0; 235 wifi_connected = true; 236 xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); 237 } 238} 239 240static void 241wifi_init(void) 242{ 243 esp_err_t ret = nvs_flash_init(); 244 if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 245 ESP_ERROR_CHECK(nvs_flash_erase()); 246 ret = nvs_flash_init(); 247 } 248 ESP_ERROR_CHECK(ret); 249 250 wifi_event_group = xEventGroupCreate(); 251 ESP_ERROR_CHECK(esp_netif_init()); 252 253 ESP_ERROR_CHECK(esp_event_loop_create_default()); 254 esp_netif_create_default_wifi_sta(); 255 256 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 257 ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 258 259 uint8_t mac[6] = { 0 }; 260 esp_wifi_get_mac(WIFI_IF_STA, mac); 261 snprintf(mac_str, sizeof(mac_str), ":%02X:%02X:%02X:%02X:%02X", 262 mac[1], mac[2], mac[3], mac[4], mac[5]); 263 264 esp_event_handler_instance_t instance_any_id; 265 esp_event_handler_instance_t instance_got_ip; 266 ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 267 ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, &instance_any_id)); 268 ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 269 IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, &instance_got_ip)); 270 271 wifi_config_t wifi_config = { 272 .sta = { 273 .ssid = WIFI_SSID, 274 .password = WIFI_PASS, 275 .threshold.authmode = WIFI_AUTH_WPA2_PSK, 276 .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, 277 .sae_h2e_identifier = "", 278 }, 279 }; 280 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 281 ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 282 ESP_ERROR_CHECK(esp_wifi_start() ); 283 284 LOG("wifi_init_sta finished."); 285 286 /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) 287 * or connection failed for the maximum number of re-tries (WIFI_FAIL_BIT). 288 * The bits are set by event_handler() (see above) */ 289 xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, 290 pdFALSE, pdFALSE, portMAX_DELAY); 291} 292 293static esp_err_t 294http_get_handler(httpd_req_t *req) 295{ 296 uint8_t current = state; 297 for (int i = 0; i <= SPEAKER; i++) { 298 if (current & (1 << i)) { 299 httpd_resp_send_chunk(req, ANDON_NAMES[i], 300 HTTPD_RESP_USE_STRLEN); 301 httpd_resp_send_chunk(req, " ", 1); 302 } 303 } 304 httpd_resp_send_chunk(req, NULL, 0); 305 return ESP_OK; 306} 307 308static esp_err_t 309http_post_handler(httpd_req_t *req) 310{ 311 char buf[128] = { 0 }; 312 int ret = httpd_req_recv(req, buf, 313 MIN(sizeof(buf)-1, req->content_len)); 314 if ((ret <= 0)) { 315 httpd_resp_send(req, "fail", 4); 316 return ESP_FAIL; 317 } 318 uint8_t next = 0; 319 for (char *c = buf; c < buf + 128; ) { 320 size_t n = strcspn(c, "\t \n"); 321 for (int i = 0; n > 0 && i <= SPEAKER; i++) { 322 if (n == strlen(ANDON_NAMES[i]) 323 && !strncmp(c, ANDON_NAMES[i], n)) { 324 next |= 1 << i; 325 httpd_resp_send_chunk(req, ANDON_NAMES[i], n); 326 httpd_resp_send_chunk(req, " ", 1); 327 } 328 } 329 c += n + 1; 330 } 331 set_output(next); 332 httpd_resp_send_chunk(req, NULL, 0); 333 return ESP_OK; 334} 335 336static void 337http_init(void) 338{ 339 httpd_handle_t server = NULL; 340 httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 341 config.lru_purge_enable = true; 342 343 LOG("Starting http server"); 344 345 if (httpd_start(&server, &config) != ESP_OK) 346 PANIC("failed to start http server"); 347 348 httpd_register_uri_handler(server, &http_get); 349 httpd_register_uri_handler(server, &http_post); 350} 351 352static void 353test_init(void) 354{ 355 uint8_t bv = 0; 356 int tick = 0; 357 while (1) { 358 printf("Tick %i\n", ++tick); 359 set_output(1 << bv); 360 bv = (bv + 1) % (SPEAKER + 1); 361 vTaskDelay(TICKS_MS(1000)); 362 } 363} 364 365void 366app_main(void) 367{ 368 for (int i = 0; i <= SPEAKER; i++) { 369 gpio_reset_pin(ANDON_PINS[i]); 370 gpio_set_direction(ANDON_PINS[i], GPIO_MODE_OUTPUT_OD); 371 gpio_set_level(ANDON_PINS[i], 1); 372 } 373 374 printf("DESK ANDON\n"); 375 i2c_master_init(&display, 0, 1, -1); 376 ssd1306_init(&display, 128, 64); 377 ssd1306_clear_screen(&display, false); 378 ssd1306_display_text(&display, 3, 24, "DESK ANDON", 10, false); 379 vTaskDelay(TICKS_MS(500)); 380 381 xTaskCreate(lcd_task_main, "lcd", 16 * 1024, NULL, tskIDLE_PRIORITY, &lcd_task); 382 vTaskDelay(TICKS_MS(500)); 383 384 // test_init(); 385 386 wifi_init(); 387 388 http_init(); 389 390 while (1) vTaskDelay(1000); 391}