9 Commits
Author SHA256 Message Date
semarnat 9d2172fa7d Docs: Add project README 2026-05-22 17:08:25 -06:00
semarnat aa6f246490 Feature:Intregados 2026-05-22 16:54:55 -06:00
semarnat c1d1f8c8f2 v1-RC-1 2026-05-22 12:53:21 -06:00
semarnat 6ca5f7f68d API:WiFi -> Error de handler 2026-05-22 10:51:16 -06:00
semarnat 80a703673f Feature:ADC 2026-05-22 10:26:17 -06:00
semarnat ccf59b22e5 Clear 3 -> Ring Buffering|FreeRTOS:xTask 2026-05-21 14:25:01 -06:00
semarnat 09133b1e5d Clear 2 -> Ring Buffering|FreeRTOS:xTask 2026-05-21 14:22:14 -06:00
semarnat d0b3d52d95 Clear -> Ring Buffering|FreeRTOS:xTask 2026-05-21 14:21:21 -06:00
semarnat 4cab6e68e6 Ring Buffering|FreeRTOS:xTask 2026-05-21 14:20:00 -06:00
11 changed files with 795 additions and 1 deletions

No files matched your search

+5
View File
@@ -1 +1,6 @@
.pio
compile_commands.json
.clangd
.vscode
.cache
.venv
+431
View File
@@ -1,2 +1,433 @@
# ESP32-WiFi-BME
API modular para adquisición ADC continua usando DMA en ESP32-S3 y transmisión binaria mediante WebSocket utilizando FreeRTOS.
---
# Arquitectura General
```text
ADC -> DMA -> RingBuffer -> WebSocket
```
El proyecto está dividido en APIs modulares independientes:
- `ADCconf`
- `WiFiConf`
- `WebSocketHandler`
Cada API encapsula una funcionalidad específica para facilitar integración, mantenimiento y reutilización.
---
# ADCconf API
API encargada de configurar y manejar el ADC continuo utilizando DMA mediante la API ESP-IDF.
## Características
- ADC continuo
- DMA
- Parseo automático de muestras
- Callback mediante interrupciones
- Conversión de datos crudos a `uint16_t`
---
## Configuración
### Inclusión
```cpp
#include "ADCconf.h"
```
---
### Definición de buffers
```cpp
#define MS_BLOQUE 10
adc_channel_t CHANNEL = ADC_CHANNEL_3;
const uint32_t HZ_ADC = 1000;
const uint32_t MUESTRAS_BLOQUE =
(HZ_ADC * MS_BLOQUE) / 1000;
uint8_t dma_buffer[
MUESTRAS_BLOQUE *
SOC_ADC_DIGI_RESULT_BYTES
] = {0};
uint16_t samples_buffer[
MUESTRAS_BLOQUE
] = {0};
```
---
### Crear objeto ADC
```cpp
ADC_BME adc1_3;
```
---
### Configuración ADC
```cpp
adc1_3.config(
MUESTRAS_BLOQUE,
CHANNEL,
HZ_ADC,
dma_buffer
);
```
---
### Inicializar ADC
```cpp
adc1_3.setup();
```
---
### Lectura de muestras
```cpp
uint32_t count =
adc1_3.read(samples_buffer);
```
La función retorna:
- `0` si no existen datos disponibles
- cantidad de muestras válidas si existen datos DMA
---
## Flujo Interno
```text
ADC HW
↓
DMA Buffer
↓
adc_continuous_read()
↓
adc_continuous_parse_data()
↓
samples_buffer[]
```
---
# WiFiConf API
API modular para configuración WiFi en modo STA.
---
## Características
- Configuración IP estática
- Manejo de eventos WiFi
- Reconexión automática
- Encapsulación de configuración de red
---
## Inclusión
```cpp
#include "WiFiConf.h"
```
---
## Crear objeto WiFi
```cpp
wifi_conf sta_esp32;
```
---
## Configuración de red
```cpp
const char* SSID = "MiRed";
const char* PASSWD = "password";
IPAddress local_ip(192,168,0,220);
IPAddress gateway(192,168,0,1);
IPAddress subnet(255,255,255,0);
IPAddress dns1(192,168,0,1);
IPAddress dns2(1,1,1,1);
```
---
## Conexión WiFi
```cpp
sta_esp32.conectar(
SSID,
PASSWD,
local_ip,
gateway,
subnet,
dns1,
dns2
);
```
---
## Eventos Implementados
La API maneja automáticamente:
- conexión WiFi
- desconexión
- reconexión
- obtención de IP
---
# WebSocketHandler API
API encargada de manejar eventos WebSocket.
---
## Características
- detección de clientes
- conexión/desconexión
- recepción de mensajes
- manejo de bandera global de conexión
---
## Inclusión
```cpp
#include "WebSocketHandler.h"
```
---
## Crear servidor WebSocket
```cpp
WebSocketsServer webSocket(81);
```
---
## Inicialización
```cpp
webSocket.begin();
webSocket.onEvent(webSocketEvent);
```
---
## Loop principal
```cpp
webSocket.loop();
```
---
## Transmisión BIN
```cpp
webSocket.broadcastBIN(
(uint8_t*)buffer,
size
);
```
---
## Handler de eventos
```cpp
void webSocketEvent(
uint8_t num,
WStype_t type,
uint8_t * payload,
size_t length
);
```
---
## Eventos soportados
### Cliente conectado
```cpp
WStype_CONNECTED
```
---
### Cliente desconectado
```cpp
WStype_DISCONNECTED
```
---
### Mensajes recibidos
```cpp
WStype_TEXT
```
---
# Integración con FreeRTOS
El sistema utiliza multitarea mediante FreeRTOS.
---
## ADCTask
Responsable de:
- adquisición ADC
- lectura DMA
- inserción en RingBuffer
---
## WebSocketTask
Responsable de:
- `webSocket.loop()`
- transmisión BIN
- extracción del RingBuffer
---
# RingBuffer
Se utiliza `xRingbuffer` para desacoplar:
```text
Adquisición ADC
```
de:
```text
Transmisión WebSocket
```
---
## Creación
```cpp
RingbufHandle_t bufferHandle =
xRingbufferCreate(
SIZE_BUFFER,
RINGBUF_TYPE_NOSPLIT
);
```
---
## Inserción
```cpp
xRingbufferSend(
bufferHandle,
samples_buffer,
count * sizeof(uint16_t),
pdMS_TO_TICKS(1000)
);
```
---
## Recepción
```cpp
uint16_t* receivedMessage =
(uint16_t*)xRingbufferReceive(
bufferHandle,
&receivedMessageSize,
pdMS_TO_TICKS(1000)
);
```
---
# Cliente Python
Ejemplo de recepción de datos binarios.
```python
from websocket import create_connection
import struct
ws = create_connection(
"ws://192.168.0.220:81"
)
while True:
data = ws.recv()
if isinstance(data, bytes):
samples = struct.unpack(
f"<{len(data)//2}H",
data
)
print(samples)
```
---
# Compilación
El proyecto esta construido en PlatformIO, para compilar el poryecto es importante instalar sus herramientas en el sistema.
# Dependencias
```ini
lib_deps =
links2004/WebSockets
```
---
# Hardware Compatible
- ESP32-S3 DevKitC-1
- ESP32-S3 N8
---
# Autor
Sebastian Mariano Flores
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "Arduino.h"
#include "esp_adc/adc_continuous.h"
class ADC_BME {
private:
uint32_t _FRAME_SIZE;
uint32_t _POOL_SIZE;
uint32_t _HZ_ADC;
adc_channel_t _channel;
uint8_t* _dma_buffer;
volatile bool dma_data_ready = false;
adc_continuous_handle_t handle_ADC = NULL;
static bool IRAM_ATTR adc_dma_callback(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data);
public:
void config(const uint32_t muestras_bloque, adc_channel_t channel, const uint32_t hz_adc, uint8_t* dma_buffer);
void setup();
uint32_t read(uint16_t* samples_buffer);
};
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "Arduino.h"
#include <WebSocketsServer.h>
void webSocketEvent( uint8_t num, WStype_t type, uint8_t * payload, size_t length );
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "Arduino.h"
#include "Network.h"
#include <WiFi.h>
extern volatile bool websocket_connected;
class wifi_conf {
private:
const char* _ssid;
const char* _passwd;
void handler_WiFi(arduino_event_id_t event, arduino_event_info_t info);
public:
void conectar(
const char* ssid,
const char* passwd,
IPAddress local_ip,
IPAddress gateway,
IPAddress subnet,
IPAddress dns1,
IPAddress dns2
);
};
+13 -1
View File
@@ -9,6 +9,18 @@
; https://docs.platformio.org/page/projectconf.html
[env:esp32-s3-devkitc-1]
platform = espressif32
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
board = esp32-s3-devkitc-1
build_type = debug
framework = arduino
monitor_speed = 230400
board_build.arduino.memory_type = qio_opi
board_build.flash_mode = qio
board_build.psram_type = opi
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.partitions = default_16MB.csv
board_build.extra_flags =
-DBOARD_HAS_PSRAM
-DCORE_DEBUG_LEVEL=5
lib_deps = links2004/WebSockets@^2.7.3
+94
View File
@@ -0,0 +1,94 @@
#include "ADCconf.h"
bool IRAM_ATTR ADC_BME::adc_dma_callback(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data){
ADC_BME* adc =static_cast<ADC_BME*>(user_data);
adc->dma_data_ready = true;
return false;
}
void ADC_BME::config(const uint32_t muestras_bloque, adc_channel_t channel, const uint32_t hz_adc, uint8_t* dma_buffer){
_FRAME_SIZE=muestras_bloque*4;
_POOL_SIZE=_FRAME_SIZE*4;
_channel=channel;
_HZ_ADC=hz_adc;
_dma_buffer=dma_buffer;
}
void ADC_BME::setup(){
adc_continuous_handle_cfg_t ADC_config = {
.max_store_buf_size = _POOL_SIZE,
.conv_frame_size = _FRAME_SIZE,
};
ESP_ERROR_CHECK(adc_continuous_new_handle(&ADC_config, &handle_ADC));
adc_digi_pattern_config_t channel_config = {0};
channel_config.channel = _channel;
channel_config.atten = ADC_ATTEN_DB_12;
channel_config.bit_width = ADC_BITWIDTH_12;
channel_config.unit = ADC_UNIT_1;
adc_continuous_config_t adc_config = {
.pattern_num = 1,
.adc_pattern = &channel_config,
.sample_freq_hz = _HZ_ADC,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
};
ESP_ERROR_CHECK(adc_continuous_config(handle_ADC, &adc_config));
adc_continuous_evt_cbs_t cb_config = {
.on_conv_done = adc_dma_callback,
.on_pool_ovf = NULL
};
ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(handle_ADC, &cb_config, this));
ESP_ERROR_CHECK(adc_continuous_start(handle_ADC));
Serial.println("DMA Iniciado con sistema de bandera.");
}
uint32_t ADC_BME::read(uint16_t* samples_buffer) {
if (!dma_data_ready) {
return 0;
}
dma_data_ready = false;
uint32_t ret_num = 0;
// Leer datos DMA
esp_err_t ret = adc_continuous_read(handle_ADC,_dma_buffer,_FRAME_SIZE,&ret_num,0);
if (ret != ESP_OK)
return 0;
// Buffer temporal parseado
adc_continuous_data_t parsed_data[_FRAME_SIZE/4];
uint32_t parsed_count = 0;
// Parsear datos
esp_err_t parse_ret =
adc_continuous_parse_data(
handle_ADC,
_dma_buffer,
ret_num,
parsed_data,
&parsed_count
);
if (parse_ret != ESP_OK) {
return 0;
}
uint32_t valid_samples = 0;
// Copiar muestras limpias
for (uint32_t i = 0; i < parsed_count; i++) {
if (!parsed_data[i].valid)
continue;
samples_buffer[valid_samples] = parsed_data[i].raw_data;
valid_samples++;
}
return valid_samples;
}
+17
View File
@@ -0,0 +1,17 @@
#include "WebSocketHandler.h"
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_CONNECTED:
Serial.printf("[%u] Cliente conectado\n", num);
break;
case WStype_DISCONNECTED:
Serial.printf("[%u] Cliente desconectado\n", num);
break;
case WStype_TEXT:
Serial.printf("[%u] Mensaje: %s\n", num, payload);
break;
}
}
+52
View File
@@ -0,0 +1,52 @@
#include "WiFiConf.h"
extern volatile bool websocket_connected;
void wifi_conf::handler_WiFi(arduino_event_id_t event, arduino_event_info_t info) {
switch(event){
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
websocket_connected = true;
Serial.printf("Wi-Fi conectado\n");
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Serial.printf( "IP Address: %s\n", IPAddress(info.got_ip.ip_info.ip.addr).toString().c_str() );
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
websocket_connected = false;
Serial.printf("WiFi: Desconectado\n");
Serial.printf("DEBUG: Reconectando...\n");
WiFi.STA.connect(_ssid,_passwd);
break;
}
}
void wifi_conf::conectar(
const char* ssid,
const char* passwd,
IPAddress local_ip,
IPAddress gateway,
IPAddress subnet,
IPAddress dns1,
IPAddress dns2
) {
_ssid = ssid;
_passwd = passwd;
Network.begin();
Network.onEvent([this](arduino_event_id_t event, arduino_event_info_t info){
handler_WiFi(event, info);
});
WiFi.STA.begin();
WiFi.STA.config( local_ip, gateway, subnet, dns1, dns2);
WiFi.STA.connect(_ssid, _passwd);
}
+101
View File
@@ -0,0 +1,101 @@
#include "Arduino.h"
#include "freertos/ringbuf.h"
#include "ADCconf.h"
#include "WiFiConf.h"
#include "WebSocketHandler.h"
#include <WebSocketsServer.h>
// API ESP-IDF para ADC modo continuo (uso de DMA)
#define MS_BLOQUE 10
adc_channel_t CHANNEL = ADC_CHANNEL_3;
const uint32_t HZ_ADC = 1000; // Limitado a 611 - 83333 para el ESP32-S3
const uint32_t MUESTRAS_BLOQUE = (HZ_ADC * MS_BLOQUE) / 1000;
uint8_t dma_buffer[MUESTRAS_BLOQUE * SOC_ADC_DIGI_RESULT_BYTES] = {0};
uint16_t samples_buffer[MUESTRAS_BLOQUE] = {0};
const uint32_t SIZE_BUFFER = MUESTRAS_BLOQUE*10;
ADC_BME adc1_3;
// Conf de ESP32-S3 en modo STA
const char* SSID="debian";
const char* PASSWD="aH2T9GFA";
IPAddress local_ip(10,42,0,100);
IPAddress gateway(40,42,0,1);
IPAddress subnet(255,255,255,0);
IPAddress dns1(10,42,0,1);
IPAddress dns2(1,1,1,1);
wifi_conf sta_esp32;
WebSocketsServer webSocket(81);
volatile bool websocket_connected = false;
// FreeRTOS
TaskHandle_t ADCTaskHandle = NULL;
TaskHandle_t WebSocketTaskHandle = NULL;
RingbufHandle_t bufferHandle = xRingbufferCreate(SIZE_BUFFER, RINGBUF_TYPE_NOSPLIT);
void ADCTask(void *parameter);
void WebSocketTask(void *parameter);
void setup(){
Serial.begin(230400);
sta_esp32.conectar(SSID, PASSWD, local_ip, gateway, subnet, dns1, dns2);
webSocket.begin();
webSocket.onEvent(webSocketEvent);
adc1_3.config(MUESTRAS_BLOQUE, CHANNEL, HZ_ADC, dma_buffer);
adc1_3.setup();
//Iniciar el buffer circular
if (bufferHandle == NULL) {
Serial.printf("Failed to create ring buffer!");
return;
}
xTaskCreatePinnedToCore( ADCTask, "ADCTask", 10000, NULL, 1, &ADCTaskHandle, 0);
xTaskCreatePinnedToCore( WebSocketTask, "WebSocketTask", 10000, NULL, 1, &WebSocketTaskHandle, 1);
}
void loop(){
}
void ADCTask(void *parameter){
for(;;) {
if (!websocket_connected) {
vTaskDelay(pdMS_TO_TICKS(100));
continue;
}
const uint32_t count = adc1_3.read(samples_buffer);
if (count > 0) {
if (xRingbufferSend(bufferHandle, samples_buffer, count * sizeof(uint16_t), pdMS_TO_TICKS(1000)) != pdTRUE) {
Serial.println("Failed to send message to ring buffer!");
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void WebSocketTask(void *parameter){
for(;;) {
webSocket.loop();
size_t receivedMessageSize;
uint16_t* receivedMessage = (uint16_t*)xRingbufferReceive(bufferHandle, &receivedMessageSize, pdMS_TO_TICKS(1000));
// 6. Check if the receive operation was successful
if (receivedMessage != NULL) {
webSocket.broadcastBIN((uint8_t*)receivedMessage, receivedMessageSize);
vRingbufferReturnItem(bufferHandle, (void*)receivedMessage);
} else {
Serial.println("Failed t+o receive message from ring buffer!");
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import struct
from websocket import create_connection
ws = create_connection("ws://10.42.0.100:81")
print("Conectado")
i = 0
try:
while True:
data = ws.recv()
i += 1
if isinstance(data, bytes):
# Convertir bytes -> uint16_t
samples = struct.unpack(f"<{len(data) // 2}H", data)
print("ADC:", samples)
else:
print("TXT:", data)
print(f"DEBUG {i}")
except KeyboardInterrupt:
print("Cerrando conexión")
ws.close()