This commit is contained in:
DTTerastar 2024-11-11 19:14:42 -05:00
parent 797ce586aa
commit c66afc53be
15 changed files with 1404 additions and 0 deletions

View File

@ -0,0 +1,106 @@
#include "ble_fingerprint.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gatt_defs.h"
#include <cmath>
static const char* TAG = "BLE_FINGER";
// Static member initialization
float BleFingerprint::maxDistance = 10.0f;
float BleFingerprint::absorption = 2.0f;
int8_t BleFingerprint::txRefRssi = -59;
int8_t BleFingerprint::rxAdjRssi = 0;
std::string BleFingerprint::query;
std::string BleFingerprint::include;
std::string BleFingerprint::exclude;
std::string BleFingerprint::knownMacs;
std::string BleFingerprint::knownIrks;
std::string BleFingerprint::countIds;
void BleFingerprint::init() {
// Configure scan parameters
esp_ble_scan_params_t scan_params = {
.scan_type = BLE_SCAN_TYPE_ACTIVE,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
.scan_interval = 0x50,
.scan_window = 0x30,
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
};
esp_err_t status = esp_ble_gap_set_scan_params(&scan_params);
if (status != ESP_OK) {
ESP_LOGE(TAG, "Failed to set scan parameters: %s", esp_err_to_name(status));
return;
}
// Register GAP callback
esp_ble_gap_register_callback(gap_callback);
}
void BleFingerprint::start_scan() {
esp_err_t status = esp_ble_gap_start_scanning(0); // Scan indefinitely
if (status != ESP_OK) {
ESP_LOGE(TAG, "Failed to start scanning: %s", esp_err_to_name(status));
}
}
void BleFingerprint::stop_scan() {
esp_err_t status = esp_ble_gap_stop_scanning();
if (status != ESP_OK) {
ESP_LOGE(TAG, "Failed to stop scanning: %s", esp_err_to_name(status));
}
}
void BleFingerprint::gap_callback(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:
ESP_LOGI(TAG, "Scan parameters set");
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");
}
break;
case ESP_GAP_BLE_SCAN_RESULT_EVT:
if (param->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
process_advertisement(param);
}
break;
default:
break;
}
}
void BleFingerprint::process_advertisement(esp_ble_gap_cb_param_t* scan_result) {
// Calculate distance based on RSSI
float distance = calculate_distance(scan_result->scan_rst.rssi);
if (distance > maxDistance) {
return; // Device too far away
}
// Convert BDA to string for MAC address
char bda_str[18];
sprintf(bda_str, "%02x:%02x:%02x:%02x:%02x:%02x",
scan_result->scan_rst.bda[0], scan_result->scan_rst.bda[1],
scan_result->scan_rst.bda[2], scan_result->scan_rst.bda[3],
scan_result->scan_rst.bda[4], scan_result->scan_rst.bda[5]);
ESP_LOGI(TAG, "Device found - MAC: %s, RSSI: %d, Distance: %.2f",
bda_str, scan_result->scan_rst.rssi, distance);
// TODO: Implement filtering based on query, include, exclude patterns
// TODO: Implement device tracking and MQTT reporting
}
float BleFingerprint::calculate_distance(int rssi) {
// Calculate distance using the log-distance path loss model
float ratio = (txRefRssi - (rssi + rxAdjRssi)) / (10.0 * absorption);
return pow(10, ratio);
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <string>
#include <vector>
#include "esp_gap_ble_api.h"
#include "esp_bt_defs.h"
class BleFingerprint {
public:
static void init();
static void start_scan();
static void stop_scan();
// Configuration
static float maxDistance;
static float absorption;
static int8_t txRefRssi;
static int8_t rxAdjRssi;
// Filters
static std::string query;
static std::string include;
static std::string exclude;
static std::string knownMacs;
static std::string knownIrks;
static std::string countIds;
private:
static void gap_callback(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
static void process_advertisement(esp_ble_gap_cb_param_t* scan_result);
static float calculate_distance(int rssi);
};

View File

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include "mqtt_client.h"
class MqttHandler {
public:
static void init(const std::string& host, uint16_t port,
const std::string& username, const std::string& password);
static bool publish(const char* topic, int qos, bool retain, const char* payload);
static bool is_connected();
// Discovery related functions
static bool send_discovery(const char* component, const char* name, const char* config);
static bool send_telemetry(const char* payload);
private:
static esp_mqtt_client_handle_t client;
static bool connected;
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
static void connect();
static void disconnect();
};

View File

@ -0,0 +1,101 @@
#include "mqtt_handler.h"
#include "esp_log.h"
#include <cstring>
static const char* TAG = "MQTT";
// Static member initialization
esp_mqtt_client_handle_t MqttHandler::client = nullptr;
bool MqttHandler::connected = false;
void MqttHandler::init(const std::string& host, uint16_t port,
const std::string& username, const std::string& password) {
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = host.c_str(),
.broker.address.port = port,
.credentials.username = username.c_str(),
.credentials.authentication.password = password.c_str(),
};
client = esp_mqtt_client_init(&mqtt_cfg);
if (!client) {
ESP_LOGE(TAG, "Failed to initialize MQTT client");
return;
}
// Register event handler
esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, NULL);
// Start client
esp_mqtt_client_start(client);
}
void MqttHandler::mqtt_event_handler(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) {
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT Connected");
connected = true;
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT Disconnected");
connected = false;
break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT Error");
break;
default:
break;
}
}
bool MqttHandler::publish(const char* topic, int qos, bool retain, const char* payload) {
if (!connected || !client) {
ESP_LOGW(TAG, "MQTT not connected");
return false;
}
int msg_id = esp_mqtt_client_publish(client, topic, payload,
strlen(payload), qos, retain);
return msg_id != -1;
}
bool MqttHandler::is_connected() {
return connected;
}
bool MqttHandler::send_discovery(const char* component, const char* name,
const char* config) {
if (!connected) return false;
// Format discovery topic
char topic[128];
snprintf(topic, sizeof(topic), "homeassistant/%s/%s/config",
component, name);
return publish(topic, 0, true, config);
}
bool MqttHandler::send_telemetry(const char* payload) {
if (!connected) return false;
return publish("espresense/telemetry", 0, false, payload);
}
void MqttHandler::connect() {
if (client) {
esp_mqtt_client_start(client);
}
}
void MqttHandler::disconnect() {
if (client) {
esp_mqtt_client_stop(client);
}
connected = false;
}

