added tweaks to filter

This commit is contained in:
Brent Perteet
2025-08-08 17:31:27 -05:00
parent fb9fd84bf1
commit c4ca91bf84
17 changed files with 210 additions and 2440 deletions

16
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

View File

@@ -35,7 +35,10 @@
"semphr.h": "c", "semphr.h": "c",
"lv_spinbox.h": "c", "lv_spinbox.h": "c",
"lv_slider.h": "c", "lv_slider.h": "c",
"lv_menu.h": "c" "lv_menu.h": "c",
"stdint.h": "c",
"random": "c"
}, },
"git.ignoreLimitWarning": true "git.ignoreLimitWarning": true,
"idf.pythonInstallPath": "/usr/bin/python"
} }

View File

@@ -1,6 +1,21 @@
# The following lines of boilerplate have to be in your project's # The following lines of boilerplate have to be in your project's CMakeLists
# CMakeLists in this exact order for cmake to work correctly # in this exact order for cmake to work correctly
configure_file(${CMAKE_SOURCE_DIR}/settings.json ${CMAKE_BINARY_DIR}/settings.json COPYONLY)
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "Parsing settings.json..."
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E env python ${CMAKE_SOURCE_DIR}/prepare_config.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(level-shot) include("${CMAKE_SOURCE_DIR}/project_settings.cmake")
project(${PROJECT_NAME})

51
bt_test/bt_test.ino Normal file
View File

@@ -0,0 +1,51 @@
#include <BluetoothSerial.h>
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
BluetoothSerial SerialBT;
void setBluetoothTxPower(esp_power_level_t powerLevel) {
esp_err_t err = esp_bredr_tx_power_set(powerLevel, powerLevel);
if (err != ESP_OK) {
Serial.printf("Failed to set TX Power: %s\n", esp_err_to_name(err));
} else {
Serial.printf("TX Power set to level %d\n", powerLevel);
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Initializing Bluetooth...");
if (!btStart()) {
Serial.println("Failed to start BT controller");
return;
}
// Disable and re-enable the classic BT stack to safely set TX power
esp_bluedroid_disable();
esp_bluedroid_deinit();
// Set Classic BT TX power (e.g., ESP_PWR_LVL_P9 = highest)
//setBluetoothTxPower(ESP_PWR_LVL_P9);
setBluetoothTxPower(ESP_PWR_LVL_N12);
esp_bluedroid_init();
esp_bluedroid_enable();
if (!SerialBT.begin("ESP32-TXPowerTest")) {
Serial.println("Bluetooth Serial failed to start");
return;
}
Serial.println("Bluetooth initialized and ready!");
}
void loop() {
SerialBT.println("Hello from ESP32 with boosted TX power!");
delay(1);
}

View File

@@ -27,8 +27,10 @@ def main():
# Define binaries and offsets # Define binaries and offsets
bootloader = root / "build" / "bootloader" / "bootloader.bin" bootloader = root / "build" / "bootloader" / "bootloader.bin"
firmware = root / "build" / f"{project}.bin" firmware = root / "build" / f"{project}.bin"
ota_intitial = root / "build" / "ota_data_initial.bin"
partition = root / "build" / "partition_table" / "partition-table.bin" partition = root / "build" / "partition_table" / "partition-table.bin"
# Verify binaries exist # Verify binaries exist
for f in [bootloader, firmware, partition]: for f in [bootloader, firmware, partition]:
if not f.exists(): if not f.exists():
@@ -47,8 +49,9 @@ def main():
"--flash-mode", flash_mode, # ✅ hyphenated flags "--flash-mode", flash_mode, # ✅ hyphenated flags
"--flash-freq", flash_freq, "--flash-freq", flash_freq,
"--flash-size", flash_size, "--flash-size", flash_size,
"0x0", str(bootloader), "0x1000", str(bootloader),
"0x10000", str(firmware), "0x20000", str(firmware),
"0x11000", str(ota_intitial),
"0x8000", str(partition) "0x8000", str(partition)
] ]

View File

@@ -3,11 +3,7 @@ import serial
import sys import sys
import time import time
from pathlib import Path from pathlib import Path
# import colorama
# colorama.just_fix_windows_console()
print("\x1b[0;32mThis should be green\x1b[0m")
print("\x1b[0;31mThis should be red\x1b[0m")
def main(): def main():
# Load config # Load config
@@ -27,6 +23,10 @@ def main():
try: try:
with serial.Serial(port, baud, timeout=0.5) as ser: with serial.Serial(port, baud, timeout=0.5) as ser:
# 🔧 Clear DTR and RTS lines (common for ESP32 reset handling)
ser.dtr = False
ser.rts = False
print("[Serial Monitor] Press Ctrl+C to exit.\n") print("[Serial Monitor] Press Ctrl+C to exit.\n")
while True: while True:
if ser.in_waiting: if ser.in_waiting:

View File

