Split ESP-NOW into core/master/slave modules, block non-OTA UART traffic during updates, and hold the host serial port exclusively so dashboard polling cannot interleave with firmware uploads. Co-authored-by: Cursor <cursoragent@cursor.com>
485 lines
17 KiB
C
485 lines
17 KiB
C
#include "esp_now_master.h"
|
|
#include "client_registry.h"
|
|
#include "esp_now_comm.h"
|
|
#include "esp_now_core.h"
|
|
#include "esp_now_proto.h"
|
|
#include "board_input.h"
|
|
#include "ota_espnow.h"
|
|
#include "ota_uart.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/idf_additions.h"
|
|
#include <string.h>
|
|
|
|
static const uint8_t ESPNOW_BCAST[ESP_NOW_ETH_ALEN] = {0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff};
|
|
|
|
#define ESPNOW_DISCOVER_INTERVAL_MS 500
|
|
#define ESPNOW_HEARTBEAT_INTERVAL_MS 1000
|
|
#define ESPNOW_HEARTBEAT_MISS_COUNT 3
|
|
#define ESPNOW_CLIENT_TIMEOUT_MS \
|
|
(ESPNOW_HEARTBEAT_INTERVAL_MS * ESPNOW_HEARTBEAT_MISS_COUNT)
|
|
#define ESPNOW_BATTERY_INTERVAL_MS 30000
|
|
|
|
static const char *TAG = "[ESPNOW_M]";
|
|
|
|
static esp_err_t send_accel_stream(const uint8_t *dest_mac, uint32_t client_id,
|
|
bool enable) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_SET_ACCEL_STREAM;
|
|
msg.which_payload = alox_EspNowMessage_accel_stream_tag;
|
|
msg.payload.accel_stream.enable = enable;
|
|
msg.payload.accel_stream.client_id = client_id;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_accel_deadzone(const uint8_t *dest_mac, uint32_t client_id,
|
|
uint32_t deadzone) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_SET_ACCEL_DEADZONE;
|
|
msg.which_payload = alox_EspNowMessage_accel_deadzone_tag;
|
|
msg.payload.accel_deadzone.deadzone = deadzone;
|
|
msg.payload.accel_deadzone.client_id = client_id;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_unicast_test(const uint8_t *dest_mac, uint32_t seq) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_UNICAST_TEST;
|
|
msg.which_payload = alox_EspNowMessage_unicast_test_tag;
|
|
msg.payload.unicast_test.seq = seq;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_find_me(const uint8_t *dest_mac, uint32_t client_id) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_FIND_ME;
|
|
msg.which_payload = alox_EspNowMessage_find_me_tag;
|
|
msg.payload.find_me.client_id = client_id;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_led_ring(const uint8_t *dest_mac, uint32_t client_id,
|
|
const alox_LedRingProgressRequest *req) {
|
|
if (req == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_LED_RING;
|
|
msg.which_payload = alox_EspNowMessage_led_ring_tag;
|
|
msg.payload.led_ring.client_id = client_id;
|
|
msg.payload.led_ring.mode = req->mode;
|
|
msg.payload.led_ring.progress = req->progress;
|
|
msg.payload.led_ring.digit = req->digit;
|
|
msg.payload.led_ring.r = req->r;
|
|
msg.payload.led_ring.g = req->g;
|
|
msg.payload.led_ring.b = req->b;
|
|
msg.payload.led_ring.intensity = req->intensity;
|
|
msg.payload.led_ring.blink_ms = req->blink_ms;
|
|
msg.payload.led_ring.blink_count = req->blink_count;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_restart(const uint8_t *dest_mac, uint32_t client_id) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_RESTART;
|
|
msg.which_payload = alox_EspNowMessage_restart_tag;
|
|
msg.payload.restart.client_id = client_id;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_tap_notify(const uint8_t *dest_mac, uint32_t client_id,
|
|
bool single, bool double_tap, bool triple) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_SET_TAP_NOTIFY;
|
|
msg.which_payload = alox_EspNowMessage_tap_notify_tag;
|
|
msg.payload.tap_notify.client_id = client_id;
|
|
msg.payload.tap_notify.single = single;
|
|
msg.payload.tap_notify.double_tap = double_tap;
|
|
msg.payload.tap_notify.triple = triple;
|
|
return esp_now_core_send(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_ota_start(const uint8_t *dest_mac, uint32_t total_size) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_OTA_START;
|
|
msg.which_payload = alox_EspNowMessage_ota_start_tag;
|
|
msg.payload.ota_start.total_size = total_size;
|
|
return esp_now_core_send_wait(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_ota_payload(const uint8_t *dest_mac, uint32_t seq,
|
|
const uint8_t *data, size_t len) {
|
|
if (data == NULL || len == 0 || len > OTA_UART_HOST_CHUNK_SIZE) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_OTA_PAYLOAD;
|
|
msg.which_payload = alox_EspNowMessage_ota_payload_tag;
|
|
msg.payload.ota_payload.seq = seq;
|
|
msg.payload.ota_payload.data.size = len;
|
|
memcpy(msg.payload.ota_payload.data.bytes, data, len);
|
|
return esp_now_core_send_wait(dest_mac, &msg);
|
|
}
|
|
|
|
static esp_err_t send_ota_end(const uint8_t *dest_mac) {
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_OTA_END;
|
|
msg.which_payload = alox_EspNowMessage_ota_end_tag;
|
|
return esp_now_core_send_wait(dest_mac, &msg);
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_ota_start(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t total_size) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
return send_ota_start(mac, total_size);
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_ota_payload(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t seq, const uint8_t *data,
|
|
size_t len) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
return send_ota_payload(mac, seq, data, len);
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_ota_end(const uint8_t mac[CLIENT_MAC_LEN]) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
return send_ota_end(mac);
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_restart(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t client_id) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err = send_restart(mac, client_id);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "unicast RESTART to %s client_id=%lu", mac_str,
|
|
(unsigned long)client_id);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast RESTART to %s failed: %s", mac_str,
|
|
esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_find_me(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t client_id) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err = send_find_me(mac, client_id);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "unicast FIND_ME to %s client_id=%lu", mac_str,
|
|
(unsigned long)client_id);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast FIND_ME to %s failed: %s", mac_str,
|
|
esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_led_ring(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t client_id,
|
|
const alox_LedRingProgressRequest *req) {
|
|
if (mac == NULL || !esp_now_core_is_master() || req == NULL) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err = send_led_ring(mac, client_id, req);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "unicast LED_RING mode %lu to %s client_id=%lu",
|
|
(unsigned long)req->mode, mac_str, (unsigned long)client_id);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast LED_RING to %s failed: %s", mac_str,
|
|
esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_unicast_test(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t seq) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err = send_unicast_test(mac, seq);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "unicast TEST to %s seq=%lu", mac_str, (unsigned long)seq);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast TEST to %s failed: %s", mac_str, esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_accel_stream(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t client_id, bool enable) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err = send_accel_stream(mac, client_id, enable);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "unicast SET_ACCEL_STREAM to %s: %s client_id=%lu", mac_str,
|
|
enable ? "on" : "off", (unsigned long)client_id);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast SET_ACCEL_STREAM to %s failed: %s", mac_str,
|
|
esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_tap_notify(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t client_id, bool single,
|
|
bool double_tap, bool triple) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err =
|
|
send_tap_notify(mac, client_id, single, double_tap, triple);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG,
|
|
"unicast SET_TAP_NOTIFY to %s: single=%d double=%d triple=%d "
|
|
"client_id=%lu",
|
|
mac_str, single, double_tap, triple, (unsigned long)client_id);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast SET_TAP_NOTIFY to %s failed: %s", mac_str,
|
|
esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
esp_err_t esp_now_comm_send_accel_deadzone(const uint8_t mac[CLIENT_MAC_LEN],
|
|
uint32_t client_id,
|
|
uint32_t deadzone) {
|
|
if (mac == NULL || !esp_now_core_is_master()) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
esp_err_t err = send_accel_deadzone(mac, client_id, deadzone);
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "unicast SET_ACCEL_DEADZONE to %s: deadzone=%lu client_id=%lu",
|
|
mac_str, (unsigned long)deadzone, (unsigned long)client_id);
|
|
} else {
|
|
ESP_LOGW(TAG, "unicast SET_ACCEL_DEADZONE to %s failed: %s", mac_str,
|
|
esp_err_to_name(err));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void handle_accel_sample(const uint8_t mac[CLIENT_MAC_LEN],
|
|
const alox_EspNowAccelSample *sample) {
|
|
if (sample == NULL) {
|
|
return;
|
|
}
|
|
esp_err_t err = client_registry_update_accel(
|
|
mac, sample->slave_id, (int16_t)sample->x, (int16_t)sample->y,
|
|
(int16_t)sample->z);
|
|
if (err == ESP_ERR_NOT_FOUND) {
|
|
return;
|
|
}
|
|
if (err != ESP_OK) {
|
|
ESP_LOGW(TAG, "accel sample id mismatch from %02x:…:%02x", mac[0], mac[5]);
|
|
}
|
|
}
|
|
|
|
static void handle_tap_event(const uint8_t mac[CLIENT_MAC_LEN],
|
|
const alox_EspNowTapEvent *event) {
|
|
if (event == NULL) {
|
|
return;
|
|
}
|
|
esp_err_t err =
|
|
client_registry_update_tap(mac, event->slave_id, event->kind);
|
|
if (err == ESP_ERR_NOT_FOUND) {
|
|
return;
|
|
}
|
|
if (err != ESP_OK) {
|
|
ESP_LOGW(TAG, "tap event id=%lu kind=%lu rejected from %02x:…:%02x",
|
|
(unsigned long)event->slave_id, (unsigned long)event->kind, mac[0],
|
|
mac[5]);
|
|
}
|
|
}
|
|
|
|
static void handle_battery_report(const uint8_t mac[CLIENT_MAC_LEN],
|
|
const alox_EspNowBatteryReport *report) {
|
|
if (report == NULL) {
|
|
return;
|
|
}
|
|
esp_err_t err = client_registry_update_battery(
|
|
mac, report->client_id, report->lipo1_valid, report->lipo1_mv,
|
|
report->lipo2_valid, report->lipo2_mv);
|
|
if (err == ESP_ERR_NOT_FOUND) {
|
|
ESP_LOGW(TAG, "battery report from unregistered slave id=%lu",
|
|
(unsigned long)report->client_id);
|
|
return;
|
|
}
|
|
if (err != ESP_OK) {
|
|
ESP_LOGW(TAG, "battery report id=%lu rejected: %s",
|
|
(unsigned long)report->client_id, esp_err_to_name(err));
|
|
return;
|
|
}
|
|
ESP_LOGI(TAG, "battery cached id=%lu L1=%s %lu mV L2=%s %lu mV",
|
|
(unsigned long)report->client_id,
|
|
report->lipo1_valid ? "ok" : "n/a",
|
|
(unsigned long)report->lipo1_mv, report->lipo2_valid ? "ok" : "n/a",
|
|
(unsigned long)report->lipo2_mv);
|
|
}
|
|
|
|
static void handle_client_presence(const alox_EspNowSlavePresence *presence,
|
|
const uint8_t mac[CLIENT_MAC_LEN]) {
|
|
if (presence->network != esp_now_core_network()) {
|
|
return;
|
|
}
|
|
|
|
esp_now_core_ensure_peer(mac);
|
|
|
|
bool is_new = false;
|
|
bool reactivated = false;
|
|
esp_err_t err = client_registry_heartbeat(
|
|
mac, presence->slave_id, presence->version, presence->used, &is_new,
|
|
&reactivated);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGW(TAG, "client registry full");
|
|
return;
|
|
}
|
|
|
|
char mac_str[18];
|
|
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
|
if (is_new) {
|
|
ESP_LOGI(TAG, "client registered id=%lu mac=%s ver=%lu",
|
|
(unsigned long)presence->slave_id, mac_str,
|
|
(unsigned long)presence->version);
|
|
} else if (reactivated) {
|
|
ESP_LOGI(TAG, "client reconnected id=%lu mac=%s",
|
|
(unsigned long)presence->slave_id, mac_str);
|
|
}
|
|
}
|
|
|
|
void esp_now_master_on_recv(const esp_now_recv_info_t *info, const uint8_t *data,
|
|
int len) {
|
|
if (info == NULL || data == NULL || len <= 0) {
|
|
return;
|
|
}
|
|
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
if (esp_now_proto_decode(data, (size_t)len, &msg) != ESP_OK) {
|
|
ESP_LOGW(TAG, "decode failed (%d bytes)", len);
|
|
return;
|
|
}
|
|
|
|
if (ota_espnow_distribution_active()) {
|
|
if (msg.which_payload == alox_EspNowMessage_ota_status_tag) {
|
|
esp_now_core_ensure_peer(info->src_addr);
|
|
ota_espnow_master_on_status(info->src_addr, &msg.payload.ota_status);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (msg.which_payload == alox_EspNowMessage_ota_status_tag) {
|
|
esp_now_core_ensure_peer(info->src_addr);
|
|
ota_espnow_master_on_status(info->src_addr, &msg.payload.ota_status);
|
|
return;
|
|
}
|
|
|
|
if (msg.which_payload == alox_EspNowMessage_accel_sample_tag) {
|
|
esp_now_core_ensure_peer(info->src_addr);
|
|
handle_accel_sample(info->src_addr, &msg.payload.accel_sample);
|
|
return;
|
|
}
|
|
|
|
if (msg.which_payload == alox_EspNowMessage_tap_event_tag) {
|
|
esp_now_core_ensure_peer(info->src_addr);
|
|
handle_tap_event(info->src_addr, &msg.payload.tap_event);
|
|
return;
|
|
}
|
|
|
|
if (msg.which_payload == alox_EspNowMessage_battery_report_tag) {
|
|
esp_now_core_ensure_peer(info->src_addr);
|
|
handle_battery_report(info->src_addr, &msg.payload.battery_report);
|
|
return;
|
|
}
|
|
|
|
if (msg.type == alox_EspNowMessageType_ESPNOW_BATTERY_REPORT &&
|
|
msg.which_payload != alox_EspNowMessage_battery_report_tag) {
|
|
ESP_LOGW(TAG, "BATTERY_REPORT type but which=%u", msg.which_payload);
|
|
}
|
|
|
|
const alox_EspNowSlavePresence *presence = esp_now_proto_get_presence(&msg);
|
|
if (presence != NULL) {
|
|
esp_now_core_ensure_peer(info->src_addr);
|
|
handle_client_presence(presence, info->src_addr);
|
|
}
|
|
}
|
|
|
|
static void discover_task(void *param) {
|
|
(void)param;
|
|
|
|
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
|
msg.type = alox_EspNowMessageType_ESPNOW_DISCOVER;
|
|
msg.which_payload = alox_EspNowMessage_discover_tag;
|
|
msg.payload.discover.network = esp_now_core_network();
|
|
|
|
ESP_LOGI(TAG, "discover on network %u ch %u", (unsigned)esp_now_core_network(),
|
|
(unsigned)esp_now_core_wifi_channel());
|
|
|
|
while (1) {
|
|
esp_now_core_send(ESPNOW_BCAST, &msg);
|
|
vTaskDelay(pdMS_TO_TICKS(ESPNOW_DISCOVER_INTERVAL_MS));
|
|
}
|
|
}
|
|
|
|
static void monitor_task(void *param) {
|
|
(void)param;
|
|
uint32_t last_local_battery_ms = 0;
|
|
|
|
ESP_LOGI(TAG, "monitor (client timeout %u ms)",
|
|
(unsigned)ESPNOW_CLIENT_TIMEOUT_MS);
|
|
|
|
board_lipo_reading_t reading;
|
|
board_input_read_lipo(&reading);
|
|
client_registry_set_master_battery(&reading);
|
|
last_local_battery_ms = esp_now_core_now_ms();
|
|
|
|
while (1) {
|
|
vTaskDelay(pdMS_TO_TICKS(ESPNOW_HEARTBEAT_INTERVAL_MS));
|
|
client_registry_check_timeouts(ESPNOW_CLIENT_TIMEOUT_MS);
|
|
|
|
uint32_t t = esp_now_core_now_ms();
|
|
if (t - last_local_battery_ms >= ESPNOW_BATTERY_INTERVAL_MS) {
|
|
board_input_read_lipo(&reading);
|
|
client_registry_set_master_battery(&reading);
|
|
last_local_battery_ms = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
esp_err_t esp_now_master_start(void) {
|
|
ESP_ERROR_CHECK(esp_now_core_ensure_broadcast_peer());
|
|
|
|
if (xTaskCreate(discover_task, "espnow_disc", 4096, NULL, 4, NULL) != pdPASS) {
|
|
ESP_LOGE(TAG, "failed to create discover task");
|
|
return ESP_FAIL;
|
|
}
|
|
if (xTaskCreate(monitor_task, "espnow_mon", 4096, NULL, 4, NULL) != pdPASS) {
|
|
ESP_LOGE(TAG, "failed to create monitor task");
|
|
return ESP_FAIL;
|
|
}
|
|
return ESP_OK;
|
|
}
|