View File

@ -0,0 +1,76 @@
#include "bh1750.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char* TAG = "BH1750";
// Static member initialization
i2c_port_t BH1750::i2c_port = I2C_NUM_0;
uint8_t BH1750::device_addr = 0x23;
BH1750::Mode BH1750::current_mode = BH1750::CONTINUOUS_HIGH_RES_MODE;
esp_err_t BH1750::init(i2c_port_t i2c_num, uint8_t addr) {
i2c_port = i2c_num;
device_addr = addr;
// Power on the sensor
esp_err_t ret = write_command(0x01);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to power on sensor");
return ret;
}
// Set default mode
ret = set_mode(CONTINUOUS_HIGH_RES_MODE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set mode");
return ret;
}
return ESP_OK;
}
esp_err_t BH1750::write_command(uint8_t cmd) {
i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
i2c_master_start(cmd_handle);
i2c_master_write_byte(cmd_handle, (device_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd_handle, cmd, true);
i2c_master_stop(cmd_handle);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd_handle, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd_handle);
return ret;
}
esp_err_t BH1750::set_mode(Mode mode) {
esp_err_t ret = write_command(mode);
if (ret == ESP_OK) {
current_mode = mode;
// Wait for measurement to be ready
// High resolution mode needs 120ms, low resolution needs 16ms
vTaskDelay(pdMS_TO_TICKS(mode == CONTINUOUS_HIGH_RES_MODE ? 120 : 16));
}
return ret;
}
esp_err_t BH1750::read_light(float* lux) {
uint8_t data[2];
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, data, 2, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read sensor data");
return ret;
}
uint16_t level = (data[0] << 8) | data[1];
*lux = level / 1.2f; // Convert to lux according to datasheet
return ESP_OK;
}

View File

