generated from wessel/boilerplate
feat(esp32-IMU): Upload IMU program progress
MQTT - functions TODO: - Fix Serial
This commit is contained in:
2
IMU/.clangd
Normal file
2
IMU/.clangd
Normal file
@@ -0,0 +1,2 @@
|
||||
CompileFlags:
|
||||
Remove: [-f*, -m*]
|
||||
13
IMU/.devcontainer/Dockerfile
Normal file
13
IMU/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN apt-get update -y && apt-get install udev -y
|
||||
|
||||
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||
|
||||
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||
|
||||
CMD ["/bin/bash", "-c"]
|
||||
21
IMU/.devcontainer/devcontainer.json
Normal file
21
IMU/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "ESP-IDF QEMU",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.espIdfPath": "/opt/esp/idf",
|
||||
"idf.toolsPath": "/opt/esp",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"espressif.esp-idf-web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": ["--privileged"]
|
||||
}
|
||||
75
IMU/.editorconfig
Executable file
75
IMU/.editorconfig
Executable file
@@ -0,0 +1,75 @@
|
||||
[*]
|
||||
cpp_indent_braces=false
|
||||
cpp_indent_multi_line_relative_to=innermost_parenthesis
|
||||
cpp_indent_within_parentheses=indent
|
||||
cpp_indent_preserve_within_parentheses=true
|
||||
cpp_indent_case_labels=true
|
||||
cpp_indent_case_contents=true
|
||||
cpp_indent_case_contents_when_block=false
|
||||
cpp_indent_lambda_braces_when_parameter=true
|
||||
cpp_indent_goto_labels=one_left
|
||||
cpp_indent_preprocessor=leftmost_column
|
||||
cpp_indent_access_specifiers=false
|
||||
cpp_indent_namespace_contents=false
|
||||
cpp_indent_preserve_comments=false
|
||||
|
||||
cpp_new_line_before_open_brace_namespace=false
|
||||
cpp_new_line_before_open_brace_type=false
|
||||
cpp_new_line_before_open_brace_function=ignore
|
||||
cpp_new_line_before_open_brace_block=ignore
|
||||
cpp_new_line_before_open_brace_lambda=ignore
|
||||
|
||||
cpp_new_line_scope_braces_on_separate_lines=false
|
||||
cpp_new_line_close_brace_same_line_empty_type=false
|
||||
cpp_new_line_close_brace_same_line_empty_function=false
|
||||
cpp_new_line_before_catch=false
|
||||
cpp_new_line_before_else=false
|
||||
cpp_new_line_before_while_in_do_while=false
|
||||
|
||||
cpp_space_before_function_open_parenthesis=remove
|
||||
cpp_space_within_parameter_list_parentheses=false
|
||||
cpp_space_between_empty_parameter_list_parentheses=false
|
||||
cpp_space_after_keywords_in_control_flow_statements=true
|
||||
cpp_space_within_control_flow_statement_parentheses=false
|
||||
cpp_space_before_lambda_open_parenthesis=false
|
||||
|
||||
cpp_space_within_cast_parentheses=false
|
||||
cpp_space_after_cast_close_parenthesis=false
|
||||
cpp_space_within_expression_parentheses=false
|
||||
cpp_space_before_block_open_brace=true
|
||||
cpp_space_between_empty_braces=false
|
||||
cpp_space_before_initializer_list_open_brace=true
|
||||
cpp_space_within_initializer_list_braces=true
|
||||
cpp_space_preserve_in_initializer_list=true
|
||||
|
||||
cpp_space_before_open_square_bracket=false
|
||||
cpp_space_within_square_brackets=false
|
||||
cpp_space_before_empty_square_brackets=false
|
||||
cpp_space_between_empty_square_brackets=false
|
||||
cpp_space_group_square_brackets=true
|
||||
|
||||
cpp_space_within_lambda_brackets=false
|
||||
cpp_space_between_empty_lambda_brackets=false
|
||||
cpp_space_before_comma=false
|
||||
cpp_space_after_comma=true
|
||||
|
||||
cpp_space_remove_around_member_operators=true
|
||||
|
||||
cpp_space_before_inheritance_colon=true
|
||||
cpp_space_before_constructor_colon=true
|
||||
|
||||
cpp_space_remove_before_semicolon=true
|
||||
cpp_space_after_semicolon=true
|
||||
|
||||
cpp_space_remove_around_unary_operator=true
|
||||
|
||||
cpp_space_around_binary_operator=insert
|
||||
cpp_space_around_assignment_operator=insert
|
||||
cpp_space_pointer_reference_alignment=left
|
||||
cpp_space_around_ternary_operator=insert
|
||||
cpp_wrap_preserve_blocks=one_liners
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
6
IMU/CMakeLists.txt
Executable file
6
IMU/CMakeLists.txt
Executable file
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(IMU)
|
||||
2
IMU/main/CMakeLists.txt
Executable file
2
IMU/main/CMakeLists.txt
Executable file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mpu6886.c"
|
||||
INCLUDE_DIRS ".")
|
||||
67
IMU/main/Kconfig.projbuild
Executable file
67
IMU/main/Kconfig.projbuild
Executable file
@@ -0,0 +1,67 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||
|
||||
config I2C_MASTER_SCL
|
||||
int "SCL GPIO Num"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 4
|
||||
help
|
||||
GPIO number for I2C Master clock line.
|
||||
|
||||
config I2C_MASTER_SDA
|
||||
int "SDA GPIO Num"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 5
|
||||
help
|
||||
GPIO number for I2C Master data line.
|
||||
|
||||
config I2C_MASTER_FREQUENCY
|
||||
int "Master Frequency"
|
||||
default 400000
|
||||
help
|
||||
I2C Speed of Master device.
|
||||
|
||||
config WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "YourNetworkName"
|
||||
help
|
||||
SSID of WiFi network to connect to.
|
||||
|
||||
config WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "YourPassword"
|
||||
help
|
||||
Password of WiFi network to connect to.
|
||||
|
||||
config MQTT_BROKER_URI
|
||||
string "MQTT Broker URI"
|
||||
default "mqtt://192.168.178.26:1883"
|
||||
help
|
||||
URI of the MQTT broker to connect to.
|
||||
|
||||
config MQTT_TOPIC
|
||||
string "MQTT Topic"
|
||||
default "esp32/imu"
|
||||
help
|
||||
MQTT topic to publish IMU data to.
|
||||
|
||||
config WIFI_AP_MODE
|
||||
bool "Enable WiFi AP Mode"
|
||||
default n
|
||||
help
|
||||
Enable this option to start the device in Access Point mode.
|
||||
If disabled, the device will start in Station mode.
|
||||
|
||||
config WIFI_AP_SSID
|
||||
string "WiFi AP SSID"
|
||||
default "ESP32_IMU_AP"
|
||||
help
|
||||
SSID of the WiFi Access Point when in AP mode.
|
||||
|
||||
config WIFI_AP_PASSWORD
|
||||
string "WiFi AP Password"
|
||||
default "esp32imuap"
|
||||
help
|
||||
Password of the WiFi Access Point when in AP mode.
|
||||
endmenu
|
||||
318
IMU/main/mpu6886.c
Normal file
318
IMU/main/mpu6886.c
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "mpu6886.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// New: initialise WiFi in either STA or AP mode. Pass ap_mode=true to start an Access Point.
|
||||
static void wifi_init(bool ap_mode)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
if (ap_mode) {
|
||||
esp_netif_create_default_wifi_ap();
|
||||
|
||||
wifi_config_t wifi_config = { 0 };
|
||||
strncpy((char *)wifi_config.ap.ssid, CONFIG_WIFI_AP_SSID, sizeof(wifi_config.ap.ssid));
|
||||
wifi_config.ap.ssid_len = strlen(CONFIG_WIFI_AP_SSID);
|
||||
strncpy((char *)wifi_config.ap.password, CONFIG_WIFI_AP_PASSWORD, sizeof(wifi_config.ap.password));
|
||||
wifi_config.ap.max_connection = 4;
|
||||
if (strlen(CONFIG_WIFI_AP_PASSWORD) == 0) {
|
||||
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
|
||||
} else {
|
||||
wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_LOGI("WIFI", "AP started SSID:%s", CONFIG_WIFI_AP_SSID);
|
||||
} else {
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = CONFIG_WIFI_SSID,
|
||||
.password = CONFIG_WIFI_PASSWORD,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_ERROR_CHECK(esp_wifi_connect());
|
||||
ESP_LOGI("WIFI", "STA started, connecting to: %s", CONFIG_WIFI_SSID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t mpu6886_write_byte(mpu6886_t *dev, uint8_t reg, uint8_t data) {
|
||||
uint8_t tx[2] = { reg, data };
|
||||
return i2c_master_write_to_device(dev->i2c_port, dev->address, tx, sizeof(tx), pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
static esp_err_t mpu6886_read_bytes(mpu6886_t *dev, uint8_t reg, uint8_t *data, size_t len) {
|
||||
return i2c_master_write_read_device(dev->i2c_port, dev->address, ®, 1, data, len, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
static int16_t bytes_to_int16(uint8_t high, uint8_t low) {
|
||||
return (int16_t)((high << 8) | low);
|
||||
}
|
||||
|
||||
// Event handler uses esp_event handler signature; it receives mqtt event via event_data
|
||||
static void mqtt_event_handler_cb(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("MQTT", "connected");
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGW("MQTT", "disconnected");
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGE("MQTT", "error");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mqtt_app_start(void)
|
||||
{
|
||||
esp_mqtt_client_config_t mqtt_cfg = { 0 };
|
||||
|
||||
mqtt_cfg.broker.address.uri = CONFIG_MQTT_BROKER_URI;
|
||||
|
||||
s_mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
if (s_mqtt_client == NULL) {
|
||||
ESP_LOGW("MQTT", "failed to init mqtt client");
|
||||
return;
|
||||
}
|
||||
esp_mqtt_client_start(s_mqtt_client);
|
||||
}
|
||||
|
||||
// new: detect accel/gyro full-scale selections and set divisors accordingly
|
||||
static esp_err_t mpu6886_update_sensitivity(mpu6886_t *dev)
|
||||
{
|
||||
uint8_t aconf = 0, gconf = 0;
|
||||
esp_err_t err;
|
||||
|
||||
err = mpu6886_read_bytes(dev, MPU6886_ACCEL_CONFIG, &aconf, 1);
|
||||
if (err != ESP_OK) return err;
|
||||
err = mpu6886_read_bytes(dev, MPU6886_GYRO_CONFIG, &gconf, 1);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
uint8_t a_fs = (aconf >> 3) & 0x03; // AFS_SEL bits [4:3]
|
||||
switch (a_fs) {
|
||||
case 0: dev->accel_div = ACCEL_SO_2G; break; // ±2g
|
||||
case 1: dev->accel_div = ACCEL_SO_4G; break; // ±4g
|
||||
case 2: dev->accel_div = ACCEL_SO_8G; break; // ±8g
|
||||
case 3: dev->accel_div = ACCEL_SO_16G; break; // ±16g
|
||||
default: dev->accel_div = ACCEL_SO_2G; break;
|
||||
}
|
||||
|
||||
uint8_t g_fs = (gconf >> 3) & 0x03; // FS_SEL bits [4:3]
|
||||
switch (g_fs) {
|
||||
case 0: dev->gyro_div = GYRO_SO_250DPS; break; // ±250 dps
|
||||
case 1: dev->gyro_div = GYRO_SO_500DPS; break; // ±500 dps
|
||||
case 2: dev->gyro_div = GYRO_SO_1000DPS; break; // ±1000 dps
|
||||
case 3: dev->gyro_div = GYRO_SO_2000DPS; break; // ±2000 dps
|
||||
default: dev->gyro_div = GYRO_SO_250DPS; break;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_init(i2c_port_t i2c_num, gpio_num_t sda_io, gpio_num_t scl_io, uint32_t clk_speed_hz)
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = sda_io,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = scl_io,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = clk_speed_hz,
|
||||
};
|
||||
esp_err_t err = i2c_param_config(i2c_num, &conf);
|
||||
if (err != ESP_OK) return err;
|
||||
return i2c_driver_install(i2c_num, I2C_MODE_MASTER, 0, 0, 0);
|
||||
}
|
||||
|
||||
esp_err_t mpu6886_init(mpu6886_t *dev, i2c_port_t i2c_port) {
|
||||
dev->i2c_port = i2c_port;
|
||||
dev->address = MPU6886_ADDR;
|
||||
|
||||
uint8_t who_am_i = 0;
|
||||
if (mpu6886_read_bytes(dev, MPU6886_WHO_AM_I, &who_am_i, 1) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (who_am_i != 0x19) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Reset
|
||||
if (mpu6886_write_byte(dev, MPU6886_PWR_MGMT_1, 0x80) != ESP_OK) return ESP_FAIL;
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// Auto select clock
|
||||
if (mpu6886_write_byte(dev, MPU6886_PWR_MGMT_1, 0x01) != ESP_OK) return ESP_FAIL;
|
||||
|
||||
// Default config: set to ±2G accel, ±250DPS gyro (device may have different settings; we'll detect them)
|
||||
mpu6886_write_byte(dev, MPU6886_ACCEL_CONFIG, 0x00);
|
||||
mpu6886_write_byte(dev, MPU6886_GYRO_CONFIG, 0x00);
|
||||
|
||||
dev->gyro_offset = (vec3_t){0, 0, 0};
|
||||
dev->accel_offset = (vec3_t){0, 0, 0};
|
||||
|
||||
// detect actual sensitivities from device registers and set divisors
|
||||
if (mpu6886_update_sensitivity(dev) != ESP_OK) {
|
||||
// fallback to defaults if read fails
|
||||
dev->accel_div = ACCEL_SO_2G;
|
||||
dev->gyro_div = GYRO_SO_250DPS;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mpu6886_read_accel(mpu6886_t *dev, vec3_t *accel) {
|
||||
uint8_t buf[6];
|
||||
esp_err_t ret = mpu6886_read_bytes(dev, MPU6886_ACCEL_XOUT_H, buf, 6);
|
||||
if (ret != ESP_OK) return ret;
|
||||
|
||||
// convert raw -> m/s^2 and apply stored offsets
|
||||
accel->x = (float)bytes_to_int16(buf[0], buf[1]) / dev->accel_div * SF_M_S2 - dev->accel_offset.x;
|
||||
accel->y = (float)bytes_to_int16(buf[2], buf[3]) / dev->accel_div * SF_M_S2 - dev->accel_offset.y;
|
||||
accel->z = (float)bytes_to_int16(buf[4], buf[5]) / dev->accel_div * SF_M_S2 - dev->accel_offset.z;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mpu6886_read_gyro(mpu6886_t *dev, vec3_t *gyro) {
|
||||
uint8_t buf[6];
|
||||
esp_err_t ret = mpu6886_read_bytes(dev, MPU6886_GYRO_XOUT_H, buf, 6);
|
||||
if (ret != ESP_OK) return ret;
|
||||
|
||||
gyro->x = (float)bytes_to_int16(buf[0], buf[1]) / dev->gyro_div * SF_RAD_S - dev->gyro_offset.x;
|
||||
gyro->y = (float)bytes_to_int16(buf[2], buf[3]) / dev->gyro_div * SF_RAD_S - dev->gyro_offset.y;
|
||||
gyro->z = (float)bytes_to_int16(buf[4], buf[5]) / dev->gyro_div * SF_RAD_S - dev->gyro_offset.z;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mpu6886_read_temp(mpu6886_t *dev, float *temp) {
|
||||
static uint8_t buf[2];
|
||||
esp_err_t ret = mpu6886_read_bytes(dev, MPU6886_TEMP_OUT_H, buf, 2);
|
||||
if (ret != ESP_OK) return ret;
|
||||
|
||||
int16_t raw = bytes_to_int16(buf[0], buf[1]);
|
||||
*temp = ((float)raw / TEMP_SO) + TEMP_OFFSET;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mpu6886_calibrate_gyro(mpu6886_t *dev, int samples, int delay_ms) {
|
||||
vec3_t sum = {0, 0, 0}, g;
|
||||
for (int i = 0; i < samples; i++) {
|
||||
if (mpu6886_read_gyro(dev, &g) != ESP_OK) return ESP_FAIL;
|
||||
sum.x += g.x;
|
||||
sum.y += g.y;
|
||||
sum.z += g.z;
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
}
|
||||
dev->gyro_offset.x = sum.x / samples;
|
||||
dev->gyro_offset.y = sum.y / samples;
|
||||
dev->gyro_offset.z = sum.z / samples;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mpu6886_calibrate_accel(mpu6886_t *dev, int samples, int delay_ms)
|
||||
{
|
||||
// Calibrate accelerometer offsets while the device is stationary.
|
||||
// The function will detect the device full-scale and compute offsets in m/s^2.
|
||||
vec3_t sum = {0, 0, 0}, a;
|
||||
for (int i = 0; i < samples; i++) {
|
||||
if (mpu6886_read_accel(dev, &a) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sum.x += a.x;
|
||||
sum.y += a.y;
|
||||
sum.z += a.z;
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
}
|
||||
vec3_t avg = { sum.x / samples, sum.y / samples, sum.z / samples };
|
||||
|
||||
// For X/Y expect ~0 m/s^2 when stationary; offset = measured average
|
||||
dev->accel_offset.x = avg.x;
|
||||
dev->accel_offset.y = avg.y;
|
||||
|
||||
// For Z expect ~+1g (SF_M_S2) if +Z points up. If device is flipped you'll get -1g.
|
||||
// Compute expected gravity sign from measured avg.z magnitude and sign.
|
||||
float expected_g = (avg.z < 0) ? -SF_M_S2 : SF_M_S2;
|
||||
dev->accel_offset.z = avg.z - expected_g;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t err = i2c_master_init(I2C_NUM_0, CONFIG_I2C_MASTER_SDA, CONFIG_I2C_MASTER_SCL, CONFIG_I2C_MASTER_FREQUENCY); // adjust pins if needed
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("MPU6886", "I2C init failed: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Start WiFi: set CONFIG_WIFI_AP_MODE in sdkconfig to build in AP mode,
|
||||
// otherwise the default is STA mode. You can also call wifi_init(true)
|
||||
// or wifi_init(false) directly to toggle at runtime.
|
||||
#ifdef CONFIG_WIFI_AP_MODE
|
||||
wifi_init(true);
|
||||
#else
|
||||
wifi_init(false);
|
||||
#endif
|
||||
|
||||
mpu6886_t mpu;
|
||||
mpu.i2c_port = I2C_NUM_0;
|
||||
mpu.address = MPU6886_ADDR;
|
||||
|
||||
esp_err_t ret = mpu6886_init(&mpu, I2C_NUM_0);
|
||||
if (ret != ESP_OK) {
|
||||
// Handle initialization error
|
||||
ESP_LOGE("MPU6886", "init failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start MQTT client (will retry until network is available)
|
||||
mqtt_app_start();
|
||||
|
||||
mpu6886_calibrate_gyro(&mpu, 100, 10);
|
||||
mpu6886_calibrate_accel(&mpu, 100, 10);
|
||||
vec3_t accel, gyro;
|
||||
float temp;
|
||||
|
||||
while (1) {
|
||||
mpu6886_read_accel(&mpu, &accel);
|
||||
mpu6886_read_gyro(&mpu, &gyro);
|
||||
mpu6886_read_temp(&mpu, &temp);
|
||||
ESP_LOGI("MPU6886", "Accel: X=%8.2f Y=%8.2f Z=%8.2f m/s² | Gyro: X=%8.2f Y=%8.2f Z=%8.2f rad/s | Temp: %8.2f °C",
|
||||
accel.x, accel.y, accel.z, gyro.x, gyro.y, gyro.z, temp);
|
||||
|
||||
// Publish readings as JSON over MQTT if client available
|
||||
if (s_mqtt_client != NULL) {
|
||||
char payload[256];
|
||||
int n = snprintf(payload, sizeof(payload),
|
||||
"{\"accel\":{\"x\":%.3f,\"y\":%.3f,\"z\":%.3f},\"gyro\":{\"x\":%.3f,\"y\":%.3f,\"z\":%.3f},\"temp\":%.2f}",
|
||||
accel.x, accel.y, accel.z,
|
||||
gyro.x, gyro.y, gyro.z,
|
||||
temp);
|
||||
if (n > 0 && n < (int)sizeof(payload)) {
|
||||
int msg_id = esp_mqtt_client_publish(s_mqtt_client, CONFIG_MQTT_TOPIC, payload, 0, 1, 0);
|
||||
ESP_LOGD("MQTT", "published msg_id=%d payload_len=%d", msg_id, n);
|
||||
} else {
|
||||
ESP_LOGW("MQTT", "payload truncated or encoding error");
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
80
IMU/main/mpu6886.h
Normal file
80
IMU/main/mpu6886.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef MPU6886_H
|
||||
#define MPU6886_H
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <math.h>
|
||||
#include "mqtt_client.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL /*!< GPIO number used for I2C master clock */
|
||||
#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA /*!< GPIO number used for I2C master data */
|
||||
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */
|
||||
#define I2C_MASTER_FREQ_HZ CONFIG_I2C_MASTER_FREQUENCY /*!< I2C master clock frequency */
|
||||
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||
#define I2C_MASTER_TIMEOUT_MS 1000
|
||||
|
||||
#define MPU6886_ADDR 0x68
|
||||
|
||||
// MPU6886 Registers
|
||||
#define MPU6886_PWR_MGMT_1 0x6B
|
||||
#define MPU6886_WHO_AM_I 0x75
|
||||
#define MPU6886_ACCEL_XOUT_H 0x3B
|
||||
#define MPU6886_GYRO_XOUT_H 0x43
|
||||
#define MPU6886_TEMP_OUT_H 0x41
|
||||
#define MPU6886_ACCEL_CONFIG 0x1C
|
||||
#define MPU6886_GYRO_CONFIG 0x1B
|
||||
|
||||
// Sensitivity scales
|
||||
#define ACCEL_SO_2G 16384.0f
|
||||
#define ACCEL_SO_4G 8192.0f
|
||||
#define ACCEL_SO_8G 4096.0f
|
||||
#define ACCEL_SO_16G 2048.0f
|
||||
|
||||
#define GYRO_SO_250DPS 131.0f
|
||||
#define GYRO_SO_500DPS 65.5f
|
||||
#define GYRO_SO_1000DPS 32.8f
|
||||
#define GYRO_SO_2000DPS 16.4f
|
||||
|
||||
#define TEMP_SO 326.8f
|
||||
#define TEMP_OFFSET 25.0f
|
||||
|
||||
// Scale factors
|
||||
#define SF_M_S2 9.80665f
|
||||
#define SF_RAD_S 0.017453292519943f
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} vec3_t;
|
||||
|
||||
typedef struct {
|
||||
i2c_port_t i2c_port;
|
||||
uint8_t address;
|
||||
float accel_div;
|
||||
float gyro_div;
|
||||
vec3_t gyro_offset;
|
||||
vec3_t accel_offset;
|
||||
} mpu6886_t;
|
||||
|
||||
static esp_mqtt_client_handle_t s_mqtt_client = NULL;
|
||||
|
||||
// Function declarations
|
||||
esp_err_t mpu6886_init(mpu6886_t *dev, i2c_port_t i2c_port);
|
||||
esp_err_t mpu6886_read_accel(mpu6886_t *dev, vec3_t *accel);
|
||||
esp_err_t mpu6886_read_gyro(mpu6886_t *dev, vec3_t *gyro);
|
||||
esp_err_t mpu6886_read_temp(mpu6886_t *dev, float *temp);
|
||||
esp_err_t mpu6886_calibrate_gyro(mpu6886_t *dev, int samples, int delay_ms);
|
||||
esp_err_t mpu6886_calibrate_accel(mpu6886_t *dev, int samples, int delay_ms);
|
||||
|
||||
#endif
|
||||
2257
IMU/sdkconfig
Normal file
2257
IMU/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
2032
IMU/sdkconfig.old
Normal file
2032
IMU/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user