init: initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
.cache/
|
||||||
|
build/
|
||||||
|
managed_components/
|
||||||
|
sdkconfig.old
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(esp-anchor)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "src/ble_scanner.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES bt esp_event
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called for every unique BLE advertisement received.
|
||||||
|
* tag_id is a null-terminated string: "aa:bb:cc:dd:ee:ff"
|
||||||
|
* rssi is in dBm (negative).
|
||||||
|
*/
|
||||||
|
typedef void (*ble_scanner_cb_t)(const char *tag_id, int8_t rssi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the Bluedroid BLE stack and register the scan callback.
|
||||||
|
* Must be called once after esp_bt_controller_init / esp_bluedroid_init.
|
||||||
|
*/
|
||||||
|
void ble_scanner_init(ble_scanner_cb_t cb);
|
||||||
|
|
||||||
|
/** Start passive BLE scanning. */
|
||||||
|
void ble_scanner_start(void);
|
||||||
|
|
||||||
|
/** Stop BLE scanning. */
|
||||||
|
void ble_scanner_stop(void);
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#include "ble_scanner.h"
|
||||||
|
#include "esp_bt.h"
|
||||||
|
#include "esp_bt_main.h"
|
||||||
|
#include "esp_gap_ble_api.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TAG "ble_scanner"
|
||||||
|
|
||||||
|
static ble_scanner_cb_t s_cb = NULL;
|
||||||
|
|
||||||
|
/* Passive scan: 100ms interval, 50ms window (50% duty cycle) */
|
||||||
|
static esp_ble_scan_params_t s_scan_params = {
|
||||||
|
.scan_type = BLE_SCAN_TYPE_PASSIVE,
|
||||||
|
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||||
|
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||||
|
.scan_interval = 0xA0, /* 100ms: 0xA0 * 0.625ms */
|
||||||
|
.scan_window = 0x50, /* 50ms: 0x50 * 0.625ms */
|
||||||
|
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gap_event_handler(esp_gap_ble_cb_event_t event,
|
||||||
|
esp_ble_gap_cb_param_t *param)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
|
if (param->scan_param_cmpl.status == ESP_BT_STATUS_SUCCESS) {
|
||||||
|
esp_ble_gap_start_scanning(0); /* 0 = scan indefinitely */
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Scan param set failed: %d", param->scan_param_cmpl.status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
|
if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "Scan start failed: %d", param->scan_start_cmpl.status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||||
|
esp_ble_gap_cb_param_t *p = param;
|
||||||
|
if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT && s_cb) {
|
||||||
|
char tag_id[18];
|
||||||
|
uint8_t *a = p->scan_rst.bda;
|
||||||
|
snprintf(tag_id, sizeof(tag_id),
|
||||||
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
a[0], a[1], a[2], a[3], a[4], a[5]);
|
||||||
|
s_cb(tag_id, p->scan_rst.rssi);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||||
|
ESP_LOGI(TAG, "Scan stopped");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_scanner_init(ble_scanner_cb_t cb)
|
||||||
|
{
|
||||||
|
s_cb = cb;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
|
||||||
|
|
||||||
|
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
|
||||||
|
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
|
||||||
|
ESP_ERROR_CHECK(esp_bluedroid_init());
|
||||||
|
ESP_ERROR_CHECK(esp_bluedroid_enable());
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_register_callback(gap_event_handler));
|
||||||
|
ESP_LOGI(TAG, "BLE scanner initialised");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_scanner_start(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Starting BLE scan");
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&s_scan_params));
|
||||||
|
/* Scanning begins in the param-set callback once confirmed */
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_scanner_stop(void)
|
||||||
|
{
|
||||||
|
esp_ble_gap_stop_scanning();
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "src/config_store.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES nvs_flash
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#define MQTT_HOST_MAX_LEN 128
|
||||||
|
#define MQTT_URI_MAX_LEN 256
|
||||||
|
|
||||||
|
esp_err_t config_store_init(void);
|
||||||
|
bool config_store_is_provisioned(void);
|
||||||
|
esp_err_t config_store_set_provisioned(void);
|
||||||
|
esp_err_t config_store_clear_provisioned(void);
|
||||||
|
|
||||||
|
/* Store an optional manual MQTT broker override (used if mDNS fails). */
|
||||||
|
esp_err_t config_store_set_mqtt_override(const char *host, uint16_t port);
|
||||||
|
|
||||||
|
/* Returns ESP_ERR_NVS_NOT_FOUND if no override is stored. */
|
||||||
|
esp_err_t config_store_get_mqtt_override(char host_out[MQTT_HOST_MAX_LEN], uint16_t *port_out);
|
||||||
|
|
||||||
|
/* Returns ESP_ERR_NVS_NOT_FOUND if no override is stored. */
|
||||||
|
esp_err_t config_store_get_mqtt_override_uri(char *uri_out, size_t uri_max_len);
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "config_store.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "nvs.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define NS "anchor_cfg"
|
||||||
|
#define KEY_PROV "provisioned"
|
||||||
|
#define KEY_HOST "mqtt_host"
|
||||||
|
#define KEY_PORT "mqtt_port"
|
||||||
|
|
||||||
|
static const char *TAG = "config_store";
|
||||||
|
|
||||||
|
esp_err_t config_store_init(void)
|
||||||
|
{
|
||||||
|
esp_err_t err = nvs_flash_init();
|
||||||
|
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
|
ESP_LOGW(TAG, "NVS needs erase, erasing...");
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
|
err = nvs_flash_init();
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool config_store_is_provisioned(void)
|
||||||
|
{
|
||||||
|
nvs_handle_t h;
|
||||||
|
if (nvs_open(NS, NVS_READONLY, &h) != ESP_OK) return false;
|
||||||
|
uint8_t val = 0;
|
||||||
|
nvs_get_u8(h, KEY_PROV, &val);
|
||||||
|
nvs_close(h);
|
||||||
|
return val == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t config_store_set_provisioned(void)
|
||||||
|
{
|
||||||
|
nvs_handle_t h;
|
||||||
|
esp_err_t err = nvs_open(NS, NVS_READWRITE, &h);
|
||||||
|
if (err != ESP_OK) return err;
|
||||||
|
err = nvs_set_u8(h, KEY_PROV, 1);
|
||||||
|
if (err == ESP_OK) err = nvs_commit(h);
|
||||||
|
nvs_close(h);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t config_store_clear_provisioned(void)
|
||||||
|
{
|
||||||
|
nvs_handle_t h;
|
||||||
|
esp_err_t err = nvs_open(NS, NVS_READWRITE, &h);
|
||||||
|
if (err != ESP_OK) return err;
|
||||||
|
nvs_erase_key(h, KEY_PROV);
|
||||||
|
nvs_erase_key(h, KEY_HOST);
|
||||||
|
nvs_erase_key(h, KEY_PORT);
|
||||||
|
err = nvs_commit(h);
|
||||||
|
nvs_close(h);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t config_store_set_mqtt_override(const char *host, uint16_t port)
|
||||||
|
{
|
||||||
|
nvs_handle_t h;
|
||||||
|
esp_err_t err = nvs_open(NS, NVS_READWRITE, &h);
|
||||||
|
if (err != ESP_OK) return err;
|
||||||
|
err = nvs_set_str(h, KEY_HOST, host);
|
||||||
|
if (err == ESP_OK) err = nvs_set_u16(h, KEY_PORT, port);
|
||||||
|
if (err == ESP_OK) err = nvs_commit(h);
|
||||||
|
nvs_close(h);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t config_store_get_mqtt_override(char host_out[MQTT_HOST_MAX_LEN], uint16_t *port_out)
|
||||||
|
{
|
||||||
|
nvs_handle_t h;
|
||||||
|
esp_err_t err = nvs_open(NS, NVS_READONLY, &h);
|
||||||
|
if (err != ESP_OK) return err;
|
||||||
|
|
||||||
|
size_t len = MQTT_HOST_MAX_LEN;
|
||||||
|
err = nvs_get_str(h, KEY_HOST, host_out, &len);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_get_u16(h, KEY_PORT, port_out);
|
||||||
|
}
|
||||||
|
nvs_close(h);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t config_store_get_mqtt_override_uri(char *uri_out, size_t uri_max_len)
|
||||||
|
{
|
||||||
|
char broker_host[MQTT_HOST_MAX_LEN] = {0};
|
||||||
|
uint16_t broker_port;
|
||||||
|
|
||||||
|
bool resolved
|
||||||
|
= (config_store_get_mqtt_override(broker_host, &broker_port) == ESP_OK);
|
||||||
|
if(!resolved) return ESP_ERR_NVS_NOT_FOUND;
|
||||||
|
|
||||||
|
snprintf(uri_out, uri_max_len, "mqtt://%s:%u", broker_host, broker_port);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "src/led_indicator.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES driver esp_timer
|
||||||
|
PRIV_REQUIRES esp_driver_gpio
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LED_OFF,
|
||||||
|
LED_PROVISIONING, /* 200ms toggle — awaiting provisioning */
|
||||||
|
LED_CONNECTING, /* 1000ms toggle — connecting to WiFi/MQTT */
|
||||||
|
LED_SCANNING, /* solid on — normal BLE scanning */
|
||||||
|
LED_CALIBRATING, /* 500ms toggle — calibration in progress */
|
||||||
|
LED_SELECTED, /* triple-flash loop — physical identification */
|
||||||
|
LED_ERROR, /* 50ms rapid blink */
|
||||||
|
} led_state_t;
|
||||||
|
|
||||||
|
void led_indicator_init(void);
|
||||||
|
void led_indicator_set(led_state_t state);
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include "led_indicator.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LED_GPIO GPIO_NUM_2
|
||||||
|
|
||||||
|
static esp_timer_handle_t s_timer;
|
||||||
|
static led_state_t s_state = LED_OFF;
|
||||||
|
static int s_phase = 0; /* generic phase counter for multi-step patterns */
|
||||||
|
|
||||||
|
static void set_level(int lvl) { gpio_set_level(LED_GPIO, lvl); }
|
||||||
|
|
||||||
|
static void IRAM_ATTR timer_cb(void *arg)
|
||||||
|
{
|
||||||
|
switch (s_state) {
|
||||||
|
case LED_OFF:
|
||||||
|
set_level(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_PROVISIONING:
|
||||||
|
set_level(s_phase ^= 1);
|
||||||
|
esp_timer_start_once(s_timer, 200 * 1000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_CONNECTING:
|
||||||
|
set_level(s_phase ^= 1);
|
||||||
|
esp_timer_start_once(s_timer, 1000 * 1000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_SCANNING:
|
||||||
|
set_level(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_CALIBRATING:
|
||||||
|
set_level(s_phase ^= 1);
|
||||||
|
esp_timer_start_once(s_timer, 500 * 1000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_SELECTED: {
|
||||||
|
/* Triple flash: on/off/on/off/on/off, then 1s dark. s_phase 0-5 = flashes, 6 = pause */
|
||||||
|
static const uint64_t us[] = {100000,100000,100000,100000,100000,100000,1000000};
|
||||||
|
if (s_phase < 6) {
|
||||||
|
set_level(s_phase % 2 == 0 ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
set_level(0);
|
||||||
|
}
|
||||||
|
uint64_t delay = us[s_phase];
|
||||||
|
s_phase = (s_phase + 1) % 7;
|
||||||
|
esp_timer_start_once(s_timer, delay);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LED_ERROR:
|
||||||
|
set_level(s_phase ^= 1);
|
||||||
|
esp_timer_start_once(s_timer, 50 * 1000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_indicator_init(void)
|
||||||
|
{
|
||||||
|
gpio_config_t io = {
|
||||||
|
.pin_bit_mask = (1ULL << LED_GPIO),
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
gpio_config(&io);
|
||||||
|
|
||||||
|
esp_timer_create_args_t args = {
|
||||||
|
.callback = timer_cb,
|
||||||
|
.name = "led",
|
||||||
|
};
|
||||||
|
esp_timer_create(&args, &s_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_indicator_set(led_state_t state)
|
||||||
|
{
|
||||||
|
esp_timer_stop(s_timer);
|
||||||
|
s_state = state;
|
||||||
|
s_phase = 0;
|
||||||
|
|
||||||
|
if (state == LED_SCANNING) {
|
||||||
|
set_level(1);
|
||||||
|
} else if (state == LED_OFF) {
|
||||||
|
set_level(0);
|
||||||
|
} else {
|
||||||
|
/* Kick off the timer-driven pattern immediately */
|
||||||
|
esp_timer_start_once(s_timer, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "src/mqtt_publisher.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES mqtt esp_event
|
||||||
|
)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/cjson: "*"
|
||||||
|
idf:
|
||||||
|
version: ">=5.1.0"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
|
||||||
|
/* Event bits set by the MQTT publisher into the shared event group */
|
||||||
|
#define MQTT_CONNECTED_BIT BIT0
|
||||||
|
#define MQTT_CALIBRATE_START BIT1
|
||||||
|
#define MQTT_CALIBRATE_STOP BIT2
|
||||||
|
#define MQTT_SELECTED_BIT BIT3
|
||||||
|
#define MQTT_DESELECTED_BIT BIT4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise and connect the MQTT client.
|
||||||
|
*
|
||||||
|
* sensor_id — stable anchor ID string (e.g. "anchor_a1b2c3")
|
||||||
|
* broker_uri — e.g. "mqtt://192.168.1.100:1883"
|
||||||
|
* evt_group — FreeRTOS event group; publisher sets bits above on events
|
||||||
|
*/
|
||||||
|
esp_err_t mqtt_publisher_init(const char *sensor_id,
|
||||||
|
const char *broker_uri,
|
||||||
|
EventGroupHandle_t evt_group);
|
||||||
|
|
||||||
|
/** Publish an RSSI reading. Non-blocking (QoS 1). */
|
||||||
|
void mqtt_publisher_send_rssi(const char *tag_id, int8_t rssi);
|
||||||
|
|
||||||
|
/** Publish the announce message (empty payload). */
|
||||||
|
void mqtt_publisher_announce(void);
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#include "mqtt_publisher.h"
|
||||||
|
#include "mqtt_client.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "cJSON.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define TAG "mqtt_publisher"
|
||||||
|
#define TOPIC_PREFIX "localiser/sensor"
|
||||||
|
|
||||||
|
static esp_mqtt_client_handle_t s_client = NULL;
|
||||||
|
static char s_sensor_id[32];
|
||||||
|
static EventGroupHandle_t s_evt = NULL;
|
||||||
|
|
||||||
|
/* Pre-built topic strings */
|
||||||
|
static char s_topic_rssi[96];
|
||||||
|
static char s_topic_announce[96];
|
||||||
|
static char s_topic_cmd[96];
|
||||||
|
|
||||||
|
static void build_topics(void)
|
||||||
|
{
|
||||||
|
snprintf(s_topic_rssi, sizeof(s_topic_rssi),
|
||||||
|
"%s/%s/rssi", TOPIC_PREFIX, s_sensor_id);
|
||||||
|
snprintf(s_topic_announce, sizeof(s_topic_announce),
|
||||||
|
"%s/%s/announce", TOPIC_PREFIX, s_sensor_id);
|
||||||
|
snprintf(s_topic_cmd, sizeof(s_topic_cmd),
|
||||||
|
"%s/%s/cmd", TOPIC_PREFIX, s_sensor_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_cmd(const char *data, int data_len)
|
||||||
|
{
|
||||||
|
char *buf = strndup(data, data_len);
|
||||||
|
if (!buf) return;
|
||||||
|
|
||||||
|
cJSON *root = cJSON_Parse(buf);
|
||||||
|
free(buf);
|
||||||
|
if (!root) return;
|
||||||
|
|
||||||
|
cJSON *action = cJSON_GetObjectItemCaseSensitive(root, "action");
|
||||||
|
if (cJSON_IsString(action)) {
|
||||||
|
const char *a = action->valuestring;
|
||||||
|
if (strcmp(a, "calibrate_start") == 0) xEventGroupSetBits(s_evt, MQTT_CALIBRATE_START);
|
||||||
|
else if (strcmp(a, "calibrate_stop") == 0) xEventGroupSetBits(s_evt, MQTT_CALIBRATE_STOP);
|
||||||
|
else if (strcmp(a, "selected") == 0) xEventGroupSetBits(s_evt, MQTT_SELECTED_BIT);
|
||||||
|
else if (strcmp(a, "deselected") == 0) xEventGroupSetBits(s_evt, MQTT_DESELECTED_BIT);
|
||||||
|
else ESP_LOGW(TAG, "Unknown cmd action: %s", a);
|
||||||
|
}
|
||||||
|
cJSON_Delete(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mqtt_event_handler(void *arg, esp_event_base_t base,
|
||||||
|
int32_t id, void *data)
|
||||||
|
{
|
||||||
|
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)data;
|
||||||
|
switch ((esp_mqtt_event_id_t)id) {
|
||||||
|
case MQTT_EVENT_CONNECTED:
|
||||||
|
ESP_LOGI(TAG, "Connected to broker");
|
||||||
|
esp_mqtt_client_subscribe(s_client, s_topic_cmd, 1);
|
||||||
|
xEventGroupSetBits(s_evt, MQTT_CONNECTED_BIT);
|
||||||
|
mqtt_publisher_announce();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MQTT_EVENT_DISCONNECTED:
|
||||||
|
ESP_LOGW(TAG, "Disconnected from broker");
|
||||||
|
xEventGroupClearBits(s_evt, MQTT_CONNECTED_BIT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MQTT_EVENT_DATA:
|
||||||
|
if (strncmp(event->topic, s_topic_cmd, event->topic_len) == 0) {
|
||||||
|
handle_cmd(event->data, event->data_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MQTT_EVENT_ERROR:
|
||||||
|
ESP_LOGE(TAG, "MQTT error");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t mqtt_publisher_init(const char *sensor_id,
|
||||||
|
const char *broker_uri,
|
||||||
|
EventGroupHandle_t evt_group)
|
||||||
|
{
|
||||||
|
strncpy(s_sensor_id, sensor_id, sizeof(s_sensor_id) - 1);
|
||||||
|
s_evt = evt_group;
|
||||||
|
build_topics();
|
||||||
|
|
||||||
|
esp_mqtt_client_config_t cfg = {
|
||||||
|
.broker.address.uri = broker_uri,
|
||||||
|
.credentials.client_id = sensor_id,
|
||||||
|
.session.keepalive = 30,
|
||||||
|
.network.reconnect_timeout_ms = 5000,
|
||||||
|
};
|
||||||
|
|
||||||
|
s_client = esp_mqtt_client_init(&cfg);
|
||||||
|
if (!s_client) return ESP_FAIL;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_mqtt_client_register_event(
|
||||||
|
s_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL));
|
||||||
|
|
||||||
|
return esp_mqtt_client_start(s_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqtt_publisher_announce(void)
|
||||||
|
{
|
||||||
|
if (!s_client) return;
|
||||||
|
esp_mqtt_client_publish(s_client, s_topic_announce, "", 0, 1, 0);
|
||||||
|
ESP_LOGI(TAG, "Announced on %s", s_topic_announce);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqtt_publisher_send_rssi(const char *tag_id, int8_t rssi)
|
||||||
|
{
|
||||||
|
if (!s_client) return;
|
||||||
|
|
||||||
|
char payload[80];
|
||||||
|
int len = snprintf(payload, sizeof(payload),
|
||||||
|
"{\"tag_id\":\"%s\",\"rssi\":%d}", tag_id, (int)rssi);
|
||||||
|
esp_mqtt_client_publish(s_client, s_topic_rssi, payload, len, 1, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "src/provisioning.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES
|
||||||
|
network_provisioning
|
||||||
|
esp_wifi
|
||||||
|
esp_event
|
||||||
|
nvs_flash
|
||||||
|
config_store
|
||||||
|
led_indicator
|
||||||
|
bt
|
||||||
|
)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/network_provisioning: "^1.2.4"
|
||||||
|
espressif/cjson: "*"
|
||||||
|
idf:
|
||||||
|
version: ">=5.1.0"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start BLE provisioning mode. Blocks until provisioning is complete,
|
||||||
|
* then deinits the provisioning manager so BLE memory can be reclaimed.
|
||||||
|
*
|
||||||
|
* WiFi credentials are stored automatically by wifi_prov_mgr.
|
||||||
|
* MQTT broker override (if sent by companion app) is stored via config_store.
|
||||||
|
*/
|
||||||
|
esp_err_t provisioning_run(void);
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#include "provisioning.h"
|
||||||
|
#include "config_store.h"
|
||||||
|
#include "led_indicator.h"
|
||||||
|
|
||||||
|
#include "network_provisioning/manager.h"
|
||||||
|
#include "network_provisioning/scheme_ble.h"
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "cJSON.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TAG "provisioning"
|
||||||
|
|
||||||
|
/* Custom GATT endpoint name for MQTT settings. The companion app sends:
|
||||||
|
* {"host":"192.168.1.100","port":1883} */
|
||||||
|
#define MQTT_ENDPOINT "custom-mqtt-config"
|
||||||
|
|
||||||
|
static EventGroupHandle_t s_prov_evt;
|
||||||
|
#define PROV_DONE_BIT BIT0
|
||||||
|
|
||||||
|
/* Called by wifi_prov_mgr when data arrives on the custom endpoint. */
|
||||||
|
static esp_err_t mqtt_endpoint_handler(uint32_t session_id,
|
||||||
|
const uint8_t *inbuf, ssize_t inlen,
|
||||||
|
uint8_t **outbuf, ssize_t *outlen,
|
||||||
|
void *priv)
|
||||||
|
{
|
||||||
|
if (!inbuf || inlen <= 0) return ESP_OK;
|
||||||
|
|
||||||
|
char *json = strndup((const char *)inbuf, inlen);
|
||||||
|
if (!json) return ESP_ERR_NO_MEM;
|
||||||
|
|
||||||
|
cJSON *root = cJSON_Parse(json);
|
||||||
|
free(json);
|
||||||
|
if (!root) {
|
||||||
|
ESP_LOGW(TAG, "Failed to parse MQTT config JSON");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *host_j = cJSON_GetObjectItemCaseSensitive(root, "host");
|
||||||
|
cJSON *port_j = cJSON_GetObjectItemCaseSensitive(root, "port");
|
||||||
|
|
||||||
|
if (cJSON_IsString(host_j) && cJSON_IsNumber(port_j)) {
|
||||||
|
uint16_t port = (uint16_t)port_j->valuedouble;
|
||||||
|
esp_err_t err = config_store_set_mqtt_override(host_j->valuestring, port);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "MQTT override stored: %s:%u", host_j->valuestring, port);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Failed to store MQTT override: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "MQTT config missing 'host' or 'port'");
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_Delete(root);
|
||||||
|
|
||||||
|
/* Respond with a simple ack */
|
||||||
|
const char *ack = "{\"status\":\"ok\"}";
|
||||||
|
*outlen = strlen(ack);
|
||||||
|
*outbuf = (uint8_t *)strdup(ack);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prov_event_handler(void *arg, esp_event_base_t base,
|
||||||
|
int32_t id, void *data)
|
||||||
|
{
|
||||||
|
if (base == NETWORK_PROV_EVENT) {
|
||||||
|
switch (id) {
|
||||||
|
case NETWORK_PROV_START:
|
||||||
|
ESP_LOGI(TAG, "Provisioning started");
|
||||||
|
break;
|
||||||
|
case NETWORK_PROV_WIFI_CRED_RECV:
|
||||||
|
ESP_LOGI(TAG, "WiFi credentials received");
|
||||||
|
break;
|
||||||
|
case NETWORK_PROV_WIFI_CRED_FAIL: {
|
||||||
|
network_prov_wifi_sta_fail_reason_t *reason = (network_prov_wifi_sta_fail_reason_t *)data;
|
||||||
|
ESP_LOGE(TAG, "Provisioning failed: %s",
|
||||||
|
(*reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR) ? "auth error" : "AP not found");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NETWORK_PROV_WIFI_CRED_SUCCESS:
|
||||||
|
ESP_LOGI(TAG, "WiFi credentials applied successfully");
|
||||||
|
break;
|
||||||
|
case NETWORK_PROV_END:
|
||||||
|
ESP_LOGI(TAG, "Provisioning complete");
|
||||||
|
network_prov_mgr_deinit();
|
||||||
|
xEventGroupSetBits(s_prov_evt, PROV_DONE_BIT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t provisioning_run(void)
|
||||||
|
{
|
||||||
|
s_prov_evt = xEventGroupCreate();
|
||||||
|
|
||||||
|
esp_event_handler_register(NETWORK_PROV_EVENT, ESP_EVENT_ANY_ID,
|
||||||
|
prov_event_handler, NULL);
|
||||||
|
|
||||||
|
network_prov_mgr_config_t config = {
|
||||||
|
.scheme = network_prov_scheme_ble,
|
||||||
|
.scheme_event_handler = NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(network_prov_mgr_init(config));
|
||||||
|
|
||||||
|
/* Register custom endpoint for MQTT broker config */
|
||||||
|
network_prov_mgr_endpoint_create(MQTT_ENDPOINT);
|
||||||
|
|
||||||
|
/* Derive device name and PoP from WiFi MAC */
|
||||||
|
uint8_t mac[6];
|
||||||
|
esp_wifi_get_mac(WIFI_IF_STA, mac);
|
||||||
|
char device_name[32];
|
||||||
|
snprintf(device_name, sizeof(device_name),
|
||||||
|
"anchor_%02x%02x%02x", mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
led_indicator_set(LED_PROVISIONING);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(network_prov_mgr_start_provisioning(
|
||||||
|
NETWORK_PROV_SECURITY_0, NULL, device_name, NULL));
|
||||||
|
|
||||||
|
network_prov_mgr_endpoint_register(MQTT_ENDPOINT, mqtt_endpoint_handler, NULL);
|
||||||
|
|
||||||
|
/* Block until provisioning completes */
|
||||||
|
xEventGroupWaitBits(s_prov_evt, PROV_DONE_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
|
||||||
|
vEventGroupDelete(s_prov_evt);
|
||||||
|
|
||||||
|
config_store_set_provisioned();
|
||||||
|
ESP_LOGI(TAG, "Provisioning done, NVS flag set");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/cjson:
|
||||||
|
component_hash: e788323270d90738662d66fffa910bfe1fba019bba087f01557e70c40485b469
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.7.19~2
|
||||||
|
espressif/mdns:
|
||||||
|
component_hash: 8bcf12e37c58c1d584aef32a02b92548124c7a3a9fcf548d3235c844a035e0f0
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.11.1
|
||||||
|
espressif/mqtt:
|
||||||
|
component_hash: ffdad5659706b4dc14bc63f8eb73ef765efa015bf7e9adf71c813d52a2dc9342
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.3'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.0.0
|
||||||
|
espressif/network_provisioning:
|
||||||
|
component_hash: 72d27784e3daf807418a34fb00be136ec50c6db49d989ce981d22e031fc0e7f8
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/cjson
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
rules:
|
||||||
|
- if: idf_version >= 6.0
|
||||||
|
version: ^1.7.19
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.1'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.2.4
|
||||||
|
idf:
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 6.0.0
|
||||||
|
direct_dependencies:
|
||||||
|
- espressif/cjson
|
||||||
|
- espressif/mdns
|
||||||
|
- espressif/mqtt
|
||||||
|
- espressif/network_provisioning
|
||||||
|
- idf
|
||||||
|
manifest_hash: c33499c91a18b09118ae65bbf2c53ea2ed5169f05d8a71dd7b7c5f46ef33c7c2
|
||||||
|
target: esp32
|
||||||
|
version: 3.0.0
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "main.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES
|
||||||
|
nvs_flash
|
||||||
|
esp_wifi
|
||||||
|
esp_event
|
||||||
|
esp_netif
|
||||||
|
mdns
|
||||||
|
config_store
|
||||||
|
provisioning
|
||||||
|
ble_scanner
|
||||||
|
mqtt_publisher
|
||||||
|
led_indicator
|
||||||
|
)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/mqtt: '*'
|
||||||
|
idf:
|
||||||
|
version: '>=5.1.0'
|
||||||
|
espressif/mdns: '*'
|
||||||
+212
@@ -0,0 +1,212 @@
|
|||||||
|
#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 "esp_bt.h"
|
||||||
|
#include "mdns.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TAG "main"
|
||||||
|
|
||||||
|
#define WIFI_CONNECTED_BIT BIT5 /* offset to avoid clashing with mqtt_publisher bits */
|
||||||
|
|
||||||
|
#define DEFAULT_MQTT_PORT 1883
|
||||||
|
#define MDNS_QUERY_TIMEOUT_MS 3000
|
||||||
|
#define MDNS_RETRY_INTERVAL_MS 30000
|
||||||
|
|
||||||
|
static EventGroupHandle_t s_evt;
|
||||||
|
|
||||||
|
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_netif_create_default_wifi_sta();
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
|
||||||
|
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_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
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->hostname && r->port) {
|
||||||
|
strncpy(host_out, r->hostname, host_len - 1);
|
||||||
|
*port_out = r->port;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "mDNS found broker: %s:%u", host_out, *port_out);
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
goto free_results;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
s_evt = xEventGroupCreate();
|
||||||
|
|
||||||
|
// Provisioning
|
||||||
|
if (!config_store_is_provisioned()) {
|
||||||
|
ESP_LOGI(TAG, "Not provisioned - starting BLE provisioning");
|
||||||
|
|
||||||
|
/* WiFi must be started (STA mode) before provisioning manager */
|
||||||
|
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());
|
||||||
|
|
||||||
|
provisioning_run(); /* blocks until complete */
|
||||||
|
|
||||||
|
/* Release BT memory now that provisioning used it; we reinit below for scanning */
|
||||||
|
esp_bt_mem_release(ESP_BT_MODE_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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size
|
||||||
|
nvs, data, nvs, 0x9000, 0x4000
|
||||||
|
ota_data, data, ota, 0xd000, 0x2000
|
||||||
|
ota_0, app, ota_0, 0x10000, 0x180000
|
||||||
|
ota_1, app, ota_1, 0x190000, 0x180000
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||||
|
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||||
|
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||||
|
|
||||||
|
# WiFi
|
||||||
|
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
|
||||||
|
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
|
||||||
|
|
||||||
|
# MQTT
|
||||||
|
CONFIG_MQTT_BUFFER_SIZE=512
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||||
|
|
||||||
|
# Partition table
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||||
|
|
||||||
|
# Enable OTA
|
||||||
|
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
||||||
Reference in New Issue
Block a user