@ -0,0 +1,194 @@
#include "bme280.h"
#include "esp_log.h"
static const char* TAG = "BME280";
// Static member initialization
i2c_port_t BME280::i2c_port = I2C_NUM_0;
uint8_t BME280::device_addr = 0x76;
// Calibration data initialization
uint16_t BME280::dig_T1 = 0;
int16_t BME280::dig_T2 = 0;
int16_t BME280::dig_T3 = 0;
uint16_t BME280::dig_P1 = 0;
int16_t BME280::dig_P2 = 0;
int16_t BME280::dig_P3 = 0;
int16_t BME280::dig_P4 = 0;
int16_t BME280::dig_P5 = 0;
int16_t BME280::dig_P6 = 0;
int16_t BME280::dig_P7 = 0;
int16_t BME280::dig_P8 = 0;
int16_t BME280::dig_P9 = 0;
uint8_t BME280::dig_H1 = 0;
int16_t BME280::dig_H2 = 0;
uint8_t BME280::dig_H3 = 0;
int16_t BME280::dig_H4 = 0;
int16_t BME280::dig_H5 = 0;
int8_t BME280::dig_H6 = 0;
esp_err_t BME280::init(i2c_port_t i2c_num, uint8_t addr) {
i2c_port = i2c_num;
device_addr = addr;
// Read chip ID
uint8_t chip_id;
esp_err_t ret = read_registers(0xD0, &chip_id, 1);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read chip ID");
return ret;
}
if (chip_id != 0x60) {
ESP_LOGE(TAG, "Unexpected chip ID: 0x%02x", chip_id);
return ESP_ERR_INVALID_RESPONSE;
}
// Read calibration data
ret = read_calibration_data();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read calibration data");
return ret;
}
// Configure sensor
// Normal mode, temperature and pressure oversampling x1
write_register(0xF4, 0x27);
// Humidity oversampling x1
write_register(0xF2, 0x01);
return ESP_OK;
}
esp_err_t BME280::read_registers(uint8_t reg, uint8_t* data, size_t len) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_READ, true);
if (len > 1) {
i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t BME280::write_register(uint8_t reg, uint8_t value) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg, true);
i2c_master_write_byte(cmd, value, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t BME280::read_calibration_data() {
uint8_t buffer[26];
esp_err_t ret;
// Read temperature and pressure calibration data
ret = read_registers(0x88, buffer, 26);
if (ret != ESP_OK) return ret;
dig_T1 = (buffer[1] << 8) | buffer[0];
dig_T2 = (buffer[3] << 8) | buffer[2];
dig_T3 = (buffer[5] << 8) | buffer[4];
dig_P1 = (buffer[7] << 8) | buffer[6];
dig_P2 = (buffer[9] << 8) | buffer[8];
dig_P3 = (buffer[11] << 8) | buffer[10];
dig_P4 = (buffer[13] << 8) | buffer[12];
dig_P5 = (buffer[15] << 8) | buffer[14];
dig_P6 = (buffer[17] << 8) | buffer[16];
dig_P7 = (buffer[19] << 8) | buffer[18];
dig_P8 = (buffer[21] << 8) | buffer[20];
dig_P9 = (buffer[23] << 8) | buffer[22];
dig_H1 = buffer[25];
// Read humidity calibration data
ret = read_registers(0xE1, buffer, 7);
if (ret != ESP_OK) return ret;
dig_H2 = (buffer[1] << 8) | buffer[0];
dig_H3 = buffer[2];
dig_H4 = (buffer[3] << 4) | (buffer[4] & 0x0F);
dig_H5 = (buffer[5] << 4) | (buffer[4] >> 4);
dig_H6 = buffer[6];
return ESP_OK;
}
esp_err_t BME280::read_temperature(float* temp) {
uint8_t data[3];
esp_err_t ret = read_registers(0xFA, data, 3);
if (ret != ESP_OK) return ret;
int32_t adc_T = (data[0] << 16) | (data[1] << 8) | data[2];
adc_T >>= 4;
int32_t var1 = ((((adc_T >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;
int32_t var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) * ((adc_T >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
int32_t t_fine = var1 + var2;
*temp = (t_fine * 5 + 128) >> 8;
*temp /= 100.0f;
return ESP_OK;
}
esp_err_t BME280::read_pressure(float* pressure) {
uint8_t data[3];
esp_err_t ret = read_registers(0xF7, data, 3);
if (ret != ESP_OK) return ret;
int32_t adc_P = (data[0] << 16) | (data[1] << 8) | data[2];
adc_P >>= 4;
// Pressure compensation calculation
int64_t var1, var2, p;
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)dig_P6;
var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
var2 = var2 + (((int64_t)dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
if (var1 == 0) return ESP_FAIL;
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
*pressure = (float)p / 256.0f;
*pressure /= 100.0f; // Convert to hPa
return ESP_OK;
}
esp_err_t BME280::read_humidity(float* humidity) {
uint8_t data[2];
esp_err_t ret = read_registers(0xFD, data, 2);
if (ret != ESP_OK) return ret;
int32_t adc_H = (data[0] << 8) | data[1];
int32_t v_x1_u32r = (t_fine - ((int32_t)76800));
v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) +
((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) *
(((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +
((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
*humidity = (float)(v_x1_u32r >> 12) / 1024.0f;
return ESP_OK;
}

View File

@ -0,0 +1,137 @@
#include "dht.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
static const char* TAG = "DHT";
// Static member initialization
gpio_num_t DHT::gpio_pin = GPIO_NUM_NC;
DHT::Type DHT::sensor_type = DHT::DHT22;
esp_err_t DHT::init(gpio_num_t gpio_num, Type type) {
gpio_pin = gpio_num;
sensor_type = type;
// Configure GPIO
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << gpio_num),
.mode = GPIO_MODE_INPUT_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
esp_err_t ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to configure GPIO");
return ret;
}
// Initial state is high
gpio_set_level(gpio_pin, 1);
vTaskDelay(pdMS_TO_TICKS(1000)); // Wait for sensor to stabilize
return ESP_OK;
}
void DHT::delay_us(uint32_t us) {
uint32_t start = esp_timer_get_time();
while (esp_timer_get_time() - start < us);
}
int DHT::wait_state_change(int level, int timeout_us) {
int us_waited = 0;
while (gpio_get_level(gpio_pin) == level) {
if (us_waited > timeout_us) return -1;
delay_us(1);
us_waited++;
}
return us_waited;
}
esp_err_t DHT::read_data(uint8_t* data) {
uint8_t bits[5] = {0};
uint8_t cnt = 7;
uint8_t idx = 0;
// Clear data array
for (int i = 0; i < 5; i++) data[i] = 0;
// Send start signal
gpio_set_direction(gpio_pin, GPIO_MODE_OUTPUT);
gpio_set_level(gpio_pin, 0);
delay_us(sensor_type == DHT11 ? 18000 : 1000); // Hold low for 18ms (DHT11) or 1ms (DHT22)
gpio_set_level(gpio_pin, 1);
delay_us(40);
gpio_set_direction(gpio_pin, GPIO_MODE_INPUT);
// Read sensor response
if (wait_state_change(0, 80) == -1) return ESP_ERR_TIMEOUT;
if (wait_state_change(1, 80) == -1) return ESP_ERR_TIMEOUT;
// Read data
for (int i = 0; i < 40; i++) {
if (wait_state_change(0, 50) == -1) return ESP_ERR_TIMEOUT;
int us = wait_state_change(1, 70);
if (us == -1) return ESP_ERR_TIMEOUT;
if (us > 40) {
bits[idx] |= (1 << cnt);
}
if (cnt == 0) {
cnt = 7;
idx++;
} else {
cnt--;
}
}
// Verify checksum
if (bits[4] != ((bits[0] + bits[1] + bits[2] + bits[3]) & 0xFF)) {
ESP_LOGE(TAG, "Checksum failed");
return ESP_ERR_INVALID_CRC;
}
for (int i = 0; i < 5; i++) data[i] = bits[i];
return ESP_OK;
}
esp_err_t DHT::read_temperature(float* temperature) {
uint8_t data[5];
esp_err_t ret = read_data(data);
if (ret != ESP_OK) return ret;
if (sensor_type == DHT11) {
*temperature = data[2];
if (data[3] & 0x80) {
*temperature = -(*temperature);
}
} else {
*temperature = ((data[2] & 0x7F) << 8) + data[3];
*temperature *= 0.1;
if (data[2] & 0x80) {
*temperature = -(*temperature);
}
}
return ESP_OK;
}
esp_err_t DHT::read_humidity(float* humidity) {
uint8_t data[5];
esp_err_t ret = read_data(data);
if (ret != ESP_OK) return ret;
if (sensor_type == DHT11) {
*humidity = data[0];
} else {
*humidity = (data[0] << 8) + data[1];
*humidity *= 0.1;
}
return ESP_OK;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/i2c.h"
class BH1750 {
public:
enum Mode {
CONTINUOUS_HIGH_RES_MODE = 0x10,
CONTINUOUS_LOW_RES_MODE = 0x13,
ONE_TIME_HIGH_RES_MODE = 0x20,
ONE_TIME_LOW_RES_MODE = 0x23
};
static esp_err_t init(i2c_port_t i2c_num, uint8_t addr = 0x23);
static esp_err_t read_light(float* lux);
static esp_err_t set_mode(Mode mode);
private:
static i2c_port_t i2c_port;
static uint8_t device_addr;
static Mode current_mode;
static esp_err_t write_command(uint8_t cmd);
};

View File

@ -0,0 +1,41 @@
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/i2c.h"
class BME280 {
public:
static esp_err_t init(i2c_port_t i2c_num, uint8_t addr = 0x76);
static esp_err_t read_temperature(float* temp);
static esp_err_t read_humidity(float* humidity);
static esp_err_t read_pressure(float* pressure);
private:
static i2c_port_t i2c_port;
static uint8_t device_addr;
// Calibration data
static uint16_t dig_T1;
static int16_t dig_T2;
static int16_t dig_T3;
static uint16_t dig_P1;
static int16_t dig_P2;
static int16_t dig_P3;
static int16_t dig_P4;
static int16_t dig_P5;
static int16_t dig_P6;
static int16_t dig_P7;
static int16_t dig_P8;
static int16_t dig_P9;
static uint8_t dig_H1;
static int16_t dig_H2;
static uint8_t dig_H3;
static int16_t dig_H4;
static int16_t dig_H5;
static int8_t dig_H6;
static esp_err_t read_calibration_data();
static esp_err_t read_registers(uint8_t reg, uint8_t* data, size_t len);
static esp_err_t write_register(uint8_t reg, uint8_t value);
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/gpio.h"
class DHT {
public:
enum Type {
DHT11 = 11,
DHT22 = 22
};
static esp_err_t init(gpio_num_t gpio_num, Type type = DHT22);
static esp_err_t read_temperature(float* temperature);
static esp_err_t read_humidity(float* humidity);
private:
static gpio_num_t gpio_pin;
static Type sensor_type;
static esp_err_t read_data(uint8_t* data);
static void delay_us(uint32_t us);
static int wait_state_change(int level, int timeout_us);
};

View File

@ -0,0 +1,35 @@
#pragma once
#include "esp_err.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "bme280.h"
#include "bh1750.h"
#include "dht.h"
class SensorManager {
public:
static esp_err_t init(i2c_port_t i2c_port = I2C_NUM_0,
gpio_num_t sda = GPIO_NUM_21,
gpio_num_t scl = GPIO_NUM_22,
uint32_t freq = 100000);
// Sensor initialization methods
static esp_err_t init_bme280(uint8_t addr = 0x76);
static esp_err_t init_bh1750(uint8_t addr = 0x23);
static esp_err_t init_dht(gpio_num_t gpio_num, DHT::Type type = DHT::DHT22);
// Sensor reading methods
static esp_err_t read_temperature(float* temp, const char* sensor = "bme280");
static esp_err_t read_humidity(float* humidity, const char* sensor = "bme280");
static esp_err_t read_pressure(float* pressure);
static esp_err_t read_light(float* lux);
private:
static i2c_port_t i2c_num;
static bool bme280_initialized;
static bool bh1750_initialized;
static bool dht_initialized;
static esp_err_t init_i2c(gpio_num_t sda, gpio_num_t scl, uint32_t freq);
};

View File

@ -0,0 +1,114 @@
#include "sensor_manager.h"
#include "esp_log.h"
static const char* TAG = "SensorManager";
// Static member initialization
i2c_port_t SensorManager::i2c_num = I2C_NUM_0;
bool SensorManager::bme280_initialized = false;
bool SensorManager::bh1750_initialized = false;
bool SensorManager::dht_initialized = false;
esp_err_t SensorManager::init(i2c_port_t i2c_port, gpio_num_t sda, gpio_num_t scl, uint32_t freq) {
i2c_num = i2c_port;
return init_i2c(sda, scl, freq);
}
esp_err_t SensorManager::init_i2c(gpio_num_t sda, gpio_num_t scl, uint32_t freq) {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = sda,
.scl_io_num = scl,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = freq
}
};
esp_err_t ret = i2c_param_config(i2c_num, &conf);
if (ret != ESP_OK) return ret;
return i2c_driver_install(i2c_num, conf.mode, 0, 0, 0);
}
esp_err_t SensorManager::init_bme280(uint8_t addr) {
esp_err_t ret = BME280::init(i2c_num, addr);
if (ret == ESP_OK) {
bme280_initialized = true;
ESP_LOGI(TAG, "BME280 initialized successfully");
}
return ret;
}
esp_err_t SensorManager::init_bh1750(uint8_t addr) {
esp_err_t ret = BH1750::init(i2c_num, addr);
if (ret == ESP_OK) {
bh1750_initialized = true;
ESP_LOGI(TAG, "BH1750 initialized successfully");
}
return ret;
}
esp_err_t SensorManager::init_dht(gpio_num_t gpio_num, DHT::Type type) {
esp_err_t ret = DHT::init(gpio_num, type);
if (ret == ESP_OK) {
dht_initialized = true;
ESP_LOGI(TAG, "DHT sensor initialized successfully");
}
return ret;
}
esp_err_t SensorManager::read_temperature(float* temp, const char* sensor) {
if (strcmp(sensor, "bme280") == 0) {
if (!bme280_initialized) {
ESP_LOGE(TAG, "BME280 not initialized");
return ESP_ERR_INVALID_STATE;
}
return BME280::read_temperature(temp);
} else if (strcmp(sensor, "dht") == 0) {
if (!dht_initialized) {
ESP_LOGE(TAG, "DHT not initialized");
return ESP_ERR_INVALID_STATE;
}
return DHT::read_temperature(temp);
}
ESP_LOGE(TAG, "Unknown sensor type for temperature reading");
return ESP_ERR_INVALID_ARG;
}
esp_err_t SensorManager::read_humidity(float* humidity, const char* sensor) {
if (strcmp(sensor, "bme280") == 0) {
if (!bme280_initialized) {
ESP_LOGE(TAG, "BME280 not initialized");
return ESP_ERR_INVALID_STATE;
}
return BME280::read_humidity(humidity);
} else if (strcmp(sensor, "dht") == 0) {
if (!dht_initialized) {
ESP_LOGE(TAG, "DHT not initialized");
return ESP_ERR_INVALID_STATE;
}
return DHT::read_humidity(humidity);
}
ESP_LOGE(TAG, "Unknown sensor type for humidity reading");
return ESP_ERR_INVALID_ARG;
}
esp_err_t SensorManager::read_pressure(float* pressure) {
if (!bme280_initialized) {
ESP_LOGE(TAG, "BME280 not initialized");
return ESP_ERR_INVALID_STATE;
}
return BME280::read_pressure(pressure);
}
esp_err_t SensorManager::read_light(float* lux) {
if (!bh1750_initialized) {
ESP_LOGE(TAG, "BH1750 not initialized");
return ESP_ERR_INVALID_STATE;
}
return BH1750::read_light(lux);
}

View File

@ -0,0 +1,59 @@
#pragma once
#include <string>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_http_server.h"
#include "esp_spiffs.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatt_defs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
#include "driver/i2c.h"
#include "cJSON.h"
// Component headers
#include "ble_fingerprint.h"
#include "mqtt_handler.h"
// Global variables
extern TimerHandle_t reconnectTimer;
extern TaskHandle_t scanTaskHandle;
extern unsigned long updateStartedMillis;
extern unsigned long lastTeleMillis;
extern int reconnectTries;
extern int teleFails;
extern int reportFailed;
extern bool online;
extern bool sentDiscovery;
extern UBaseType_t bleStack;
// Configuration
extern std::string room;
extern std::string mqttHost;
extern std::string mqttUser;
extern std::string mqttPass;
extern uint16_t mqttPort;
// Settings
extern bool discovery;
extern bool publishTele;
extern bool publishRooms;
extern bool publishDevices;
// Function declarations
void app_main(void);
bool sendTelemetry(unsigned int totalSeen, unsigned int totalFpSeen,
unsigned int totalFpQueried, unsigned int totalFpReported,
unsigned int count);
// Constants
constexpr const char* DEFAULT_MQTT_HOST = "mqtt.local";
constexpr uint16_t DEFAULT_MQTT_PORT = 1883;
constexpr const char* DEFAULT_MQTT_USER = "";
constexpr const char* DEFAULT_MQTT_PASSWORD = "";

237
esp_project/main/main.cpp Normal file
View File

@ -0,0 +1,237 @@
#include "main.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "esp_mac.h"
#include "cJSON.h"
#include "sensor_manager.h"
static const char *TAG = "ESPresense";
// Global variables initialization
TimerHandle_t reconnectTimer = nullptr;
TaskHandle_t scanTaskHandle = nullptr;
unsigned long updateStartedMillis = 0;
unsigned long lastTeleMillis = 0;
int reconnectTries = 0;
int teleFails = 0;
int reportFailed = 0;
bool online = false;
bool sentDiscovery = false;
UBaseType_t bleStack = 0;
// Configuration
std::string room;
std::string mqttHost = DEFAULT_MQTT_HOST;
std::string mqttUser = DEFAULT_MQTT_USER;
std::string mqttPass = DEFAULT_MQTT_PASSWORD;
uint16_t mqttPort = DEFAULT_MQTT_PORT;
// Settings
bool discovery = true;
bool publishTele = true;
bool publishRooms = false;
bool publishDevices = true;
static void scan_task(void* arg) {
while (true) {
BleFingerprint::start_scan();
vTaskDelay(pdMS_TO_TICKS(100)); // Small delay between scans
}
}
static void initialize_nvs(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
}
static void initialize_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
}
static void load_config(void)
{
nvs_handle_t nvs;
esp_err_t err = nvs_open("config", NVS_READWRITE, &nvs);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error opening NVS handle");
return;
}
// Read room name or generate default
size_t required_size;
err = nvs_get_str(nvs, "room", nullptr, &required_size);
if (err == ESP_OK) {
char* room_buf = new char[required_size];
nvs_get_str(nvs, "room", room_buf, &required_size);
room = room_buf;
delete[] room_buf;
} else {
uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_WIFI_STA);
char room_buf[18];
snprintf(room_buf, sizeof(room_buf), "esp-%02x%02x%02x", mac[3], mac[4], mac[5]);
room = room_buf;
}
nvs_close(nvs);
}
static void initialize_sensors(void)
{
esp_err_t ret = SensorManager::init(I2C_NUM_0, GPIO_NUM_21, GPIO_NUM_22);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize I2C");
return;
}
// Initialize BME280 if available
ret = SensorManager::init_bme280();
if (ret != ESP_OK) {
ESP_LOGW(TAG, "BME280 not found");
}
// Initialize BH1750 if available
ret = SensorManager::init_bh1750();
if (ret != ESP_OK) {
ESP_LOGW(TAG, "BH1750 not found");
}
// Initialize DHT22 if available
ret = SensorManager::init_dht(GPIO_NUM_23, DHT::DHT22);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "DHT22 not found");
}
}
void app_main(void)
{
ESP_LOGI(TAG, "ESPresense starting...");
// Initialize NVS
initialize_nvs();
// Load configuration
load_config();
// Initialize WiFi
initialize_wifi();
// Initialize sensors
initialize_sensors();
// Initialize BLE
esp_err_t ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
if (ret) {
ESP_LOGE(TAG, "Bluetooth memory release failed: %s", esp_err_to_name(ret));
return;
}
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
ESP_LOGE(TAG, "Initialize controller failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(TAG, "Enable controller failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(TAG, "Init bluedroid failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(TAG, "Enable bluedroid failed: %s", esp_err_to_name(ret));
return;
}
// Initialize BLE scanning
BleFingerprint::init();
// Create BLE scan task
xTaskCreate(scan_task, "scan_task", 4096, nullptr, 5, &scanTaskHandle);
// Initialize MQTT
MqttHandler::init(mqttHost, mqttPort, mqttUser, mqttPass);
}
bool sendTelemetry(unsigned int totalSeen, unsigned int totalFpSeen,
unsigned int totalFpQueried, unsigned int totalFpReported,
unsigned int count)
{
if (!MqttHandler::is_connected()) {
ESP_LOGW(TAG, "MQTT not connected");
return false;
}
uint64_t now = esp_timer_get_time() / 1000; // Convert to milliseconds
if (now - lastTeleMillis < 15000) {
return false;
}
lastTeleMillis = now;
// Create JSON document using cJSON
cJSON *doc = cJSON_CreateObject();
// Add telemetry data
char ip[16];
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info);
sprintf(ip, IPSTR, IP2STR(&ip_info.ip));
cJSON_AddStringToObject(doc, "ip", ip);
cJSON_AddNumberToObject(doc, "uptime", esp_timer_get_time() / 1000000);
int8_t rssi;
esp_wifi_get_rssi(&rssi);
cJSON_AddNumberToObject(doc, "rssi", rssi);
// Add sensor data if available
float value;
if (SensorManager::read_temperature(&value, "bme280") == ESP_OK) {
cJSON_AddNumberToObject(doc, "temperature", value);
}
if (SensorManager::read_humidity(&value, "bme280") == ESP_OK) {
cJSON_AddNumberToObject(doc, "humidity", value);
}
if (SensorManager::read_pressure(&value) == ESP_OK) {
cJSON_AddNumberToObject(doc, "pressure", value);
}
if (SensorManager::read_light(&value) == ESP_OK) {
cJSON_AddNumberToObject(doc, "light", value);
}
if (count > 0) {
cJSON_AddNumberToObject(doc, "count", count);
}
if (totalSeen > 0) {
cJSON_AddNumberToObject(doc, "adverts", totalSeen);
}
char *json_str = cJSON_Print(doc);
bool success = MqttHandler::send_telemetry(json_str);
cJSON_Delete(doc);
free(json_str);
return success;
}

198
esp_project/platformio.ini Normal file
View File

@ -0,0 +1,198 @@
[platformio]
default_envs = esp32
build_cache_dir = ~/.buildcache
extra_configs = platformio_override.ini
[common]
framework = espidf
platform = espressif32
platform_packages =
framework-espidf @ ~5.1.0
check_tool = cppcheck, clangtidy
check_flags = clangtidy: --config-file=.clang-tidy
check_skip_packages = yes
build_flags =
-Wall
-Wextra
-Wformat=2
-Wno-format-nonliteral
-D CONFIG_BT_NIMBLE_TASK_STACK_SIZE=5632
-D CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=5632
-D CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
-D CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=20
-D CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
-D CONFIG_ASYNC_TCP_USE_WDT=0
build_unflags =
-Wswitch-unreachable
-Wstringop-overflow
-Wincompatible-pointer-types
-Wnonnull-compare
board_build.partitions = partitions_singleapp.csv
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, time
upload_speed = 1500000
extra_scripts = update_ts.py
[esp32]
extends = common
board = esp32dev
debug_tool = esp-prog
build_flags =
-mtarget-align
-D ARDUINO_ARCH_ESP32
-D CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
-D REPORT_PINNED_TO_CORE=1
-D CONFIG_USE_ETHERNET
${common.build_flags}
[esp32c3]
extends = common
board = esp32-c3-devkitm-1
board_build.flash_mode = dio
build_flags =
-D ARDUINO_ARCH_ESP32
-D ARDUINO_ARCH_ESP32C3
-D CONFIG_IDF_TARGET_ESP32C3
-D CONFIG_BT_NIMBLE_PINNED_TO_CORE=0
-D REPORT_PINNED_TO_CORE=0
-D ESP32C3
${common.build_flags}
[esp32c3-cdc]
extends = esp32c3
build_flags =
-D ARDUINO_USB_MODE=1
-D ARDUINO_USB_CDC_ON_BOOT=1
${esp32c3.build_flags}
[esp32s3]
extends = common
board = dfrobot_firebeetle2_esp32s3
build_flags =
-D ARDUINO_ARCH_ESP32
-D ARDUINO_ARCH_ESP32S3
-D CONFIG_IDF_TARGET_ESP32S3
-D ARDUINO_USB_MODE=1
-D REPORT_PINNED_TO_CORE=1
-D ESP32S3
${common.build_flags}
[esp32s3-cdc]
extends = esp32s3
build_flags =
-D ARDUINO_USB_MODE=1
-D ARDUINO_USB_CDC_ON_BOOT=1
${esp32s3.build_flags}
[env:esp32]
extends = esp32
build_flags =
-D CORE_DEBUG_LEVEL=1
-D FIRMWARE='"esp32"'
-D SENSORS
${esp32.build_flags}
[env:esp32c3]
extends = esp32c3
build_flags =
-D CORE_DEBUG_LEVEL=1
-D FIRMWARE='"esp32c3"'
-D SENSORS
${esp32c3.build_flags}
[env:esp32c3-cdc]
extends = esp32c3-cdc
build_flags =
-D CORE_DEBUG_LEVEL=1
-D FIRMWARE='"esp32c3-cdc"'
-D SENSORS
${esp32c3-cdc.build_flags}
[env:esp32s3]
extends = esp32s3
build_flags =
-D CORE_DEBUG_LEVEL=1
-D FIRMWARE='"esp32s3"'
-D SENSORS
${esp32s3.build_flags}
[env:esp32s3-cdc]
extends = esp32s3-cdc
build_flags =
-D CORE_DEBUG_LEVEL=1
-D FIRMWARE='"esp32s3-cdc"'
-D SENSORS
${esp32s3-cdc.build_flags}
[env:esp32-verbose]
extends = esp32
build_flags =
-D CORE_DEBUG_LEVEL=2
-D FIRMWARE='"esp32-verbose"'
-D VERBOSE
-D SENSORS
${esp32.build_flags}
[env:esp32c3-verbose]
extends = esp32c3
build_flags =
-D CORE_DEBUG_LEVEL=2
-D FIRMWARE='"esp32c3-verbose"'
-D VERBOSE
-D SENSORS
${esp32c3.build_flags}
[env:esp32s3-verbose]
extends = esp32s3
build_flags =
-D CORE_DEBUG_LEVEL=2
-D FIRMWARE='"esp32s3-verbose"'
-D VERBOSE
-D SENSORS
${esp32s3.build_flags}
[env:m5stickc]
extends = esp32
board = m5stick-c
build_flags =
-D CORE_DEBUG_LEVEL=1
-D TFT_FONT=1
-D M5STICK
-D FIRMWARE='"m5stickc"'
-D SENSORS
${esp32.build_flags}
[env:m5stickc-plus]
extends = esp32
board = m5stick-c
build_flags =
-D CORE_DEBUG_LEVEL=1
-D TFT_FONT=2
-D M5STICK
-D PLUS
-D FIRMWARE='"m5stickc-plus"'
-D SENSORS
${esp32.build_flags}
[env:m5atom]
extends = esp32
board = m5stack-atom
build_flags =
-D CORE_DEBUG_LEVEL=1
-D M5ATOM
-D MATRIX
-D FIRMWARE='"m5atom"'
-D SENSORS
${esp32.build_flags}
[env:macchina-a0]
extends = esp32
build_flags =
-D CORE_DEBUG_LEVEL=1
-D MACCHINA_A0
-D FIRMWARE='"macchina-a0"'
-D SENSORS
${esp32.build_flags}