File
Blob: archive/sfu-bringup/firmware/main/hardware_check.c
| 1 | #include <inttypes.h> |
| 2 | #include <stdio.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <string.h> |
| 5 | #include <time.h> |
| 6 | #include "driver/gpio.h" |
| 7 | #include "esp_chip_info.h" |
| 8 | #include "esp_crt_bundle.h" |
| 9 | #include "esp_event.h" |
| 10 | #include "esp_flash.h" |
| 11 | #include "esp_heap_caps.h" |
| 12 | #include "esp_http_client.h" |
| 13 | #include "esp_log.h" |
| 14 | #include "esp_netif.h" |
| 15 | #include "esp_netif_sntp.h" |
| 16 | #include "esp_psram.h" |
| 17 | #include "esp_system.h" |
| 18 | #include "esp_timer.h" |
| 19 | #include "esp_wifi.h" |
| 20 | #include "freertos/FreeRTOS.h" |
| 21 | #include "freertos/event_groups.h" |
| 22 | #include "freertos/task.h" |
| 23 | #include "led_strip.h" |
| 24 | #include "peer_probe.h" |
| 25 | #include "wifi_private.h" |
| 26 | |
| 27 | static const char *TAG = "hardware_check"; |
| 28 | static EventGroupHandle_t wifi_events; |
| 29 | static led_strip_handle_t led; |
| 30 | static unsigned reconnects; |
| 31 | static bool want_connection; |
| 32 | |
| 33 | void probe_emit(cJSON *message) |
| 34 | { |
| 35 | if (!message) return; |
| 36 | char *text = cJSON_PrintUnformatted(message); |
| 37 | if (text) { |
| 38 | printf("PROBE %s\n", text); |
| 39 | fflush(stdout); |
| 40 | free(text); |
| 41 | } |
| 42 | cJSON_Delete(message); |
| 43 | } |
| 44 | |
| 45 | void probe_set_led(unsigned red, unsigned green, unsigned blue) |
| 46 | { |
| 47 | if (!led) return; |
| 48 | esp_err_t err = led_strip_set_pixel(led, 0, red & 255, green & 255, blue & 255); |
| 49 | if (err == ESP_OK) err = led_strip_refresh(led); |
| 50 | cJSON *msg = cJSON_CreateObject(); |
| 51 | cJSON_AddStringToObject(msg, "event", "led"); |
| 52 | cJSON_AddNumberToObject(msg, "result", err); |
| 53 | cJSON_AddNumberToObject(msg, "r", red & 255); |
| 54 | cJSON_AddNumberToObject(msg, "g", green & 255); |
| 55 | cJSON_AddNumberToObject(msg, "b", blue & 255); |
| 56 | probe_emit(msg); |
| 57 | } |
| 58 | |
| 59 | static bool test_psram(void) |
| 60 | { |
| 61 | const size_t bytes = 12 * 1024 * 1024; |
| 62 | volatile uint32_t *memory = heap_caps_malloc(bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); |
| 63 | if (!memory) { |
| 64 | ESP_LOGE(TAG, "PSRAM test: allocation of %u bytes failed", (unsigned)bytes); |
| 65 | return false; |
| 66 | } |
| 67 | for (unsigned pass = 0; pass < 4; pass++) { |
| 68 | for (size_t i = 0; i < bytes / 4; i++) { |
| 69 | uint32_t value = pass == 0 ? 0 : pass == 1 ? UINT32_MAX : (uint32_t)i * 2654435761u; |
| 70 | memory[i] = pass == 3 ? ~value : value; |
| 71 | if ((i & 0x3ffff) == 0) vTaskDelay(1); |
| 72 | } |
| 73 | for (size_t i = 0; i < bytes / 4; i++) { |
| 74 | uint32_t value = pass == 0 ? 0 : pass == 1 ? UINT32_MAX : (uint32_t)i * 2654435761u; |
| 75 | if (pass == 3) value = ~value; |
| 76 | if (memory[i] != value) { |
| 77 | ESP_LOGE(TAG, "PSRAM mismatch: pass=%u word=%u", pass, (unsigned)i); |
| 78 | free((void *)memory); |
| 79 | return false; |
| 80 | } |
| 81 | if ((i & 0x3ffff) == 0) vTaskDelay(1); |
| 82 | } |
| 83 | ESP_LOGI(TAG, "PSRAM pass %u/4: 12 MiB verified", pass + 1); |
| 84 | } |
| 85 | free((void *)memory); |
| 86 | return heap_caps_check_integrity_all(true); |
| 87 | } |
| 88 | |
| 89 | static void wifi_event(void *arg, esp_event_base_t base, int32_t id, void *data) |
| 90 | { |
| 91 | if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) { |
| 92 | xEventGroupSetBits(wifi_events, BIT0); |
| 93 | } else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED && want_connection) { |
| 94 | wifi_event_sta_disconnected_t *event = data; |
| 95 | ESP_LOGW(TAG, "Wi-Fi disconnected: reason=%u", event->reason); |
| 96 | if (reconnects++ < 3) esp_wifi_connect(); |
| 97 | else xEventGroupSetBits(wifi_events, BIT1); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | static bool test_wifi(void) |
| 102 | { |
| 103 | ESP_ERROR_CHECK(esp_netif_init()); |
| 104 | ESP_ERROR_CHECK(esp_event_loop_create_default()); |
| 105 | esp_netif_create_default_wifi_sta(); |
| 106 | wifi_events = xEventGroupCreate(); |
| 107 | wifi_init_config_t init = WIFI_INIT_CONFIG_DEFAULT(); |
| 108 | init.nvs_enable = 0; |
| 109 | ESP_ERROR_CHECK(esp_wifi_init(&init)); |
| 110 | ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); |
| 111 | ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event, NULL)); |
| 112 | ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event, NULL)); |
| 113 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); |
| 114 | ESP_ERROR_CHECK(esp_wifi_start()); |
| 115 | wifi_scan_config_t scan = {.scan_type = WIFI_SCAN_TYPE_PASSIVE}; |
| 116 | esp_err_t scan_result = esp_wifi_scan_start(&scan, true); |
| 117 | uint16_t count = 0; |
| 118 | if (scan_result == ESP_OK) esp_wifi_scan_get_ap_num(&count); |
| 119 | ESP_LOGI(TAG, "Wi-Fi passive scan: result=%s networks=%u", esp_err_to_name(scan_result), count); |
| 120 | esp_wifi_clear_ap_list(); |
| 121 | |
| 122 | wifi_config_t config = {0}; |
| 123 | memcpy(config.sta.ssid, WIFI_SSID, sizeof(WIFI_SSID) - 1); |
| 124 | memcpy(config.sta.password, WIFI_PASSWORD, sizeof(WIFI_PASSWORD) - 1); |
| 125 | config.sta.threshold.authmode = sizeof(WIFI_PASSWORD) > 1 ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN; |
| 126 | config.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH; |
| 127 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &config)); |
| 128 | ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); |
| 129 | want_connection = true; |
| 130 | ESP_ERROR_CHECK(esp_wifi_connect()); |
| 131 | EventBits_t bits = xEventGroupWaitBits(wifi_events, BIT0 | BIT1, false, false, pdMS_TO_TICKS(35000)); |
| 132 | if (!(bits & BIT0)) return false; |
| 133 | wifi_ap_record_t ap = {0}; |
| 134 | ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap)); |
| 135 | ESP_LOGI(TAG, "Wi-Fi associated and DHCP complete: RSSI=%d dBm channel=%u auth=%u", ap.rssi, ap.primary, ap.authmode); |
| 136 | return true; |
| 137 | } |
| 138 | |
| 139 | static bool test_https(void) |
| 140 | { |
| 141 | esp_sntp_config_t time_config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(2, |
| 142 | ESP_SNTP_SERVER_LIST("time.cloudflare.com", "pool.ntp.org")); |
| 143 | ESP_ERROR_CHECK(esp_netif_sntp_init(&time_config)); |
| 144 | esp_err_t sync = esp_netif_sntp_sync_wait(pdMS_TO_TICKS(12000)); |
| 145 | ESP_LOGI(TAG, "NTP synchronization: %s", esp_err_to_name(sync)); |
| 146 | if (sync != ESP_OK) return false; |
| 147 | esp_http_client_config_t config = { |
| 148 | .url = "https://www.cloudflare.com/cdn-cgi/trace", |
| 149 | .crt_bundle_attach = esp_crt_bundle_attach, |
| 150 | .timeout_ms = 12000, |
| 151 | }; |
| 152 | esp_http_client_handle_t client = esp_http_client_init(&config); |
| 153 | if (!client) return false; |
| 154 | esp_err_t result = esp_http_client_perform(client); |
| 155 | int status = esp_http_client_get_status_code(client); |
| 156 | ESP_LOGI(TAG, "HTTPS with certificate verification: result=%s status=%d", esp_err_to_name(result), status); |
| 157 | esp_http_client_cleanup(client); |
| 158 | return result == ESP_OK && status == 200; |
| 159 | } |
| 160 | |
| 161 | static void heartbeat(void *arg) |
| 162 | { |
| 163 | bool down = false; |
| 164 | unsigned count = 0; |
| 165 | for (;;) { |
| 166 | bool now = gpio_get_level(GPIO_NUM_0) == 0; |
| 167 | if (now && !down) ESP_LOGI(TAG, "BOOT button pressed: count=%u", ++count); |
| 168 | down = now; |
| 169 | vTaskDelay(pdMS_TO_TICKS(30)); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | void app_main(void) |
| 174 | { |
| 175 | setvbuf(stdout, NULL, _IONBF, 0); |
| 176 | esp_log_level_set("wifi", ESP_LOG_WARN); |
| 177 | esp_chip_info_t chip; |
| 178 | esp_chip_info(&chip); |
| 179 | uint32_t flash_bytes = 0; |
| 180 | ESP_ERROR_CHECK(esp_flash_get_size(NULL, &flash_bytes)); |
| 181 | ESP_LOGI(TAG, "Chip cores=%u revision=%u flash=%" PRIu32 " PSRAM=%u", chip.cores, chip.revision, |
| 182 | flash_bytes, (unsigned)esp_psram_get_size()); |
| 183 | bool ram_ok = test_psram(); |
| 184 | led_strip_config_t strip_config = {.strip_gpio_num = 38, .max_leds = 1}; |
| 185 | led_strip_rmt_config_t rmt_config = {.resolution_hz = 10000000}; |
| 186 | ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led)); |
| 187 | probe_set_led(16, 0, 0); |
| 188 | vTaskDelay(pdMS_TO_TICKS(600)); |
| 189 | probe_set_led(0, 16, 0); |
| 190 | vTaskDelay(pdMS_TO_TICKS(600)); |
| 191 | probe_set_led(0, 0, 16); |
| 192 | gpio_config_t button = {.pin_bit_mask = 1ULL << GPIO_NUM_0, .mode = GPIO_MODE_INPUT, |
| 193 | .pull_up_en = GPIO_PULLUP_ENABLE}; |
| 194 | ESP_ERROR_CHECK(gpio_config(&button)); |
| 195 | bool wifi_ok = test_wifi(); |
| 196 | bool https_ok = wifi_ok && test_https(); |
| 197 | probe_set_led(0, ram_ok && https_ok ? 16 : 0, ram_ok && https_ok ? 0 : 16); |
| 198 | cJSON *summary = cJSON_CreateObject(); |
| 199 | cJSON_AddStringToObject(summary, "event", "hardware_ready"); |
| 200 | cJSON_AddBoolToObject(summary, "psram_test", ram_ok); |
| 201 | cJSON_AddBoolToObject(summary, "wifi", wifi_ok); |
| 202 | cJSON_AddBoolToObject(summary, "https", https_ok); |
| 203 | cJSON_AddNumberToObject(summary, "flash_bytes", flash_bytes); |
| 204 | cJSON_AddNumberToObject(summary, "psram_bytes", esp_psram_get_size()); |
| 205 | cJSON_AddNumberToObject(summary, "internal_free", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); |
| 206 | probe_emit(summary); |
| 207 | xTaskCreate(heartbeat, "button", 2048, NULL, 2, NULL); |
| 208 | char *line = calloc(1, 16384); |
| 209 | if (!line) abort(); |
| 210 | size_t used = 0; |
| 211 | bool overflow = false; |
| 212 | for (;;) { |
| 213 | int ch = getchar(); |
| 214 | if (ch == EOF) { |
| 215 | clearerr(stdin); |
| 216 | vTaskDelay(pdMS_TO_TICKS(20)); |
| 217 | } else if (ch == '\n') { |
| 218 | line[used] = 0; |
| 219 | cJSON *cmd = overflow ? NULL : cJSON_Parse(line); |
| 220 | if (cmd) { |
| 221 | probe_command(cmd); |
| 222 | cJSON_Delete(cmd); |
| 223 | } |
| 224 | used = 0; |
| 225 | overflow = false; |
| 226 | } else if (ch != '\r' && !overflow) { |
| 227 | if (used < 16383) line[used++] = ch; |
| 228 | else overflow = true; |
| 229 | } |
| 230 | } |
| 231 | } |