Skip to content
File

Blob: archive/sfu-bringup/firmware/main/hardware_check.c

c232 lines
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 
27static const char *TAG = "hardware_check";
28static EventGroupHandle_t wifi_events;
29static led_strip_handle_t led;
30static unsigned reconnects;
31static bool want_connection;
32 
33void 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 
45void 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 
59static 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 
89static 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 
101static 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 
139static 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 
161static 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 
173void 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}