feat(factory-cli): add Wi-Fi credential management commands
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
#include "control_plane_internal.h"
|
#include "control_plane_internal.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -29,7 +30,9 @@
|
|||||||
#define FACTORY_CLI_DEVICE_ID_MAX_LEN 64
|
#define FACTORY_CLI_DEVICE_ID_MAX_LEN 64
|
||||||
#define FACTORY_CLI_SERIAL_MAX_LEN 64
|
#define FACTORY_CLI_SERIAL_MAX_LEN 64
|
||||||
#define FACTORY_CLI_JSON_MAX_LEN 640
|
#define FACTORY_CLI_JSON_MAX_LEN 640
|
||||||
|
#define FACTORY_CLI_WIFI_JSON_MAX_LEN 3072
|
||||||
#define FACTORY_CLI_READ_TIMEOUT_MS 100
|
#define FACTORY_CLI_READ_TIMEOUT_MS 100
|
||||||
|
#define FACTORY_CLI_RESET_REPLY_DELAY_MS 100
|
||||||
#ifndef CONFIG_TQ_FACTORY_CLI_TASK_STACK_BYTES
|
#ifndef CONFIG_TQ_FACTORY_CLI_TASK_STACK_BYTES
|
||||||
#define CONFIG_TQ_FACTORY_CLI_TASK_STACK_BYTES (10U * 1024U)
|
#define CONFIG_TQ_FACTORY_CLI_TASK_STACK_BYTES (10U * 1024U)
|
||||||
#endif
|
#endif
|
||||||
@@ -184,23 +187,56 @@ static void trim_text(char *value)
|
|||||||
static size_t split_args(char *line, char *argv[], size_t max_args)
|
static size_t split_args(char *line, char *argv[], size_t max_args)
|
||||||
{
|
{
|
||||||
size_t argc = 0;
|
size_t argc = 0;
|
||||||
char *cursor = line;
|
char *read_cursor = line;
|
||||||
while (*cursor != '\0' && argc < max_args) {
|
char *write_cursor = line;
|
||||||
while (*cursor != '\0' && isspace((unsigned char)(*cursor))) {
|
|
||||||
cursor++;
|
while (*read_cursor != '\0') {
|
||||||
|
while (*read_cursor != '\0' && isspace((unsigned char)(*read_cursor))) {
|
||||||
|
read_cursor++;
|
||||||
}
|
}
|
||||||
if (*cursor == '\0') {
|
if (*read_cursor == '\0') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
argv[argc++] = cursor;
|
if (argc >= max_args) {
|
||||||
while (*cursor != '\0' && !isspace((unsigned char)(*cursor))) {
|
return SIZE_MAX;
|
||||||
cursor++;
|
|
||||||
}
|
}
|
||||||
if (*cursor == '\0') {
|
|
||||||
|
argv[argc++] = write_cursor;
|
||||||
|
char quote = '\0';
|
||||||
|
while (*read_cursor != '\0') {
|
||||||
|
char ch = *read_cursor;
|
||||||
|
if (quote != '\0') {
|
||||||
|
if (ch == quote) {
|
||||||
|
quote = '\0';
|
||||||
|
read_cursor++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isspace((unsigned char)ch)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*cursor = '\0';
|
if (ch == '\'' || ch == '"') {
|
||||||
cursor++;
|
quote = ch;
|
||||||
|
read_cursor++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '\\' && read_cursor[1] != '\0') {
|
||||||
|
read_cursor++;
|
||||||
|
ch = *read_cursor;
|
||||||
|
}
|
||||||
|
*write_cursor++ = ch;
|
||||||
|
read_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote != '\0') {
|
||||||
|
return SIZE_MAX;
|
||||||
|
}
|
||||||
|
while (*read_cursor != '\0' && isspace((unsigned char)(*read_cursor))) {
|
||||||
|
read_cursor++;
|
||||||
|
}
|
||||||
|
*write_cursor++ = '\0';
|
||||||
}
|
}
|
||||||
return argc;
|
return argc;
|
||||||
}
|
}
|
||||||
@@ -226,6 +262,150 @@ static void cli_write_error(const char *message)
|
|||||||
cli_write_line(line);
|
cli_write_line(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool json_append_text(char *out,
|
||||||
|
size_t out_len,
|
||||||
|
size_t *offset,
|
||||||
|
const char *text)
|
||||||
|
{
|
||||||
|
if (out == NULL || offset == NULL || text == NULL || *offset >= out_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t text_len = strlen(text);
|
||||||
|
if (text_len >= out_len - *offset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(out + *offset, text, text_len);
|
||||||
|
*offset += text_len;
|
||||||
|
out[*offset] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool json_append_escaped_string(char *out,
|
||||||
|
size_t out_len,
|
||||||
|
size_t *offset,
|
||||||
|
const char *value)
|
||||||
|
{
|
||||||
|
if (!json_append_text(out, out_len, offset, "\"")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const unsigned char *cursor = (const unsigned char *)value;
|
||||||
|
*cursor != '\0';
|
||||||
|
++cursor) {
|
||||||
|
char escape[8] = {0};
|
||||||
|
const char *encoded = escape;
|
||||||
|
switch (*cursor) {
|
||||||
|
case '"':
|
||||||
|
encoded = "\\\"";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
encoded = "\\\\";
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
encoded = "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
encoded = "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
encoded = "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
encoded = "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
encoded = "\\t";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (*cursor < 0x20U) {
|
||||||
|
snprintf(escape, sizeof(escape), "\\u%04x", (unsigned)*cursor);
|
||||||
|
} else {
|
||||||
|
escape[0] = (char)*cursor;
|
||||||
|
escape[1] = '\0';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!json_append_text(out, out_len, offset, encoded)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json_append_text(out, out_len, offset, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_get_wifi(void)
|
||||||
|
{
|
||||||
|
domain_wifi_saved_networks_t networks = {0};
|
||||||
|
esp_err_t err = domain_runtime_wifi_portal_get_saved_networks(&networks);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
cli_write_error("wifi_read_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char json[FACTORY_CLI_WIFI_JSON_MAX_LEN] = {0};
|
||||||
|
size_t offset = 0;
|
||||||
|
char prefix[48] = {0};
|
||||||
|
snprintf(prefix, sizeof(prefix), "{\"count\":%u,\"networks\":[", (unsigned)networks.count);
|
||||||
|
bool valid = json_append_text(json, sizeof(json), &offset, prefix);
|
||||||
|
for (size_t i = 0; valid && i < networks.count; ++i) {
|
||||||
|
char item_prefix[40] = {0};
|
||||||
|
snprintf(item_prefix,
|
||||||
|
sizeof(item_prefix),
|
||||||
|
"%s{\"priority\":%u,\"ssid\":",
|
||||||
|
i == 0U ? "" : ",",
|
||||||
|
(unsigned)i);
|
||||||
|
valid = json_append_text(json, sizeof(json), &offset, item_prefix) &&
|
||||||
|
json_append_escaped_string(json,
|
||||||
|
sizeof(json),
|
||||||
|
&offset,
|
||||||
|
networks.ssids[i]) &&
|
||||||
|
json_append_text(json, sizeof(json), &offset, "}");
|
||||||
|
}
|
||||||
|
valid = valid && json_append_text(json, sizeof(json), &offset, "]}");
|
||||||
|
if (!valid) {
|
||||||
|
cli_write_error("wifi_response_too_large");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_write_line(json);
|
||||||
|
cli_write_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_set_wifi(size_t argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc < 2 || is_text_empty(argv[1])) {
|
||||||
|
cli_write_error("missing_wifi_ssid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argc > 3) {
|
||||||
|
cli_write_error("invalid_wifi_arguments");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *password = argc >= 3 ? argv[2] : "";
|
||||||
|
esp_err_t err = domain_runtime_wifi_portal_save_credentials(argv[1], password);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
cli_write_error(err == ESP_ERR_INVALID_ARG ?
|
||||||
|
"invalid_wifi_credentials" :
|
||||||
|
"wifi_save_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_write_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_reset_wifi(void)
|
||||||
|
{
|
||||||
|
esp_err_t err = domain_runtime_wifi_portal_clear_saved_credentials();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
cli_write_error("wifi_reset_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_write_ok();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(FACTORY_CLI_RESET_REPLY_DELAY_MS));
|
||||||
|
domain_runtime_restart();
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t resolve_device_identity(char *device_id, size_t device_id_len)
|
static esp_err_t resolve_device_identity(char *device_id, size_t device_id_len)
|
||||||
{
|
{
|
||||||
char serial_number[FACTORY_CLI_SERIAL_MAX_LEN] = {0};
|
char serial_number[FACTORY_CLI_SERIAL_MAX_LEN] = {0};
|
||||||
@@ -406,6 +586,10 @@ static void handle_command_line(char *line)
|
|||||||
|
|
||||||
char *argv[FACTORY_CLI_MAX_ARGC] = {0};
|
char *argv[FACTORY_CLI_MAX_ARGC] = {0};
|
||||||
size_t argc = split_args(line, argv, FACTORY_CLI_MAX_ARGC);
|
size_t argc = split_args(line, argv, FACTORY_CLI_MAX_ARGC);
|
||||||
|
if (argc == SIZE_MAX) {
|
||||||
|
cli_write_error("invalid_command_syntax");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -443,6 +627,12 @@ static void handle_command_line(char *line)
|
|||||||
cli_write_ok();
|
cli_write_ok();
|
||||||
} else if (strcmp(cmd, "save_config") == 0) {
|
} else if (strcmp(cmd, "save_config") == 0) {
|
||||||
handle_save_config();
|
handle_save_config();
|
||||||
|
} else if (strcmp(cmd, "get_wifi") == 0) {
|
||||||
|
handle_get_wifi();
|
||||||
|
} else if (strcmp(cmd, "set_wifi") == 0) {
|
||||||
|
handle_set_wifi(argc, argv);
|
||||||
|
} else if (strcmp(cmd, "reset_wifi") == 0) {
|
||||||
|
handle_reset_wifi();
|
||||||
} else if (strcmp(cmd, "get_auth_material") == 0 ||
|
} else if (strcmp(cmd, "get_auth_material") == 0 ||
|
||||||
strcmp(cmd, "auth_material") == 0 ||
|
strcmp(cmd, "auth_material") == 0 ||
|
||||||
strcmp(cmd, "get_device_auth_material") == 0 ||
|
strcmp(cmd, "get_device_auth_material") == 0 ||
|
||||||
|
|||||||
@@ -548,6 +548,18 @@ esp_err_t domain_runtime_behavior_play_prompt_file(const char *path);
|
|||||||
void domain_runtime_wifi_portal_set_behavior_hooks(const domain_wifi_behavior_hooks_t *hooks);
|
void domain_runtime_wifi_portal_set_behavior_hooks(const domain_wifi_behavior_hooks_t *hooks);
|
||||||
esp_err_t domain_runtime_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
|
esp_err_t domain_runtime_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
|
||||||
bool domain_runtime_wifi_portal_has_saved_credentials(void);
|
bool domain_runtime_wifi_portal_has_saved_credentials(void);
|
||||||
|
|
||||||
|
#define DOMAIN_WIFI_SSID_MAX_LEN 33
|
||||||
|
#define DOMAIN_WIFI_SAVED_NETWORK_MAX_COUNT 10
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t count;
|
||||||
|
char ssids[DOMAIN_WIFI_SAVED_NETWORK_MAX_COUNT][DOMAIN_WIFI_SSID_MAX_LEN];
|
||||||
|
} domain_wifi_saved_networks_t;
|
||||||
|
|
||||||
|
/** Copy saved SSIDs in persistent priority order. Passwords are not exposed. */
|
||||||
|
esp_err_t domain_runtime_wifi_portal_get_saved_networks(
|
||||||
|
domain_wifi_saved_networks_t *out_networks);
|
||||||
bool domain_runtime_wifi_portal_sta_is_connected(void);
|
bool domain_runtime_wifi_portal_sta_is_connected(void);
|
||||||
esp_err_t domain_runtime_wifi_portal_start_config_ap(const char *ssid_prefix);
|
esp_err_t domain_runtime_wifi_portal_start_config_ap(const char *ssid_prefix);
|
||||||
bool domain_runtime_wifi_connect_error_keeps_credentials(esp_err_t err);
|
bool domain_runtime_wifi_connect_error_keeps_credentials(esp_err_t err);
|
||||||
@@ -579,7 +591,7 @@ bool domain_runtime_is_network_connect_error(esp_err_t err);
|
|||||||
esp_err_t domain_runtime_wifi_portal_save_credentials(const char *ssid, const char *password);
|
esp_err_t domain_runtime_wifi_portal_save_credentials(const char *ssid, const char *password);
|
||||||
esp_err_t domain_runtime_wifi_portal_clear_saved_credentials(void);
|
esp_err_t domain_runtime_wifi_portal_clear_saved_credentials(void);
|
||||||
|
|
||||||
#define DOMAIN_WIFI_PROVISIONING_SSID_MAX_LEN 33
|
#define DOMAIN_WIFI_PROVISIONING_SSID_MAX_LEN DOMAIN_WIFI_SSID_MAX_LEN
|
||||||
#define DOMAIN_WIFI_PROVISIONING_PASSWORD_MAX_LEN 65
|
#define DOMAIN_WIFI_PROVISIONING_PASSWORD_MAX_LEN 65
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -1286,6 +1286,21 @@ bool domain_runtime_wifi_portal_has_saved_credentials(void)
|
|||||||
return platform_wifi_portal_has_saved_credentials();
|
return platform_wifi_portal_has_saved_credentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t domain_runtime_wifi_portal_get_saved_networks(
|
||||||
|
domain_wifi_saved_networks_t *out_networks)
|
||||||
|
{
|
||||||
|
if (out_networks == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(out_networks, 0, sizeof(*out_networks));
|
||||||
|
return platform_wifi_portal_get_saved_ssids(
|
||||||
|
&out_networks->ssids[0][0],
|
||||||
|
sizeof(out_networks->ssids[0]),
|
||||||
|
DOMAIN_WIFI_SAVED_NETWORK_MAX_COUNT,
|
||||||
|
&out_networks->count);
|
||||||
|
}
|
||||||
|
|
||||||
bool domain_runtime_wifi_portal_sta_is_connected(void)
|
bool domain_runtime_wifi_portal_sta_is_connected(void)
|
||||||
{
|
{
|
||||||
return platform_wifi_portal_sta_is_connected();
|
return platform_wifi_portal_sta_is_connected();
|
||||||
|
|||||||
@@ -359,6 +359,14 @@ esp_err_t platform_behavior_play_prompt_file(const char *path);
|
|||||||
void platform_wifi_portal_set_behavior_hooks(const platform_wifi_behavior_hooks_t *hooks);
|
void platform_wifi_portal_set_behavior_hooks(const platform_wifi_behavior_hooks_t *hooks);
|
||||||
esp_err_t platform_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
|
esp_err_t platform_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
|
||||||
bool platform_wifi_portal_has_saved_credentials(void);
|
bool platform_wifi_portal_has_saved_credentials(void);
|
||||||
|
/**
|
||||||
|
* Copy saved SSIDs in persistent priority order without exposing passwords.
|
||||||
|
* `out_ssids` contains `capacity` rows of `ssid_stride` bytes.
|
||||||
|
*/
|
||||||
|
esp_err_t platform_wifi_portal_get_saved_ssids(char *out_ssids,
|
||||||
|
size_t ssid_stride,
|
||||||
|
size_t capacity,
|
||||||
|
size_t *out_count);
|
||||||
bool platform_wifi_portal_sta_is_connected(void);
|
bool platform_wifi_portal_sta_is_connected(void);
|
||||||
/** Apply standby modem power save, or restore the configured active policy. */
|
/** Apply standby modem power save, or restore the configured active policy. */
|
||||||
esp_err_t platform_wifi_portal_set_light_standby(bool standby);
|
esp_err_t platform_wifi_portal_set_light_standby(bool standby);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -26,6 +27,17 @@ esp_err_t wifi_portal_init_defaults(const char *seed_ssid, const char *seed_pass
|
|||||||
*/
|
*/
|
||||||
bool wifi_portal_has_saved_credentials(void);
|
bool wifi_portal_has_saved_credentials(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy saved SSIDs in persistent priority order.
|
||||||
|
*
|
||||||
|
* Passwords are intentionally not exposed. `out_ssids` contains `capacity`
|
||||||
|
* rows of `ssid_stride` bytes. `out_count` receives the total saved count.
|
||||||
|
*/
|
||||||
|
esp_err_t wifi_portal_get_saved_ssids(char *out_ssids,
|
||||||
|
size_t ssid_stride,
|
||||||
|
size_t capacity,
|
||||||
|
size_t *out_count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check whether the STA interface is connected (has an IP).
|
* @brief Check whether the STA interface is connected (has an IP).
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -128,6 +128,17 @@ bool platform_wifi_portal_has_saved_credentials(void)
|
|||||||
return wifi_portal_has_saved_credentials();
|
return wifi_portal_has_saved_credentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t platform_wifi_portal_get_saved_ssids(char *out_ssids,
|
||||||
|
size_t ssid_stride,
|
||||||
|
size_t capacity,
|
||||||
|
size_t *out_count)
|
||||||
|
{
|
||||||
|
return wifi_portal_get_saved_ssids(out_ssids,
|
||||||
|
ssid_stride,
|
||||||
|
capacity,
|
||||||
|
out_count);
|
||||||
|
}
|
||||||
|
|
||||||
bool platform_wifi_portal_sta_is_connected(void)
|
bool platform_wifi_portal_sta_is_connected(void)
|
||||||
{
|
{
|
||||||
return wifi_portal_sta_is_connected();
|
return wifi_portal_sta_is_connected();
|
||||||
|
|||||||
@@ -298,6 +298,34 @@ bool wifi_portal_has_saved_credentials(void)
|
|||||||
return !SsidManager::GetInstance().GetSsidList().empty();
|
return !SsidManager::GetInstance().GetSsidList().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t wifi_portal_get_saved_ssids(char *out_ssids,
|
||||||
|
size_t ssid_stride,
|
||||||
|
size_t capacity,
|
||||||
|
size_t *out_count)
|
||||||
|
{
|
||||||
|
if (out_count == nullptr ||
|
||||||
|
(capacity > 0U && (out_ssids == nullptr || ssid_stride < 33U))) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
install_wifi_storage_hooks();
|
||||||
|
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
|
||||||
|
PortalUnlockGuard guard;
|
||||||
|
|
||||||
|
const auto &ssid_list = SsidManager::GetInstance().GetSsidList();
|
||||||
|
*out_count = ssid_list.size();
|
||||||
|
if (ssid_list.size() > capacity) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ssid_list.size(); ++i) {
|
||||||
|
strlcpy(out_ssids + (i * ssid_stride),
|
||||||
|
ssid_list[i].ssid.c_str(),
|
||||||
|
ssid_stride);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool wifi_portal_sta_is_connected(void)
|
bool wifi_portal_sta_is_connected(void)
|
||||||
{
|
{
|
||||||
if (!s_wifi_manager_initialized) {
|
if (!s_wifi_manager_initialized) {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Factory CLI Wi-Fi Commands
|
||||||
|
|
||||||
|
The Factory CLI accepts commands terminated by CR, LF, or CRLF on UART0. A
|
||||||
|
successful command returns `OK`. Commands that return data emit one JSON line
|
||||||
|
before `OK`. Failures return `ERROR <reason>`.
|
||||||
|
|
||||||
|
## Read Saved Networks
|
||||||
|
|
||||||
|
```text
|
||||||
|
get_wifi
|
||||||
|
```
|
||||||
|
|
||||||
|
The response lists saved SSIDs in connection-priority order. Passwords are
|
||||||
|
never returned. Up to 10 networks can be stored.
|
||||||
|
|
||||||
|
```text
|
||||||
|
{"count":2,"networks":[{"priority":0,"ssid":"Office WiFi"},{"priority":1,"ssid":"Backup"}]}
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
An empty list is returned as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
{"count":0,"networks":[]}
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
## Save a Network
|
||||||
|
|
||||||
|
```text
|
||||||
|
set_wifi "Office WiFi" "network-password"
|
||||||
|
```
|
||||||
|
|
||||||
|
The SSID is required and may contain up to 32 bytes. The password may contain
|
||||||
|
up to 64 bytes. Quote arguments that contain spaces. A missing or explicitly
|
||||||
|
empty password saves an open network:
|
||||||
|
|
||||||
|
```text
|
||||||
|
set_wifi "Guest Network"
|
||||||
|
set_wifi "Guest Network" ""
|
||||||
|
```
|
||||||
|
|
||||||
|
Backslash escapes the following character inside or outside quoted arguments.
|
||||||
|
To include a literal backslash, enter it twice.
|
||||||
|
|
||||||
|
Saving a new SSID inserts it at priority 0. Saving an existing SSID replaces
|
||||||
|
its password and moves it to priority 0. If 10 networks are already saved,
|
||||||
|
adding a new SSID removes the lowest-priority entry. This command only updates
|
||||||
|
persistent credentials; it does not disconnect or immediately reconnect the
|
||||||
|
current station.
|
||||||
|
|
||||||
|
## Clear Saved Networks
|
||||||
|
|
||||||
|
```text
|
||||||
|
reset_wifi
|
||||||
|
```
|
||||||
|
|
||||||
|
This command clears all saved Wi-Fi credentials, returns `OK`, allows 100 ms
|
||||||
|
for the UART reply to be transmitted, and restarts the device. It has the same
|
||||||
|
persistence effect as the physical Wi-Fi reset action.
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
| Response | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `ERROR invalid_command_syntax` | An argument quote was not closed or the command contains too many arguments. |
|
||||||
|
| `ERROR wifi_read_failed` | Saved networks could not be read. |
|
||||||
|
| `ERROR wifi_response_too_large` | The saved network response exceeded the internal reply buffer. |
|
||||||
|
| `ERROR missing_wifi_ssid` | `set_wifi` did not receive a non-empty SSID. |
|
||||||
|
| `ERROR invalid_wifi_arguments` | `set_wifi` received more than an SSID and password. |
|
||||||
|
| `ERROR invalid_wifi_credentials` | The SSID or password exceeded its supported length. |
|
||||||
|
| `ERROR wifi_save_failed` | Credentials could not be saved. |
|
||||||
|
| `ERROR wifi_reset_failed` | Saved credentials could not be cleared. |
|
||||||
Reference in New Issue
Block a user