Files
esp-anchor/main/main.c
T

282 lines
9.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "config_store.h"
#include "provisioning.h"
#include "ble_scanner.h"
#include "mqtt_publisher.h"
#include "led_indicator.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_log.h"
#include "mdns.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include <stdio.h>
#include <string.h>
#define TAG "main"
#define WIFI_CONNECTED_BIT BIT8 /* must not clash with mqtt_publisher bits (BIT0BIT6) */
#define DEFAULT_MQTT_PORT 1883
#define MDNS_QUERY_TIMEOUT_MS 3000
#define MDNS_RETRY_INTERVAL_MS 30000
static EventGroupHandle_t s_evt;
static void reset_button_task(void *arg)
{
gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << CONFIG_SENSOR_RESET_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf);
TickType_t held_since = 0;
const TickType_t threshold = pdMS_TO_TICKS(CONFIG_SENSOR_RESET_HOLD_MS);
for (;;) {
if (gpio_get_level(CONFIG_SENSOR_RESET_GPIO) == 0) {
if (held_since == 0) held_since = xTaskGetTickCount();
if ((xTaskGetTickCount() - held_since) >= threshold) {
ESP_LOGI(TAG, "Reset button held %d ms — factory resetting", CONFIG_SENSOR_RESET_HOLD_MS);
led_indicator_set(LED_ERROR);
vTaskDelay(pdMS_TO_TICKS(500));
config_store_erase();
esp_restart();
}
} else {
held_since = 0;
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
static void wifi_event_handler(void *arg, esp_event_base_t base,
int32_t id, void *data)
{
if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGW(TAG, "WiFi disconnected, reconnecting...");
led_indicator_set(LED_CONNECTING);
xEventGroupClearBits(s_evt, WIFI_CONNECTED_BIT);
esp_wifi_connect();
} else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *evt = (ip_event_got_ip_t *)data;
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&evt->ip_info.ip));
xEventGroupSetBits(s_evt, WIFI_CONNECTED_BIT);
}
}
static void on_ble_scan_result(const char *tag_id, int8_t rssi)
{
mqtt_publisher_send_rssi(tag_id, rssi);
}
static void wifi_init_sta(void)
{
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL);
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
esp_netif_ip_info_t ip_info = {0};
if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK && ip_info.ip.addr != 0) {
ESP_LOGI(TAG, "WiFi already connected (IP: " IPSTR ")", IP2STR(&ip_info.ip));
xEventGroupSetBits(s_evt, WIFI_CONNECTED_BIT);
} else {
ESP_ERROR_CHECK(esp_wifi_connect());
}
}
static bool resolve_mqtt_broker_mdns(char *host_out, size_t host_len, uint16_t *port_out)
{
ESP_ERROR_CHECK(mdns_init());
mdns_result_t *results = NULL;
bool found = false;
esp_err_t err = mdns_query_ptr("_mqtt", "_tcp", MDNS_QUERY_TIMEOUT_MS, 4, &results);
if (err != ESP_OK || !results) {
ESP_LOGW(TAG, "mDNS query for _mqtt._tcp failed");
goto free_results;
}
mdns_result_t *r = results;
while (r) {
if (r->port) {
if (r->addr) {
snprintf(host_out, host_len, IPSTR, IP2STR(&r->addr->addr.u_addr.ip4));
*port_out = r->port;
ESP_LOGI(TAG, "mDNS found broker: %s:%u", host_out, *port_out);
found = true;
goto free_results;
} else if (r->hostname) {
esp_ip4_addr_t ip4;
if (mdns_query_a(r->hostname, MDNS_QUERY_TIMEOUT_MS, &ip4) == ESP_OK) {
snprintf(host_out, host_len, IPSTR, IP2STR(&ip4));
*port_out = r->port;
ESP_LOGI(TAG, "mDNS found broker: %s:%u", host_out, *port_out);
found = true;
goto free_results;
}
ESP_LOGW(TAG, "mDNS A query for %s failed", r->hostname);
}
}
r = r->next;
}
free_results:
mdns_query_results_free(results);
mdns_free();
return found;
}
void app_main(void)
{
ESP_ERROR_CHECK(config_store_init());
led_indicator_init();
led_indicator_set(LED_CONNECTING);
xTaskCreate(reset_button_task, "reset_btn", 4096, NULL, 1, NULL);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
s_evt = xEventGroupCreate();
/* WiFi hardware must be up before the provisioning manager runs */
esp_netif_create_default_wifi_sta();
wifi_init_config_t wcfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wcfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
if (!config_store_is_provisioned()) {
ESP_LOGI(TAG, "Not provisioned - starting BLE provisioning");
provisioning_run(); /* blocks until complete */
/* BT memory released by NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM */
}
// connect to wifi
ESP_LOGI(TAG, "Connecting to WiFi...");
led_indicator_set(LED_CONNECTING);
// wifi_init_sta registers events and calls connect; skip if already inited
wifi_init_sta();
xEventGroupWaitBits(s_evt, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "WiFi connected");
// derive sensor_id from WiFi MAC
uint8_t mac[6];
esp_wifi_get_mac(WIFI_IF_STA, mac);
char sensor_id[32];
snprintf(sensor_id, sizeof(sensor_id),
"anchor_%02x%02x%02x", mac[3], mac[4], mac[5]);
ESP_LOGI(TAG, "Sensor ID: %s", sensor_id);
// resolve MQTT broker
char broker_host[MQTT_HOST_MAX_LEN] = {0};
uint16_t broker_port = DEFAULT_MQTT_PORT;
char broker_uri[160];
bool resolved = false;
while (!resolved) {
// favour config store override over mdns
resolved = (config_store_get_mqtt_override(broker_host, &broker_port) == ESP_OK);
if (resolved) {
ESP_LOGI(TAG, "Using NVS MQTT override: %s:%u", broker_host, broker_port);
} else {
resolved = resolve_mqtt_broker_mdns(broker_host, sizeof(broker_host), &broker_port);
}
if (!resolved) {
ESP_LOGW(TAG, "No broker found, retrying in %ds...", MDNS_RETRY_INTERVAL_MS / 1000);
led_indicator_set(LED_ERROR);
vTaskDelay(pdMS_TO_TICKS(MDNS_RETRY_INTERVAL_MS));
led_indicator_set(LED_CONNECTING);
}
}
snprintf(broker_uri, sizeof(broker_uri), "mqtt://%s:%u", broker_host, broker_port);
// init MQTT
ESP_ERROR_CHECK(mqtt_publisher_init(sensor_id, broker_uri, s_evt));
xEventGroupWaitBits(s_evt, MQTT_CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
// init BLE scanner
ble_scanner_init(on_ble_scan_result);
ble_scanner_start();
led_indicator_set(LED_SCANNING);
ESP_LOGI(TAG, "Anchor running - scanning BLE and publishing RSSI");
// main event loop
const EventBits_t cmd_bits =
MQTT_CALIBRATE_START | MQTT_CALIBRATE_STOP |
MQTT_SELECTED_BIT | MQTT_DESELECTED_BIT |
MQTT_FACTORY_RESET_BIT | MQTT_RECONFIGURE_SETTINGS_BIT;
bool calibrating = false;
bool selected = false;
for (;;) {
EventBits_t bits = xEventGroupWaitBits(
s_evt, cmd_bits, pdTRUE, pdFALSE, pdMS_TO_TICKS(100));
if (bits & MQTT_CALIBRATE_START) {
ESP_LOGI(TAG, "Calibration started");
calibrating = true;
led_indicator_set(LED_CALIBRATING);
}
if (bits & MQTT_CALIBRATE_STOP) {
ESP_LOGI(TAG, "Calibration stopped");
calibrating = false;
led_indicator_set(selected ? LED_SELECTED : LED_SCANNING);
}
if (bits & MQTT_SELECTED_BIT) {
ESP_LOGI(TAG, "Anchor selected");
selected = true;
if (!calibrating) led_indicator_set(LED_SELECTED);
}
if (bits & MQTT_DESELECTED_BIT) {
ESP_LOGI(TAG, "Anchor deselected");
selected = false;
if (!calibrating) led_indicator_set(LED_SCANNING);
}
if (bits & MQTT_FACTORY_RESET_BIT) {
// factory reset requested - erase NVS and reboot
ESP_LOGI(TAG, "Factory reset requested");
config_store_erase();
esp_restart();
}
if (bits & MQTT_RECONFIGURE_SETTINGS_BIT) {
ESP_LOGI(TAG, "Reconfigure settings requested");
const mqtt_reconfigure_data_t *cfg = mqtt_publisher_get_reconfigure_data();
if (cfg->ssid[0] != '\0') {
wifi_config_t wifi_cfg = {0};
strncpy((char *)wifi_cfg.sta.ssid, cfg->ssid, sizeof(wifi_cfg.sta.ssid) - 1);
strncpy((char *)wifi_cfg.sta.password, cfg->password, sizeof(wifi_cfg.sta.password) - 1);
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
ESP_LOGI(TAG, "Updated WiFi SSID: %s", cfg->ssid);
}
if (cfg->mqtt_host[0] != '\0') {
uint16_t port = cfg->mqtt_port ? cfg->mqtt_port : DEFAULT_MQTT_PORT;
config_store_set_mqtt_override(cfg->mqtt_host, port);
ESP_LOGI(TAG, "Updated MQTT broker: %s:%u", cfg->mqtt_host, port);
}
ESP_LOGI(TAG, "Rebooting to apply new settings");
esp_restart();
}
}
}