Files
board-mate/esp32-thread/open-thread-client/main/esp_ot_cli.c
2025-12-30 12:59:14 +01:00

333 lines
9.6 KiB
C

/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*
* OpenThread Command Line Example
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_types.h"
#include "esp_openthread.h"
#include "esp_openthread_cli.h"
#include "esp_openthread_lock.h"
#include "esp_openthread_netif_glue.h"
#include "esp_openthread_types.h"
#include "esp_ot_config.h"
#include "esp_vfs_eventfd.h"
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/uart_types.h"
#include "nvs_flash.h"
#include "openthread/cli.h"
#include "openthread/instance.h"
#include "openthread/logging.h"
#include "openthread/tasklet.h"
#include "esp_http_client.h"
#include "mqtt_client.h"
#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
#include "ot_led_strip.h"
#endif
#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
#include "esp_ot_cli_extension.h"
#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
#define TAG "ot_esp_cli"
void thread_state_changed_callback(uint32_t flags, void *context)
{
ESP_LOGI(TAG, "State changed\n");
otInstance *instance = (otInstance *)context;
if (flags & OT_CHANGED_THREAD_ROLE)
{
otDeviceRole role = otThreadGetDeviceRole(instance);
if (role == OT_DEVICE_ROLE_DETACHED || role == OT_DEVICE_ROLE_DISABLED)
{
ESP_LOGI(TAG, "Disconnected from thread network\n");
} else {
ESP_LOGI(TAG, "Connect to thread network\n");
}
}
}
static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config)
{
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
esp_netif_t *netif = esp_netif_new(&cfg);
assert(netif != NULL);
ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
return netif;
}
static void ot_task_worker(void *aContext)
{
esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
// 1. Initialize OpenThread stack
ESP_ERROR_CHECK(esp_openthread_init(&config));
otInstance *instance = esp_openthread_get_instance();
otSetStateChangedCallback(instance, thread_state_changed_callback, instance);
#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
ESP_ERROR_CHECK(esp_openthread_state_indicator_init(instance));
#endif
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
#endif
#if CONFIG_OPENTHREAD_CLI
esp_openthread_cli_init();
#endif
esp_netif_t *openthread_netif = init_openthread_netif(&config);
esp_netif_set_default_netif(openthread_netif);
#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
esp_cli_custom_command_init();
#endif
#if CONFIG_OPENTHREAD_AUTO_START
static const uint8_t dataset_tlv[] = {
0x0e, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x4a,
0x03, 0x00, 0x00, 0x10, 0x35, 0x06, 0x00, 0x04,
0x00, 0x1f, 0xff, 0xe0, 0x02, 0x08, 0xc4, 0xa8,
0x12, 0xe1, 0x4e, 0x69, 0x1d, 0x3c, 0x07, 0x08,
0xfd, 0xc0, 0xa0, 0xdc, 0x4e, 0xbb, 0x2e, 0x9c,
0x05, 0x10, 0x58, 0x4a, 0x84, 0xc2, 0xa1, 0xbc,
0x2e, 0xcb, 0x2f, 0xfc, 0xb2, 0xa5, 0xa0, 0x54,
0x6b, 0xac, 0x03, 0x0f, 0x4f, 0x70, 0x65, 0x6e,
0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2d, 0x39,
0x64, 0x61, 0x33, 0x01, 0x02, 0x9d, 0xa3, 0x04,
0x10, 0x6c, 0xa6, 0x7c, 0x62, 0x4f, 0xc6, 0xd4,
0x37, 0x25, 0x39, 0x2b, 0xde, 0x6c, 0xec, 0xb1,
0x3a, 0x0c, 0x04, 0x02, 0xa0, 0xf7, 0xf8
};
otOperationalDatasetTlvs dataset;
dataset.mLength = sizeof(dataset_tlv);
memcpy(dataset.mTlvs, dataset_tlv, sizeof(dataset_tlv));
// 2. Auto-start Thread with dataset
otError err = esp_openthread_auto_start(&dataset);
ESP_LOGI(TAG, "Auto-start returned: %d", err);
#endif
#if CONFIG_OPENTHREAD_CLI
esp_openthread_cli_create_task();
#endif
// 3. Launch main loop
esp_openthread_launch_mainloop();
// 4. Clean up
esp_openthread_netif_glue_deinit();
esp_netif_destroy(openthread_netif);
esp_vfs_eventfd_unregister();
vTaskDelete(NULL);
}
static bool mqtt_connected = false;
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
esp_mqtt_event_handle_t event = event_data;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT Connected");
mqtt_connected = true;
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT Disconnected");
mqtt_connected = false;
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "Message published, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT Error");
mqtt_connected = false;
break;
default:
break;
}
}
static esp_mqtt_client_handle_t client = NULL;
void mqtt_init(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = "mqtt://192.168.15.119:1883",
.credentials = {
.username = "device",
.authentication = {
.password = "hepl",
},
.client_id = NULL,
.set_null_client_id = true
},
.session.keepalive = 30,
.session.disable_clean_session = false,
};
client = esp_mqtt_client_init(&mqtt_cfg);
if (!client) {
ESP_LOGE(TAG, "Failed to init MQTT client");
return;
}
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
ESP_LOGI(TAG, "Starting MQTT client...");
esp_mqtt_client_start(client);
}
void send_gps_data(const char *topic, const char *json)
{
if (!client || !json) return;
if (!mqtt_connected) {
ESP_LOGW(TAG, "MQTT not connected, skipping GPS publish");
return;
}
int msg_id = esp_mqtt_client_publish(client, topic, json, 0, 1, 0);
if (msg_id < 0) {
ESP_LOGE(TAG, "Failed to publish GPS");
} else {
ESP_LOGI(TAG, "Published GPS: msg_id=%d", msg_id);
}
}
#define GPS_UART_NUM UART_NUM_1
#define GPS_TX_PIN 4
#define GPS_RX_PIN 5
#define BUF_SIZE 1024
static uint8_t gps_buffer[BUF_SIZE];
void init_gps() {
const uart_config_t uart_config = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(GPS_UART_NUM, &uart_config);
uart_set_pin(GPS_UART_NUM, GPS_TX_PIN, GPS_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(GPS_UART_NUM, BUF_SIZE, 0, 0, NULL, 0);
}
// Convertit NMEA "ddmm.mmmm" + N/S/E/W en degrés décimaux
double nmea_to_decimal(const char* nmea_coord, const char direction) {
double raw = atof(nmea_coord);
int degrees = (int)(raw / 100);
double minutes = raw - (degrees * 100);
double decimal = degrees + minutes / 60.0;
if (direction == 'S' || direction == 'W') decimal = -decimal;
return decimal;
}
char* gps_to_json(char* gps_data) {
static char json[128];
double latitude = 0.0;
double longitude = 0.0;
if (gps_data && strncmp(gps_data, "$GPGGA,", 7) == 0) {
char* fields[15];
int i = 0;
char gps_copy[BUF_SIZE];
strncpy(gps_copy, gps_data, BUF_SIZE - 1);
gps_copy[BUF_SIZE - 1] = '\0';
char* token = strtok(gps_copy, ",");
while (token && i < 15) {
fields[i++] = token;
token = strtok(NULL, ",");
}
if (i >= 6 && fields[2][0] != '\0' && fields[4][0] != '\0') {
latitude = nmea_to_decimal(fields[2], fields[3][0]);
longitude = nmea_to_decimal(fields[4], fields[5][0]);
}
}
snprintf(json, sizeof(json), "{\"data\":{\"latitude\":%.6f,\"longitude\":%.6f}}",
latitude, longitude);
return json;
}
char* read_gps() {
int len = uart_read_bytes(GPS_UART_NUM, gps_buffer, BUF_SIZE - 1, 100 / portTICK_PERIOD_MS);
if (len > 0) {
gps_buffer[len] = '\0';
return (char*)gps_buffer;
}
return NULL;
}
void send_position_task(void *pvParameters)
{
const char* gps_topic = "/system/sensor/gps";
while (1) {
char* json;
char* raw_data = read_gps();
if (raw_data) {
char* json = gps_to_json(raw_data);
send_gps_data(gps_topic, json);
} else {
ESP_LOGI(TAG, "NO GPS DATA");
}
vTaskDelay(25000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
// Used eventfds:
// * netif
// * ot task queue
// * radio driver
esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = 3,
};
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
mqtt_init();
init_gps();
xTaskCreate(ot_task_worker, "ot_cli_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL);
xTaskCreate(send_position_task, "gps_task", 4096, NULL, 5, NULL);
}