@@ -1,19 +1,4 @@
<<<<<<< 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" idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
"gui.c" "gui.c"
"lsm6dsv.c" "lsm6dsv.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
@@ -24,5 +9,4 @@ idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
"esp_timer" "esp_timer"
"nvs_flash" "nvs_flash"
"bt") "bt")
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
#ifndef GPIO_H #ifndef GPIO_H
#define GPIO_H #define GPIO_H
@@ -27,34 +26,4 @@
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used #define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
#endif #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 #endif

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@@ -617,624 +616,4 @@ 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 keypadderived 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
&notifiedBits,
pdMS_TO_TICKS(100));
if (notifiedBits & EM_EVENT_NEW_DATA)
{
ImuData_t d = system_getImuData();
bubble_setValue(_bubble, -d.angle);
}
}
}
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
} }

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
#ifndef _KEYPAD_H #ifndef _KEYPAD_H
#define _KEYPAD_H #define _KEYPAD_H
@@ -21,28 +20,4 @@ typedef enum
void keypad_start(void); void keypad_start(void);
QueueHandle_t keypad_getQueue(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 #endif

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
/* /*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* *
@@ -40,6 +39,31 @@ typedef struct {
float prev_output; // y[n-1] float prev_output; // y[n-1]
} LowPassFilter; } LowPassFilter;
#define ALPHA_MIN 0.2f
#define ALPHA_MAX 0.4f
// Clamp function to bound value between min and max
double clamp(double value, double min, double max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
// Compute alpha using linear ramp
float compute_alpha_linear(double x, float alpha_min, float alpha_max, float threshold) {
float abs_x = fabs(x);
float t = clamp(abs_x / threshold, 0.0, 1.0); // Normalize |x| to [0,1]
return alpha_min + (alpha_max - alpha_min) * t;
}
// Compute sigmoid-based dynamic alpha
float compute_alpha_sigmoid(float x, float alpha_min, float alpha_max, float k, float midpoint) {
float abs_x = fabs(x);
float exponent = -k * (abs_x - midpoint);
float sigmoid = 1.0 / (1.0 + exp(exponent));
return alpha_min + (alpha_max - alpha_min) * sigmoid;
}
// Initialize the filter. alpha = smoothing factor. // Initialize the filter. alpha = smoothing factor.
// e.g. alpha = dt/(RC+dt) if you derive from cutoff freq & sample time. // 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. // init_output can be 0 or your first sample to avoid startup glitch.
@@ -51,7 +75,11 @@ static inline void LPF_Init(LowPassFilter *f, float alpha, float init_output) {
// Run one input sample through the filter. // Run one input sample through the filter.
// Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1]. // Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1].
static inline float LPF_Update(LowPassFilter *f, float input) { static inline float LPF_Update(LowPassFilter *f, float input) {
float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
//float alpha = compute_alpha_sigmoid(input, ALPHA_MIN, ALPHA_MAX, 1.0f, 0.0f);
float alpha = compute_alpha_linear(input, ALPHA_MIN, ALPHA_MAX, 2.0f);
float out = alpha * input + (1.0f - alpha) * f->prev_output;
//float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
f->prev_output = out; f->prev_output = out;
return out; return out;
} }
@@ -290,296 +318,3 @@ void app_main(void)
} }
#endif #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

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
#include "system.h" #include "system.h"
#include "esp_log.h" #include "esp_log.h"
@@ -167,174 +166,4 @@ void system_notifyAll(uint32_t eventBits) {
} }
xSemaphoreGive(em->mutex); 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
} }

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
#ifndef SYSTEM_H #ifndef SYSTEM_H
#define SYSTEM_H #define SYSTEM_H
@@ -37,8 +36,9 @@ typedef struct SystemState_s
#define MAX_INDICATION_ANGLE 5.0f #define MAX_INDICATION_ANGLE 5.0f
#define FILTER_COEFF 0.2f // 0 to 1 Smaller number is heavier filter #define FILTER_COEFF 0.4f // 0 to 1 Smaller number is heavier filter
#define PRIMARY_AXIS ANGLE_YZ #define PRIMARY_AXIS ANGLE_YZ
#define RIFLE_AXIS Y
#define EM_MAX_SUBSCRIBERS 8 // tweak as needed #define EM_MAX_SUBSCRIBERS 8 // tweak as needed
#define EM_EVENT_NEW_DATA (1UL<<0) #define EM_EVENT_NEW_DATA (1UL<<0)
@@ -73,80 +73,4 @@ BaseType_t system_unsubscribe(TaskHandle_t task);
void system_notifyAll(uint32_t eventBits); 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 #endif

View File

@@ -7,4 +7,4 @@ with open("project_settings.cmake", "w") as out:
out.write(f'set(PROJECT_NAME "{cfg["project_name"]}")\n') out.write(f'set(PROJECT_NAME "{cfg["project_name"]}")\n')
out.write(f'set(CHIP "{cfg["chip"]}")\n') out.write(f'set(CHIP "{cfg["chip"]}")\n')
out.write(f'set(PORT "{cfg["port"]}")\n') out.write(f'set(PORT "{cfg["port"]}")\n')
out.write(f'set(BAUD "{cfg["baud"]}")\n') out.write(f'set(BAUD "{cfg["flash_baud"]}")\n')

4
project_settings.cmake Normal file
View File

@@ -0,0 +1,4 @@
set(PROJECT_NAME "soundshot")
set(CHIP "esp32")
set(PORT "COM3")
set(BAUD "460800")

View File

@@ -1,11 +1,11 @@
{ {
"project_name": "soundshot", "project_name": "soundshot",
"chip": "esp32h2", "chip": "esp32",
"port": "COM8", "port": "COM3",
"monitor_baud": 115200, "monitor_baud": 115200,
"flash_baud": 460800, "flash_baud": 460800,
"flash_mode": "dio", "flash_mode": "dio",
"flash_freq": "48m", "flash_freq": "40m",
"flash_size": "2MB", "flash_size": "2MB",
"before": "default-reset", "before": "default-reset",
"after": "hard-reset" "after": "hard-reset"