Merge branch 'master' of https://git.sparksoftdesign.com/firstpass/soundshot
This commit is contained in:
23
.vscode/c_cpp_properties.json
vendored
23
.vscode/c_cpp_properties.json
vendored
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ESP-IDF",
|
||||
"compilerPath": "${config:idf.toolsPathWin}\\tools\\xtensa-esp-elf\\esp-13.2.0_20240530\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe",
|
||||
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
|
||||
"includePath": [
|
||||
"${config:idf.espIdfPath}/components/**",
|
||||
"${config:idf.espIdfPathWin}/components/**",
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${config:idf.espIdfPath}/components",
|
||||
"${config:idf.espIdfPathWin}/components",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "gdbtarget",
|
||||
"request": "attach",
|
||||
"name": "Eclipse CDT GDB Adapter"
|
||||
},
|
||||
{
|
||||
"type": "espidf",
|
||||
"name": "Launch",
|
||||
"request": "launch"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -35,8 +35,7 @@
|
||||
"semphr.h": "c",
|
||||
"lv_spinbox.h": "c",
|
||||
"lv_slider.h": "c",
|
||||
"lv_menu.h": "c",
|
||||
"stream_buffer.h": "c"
|
||||
"lv_menu.h": "c"
|
||||
},
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
|
||||
93
README.md
93
README.md
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
## 🐳 ESP-IDF Dockerized Development Environment (Windows)
|
||||
|
||||
This project includes a fully containerized [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html) development environment using **Docker** and **Visual Studio Code**.
|
||||
@@ -209,3 +210,95 @@ Features:
|
||||
|
||||
* If `idf.py` is not found, make sure the container terminal sources `export.sh`
|
||||
|
||||
=======
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
A2DP-SOURCE EXAMPLE
|
||||
========================
|
||||
|
||||
Example of A2DP audio source role
|
||||
|
||||
This is the example of using Advanced Audio Distribution Profile (A2DP) APIs to transmit audio stream. Application can take advantage of this example to implement portable audio players or microphones to transmit audio stream to A2DP sink devices.
|
||||
|
||||
## How to use this example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example is able to run on any commonly available ESP32 development board, and is supposed to connect to [A2DP sink example](../a2dp_sink) in ESP-IDF.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Enable Classic Bluetooth and A2DP under Component config --> Bluetooth --> Bluedroid Enable
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output.
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
For the first step, this example performs device discovery to search for a target device (A2DP sink) whose device name is "ESP_SPEAKER" and whose "Rendering" bit of its Service Class field is set in its Class of Device (COD). If a candidate target is found, the local device will initiate connection with it.
|
||||
|
||||
After connection with A2DP sink is established, the example performs the following running loop 1-2-3-4-1:
|
||||
1. audio transmission starts and lasts for a while
|
||||
2. audio transmission stops
|
||||
3. disconnect with target device
|
||||
4. reconnect to target device
|
||||
|
||||
The example implements an event loop triggered by a periodic "heart beat" timer and events from Bluetooth protocol stack callback functions.
|
||||
|
||||
After the local device discovers the target device and initiates connection, there will be logging message like this:
|
||||
|
||||
```
|
||||
I (4090) BT_AV: Found a target device, address xx:xx:xx:xx:xx:xx, name ESP_SPEAKER
|
||||
I (4090) BT_AV: Cancel device discovery ...
|
||||
I (4100) BT_AV: Device discovery stopped.
|
||||
I (4100) BT_AV: a2dp connecting to peer: ESP_SPEAKER
|
||||
```
|
||||
|
||||
If connection is set up successfully, there will be such message:
|
||||
|
||||
```
|
||||
I (5100) BT_AV: a2dp connected
|
||||
```
|
||||
|
||||
Starting of audio transmission has the following notification message:
|
||||
|
||||
```
|
||||
I (10880) BT_AV: a2dp media ready checking ...
|
||||
...
|
||||
I (10880) BT_AV: a2dp media ready, starting ...
|
||||
...
|
||||
I (11400) BT_AV: a2dp media start successfully.
|
||||
```
|
||||
|
||||
Stop of audio transmission, and disconnection with remote device generate the following notification message:
|
||||
|
||||
```
|
||||
I (110880) BT_AV: a2dp media stopping...
|
||||
...
|
||||
I (110920) BT_AV: a2dp media stopped successfully, disconnecting...
|
||||
...
|
||||
I (111040) BT_AV: a2dp disconnected
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
* For current stage, the supported audio codec in ESP32 A2DP is SBC. SBC audio stream is encoded from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data.
|
||||
* The raw PCM media stream in the example is generated by a sequence of random number, so the sound played on the sink side will be piercing noise.
|
||||
* As a usage limitation, ESP32 A2DP source can support at most one connection with remote A2DP sink devices. Also, A2DP source cannot be used together with A2DP sink at the same time, but can be used with other profiles such as SPP and HFP.
|
||||
* Usually, the `ACL_CONN_NUM` is set to `2` but not `1` as one is used for a2dp connection and the other is used for avrcp connection.
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
<<<<<<< HEAD
|
||||
idf_component_register(SRCS "system.c.LOCAL.c" "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
|
||||
|
||||
"gui.c"
|
||||
"lsm6dsv.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES "driver"
|
||||
"esp_lcd"
|
||||
"lvgl"
|
||||
"esp_lvgl_port"
|
||||
"esp_timer"
|
||||
"nvs_flash"
|
||||
"bt")
|
||||
=======
|
||||
idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
|
||||
|
||||
"gui.c"
|
||||
@@ -10,3 +24,5 @@ idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
|
||||
"esp_timer"
|
||||
"nvs_flash"
|
||||
"bt")
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
|
||||
|
||||
1159
main/bt_app.c
1159
main/bt_app.c
File diff suppressed because it is too large
Load Diff
31
main/gpio.h
31
main/gpio.h
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
#ifndef GPIO_H
|
||||
#define GPIO_H
|
||||
|
||||
@@ -26,4 +27,34 @@
|
||||
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
|
||||
#endif
|
||||
|
||||
=======
|
||||
#ifndef GPIO_H
|
||||
#define GPIO_H
|
||||
|
||||
// Define keypad buttons
|
||||
#define PIN_NUM_BUTTON_0 36
|
||||
#define PIN_NUM_BUTTON_1 39
|
||||
#define PIN_NUM_LED_1 32
|
||||
#define PIN_NUM_LED_2 33
|
||||
#define PIN_NUM_nON 26
|
||||
|
||||
|
||||
// Define GPIO pins
|
||||
#ifdef DEVKIT
|
||||
#define PIN_NUM_MOSI 23 // SDA pin for LCD
|
||||
#define PIN_NUM_CLK 18 // SCL pin for LCD
|
||||
#define PIN_NUM_CS 2 // CS pin
|
||||
#define PIN_NUM_DC 12 // Data/Command pin (RS)
|
||||
#define PIN_NUM_RST 13 // Reset pin
|
||||
#define PIN_NUM_BK_LIGHT -1 // Backlight control pin, -1 if not used
|
||||
#else
|
||||
#define PIN_NUM_MOSI 23 // SDA pin for LCD
|
||||
#define PIN_NUM_CLK 18 // SCL pin for LCD
|
||||
#define PIN_NUM_CS 12 // CS pin
|
||||
#define PIN_NUM_DC 15 // Data/Command pin (RS)
|
||||
#define PIN_NUM_RST 13 // Reset pin
|
||||
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
|
||||
#endif
|
||||
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
#endif
|
||||
621
main/gui.c
621
main/gui.c
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -616,4 +617,624 @@ static void gui_task(void)
|
||||
|
||||
|
||||
|
||||
=======
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "lvgl.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#include "gui.h"
|
||||
#include "gpio.h"
|
||||
#include "keypad.h"
|
||||
#include "bubble.h"
|
||||
#include "system.h"
|
||||
|
||||
#define DEVKIT
|
||||
#undef DEVKIT
|
||||
|
||||
static const char *TAG = "gui";
|
||||
|
||||
#define UNLOCK() lvgl_port_unlock()
|
||||
#define LOCK() lvgl_port_lock(0)
|
||||
|
||||
// LCD Pin Configuration
|
||||
#define LCD_PIXEL_CLOCK_HZ (5 * 1000 * 1000)
|
||||
#define LCD_SPI_HOST SPI2_HOST
|
||||
|
||||
#define PIN_NUM_nON 26
|
||||
|
||||
|
||||
|
||||
// ST7735S properties
|
||||
#define LCD_H_RES 160
|
||||
#define LCD_V_RES 80
|
||||
#define LCD_CMD_BITS 8
|
||||
#define LCD_PARAM_BITS 8
|
||||
#define LCD_COLOR_SPACE ESP_LCD_COLOR_SPACE_RGB
|
||||
#define LCD_BITS_PER_PIXEL 16
|
||||
|
||||
static esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
static lv_disp_t *disp = NULL;
|
||||
static lv_obj_t *imu_label = NULL; // Label for IMU data
|
||||
|
||||
static lv_style_t style_mono8;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int selected;
|
||||
int count;
|
||||
lv_obj_t *obj;
|
||||
} menu_context_t;
|
||||
|
||||
static void gui_task(void);
|
||||
static void createBubble(lv_obj_t * scr);
|
||||
static void build_scrollable_menu(void);
|
||||
static void currentFocusIndex(menu_context_t *ctx);
|
||||
|
||||
#define MAX_ITEMS 10
|
||||
#define VISIBLE_ITEMS 3
|
||||
#define MENU_MAX_STRING_LENGTH 30
|
||||
|
||||
static const char *menu_items[MAX_ITEMS] = {
|
||||
"V-Moda Crossfade", "Item 2", "Item 3", "Item 4",
|
||||
"Item 5", "Item 6", "Item 7", "Item 8",
|
||||
"Item 9", "Item 10"
|
||||
};
|
||||
|
||||
static lv_obj_t *list;
|
||||
static lv_obj_t *buttons[MAX_ITEMS];
|
||||
static int selected_index = 0;
|
||||
static lv_obj_t *_bubble = NULL;
|
||||
static lv_obj_t * _menu = NULL;
|
||||
static lv_obj_t *_currentPage = NULL;
|
||||
|
||||
static GuiMode_t _mode = GUI_BUBBLE;
|
||||
|
||||
/* 1. Prepare a default (unfocused) style */
|
||||
static lv_style_t _styleUnfocusedBtn;
|
||||
|
||||
|
||||
/* 2. Prepare a focus style */
|
||||
static lv_style_t _styleFocusedBtn;
|
||||
|
||||
static menu_context_t _menuContext;
|
||||
|
||||
static bool notify_lvgl_flush_ready(void *user_ctx) {
|
||||
if (disp) {
|
||||
lv_display_flush_ready(disp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void create_lvgl_demo(void)
|
||||
{
|
||||
|
||||
lvgl_port_lock(0);
|
||||
// Create a screen with black background
|
||||
lv_obj_t *scr = lv_scr_act();
|
||||
|
||||
createBubble(scr);
|
||||
//build_scrollable_menu();
|
||||
|
||||
lvgl_port_unlock();
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void lcd_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initialize SPI bus");
|
||||
spi_bus_config_t buscfg = {
|
||||
.sclk_io_num = PIN_NUM_CLK,
|
||||
.mosi_io_num = PIN_NUM_MOSI,
|
||||
.miso_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = LCD_H_RES * LCD_V_RES * 2
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(LCD_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
|
||||
ESP_LOGI(TAG, "Install panel IO");
|
||||
|
||||
esp_lcd_panel_io_spi_config_t io_config = {
|
||||
.dc_gpio_num = PIN_NUM_DC,
|
||||
.cs_gpio_num = PIN_NUM_CS,
|
||||
.pclk_hz = LCD_PIXEL_CLOCK_HZ,
|
||||
.lcd_cmd_bits = LCD_CMD_BITS,
|
||||
.lcd_param_bits = LCD_PARAM_BITS,
|
||||
.spi_mode = 3,
|
||||
.trans_queue_depth = 10,
|
||||
.user_ctx = NULL,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_config, &io_handle));
|
||||
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = PIN_NUM_RST,
|
||||
.rgb_endian = LCD_RGB_ENDIAN_BGR,
|
||||
.bits_per_pixel = LCD_BITS_PER_PIXEL,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
|
||||
|
||||
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, 1, 26)); // ST7735S typically needs these offsets
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
|
||||
}
|
||||
|
||||
static void lvgl_init(void)
|
||||
{
|
||||
lv_init();
|
||||
|
||||
|
||||
#if 1
|
||||
const lvgl_port_cfg_t lvgl_cfg = {
|
||||
.task_priority = 4, // LVGL task priority
|
||||
.task_stack = 16384, // LVGL task stack size
|
||||
.task_affinity = 0, // LVGL task can run on any core
|
||||
.task_max_sleep_ms = 500, // Maximum sleep in LVGL task
|
||||
.timer_period_ms = 5 // LVGL timer period
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(lvgl_port_init(&lvgl_cfg));
|
||||
#endif
|
||||
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = io_handle,
|
||||
.panel_handle = panel_handle,
|
||||
.buffer_size = LCD_H_RES * LCD_V_RES * 2,
|
||||
.double_buffer = false,
|
||||
.hres = LCD_H_RES,
|
||||
.vres = LCD_V_RES,
|
||||
.monochrome = false,
|
||||
.rotation = {
|
||||
.swap_xy = true,
|
||||
.mirror_x = true,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.flags = {
|
||||
//.buff_dma = true,
|
||||
.swap_bytes = true,
|
||||
}
|
||||
};
|
||||
|
||||
disp = lvgl_port_add_disp(&disp_cfg);
|
||||
//lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void createBubble(lv_obj_t * scr)
|
||||
{
|
||||
|
||||
// 2) Create a bubble level of size 200×60, with range [−30°, +30°], initial 0°:
|
||||
lv_obj_t * level = bubble_create(scr, 150, 40, -10.0f, +10.0f, 0.0f);
|
||||
lv_obj_align(level, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
// 3) … Later, when you read your accelerometer or keypad‐derived angle …
|
||||
float new_angle = 10.0f;
|
||||
bubble_setValue(level, new_angle);
|
||||
|
||||
// 4) You can call bubble_level_set_value(level, …) as often as you like.
|
||||
// Each call invalidates the object and LVGL will call the draw callback
|
||||
// (usually on the next tick, or immediately if LVGL is idle).
|
||||
|
||||
_bubble = level;
|
||||
}
|
||||
|
||||
|
||||
void gui_start(void)
|
||||
{
|
||||
|
||||
// Initialize LCD
|
||||
lcd_init();
|
||||
|
||||
// Initialize LVGL
|
||||
lvgl_init();
|
||||
|
||||
// Create UI
|
||||
create_lvgl_demo();
|
||||
|
||||
keypad_start();
|
||||
|
||||
gpio_set_level(PIN_NUM_BK_LIGHT, 1);
|
||||
|
||||
xTaskCreate(gui_task, "gui_task", 4096, NULL, 5, NULL);
|
||||
|
||||
}
|
||||
|
||||
static uint32_t waitForKeyPress(void)
|
||||
{
|
||||
QueueHandle_t q = keypad_getQueue();
|
||||
uint32_t ev = 0;
|
||||
|
||||
if (xQueueReceive(q, &ev, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
return ev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handleMainMenu(void)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
// Create a screen with black background
|
||||
lv_obj_t *scr = lv_scr_act();
|
||||
|
||||
//create_menu(scr);
|
||||
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
|
||||
// ───── MENU CONFIG ─────
|
||||
#define ROW_H 20 // height of each row
|
||||
static const char * items[] = { // your menu entries
|
||||
"VMode Crossmade",
|
||||
"Second Choice",
|
||||
"Another Option",
|
||||
"Yet Another",
|
||||
"Yet Another",
|
||||
"Last One"
|
||||
};
|
||||
#define ITEM_COUNT (sizeof(items)/sizeof(items[0]))
|
||||
|
||||
// ───── STATE & HELPERS ─────
|
||||
static lv_obj_t * btn_array[ITEM_COUNT];
|
||||
static int selected_idx = 0;
|
||||
|
||||
// Called whenever a button is “activated” (Enter/Click)
|
||||
static void btn_click_cb(lv_event_t * e) {
|
||||
lv_obj_t * btn = lv_event_get_target(e);
|
||||
const char * txt = lv_label_get_text(lv_obj_get_child(btn, 0));
|
||||
ESP_LOGI(TAG, "Activated: %s\n", txt);
|
||||
}
|
||||
|
||||
// Repaint all rows so only btn_array[selected_idx] is highlighted
|
||||
static void refresh_highlight(void) {
|
||||
|
||||
return;
|
||||
lvgl_port_lock(0);
|
||||
|
||||
int index = 0;
|
||||
|
||||
lv_obj_t *page = _currentPage;
|
||||
|
||||
lv_obj_t * child = NULL;
|
||||
lv_obj_t * next = lv_obj_get_child(page, index);
|
||||
lv_obj_t * selected = NULL;
|
||||
while(next) {
|
||||
child = next;
|
||||
|
||||
ESP_LOGI(TAG, "Child: %p", child);
|
||||
|
||||
|
||||
if (lv_obj_has_flag(child, LV_OBJ_FLAG_USER_1))
|
||||
{
|
||||
lv_obj_set_style_bg_color(child, lv_color_hex(0xFF8800), 0);
|
||||
lv_obj_set_style_text_color(lv_obj_get_child(child,0),
|
||||
lv_color_white(), 0);
|
||||
|
||||
selected = child;
|
||||
ESP_LOGI(TAG, "Selected");
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_obj_set_style_bg_color(child, lv_color_white(), 0);
|
||||
lv_obj_set_style_text_color(lv_obj_get_child(child,0),
|
||||
lv_color_black(), 0);
|
||||
}
|
||||
|
||||
index++;
|
||||
next = lv_obj_get_child(page, index);
|
||||
}
|
||||
|
||||
if (selected)
|
||||
{
|
||||
lv_obj_scroll_to_view(selected, LV_ANIM_ON);
|
||||
}
|
||||
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
|
||||
static void menuInc(int inc)
|
||||
{
|
||||
LOCK();
|
||||
|
||||
currentFocusIndex(&_menuContext);
|
||||
|
||||
ESP_LOGI(TAG, "Current Index: %d", _menuContext.selected);
|
||||
|
||||
lv_obj_t *next = NULL;
|
||||
|
||||
// check if we are at the first or last in the page
|
||||
lv_obj_t *test = lv_obj_get_child(_currentPage, (inc > 0 ? -1 : 0));
|
||||
|
||||
if (_menuContext.obj != test)
|
||||
{
|
||||
next = lv_obj_get_child(_currentPage, _menuContext.selected + inc);
|
||||
lv_obj_add_state(next, LV_STATE_FOCUSED);
|
||||
lv_obj_clear_state(_menuContext.obj, LV_STATE_FOCUSED);
|
||||
lv_obj_scroll_to_view(next, LV_ANIM_ON);
|
||||
|
||||
_menuContext.obj = next;
|
||||
_menuContext.selected += inc;
|
||||
}
|
||||
|
||||
UNLOCK();
|
||||
}
|
||||
|
||||
static void menuNext(void)
|
||||
{
|
||||
menuInc(1);
|
||||
}
|
||||
|
||||
|
||||
static void menuPrevious(void)
|
||||
{
|
||||
menuInc(-1);
|
||||
}
|
||||
|
||||
// Fire the “clicked” event on the selected row
|
||||
static void activate_selected(void)
|
||||
{
|
||||
LOCK();
|
||||
lv_obj_send_event(_menuContext.obj, LV_EVENT_CLICKED, NULL);
|
||||
UNLOCK();
|
||||
}
|
||||
|
||||
static lv_obj_t * addMenuItem(lv_obj_t *page, const char *text)
|
||||
{
|
||||
lv_obj_t * btn = lv_btn_create(page);
|
||||
lv_obj_set_size(btn, LV_PCT(100), ROW_H);
|
||||
|
||||
|
||||
lv_obj_add_style(btn, &_styleUnfocusedBtn, LV_PART_MAIN | LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn, &_styleFocusedBtn, LV_PART_MAIN | LV_STATE_FOCUSED);
|
||||
|
||||
lv_obj_add_state(btn, LV_STATE_DEFAULT);
|
||||
|
||||
|
||||
// style it just like your old list
|
||||
// lv_obj_set_style_bg_color(btn, lv_color_white(), 0);
|
||||
lv_obj_set_style_radius(btn, 0, LV_PART_MAIN);
|
||||
lv_obj_set_style_border_width(btn, 0, LV_PART_MAIN);
|
||||
lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
// label & center
|
||||
lv_obj_t * lbl = lv_label_create(btn);
|
||||
lv_label_set_text(lbl, text);
|
||||
lv_obj_set_style_radius(lbl, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_style_border_width(lbl, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
|
||||
// click callback
|
||||
lv_obj_add_event_cb(btn, btn_click_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
static int getSelectedIndex(lv_obj_t *page)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void currentFocusIndex(menu_context_t *ctx)
|
||||
{
|
||||
ctx->count = lv_obj_get_child_cnt(_currentPage);
|
||||
ctx->obj = NULL;
|
||||
ctx->selected = -1;
|
||||
|
||||
// return the index of the currently focused object
|
||||
for(int i = 0; i < ctx->count; i++) {
|
||||
lv_obj_t * child = lv_obj_get_child(_currentPage, i);
|
||||
|
||||
if (lv_obj_has_state(child, LV_STATE_FOCUSED))
|
||||
{
|
||||
ctx->obj = child;
|
||||
ctx->selected = i;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
// ───── BUILD THE MENU ─────
|
||||
static void build_scrollable_menu(void) {
|
||||
|
||||
|
||||
lv_style_init(&_styleFocusedBtn);
|
||||
lv_style_init(&_styleUnfocusedBtn);
|
||||
|
||||
lv_style_set_bg_color(&_styleUnfocusedBtn, lv_color_make(0xff,0xff,0xff)); // gray bg
|
||||
lv_style_set_text_color(&_styleUnfocusedBtn, lv_color_hex(0xFF8800));
|
||||
|
||||
lv_style_set_bg_color(&_styleFocusedBtn, lv_color_make(0x33,0x99,0xFF)); // blue bg
|
||||
lv_style_set_text_color(&_styleUnfocusedBtn,lv_color_black());
|
||||
|
||||
|
||||
// 2) Inside it, create the lv_menu and hide its sidebar/header
|
||||
lv_obj_t *menu = lv_menu_create(lv_scr_act());
|
||||
_menu = menu;
|
||||
|
||||
lv_obj_set_style_radius(menu, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_style_border_width(menu, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_size(menu, lv_pct(100), lv_pct(100));
|
||||
lv_obj_center(menu);
|
||||
|
||||
lv_obj_set_scrollbar_mode(menu, LV_SCROLLBAR_MODE_AUTO);
|
||||
|
||||
|
||||
lv_obj_t * main = lv_menu_page_create(menu, NULL);
|
||||
lv_obj_set_style_radius(main, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_style_border_width(main, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_scrollbar_mode(main, LV_SCROLLBAR_MODE_AUTO);
|
||||
lv_obj_set_size(main, lv_pct(100), lv_pct(100));
|
||||
|
||||
lv_menu_set_page(menu, main);
|
||||
|
||||
lv_obj_t * tmpObj;
|
||||
|
||||
|
||||
|
||||
lv_obj_t * calMenu = lv_menu_page_create(menu, NULL);
|
||||
lv_obj_set_style_radius(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_style_border_width(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY);
|
||||
lv_obj_set_scrollbar_mode(calMenu, LV_SCROLLBAR_MODE_AUTO);
|
||||
|
||||
tmpObj = addMenuItem(main, "Bluetooth");
|
||||
lv_obj_add_state(tmpObj, LV_STATE_FOCUSED);
|
||||
//lv_obj_add_flag(tmpObj, LV_OBJ_FLAG_USER_1);
|
||||
|
||||
tmpObj = addMenuItem(main, "Calibration");
|
||||
lv_menu_set_load_page_event(menu, tmpObj, calMenu);
|
||||
|
||||
|
||||
tmpObj = addMenuItem(main, "Volume");
|
||||
|
||||
addMenuItem(main, "About");
|
||||
addMenuItem(main, "Exit");
|
||||
|
||||
|
||||
addMenuItem(calMenu, "Calibrate Level");
|
||||
addMenuItem(calMenu, "Reset Calibration");
|
||||
addMenuItem(calMenu, "Exit");
|
||||
|
||||
_currentPage = main;
|
||||
|
||||
// 6) Initial highlight
|
||||
selected_idx = 0;
|
||||
refresh_highlight();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void gui_task(void)
|
||||
{
|
||||
system_subscribe(xTaskGetCurrentTaskHandle());
|
||||
|
||||
// Grab queue handle
|
||||
QueueHandle_t q = keypad_getQueue();
|
||||
uint32_t ev = 0;
|
||||
|
||||
LOCK();
|
||||
// _mode = GUI_MENU;
|
||||
// lv_obj_remove_flag(_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_remove_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
|
||||
//lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
UNLOCK();
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
||||
|
||||
if (xQueueReceive(q, &ev, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
switch (ev) {
|
||||
|
||||
case (KEY_UP << KEY_LONG_PRESS):
|
||||
{
|
||||
system_setZeroAngle();
|
||||
break;
|
||||
}
|
||||
|
||||
case (KEY_DOWN << KEY_LONG_PRESS):
|
||||
{
|
||||
system_clearZeroAngle();
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
case (KEY0 << KEY_SHORT_PRESS):
|
||||
|
||||
if (_mode == GUI_MENU)
|
||||
{
|
||||
menuNext();
|
||||
}
|
||||
ESP_LOGI(TAG, "MAIN: Button 1 SHORT");
|
||||
break;
|
||||
case (KEY0 << KEY_LONG_PRESS):
|
||||
{
|
||||
|
||||
|
||||
if (_mode != GUI_MENU)
|
||||
{
|
||||
_mode = GUI_MENU;
|
||||
lv_obj_remove_flag(_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
else
|
||||
if (_mode == GUI_MENU)
|
||||
{
|
||||
activate_selected();
|
||||
_mode = GUI_BUBBLE;
|
||||
// lv_obj_remove_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
|
||||
// lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (KEY1 << KEY_SHORT_PRESS):
|
||||
{
|
||||
if (_mode == GUI_MENU)
|
||||
{
|
||||
menuPrevious();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (KEY1 << KEY_LONG_PRESS):
|
||||
ESP_LOGI(TAG, "MAIN: Button 2 LONG");
|
||||
gpio_set_level(PIN_NUM_nON, 0);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_mode == GUI_BUBBLE)
|
||||
{
|
||||
uint32_t notifiedBits = 0;
|
||||
// clear on entry (first param), wait for any bit, block forever
|
||||
xTaskNotifyWait(
|
||||
0xFFFFFFFF, // clear any old bits on entry
|
||||
0xFFFFFFFF, // clear bits on exit
|
||||
¬ifiedBits,
|
||||
pdMS_TO_TICKS(100));
|
||||
|
||||
if (notifiedBits & EM_EVENT_NEW_DATA)
|
||||
{
|
||||
ImuData_t d = system_getImuData();
|
||||
bubble_setValue(_bubble, -d.angle);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
#ifndef _KEYPAD_H
|
||||
#define _KEYPAD_H
|
||||
|
||||
@@ -20,4 +21,28 @@ typedef enum
|
||||
void keypad_start(void);
|
||||
QueueHandle_t keypad_getQueue(void);
|
||||
|
||||
=======
|
||||
#ifndef _KEYPAD_H
|
||||
#define _KEYPAD_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KEY0 = (1 << 0),
|
||||
KEY1 = (1 << 1),
|
||||
} keycode_t;
|
||||
|
||||
|
||||
#define KEY_SHORT_PRESS 0
|
||||
#define KEY_LONG_PRESS 4
|
||||
|
||||
#define KEY_UP KEY0
|
||||
#define KEY_DOWN KEY1
|
||||
|
||||
void keypad_start(void);
|
||||
QueueHandle_t keypad_getQueue(void);
|
||||
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
#endif
|
||||
294
main/main.c
294
main/main.c
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
@@ -289,3 +290,296 @@ void app_main(void)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
=======
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "math.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
|
||||
#include "bt_app.h"
|
||||
#include "lsm6dsv.h"
|
||||
#include "gui.h"
|
||||
#include "gpio.h"
|
||||
#include "keypad.h"
|
||||
#include "system.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************
|
||||
* STATIC FUNCTION DECLARATIONS
|
||||
********************************/
|
||||
typedef struct {
|
||||
float alpha; // smoothing factor, 0<alpha<1
|
||||
float prev_output; // y[n-1]
|
||||
} LowPassFilter;
|
||||
|
||||
// Initialize the filter. alpha = smoothing factor.
|
||||
// e.g. alpha = dt/(RC+dt) if you derive from cutoff freq & sample time.
|
||||
// init_output can be 0 or your first sample to avoid startup glitch.
|
||||
static inline void LPF_Init(LowPassFilter *f, float alpha, float init_output) {
|
||||
f->alpha = alpha;
|
||||
f->prev_output = init_output;
|
||||
}
|
||||
|
||||
// Run one input sample through the filter.
|
||||
// Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1].
|
||||
static inline float LPF_Update(LowPassFilter *f, float input) {
|
||||
float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
|
||||
f->prev_output = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
* STATIC VARIABLE DEFINITIONS
|
||||
********************************/
|
||||
static const char *TAG = "main";
|
||||
|
||||
/*********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
********************************/
|
||||
|
||||
static void init_gpio(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
|
||||
|
||||
// Configure output GPIO
|
||||
io_conf.pin_bit_mask = (1ULL << PIN_NUM_LED_1) | (1ULL << PIN_NUM_LED_2);
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
// Configure output power signal
|
||||
gpio_set_level(PIN_NUM_nON, 1);
|
||||
|
||||
io_conf.pin_bit_mask = (1ULL << PIN_NUM_nON);
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Configure LCD Backlight GPIO
|
||||
io_conf.pin_bit_mask = (1ULL << PIN_NUM_BK_LIGHT);
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// IMU task to read sensor data
|
||||
static void imu_task(void *pvParameters) {
|
||||
lsm6dsv_data_t imu_data;
|
||||
char data_str[128];
|
||||
|
||||
float roll, pitch, yaw;
|
||||
lsm6dsv_fifo_t fifo;
|
||||
|
||||
ImuData_t imu;
|
||||
|
||||
LowPassFilter lpf;
|
||||
LPF_Init(&lpf, FILTER_COEFF, 0);
|
||||
|
||||
while (1) {
|
||||
|
||||
// uint8_t samples = lsm6dsv_fifo_ready();
|
||||
|
||||
//ESP_LOGI(TAG, "Samples: %d", samples);
|
||||
// while (samples)
|
||||
// {
|
||||
// lsm6dsv_fifo_read(&fifo);
|
||||
|
||||
|
||||
|
||||
// // snprintf(data_str, sizeof(data_str),
|
||||
// // "tag: %d, count: %d, (%d, %d, %d)",
|
||||
// // fifo.tag, fifo.count, x, y, z);
|
||||
|
||||
// // ESP_LOGI(TAG, "%s", data_str);
|
||||
|
||||
// samples--;
|
||||
// }
|
||||
|
||||
// lsm6dsv_getAttitude(&fifo, &roll, &pitch, &yaw);
|
||||
|
||||
// snprintf(data_str, sizeof(data_str),
|
||||
// "r: %0.3f, p: %0.3f, y: %0.3f",
|
||||
// roll, pitch, yaw);
|
||||
|
||||
// ESP_LOGI(TAG, "%s", data_str);
|
||||
|
||||
float xz;
|
||||
float yz;
|
||||
float xy;
|
||||
|
||||
#if 1
|
||||
if (lsm6dsv_read_data(&imu_data) == ESP_OK)
|
||||
{
|
||||
// snprintf(data_str, sizeof(data_str),
|
||||
// "Acc: %.2f, %.2f, %.2f; "
|
||||
// "Gyro: %.2f, %.2f, %.2f (%d)\n",
|
||||
// imu_data.acc_x, imu_data.acc_y, imu_data.acc_z,
|
||||
// imu_data.gyro_x, imu_data.gyro_y, imu_data.gyro_z, lsm6dsv_fifo_ready());
|
||||
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
imu.raw[ANGLE_XZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_x) * 180 / M_PI;
|
||||
imu.raw[ANGLE_YZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_y) * 180 / M_PI;
|
||||
imu.raw[ANGLE_XY] = atan2f((float)imu_data.acc_x, (float)imu_data.acc_y) * 180 / M_PI;
|
||||
|
||||
|
||||
float angle;
|
||||
|
||||
|
||||
angle = system_getAngle();
|
||||
|
||||
if (angle > MAX_INDICATION_ANGLE)
|
||||
{
|
||||
angle = MAX_INDICATION_ANGLE;
|
||||
}
|
||||
else
|
||||
if (angle < -MAX_INDICATION_ANGLE)
|
||||
{
|
||||
angle = -MAX_INDICATION_ANGLE;
|
||||
}
|
||||
|
||||
// low pass filter the angle measurement
|
||||
imu.angle = LPF_Update(&lpf, angle);
|
||||
|
||||
//imu.filtered[ANGLE_XY] = LPF_Update(&lpf, imu.raw[ANGLE_XY]);
|
||||
|
||||
system_setImuData(imu);
|
||||
|
||||
// snprintf(data_str, sizeof(data_str),
|
||||
// "(%.2f, %.2f, %.2f)", xy, yz, xy);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
snprintf(data_str, sizeof(data_str),
|
||||
"(%.1f, %.1f, %.1f) (%.2f, %.2f, %.2f)",
|
||||
imu.raw[ANGLE_XZ], imu.raw[ANGLE_YZ], imu.raw[ANGLE_XY],
|
||||
(float)imu_data.acc_x, (float)imu_data.acc_y, (float)imu_data.acc_z);
|
||||
ESP_LOGI(TAG, "%s", data_str);
|
||||
#endif
|
||||
// Update label text in LVGL task
|
||||
//lv_label_set_text(imu_label, data_str);
|
||||
|
||||
}
|
||||
#endif
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // Update every 100ms
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* MAIN ENTRY POINT
|
||||
********************************/
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
|
||||
/* initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
init_gpio();
|
||||
|
||||
system_init();
|
||||
|
||||
|
||||
|
||||
// Initialize IMU
|
||||
ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15
|
||||
|
||||
|
||||
|
||||
// Create IMU task
|
||||
TaskHandle_t h = xTaskCreate(imu_task, "imu_task", 4096, NULL, 5, NULL);
|
||||
|
||||
|
||||
|
||||
bt_app_init();
|
||||
|
||||
gui_start();
|
||||
|
||||
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
keypad_start();
|
||||
QueueHandle_t q = keypad_getQueue();
|
||||
uint32_t ev = 0;
|
||||
|
||||
//gpio_set_level(PIN_NUM_LED_1, 1);
|
||||
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||
|
||||
// Main loop - LVGL task will run automatically
|
||||
while (1) {
|
||||
|
||||
if (xQueueReceive(q, &ev, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
switch (ev)
|
||||
{
|
||||
case (KEY_UP << KEY_LONG_PRESS):
|
||||
{
|
||||
system_setZeroAngle();
|
||||
break;
|
||||
}
|
||||
|
||||
case (KEY_DOWN << KEY_LONG_PRESS):
|
||||
{
|
||||
system_clearZeroAngle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
while (1)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
|
||||
171
main/system.c
171
main/system.c
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
#include "system.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
@@ -166,4 +167,174 @@ void system_notifyAll(uint32_t eventBits) {
|
||||
}
|
||||
xSemaphoreGive(em->mutex);
|
||||
}
|
||||
=======
|
||||
#include "system.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static SystemState_t _systemState;
|
||||
|
||||
static EventGroupHandle_t _systemEvent;
|
||||
static EventManager_t _eventManager;
|
||||
|
||||
void system_init(void)
|
||||
{
|
||||
_systemState.zeroAngle = 0.0f;
|
||||
_systemState.primaryAxis = PRIMARY_AXIS;
|
||||
|
||||
EventGroupHandle_t evt = xEventGroupCreate();
|
||||
|
||||
_eventManager.count = 0;
|
||||
_eventManager.mutex = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
int system_getPrimaryAxis(void)
|
||||
{
|
||||
int axis;
|
||||
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
axis = _systemState.primaryAxis;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
float system_getAngle(void)
|
||||
{
|
||||
float angle;
|
||||
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
|
||||
angle = _systemState.imu.raw[_systemState.primaryAxis] - _systemState.zeroAngle;
|
||||
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
void system_setZeroAngle(void)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
|
||||
_systemState.zeroAngle = _systemState.imu.raw[_systemState.primaryAxis];
|
||||
|
||||
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
}
|
||||
|
||||
void system_clearZeroAngle(void)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
_systemState.zeroAngle = 0.0f;
|
||||
|
||||
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
}
|
||||
|
||||
float system_getZeroAngle(void)
|
||||
{
|
||||
float angle;
|
||||
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
angle = _systemState.zeroAngle;
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
void system_setImuData(ImuData_t imu)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
_systemState.imu = imu;
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
//ESP_LOGI("g", "New IMU Data: %.2f", xy);
|
||||
|
||||
system_notifyAll(EM_EVENT_NEW_DATA);
|
||||
}
|
||||
|
||||
ImuData_t system_getImuData(void)
|
||||
{
|
||||
ImuData_t imu;
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
imu = _systemState.imu;
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
return imu;
|
||||
}
|
||||
|
||||
void system_waitForEvent(void)
|
||||
{
|
||||
}
|
||||
|
||||
SystemState_t *system_getState(void)
|
||||
{
|
||||
return &_systemState;
|
||||
}
|
||||
|
||||
|
||||
BaseType_t system_subscribe(TaskHandle_t task) {
|
||||
|
||||
EventManager_t *em = &_eventManager;
|
||||
|
||||
ESP_LOGI("g", "Subscribe: %p", task);
|
||||
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdFALSE) {
|
||||
return pdFAIL;
|
||||
}
|
||||
if (em->count < EM_MAX_SUBSCRIBERS) {
|
||||
// avoid duplicates?
|
||||
ESP_LOGI("g", "PASS");
|
||||
em->subscribers[em->count++] = task;
|
||||
xSemaphoreGive(em->mutex);
|
||||
return pdPASS;
|
||||
}
|
||||
xSemaphoreGive(em->mutex);
|
||||
return pdFAIL; // full
|
||||
}
|
||||
|
||||
BaseType_t system_unsubscribe(TaskHandle_t task) {
|
||||
|
||||
EventManager_t *em = &_eventManager;
|
||||
|
||||
BaseType_t removed = pdFAIL;
|
||||
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
|
||||
for (size_t i = 0; i < em->count; ++i) {
|
||||
if (em->subscribers[i] == task) {
|
||||
// shift down
|
||||
for (size_t j = i; j + 1 < em->count; ++j)
|
||||
em->subscribers[j] = em->subscribers[j+1];
|
||||
em->count--;
|
||||
removed = pdPASS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(em->mutex);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
void system_notifyAll(uint32_t eventBits) {
|
||||
EventManager_t *em = &_eventManager;
|
||||
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
|
||||
for (size_t i = 0; i < em->count; ++i) {
|
||||
// set the bits in each task's notification value
|
||||
//ESP_LOGI("g", "Notify: %p", em->subscribers[i]);
|
||||
xTaskNotify(em->subscribers[i],
|
||||
eventBits,
|
||||
eSetBits);
|
||||
}
|
||||
xSemaphoreGive(em->mutex);
|
||||
}
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
@@ -72,4 +73,80 @@ BaseType_t system_unsubscribe(TaskHandle_t task);
|
||||
void system_notifyAll(uint32_t eventBits);
|
||||
|
||||
|
||||
=======
|
||||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#define DISABLE_GUI
|
||||
|
||||
enum
|
||||
{
|
||||
ANGLE_XY = 0,
|
||||
ANGLE_XZ,
|
||||
ANGLE_YZ,
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float raw[3];
|
||||
float filtered[3];
|
||||
float angle;
|
||||
} ImuData_t;
|
||||
|
||||
typedef struct SystemState_s
|
||||
{
|
||||
ImuData_t imu;
|
||||
|
||||
EventGroupHandle_t event;
|
||||
|
||||
float zeroAngle;
|
||||
int primaryAxis;
|
||||
|
||||
} SystemState_t;
|
||||
|
||||
|
||||
#define MAX_INDICATION_ANGLE 5.0f
|
||||
#define FILTER_COEFF 0.2f // 0 to 1 Smaller number is heavier filter
|
||||
#define PRIMARY_AXIS ANGLE_YZ
|
||||
|
||||
#define EM_MAX_SUBSCRIBERS 8 // tweak as needed
|
||||
#define EM_EVENT_NEW_DATA (1UL<<0)
|
||||
#define EM_EVENT_ERROR (1UL<<1)
|
||||
// …add more event bit-masks here…
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t subscribers[EM_MAX_SUBSCRIBERS];
|
||||
size_t count;
|
||||
SemaphoreHandle_t mutex;
|
||||
} EventManager_t;
|
||||
|
||||
void system_init(void);
|
||||
void system_setImuData(ImuData_t imu);
|
||||
|
||||
ImuData_t system_getImuData(void);
|
||||
|
||||
int system_getPrimaryAxis(void);
|
||||
float system_getAngle(void);
|
||||
|
||||
void system_setZeroAngle(void);
|
||||
void system_clearZeroAngle(void);
|
||||
float system_getZeroAngle(void);
|
||||
|
||||
// Subscribe (register) current task to receive events
|
||||
BaseType_t system_subscribe(TaskHandle_t task);
|
||||
|
||||
// Unsubscribe if you ever need
|
||||
BaseType_t system_unsubscribe(TaskHandle_t task);
|
||||
|
||||
// Notify all subscribers of one or more event bits
|
||||
void system_notifyAll(uint32_t eventBits);
|
||||
|
||||
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
#endif
|
||||
@@ -1560,7 +1560,7 @@ CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
|
||||
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
|
||||
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
|
||||
# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set
|
||||
CONFIG_LWIP_DHCP_OPTIONS_LEN=68
|
||||
CONFIG_LWIP_DHCP_OPTIONS_LEN=69
|
||||
CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0
|
||||
CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
# Override some defaults so BT stack is enabled and
|
||||
# Classic BT is enabled
|
||||
CONFIG_BT_ENABLED=y
|
||||
@@ -7,3 +8,14 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_A2DP_ENABLE=y
|
||||
=======
|
||||
# Override some defaults so BT stack is enabled and
|
||||
# Classic BT is enabled
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_A2DP_ENABLE=y
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
|
||||
2891
sdkconfig.old
2891
sdkconfig.old
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user