feat: Initial esp32-hacking project with firmware sources and docs

This commit is contained in:
user
2026-02-04 12:59:28 +01:00
commit 298e98befb
120 changed files with 22094 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_log.h"
#include <sys/types.h>
#define GPIO_INPUT_IO 27
#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO)
extern int64_t time_zero;
static const char *TAG = "GPIO";
extern QueueHandle_t csi_recv_queue;
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
xQueueReset(csi_recv_queue);
time_zero = esp_timer_get_time();
uint32_t gpio_num = (uint32_t)arg;
}
void init_gpio()
{
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_NEGEDGE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = GPIO_INPUT_PIN_SEL,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
};
gpio_config(&io_conf);
gpio_install_isr_service(0);
gpio_isr_handler_add(GPIO_INPUT_IO, gpio_isr_handler, (void *)GPIO_INPUT_IO);
ESP_LOGI(TAG, "GPIO %d configured with negative edge interrupt.", GPIO_INPUT_IO);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void init_gpio(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,162 @@
/* SPI Slave example, receiver (uses SPI Slave driver to communicate with sender)
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#include "app_ifft.h"
#include "IQmathLib.h"
#define PI 3.14159265358979323846f
#define PI_IQ _IQ(3.14159265358979323846)
#define PI_IQ_N2 _IQ(-6.28318530717958647692)
const int bitReverseTable[64] = {
0, 32, 16, 48, 8, 40, 24, 56,
4, 36, 20, 52, 12, 44, 28, 60,
2, 34, 18, 50, 10, 42, 26, 58,
6, 38, 22, 54, 14, 46, 30, 62,
1, 33, 17, 49, 9, 41, 25, 57,
5, 37, 21, 53, 13, 45, 29, 61,
3, 35, 19, 51, 11, 43, 27, 59,
7, 39, 23, 55, 15, 47, 31, 63
};
void IRAM_ATTR fft_iq(Complex_Iq *X, int inverse)
{
int log2N = 6;
int N = 64;
Complex_Iq *temp = (Complex_Iq *)malloc(64 * sizeof(Complex_Iq));
// Bit-reversed addressing permutation
for (int i = 0; i < N; i++) {
temp[i] = X[bitReverseTable[i]];
}
for (int i = 0; i < N; i++) {
X[i] = temp[i];
}
// Cooley-Tukey iterative FFT
for (int s = 1; s <= log2N; ++s) {
int m = 1 << s; // 2 power s
int m2 = m >> 1; // m/2
Complex_Iq w;
w.real = _IQ16(1.0);
w.imag = _IQ16(0.0);
Complex_Iq wm;
_iq16 angle = _IQ16div(PI_IQ_N2, m);
wm.real = _IQ16cos(angle);
wm.imag = _IQ16sin(angle);
if (inverse) wm.imag = -wm.imag;
for (int j = 0; j < m2; ++j) {
for (int k = j; k < N; k += m) {
Complex_Iq t, u;
u = X[k];
t.real = _IQ16mpy(w.real , X[k + m2].real) - _IQ16mpy(w.imag , X[k + m2].imag);
t.imag = _IQ16mpy(w.real , X[k + m2].imag) + _IQ16mpy(w.imag , X[k + m2].real);
X[k].real = u.real + t.real;
X[k].imag = u.imag + t.imag;
X[k + m2].real = u.real - t.real;
X[k + m2].imag = u.imag - t.imag;
}
float tmpReal = _IQ16mpy(w.real , wm.real) - _IQ16mpy(w.imag , wm.imag);
w.imag = _IQ16mpy(w.real , wm.imag) + _IQ16mpy(w.imag , wm.real);
w.real = tmpReal;
}
}
// Scale for inverse FFT
if (inverse) {
for (int i = 0; i < N; i++) {
X[i].real = _IQdiv64(X[i].real);
X[i].imag = _IQdiv64(X[i].imag);
}
}
free(temp);
}
// Bit reversal of given index 'x' with 'log2n' bits
unsigned int inline bitReverse(unsigned int x, int log2n) {
int n = 0;
for (int i = 0; i < log2n; i++) {
n <<= 1;
n |= (x & 1);
x >>= 1;
}
return n;
}
void IRAM_ATTR fft(Complex *X, int N, int inverse)
{
int log2N = log2(N);
Complex *temp = (Complex *)malloc(N * sizeof(Complex));
// Bit-reversed addressing permutation
for (int i = 0; i < N; i++) {
temp[i] = X[bitReverse(i, log2N)];
}
for (int i = 0; i < N; i++) {
X[i] = temp[i];
}
// Cooley-Tukey iterative FFT
for (int s = 1; s <= log2N; ++s) {
int m = 1 << s; // 2 power s
int m2 = m >> 1; // m/2
Complex w;
w.real = 1.0;
w.imag = 0.0;
Complex wm;
wm.real = cosf(-2.0f * PI / m);
wm.imag = sinf(-2.0f * PI / m);
if (inverse) wm.imag = -wm.imag;
for (int j = 0; j < m2; ++j) {
for (int k = j; k < N; k += m) {
Complex t, u;
u = X[k];
t.real = w.real * X[k + m2].real - w.imag * X[k + m2].imag;
t.imag = w.real * X[k + m2].imag + w.imag * X[k + m2].real;
X[k].real = u.real + t.real;
X[k].imag = u.imag + t.imag;
X[k + m2].real = u.real - t.real;
X[k + m2].imag = u.imag - t.imag;
}
float tmpReal = w.real * wm.real - w.imag * wm.imag;
w.imag = w.real * wm.imag + w.imag * wm.real;
w.real = tmpReal;
}
}
// Scale for inverse FFT
if (inverse) {
for (int i = 0; i < N; i++) {
X[i].real /= N;
X[i].imag /= N;
}
}
free(temp);
}
float complex_magnitude_iq(Complex_Iq z) {
return _IQ16toF(_IQ16mag(z.real, z.imag));
}
float complex_phase_iq(Complex_Iq z) {
return _IQ16toF(_IQ16atan2(z.imag, z.real));
}
float complex_magnitude(Complex z) {
return sqrt(z.real * z.real + z.imag * z.imag);
}
float complex_phase(Complex z) {
return atan2(z.imag, z.real);
}

View File

@@ -0,0 +1,26 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "IQmathLib.h"
typedef struct {
float real;
float imag;
} Complex;
typedef struct {
_iq16 real;
_iq16 imag;
} Complex_Iq;
void IRAM_ATTR fft_iq(Complex_Iq *X, int inverse) ;
void IRAM_ATTR fft(Complex *X, int N, int inverse);
float complex_magnitude_iq(Complex_Iq z);
float complex_phase_iq(Complex_Iq z);
float complex_magnitude(Complex z);
float complex_phase(Complex z);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,104 @@
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "app_uart.h"
#include "bsp_C5_dual_antenna.h"
#define UART_PORT_NUM UART_NUM_1
#define UART_BAUD_RATE 2000000
#define TXD_PIN (BSP_CNT_4)
#define RXD_PIN (BSP_CNT_3)
#define BUF_SIZE 4096
static const char *TAG = "UART";
QueueHandle_t uart_recv_queue;
static QueueHandle_t uart0_queue;
static void uart_event_task(void *pvParameters)
{
uart_event_t event;
uint8_t* dtmp = (uint8_t*) malloc(2 * BUF_SIZE);
csi_data_t* csi_data;
uint16_t table_row_last=0;
for (;;) {
if (xQueueReceive(uart0_queue, (void *)&event, (TickType_t)portMAX_DELAY)) {
switch (event.type) {
case UART_DATA:
UBaseType_t queue_size = uxQueueMessagesWaiting(uart0_queue);
uart_read_bytes(UART_PORT_NUM, dtmp, event.size, portMAX_DELAY);
// ESP_LOGI(TAG, "[UART DATA]: %d %u", event.size,queue_size);
for (int i = 0; i <= (int)event.size - 32; i++) {
if (dtmp[i] == 0xAA && dtmp[i + 1] == 0x55 && dtmp[i + 30] == 0x55 && dtmp[i + 31] == 0xAA) {
xQueueSend(uart_recv_queue,(csi_data_t*)&dtmp[i], 0);
csi_data = (csi_data_t *)&dtmp[i];
ESP_LOGD(TAG,"%d,%lld,%.2f",csi_data->start[0],csi_data->time_delta,csi_data->cir[0]);
i = i + 24-1;
}
}
break;
case UART_FIFO_OVF:
ESP_LOGW(TAG, "hw fifo overflow");
uart_flush_input(UART_PORT_NUM);
xQueueReset(uart0_queue);
break;
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "ring buffer full");
uart_flush_input(UART_PORT_NUM);
xQueueReset(uart0_queue);
break;
case UART_BREAK:
ESP_LOGD(TAG, "uart rx break");
break;
case UART_PARITY_ERR:
ESP_LOGD(TAG, "uart parity error");
break;
case UART_FRAME_ERR:
ESP_LOGD(TAG, "uart frame error");
break;
default:
ESP_LOGD(TAG, "uart event type: %d", event.type);
break;
}
}
}
free(dtmp);
dtmp = NULL;
vTaskDelete(NULL);
}
void init_uart() {
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
int intr_alloc_flags = 0;
#if CONFIG_UART_ISR_IN_IRAM
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif
ESP_ERROR_CHECK(uart_driver_install(UART_PORT_NUM, BUF_SIZE * 2, 0 , 20, &uart0_queue, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(UART_PORT_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_PORT_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_LOGI("UART", "UART initialized");
uart_recv_queue = xQueueCreate(20, sizeof(csi_data_t));
xTaskCreate(uart_event_task, "uart_event_task", 4096, NULL, 9, NULL); // 核心 1
}
int uart_send_data(const char *data, uint8_t len) {
return uart_write_bytes(UART_PORT_NUM, data, len);
}
// void uart_receive_data() {
// uint8_t data[BUF_SIZE];
// int length = uart_read_bytes(UART_PORT_NUM, data, BUF_SIZE, 20 / portTICK_RATE_MS);
// if (length > 0) {
// data[length] = '\0';
// ESP_LOGI("UART", "Received %d bytes: '%s'", length, (char *)data);
// }
// }

View File

@@ -0,0 +1,21 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint8_t start[2];
uint32_t id;
int64_t time_delta;
float cir[4];
uint8_t end[2];
} __attribute__((packed)) csi_data_t;
#define DATA_TABLE_SIZE 100
void init_uart(void);
int uart_send_data(const char *data, uint8_t len);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,224 @@
#include <string.h>
#include <stdio.h>
#include "app_ui.h"
#include "ui.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "bsp_C5_dual_antenna.h"
#include "app_uart.h"
#include <math.h>
#define DISPLAY_SAMPLE_STEP 3
#define LVGL_CHART_POINTS (100 / DISPLAY_SAMPLE_STEP)
#define PI 3.14159265
#define SAMPLE_RATE LVGL_CHART_POINTS
extern QueueHandle_t uart_recv_queue;
extern QueueHandle_t csi_display_queue;
extern csi_data_t master_data[DATA_TABLE_SIZE];
lv_chart_series_t * ser[6];
int16_t sine_wave[LVGL_CHART_POINTS*3];
float angle = 0;
static const char *TAG = "app_ui";
void generate_sine_wave(int16_t *data)
{
int16_t num_samples = SAMPLE_RATE*3;
for (int i = 0; i < num_samples; i++) {
data[i] = (int16_t)(100 * sin(2 * PI * ((float)i / (float)SAMPLE_RATE)));
}
}
int get_sine_wave_index(float angle)
{
int num_samples = SAMPLE_RATE * 3;
int index = (int)((angle / (2 * PI)) * SAMPLE_RATE);
if (index <= SAMPLE_RATE/2) {
index += SAMPLE_RATE;
}
return (index-SAMPLE_RATE/2);
}
void ScreenSSliderLight_function(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t * target = lv_event_get_target(e);
int slider_value = lv_slider_get_value(target);
bsp_display_brightness_set(slider_value);
}
void PhaseCalibration_button(lv_event_t * e)
{
angle =0;
ESP_LOGI(TAG, "PhaseCalibration_button");
}
void app_ui_init(void)
{
lv_chart_set_range(ui_ScreenW_Chart, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
lv_chart_set_point_count(ui_ScreenW_Chart, LVGL_CHART_POINTS);
ser[0] = lv_chart_add_series(ui_ScreenW_Chart, lv_color_hex(0x388E3C), LV_CHART_AXIS_PRIMARY_Y); // 深绿
ser[1] = lv_chart_add_series(ui_ScreenW_Chart, lv_color_hex(0x81C784), LV_CHART_AXIS_PRIMARY_Y); // 浅绿
ser[4] = lv_chart_add_series(ui_ScreenW_Chart, lv_color_hex(0x1976D2), LV_CHART_AXIS_PRIMARY_Y); // 深蓝
ser[5] = lv_chart_add_series(ui_ScreenW_Chart, lv_color_hex(0x64B5F6), LV_CHART_AXIS_PRIMARY_Y); // 浅蓝
lv_chart_set_range(ui_ScreenWP_Chart, LV_CHART_AXIS_PRIMARY_Y, -100, 100);
lv_chart_set_point_count(ui_ScreenWP_Chart, LVGL_CHART_POINTS);
ser[2] = lv_chart_add_series(ui_ScreenWP_Chart, lv_color_hex(0x1976D2), LV_CHART_AXIS_PRIMARY_Y); // 深蓝
ser[3] = lv_chart_add_series(ui_ScreenWP_Chart, lv_color_hex(0x64B5F6), LV_CHART_AXIS_PRIMARY_Y); // 浅蓝
lv_chart_refresh(ui_ScreenW_Chart);
lv_chart_refresh(ui_ScreenWP_Chart);
generate_sine_wave(sine_wave);
}
float circular_difference(float angle1, float angle2)
{
float diff = fmod(angle2 - angle1 + PI, 2 * PI);
if (diff < 0) {
diff += 2 * PI;
}
return diff - PI;
}
void csi_data_display_task(void *arg)
{
app_ui_init();
static uint16_t y_range[2] ={0};
uint8_t count=0;
csi_data_t csi_display_data;
uint8_t sine_offest[2];
uint8_t cnt=0;
float range[4][LVGL_CHART_POINTS] = {};
uint8_t csi_mode = *((bool *)arg);
if (csi_mode){
ESP_LOGI(TAG,"Self_Transmit_and_Receive_Mode");
} else {
ESP_LOGI(TAG,"Single_Transmit_and_Dual_Receive_Mode");
}
if (csi_mode){
while (xQueueReceive(csi_display_queue, &csi_display_data, portMAX_DELAY) == pdTRUE) {
cnt++;
UBaseType_t queueLength = uxQueueMessagesWaiting(csi_display_queue);
if (queueLength>10){
ESP_LOGI(TAG, "ui queueLength:%d", queueLength);
}
range[0][count] = csi_display_data.cir[0]*5;
range[1][count] = csi_display_data.cir[1]*5;
range[2][count] = csi_display_data.cir[2];
range[3][count] = csi_display_data.cir[3];
y_range[0] = 500;
y_range[1] = 0;
for (int i=0;i<LVGL_CHART_POINTS;i++){
if (y_range[0]>range[0][i]){
y_range[0] = range[0][i];
}
if (y_range[0]>range[1][i]){
y_range[0] = range[1][i];
}
if (y_range[1]<range[0][i]){
y_range[1] = range[0][i];
}
if (y_range[1]<range[1][i]){
y_range[1] = range[1][i];
}
}
if ( (y_range[1]-y_range[0])<100){
y_range[1] += (100-y_range[1]+y_range[0])/2;
y_range[0] -= (100-y_range[1]+y_range[0])/2;
}
lvgl_port_lock(0);
lv_chart_set_next_value(ui_ScreenW_Chart, ser[0], (uint16_t)(range[0][count]));
lv_chart_set_next_value(ui_ScreenW_Chart, ser[1], (uint16_t)(range[1][count]));
float sum[2]={0};
for (int i=count;i>count-20;i--){
uint8_t index = (i+LVGL_CHART_POINTS)%LVGL_CHART_POINTS;
sum[0] += circular_difference(range[2][count], range[2][index]);
sum[1] += circular_difference(range[3][count], range[3][index]);
}
sum[0] = fmod(sum[0]/20 + range[2][count] + 2 * PI, 2 * PI) - PI;
sum[1] = fmod(sum[1]/20 + range[3][count] + 2 * PI, 2 * PI) - PI;
sine_offest[0] = get_sine_wave_index(sum[0]);
sine_offest[1] = get_sine_wave_index(sum[1]);
lv_chart_set_ext_y_array(ui_ScreenWP_Chart, ser[2], sine_wave+sine_offest[0]);
lv_chart_set_range(ui_ScreenW_Chart, LV_CHART_AXIS_PRIMARY_Y, y_range[0], y_range[1]);
lvgl_port_unlock();
count++;
if (count == LVGL_CHART_POINTS) {
count = 0;
}
}
}else {
static csi_data_t csi_display_data_new = {0};
while (xQueueReceive(uart_recv_queue, &csi_display_data_new, portMAX_DELAY) == pdTRUE) {
cnt++;
if (csi_display_data.start[0] == 0){
goto NEXT;
}
csi_data_t csi_master_data = master_data[csi_display_data.id % DATA_TABLE_SIZE];
if (csi_master_data.id != csi_display_data.id){
ESP_LOGE(TAG, "%ld id_master %ld csi_display_data.id %ld",csi_display_data.id % DATA_TABLE_SIZE,csi_master_data.id,csi_display_data.id);
goto NEXT;
}
range[0][count] = csi_display_data.cir[0]*5;
range[1][count] = csi_master_data.cir[1]*5;
range[2][count] = csi_master_data.cir[2] - csi_display_data.cir[2];
range[3][count] = csi_display_data.cir[3];
y_range[0] = 500;
y_range[1] = 0;
for (int i=0;i<LVGL_CHART_POINTS;i++){
if (y_range[0]>range[0][i]){
y_range[0] = range[0][i];
}
if (y_range[0]>range[1][i]){
y_range[0] = range[1][i];
}
if (y_range[1]<range[0][i]){
y_range[1] = range[0][i];
}
if (y_range[1]<range[1][i]){
y_range[1] = range[1][i];
}
}
if ( (y_range[1]-y_range[0])<100){
y_range[1] += (100-y_range[1]+y_range[0])/2;
y_range[0] -= (100-y_range[1]+y_range[0])/2;
}
lvgl_port_lock(0);
lv_chart_set_next_value(ui_ScreenW_Chart, ser[0], (uint16_t)(range[0][count]));
lv_chart_set_next_value(ui_ScreenW_Chart, ser[1], (uint16_t)(range[1][count]));
float sum[2]={0};
for (int i=count;i>count-20;i--){
uint8_t index = (i+LVGL_CHART_POINTS)%LVGL_CHART_POINTS;
sum[0] += circular_difference(range[2][count], range[2][index]);
sum[1] += circular_difference(range[3][count], range[3][index]);
}
sum[0] = fmod(sum[0]/20 + range[2][count] + 2 * PI, 2 * PI) - PI;
sum[1] = fmod(sum[1]/20 + range[3][count] + 2 * PI, 2 * PI) - PI;
sine_offest[0] = get_sine_wave_index(sum[0]);
sine_offest[1] = get_sine_wave_index(sum[1]);
lv_chart_set_ext_y_array(ui_ScreenWP_Chart, ser[2], sine_wave+sine_offest[0]);
lv_chart_set_range(ui_ScreenW_Chart, LV_CHART_AXIS_PRIMARY_Y, y_range[0], y_range[1]);
lvgl_port_unlock();
count++;
if (count == LVGL_CHART_POINTS) {
count = 0;
}
NEXT:
csi_display_data = csi_display_data_new;
}
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "ui.h"
void csi_data_display_task(void *arg);
void ScreenSSliderLight_function(lv_event_t * e);
#ifdef __cplusplus
}
#endif