Telemetria WiFi+SD

This commit is contained in:
Alejandro Guerrero 2026-07-28 00:59:03 +02:00
parent 7c069efa14
commit 4b58b1ec97
6 changed files with 181 additions and 152 deletions

View file

@ -1,91 +1,105 @@
#include "include/data_processor.hpp"
#include "include/can.hpp"
#include "include/common_libraries.hpp"
#include "include/common_libraries.hpp"
DataProcessor dataProcessor;
CAN canController;
// Objeto UDP para manejar la conexión UDP
// SD
SPIClass spiSD(HSPI);
SdFat sd;
SdFile logFile;
// UDP
WiFiUDP udp;
//Envio de datos a través de UDP
void TaskUdpSender(void *pvParameters){
// --- Tarea UDP (Nucleo 0) ---
void TaskUdpSender(void *pvParameters) {
Serial.println("Iniciando tarea de envio UDP...");
Serial.println("Iniciando tarea de envío...");
while (true) {
if (WiFi.status() == WL_CONNECTED) {
int tempActual = dataProcessor.current_ect_value;
int rpmActual = dataProcessor.current_rpm_value;
float battActual = dataProcessor.current_vbatt_value;
float tpsActual = dataProcessor.current_tps_value;
float frenoDelActual = dataProcessor.current_freno_del_value;
float pcombActual = dataProcessor.current_pcomb_value;
float taceiteActual = dataProcessor.current_taceite_value;
float paceiteActual = dataProcessor.current_paceite_value;
float mapActual = dataProcessor.current_map_value;
float lambdaActual = dataProcessor.current_lambda_value;
float lambdaObjActual = dataProcessor.current_lambda_obj_value;
while (true){
if(WiFi.status() == WL_CONNECTED){
char mensaje[256];
snprintf(mensaje, sizeof(mensaje),
"ect=%d;rpm=%d;vbatt=%.2f;tps=%.1f;freno_del=%.1f;"
"pcomb=%.2f;taceite=%.1f;paceite=%.2f;map=%.1f;lambda=%.3f;lambda_obj=%.3f",
tempActual, rpmActual, battActual, tpsActual, frenoDelActual,
pcombActual, taceiteActual, paceiteActual, mapActual, lambdaActual, lambdaObjActual);
//Obtenemos los datos actuales
int tempActual = dataProcessor.current_ect_value;
int rpmActual = dataProcessor.current_rpm_value;
float battActual = dataProcessor.current_vbatt_value;
udp.beginPacket(IPAddress(255, 255, 255, 255), UDP_PORT);
udp.print(mensaje);
udp.endPacket();
} else {
Serial.println("[WIFI] Desconectado...");
WiFi.disconnect();
WiFi.reconnect();
}
float tpsActual = dataProcessor.current_tps_value;
float frenoDelActual = dataProcessor.current_freno_del_value; // freno delantero (instalado)
float pcombActual = dataProcessor.current_pcomb_value;
float taceiteActual = dataProcessor.current_taceite_value;
float paceiteActual = dataProcessor.current_paceite_value;
float mapActual = dataProcessor.current_map_value;
float lambdaActual = dataProcessor.current_lambda_value;
float lambdaObjActual = dataProcessor.current_lambda_obj_value;
// Pendientes de montar/configurar en el coche. Cuando se instalen, descomentar aquí
// y añadir su campo al snprintf; mientras no lleguen, el monitor los muestra como "--".
//float velocidadActual = dataProcessor.current_velocidad_value;
//float frenoTraActual = dataProcessor.current_freno_tra_value; // freno trasero
//Formato "clave=valor" separado por ';'
char mensaje[256];
snprintf(mensaje, sizeof(mensaje),
"ect=%d;rpm=%d;vbatt=%.2f;tps=%.1f;freno_del=%.1f;"
"pcomb=%.2f;taceite=%.1f;paceite=%.2f;map=%.1f;lambda=%.3f;lambda_obj=%.3f",
tempActual, rpmActual, battActual, tpsActual, frenoDelActual,
pcombActual, taceiteActual, paceiteActual, mapActual, lambdaActual, lambdaObjActual);
// Al añadir los pendientes: sumar ";velocidad=%.0f;freno_tra=%.1f" al formato
// y velocidadActual, frenoTraActual al final de los argumentos.
//Enviamos el paquete por broadcast a toda la red local (no hace falta conocer la IP del portátil)
udp.beginPacket(IPAddress(255,255,255,255), UDP_PORT);
udp.print(mensaje);
udp.endPacket();
//Para comprobar que el paquete se está enviado correctamente podemos usar estos prints
//Serial.print("UDP Enviado: ");
//Serial.println(mensaje);
vTaskDelay(50 / portTICK_PERIOD_MS);
}
else {
//Si no hay Wifi o no se consigue conectar, lo intentamos reconectar
Serial.println("[WIFI] Desconectado...");
WiFi.disconnect();
WiFi.reconnect();
}
//Ponemos de velocidad de envio 50ms, es ajustable
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
// 1. INICIAR CAN Y PANTALLA
// Pasamos el puntero de dataProcessor al controlador CAN
canController.set_data_proccessor(&dataProcessor);
delay(1000);
Serial.println("\n--- G26 TELEMETRY: INICIO DE SISTEMA ---");
// 1. INICIALIZACION SD
spiSD.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
if (!sd.begin(SdSpiConfig(SD_CS, DEDICATED_SPI, SD_SCK_MHZ(1), &spiSD))) {
Serial.println("[FALLO] SD no detectada. El sistema continuara sin Datalogging.");
} else {
char filename[24];
int session = 1;
bool opened = false;
while (!opened && session < 100000) {
snprintf(filename, sizeof(filename), "G26-%d.csv", session);
if (logFile.open(filename, O_RDWR | O_CREAT | O_EXCL)) {
opened = true;
Serial.printf("[OK] Nueva sesion: %s\n", filename);
} else {
session++;
}
}
if (opened) {
logFile.println("Time,ECT,RPM,TPS,VBATT,FRENO_DEL,PCOMB,TACEITE,PACEITE,MAP,LAMBDA,LAMBDA_OBJ");
logFile.sync();
} else {
Serial.println("[ERROR] No se pudo abrir archivo de sesion");
}
}
dataProcessor.setLogSystem(&sd, &logFile);
// 2. INICIAR CAN
canController.set_data_proccessor(&dataProcessor);
canController.start();
canController.start_listening_task();
// 2. INICIAR WIFI
// 3. INICIAR WIFI
Serial.println("--- CONECTANDO WIFI ---");
//IP estática dentro de la red del CPE (DHCP desactivado en el CPE)
IPAddress local_IP(192, 168, 0, 50);
IPAddress gateway(192, 168, 0, 254);
IPAddress subnet(255, 255, 255, 0);
if (!WiFi.config(local_IP, gateway, subnet)) {
Serial.println("[ERR] Fallo al configurar IP estática");
Serial.println("[ERR] Fallo al configurar IP estatica");
}
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
@ -97,27 +111,26 @@ void setup() {
intentos++;
}
if(WiFi.status() == WL_CONNECTED){
Serial.println("\n[OK] WiFi Conectado.");
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n[OK] WiFi Conectado.");
} else {
Serial.println("\n[ERR] No se pudo conectar WiFi (Continuando offline).");
Serial.println("\n[ERR] No se pudo conectar WiFi (Continuando offline).");
}
//Creamos la tarea UDP para el envio de datos
//Usaremos el núcleo 1 o 0 ya que la ESP32 es Dual Core
// 4. TAREA UDP
xTaskCreatePinnedToCore(
TaskUdpSender, // Función que debe de ejecutar
"UdpSender", // Nombre de la tarea
4096, // Stack size
NULL, // Parámetros extras?
1, // Prioridad (1 = Baja, 10 = Alta, la ponemos 1 ya que el CAN debe de tener más prioridad)
NULL, // Handle
0 // Núcleo
TaskUdpSender,
"UdpSender",
4096,
NULL,
1,
NULL,
0
);
Serial.println("OK CAN + UDP Sender ");
Serial.println("[OK] Sistema ONLINE (CAN + SD + WiFi)");
}
void loop(){
void loop() {
vTaskDelay(5 / portTICK_PERIOD_MS);
}
}

View file

@ -6,15 +6,25 @@
#include <ArduinoJson.h>
#include <vector>
//Librerías WiFi y UDP ---
// --- WiFi y UDP ---
#include <WiFi.h>
#include <WifiUdp.h>
#include <WiFiUdp.h>
//CONFIGURACIÓN WIFI Y FIREBASE
// --- SD ---
#include <SPI.h>
#include "SdFat.h"
// CONFIGURACION WIFI
#define WIFI_SSID "FGades"
#define WIFI_PASSWORD "GadesCPE"
//CONFIGURACIÓN WIFI Y FIREBASE
// CONFIGURACION UDP
#define UDP_PORT 4210
#endif
// CONFIGURACION SD (HSPI)
#define SD_CS 25
#define SD_MOSI 26
#define SD_SCK 27
#define SD_MISO 14
#endif

View file

@ -5,44 +5,53 @@
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
class DataProcessor {
public:
DataProcessor() = default;
//Variables publicas para el CAN
//Confirmadas: llegan en la trama 0
// Variables CAN (actualizadas por los frames)
volatile int current_ect_value = 0;
volatile int current_rpm_value = 0;
volatile float current_vbatt_value = 0.0;
volatile float current_tps_value = 0.0;
volatile float current_freno_del_value = 0.0; // presion freno delantero, bar (instalado)
volatile float current_pcomb_value = 0.0; // bar
volatile float current_taceite_value = 0.0; // grados C
volatile float current_paceite_value = 0.0; // bar
volatile float current_map_value = 0.0; // kPa
volatile float current_freno_del_value = 0.0;
volatile float current_pcomb_value = 0.0;
volatile float current_taceite_value = 0.0;
volatile float current_paceite_value = 0.0;
volatile float current_map_value = 0.0;
volatile float current_lambda_value = 0.0;
volatile float current_lambda_obj_value = 0.0;
// Pendientes de montar/configurar en el coche
volatile float current_freno_tra_value = 0.0; // presion freno trasero, bar
volatile float current_velocidad_value = 0.0; // km/h
// Pendientes de instalar
volatile float current_freno_tra_value = 0.0;
volatile float current_velocidad_value = 0.0;
//Métodos de recepción de CAN
// Configuracion SD
void setLogSystem(SdFat* sd_inst, SdFile* file_inst) {
_sd = sd_inst;
_logFile = file_inst;
}
// Metodos CAN
void send_serial_frame_0(int rpmh, int rpml, int tpsh, int tpsl, int vbatth, int vbattl, int ect);
void send_serial_frame_1(int lmbh, int lmbl, int lmbth, int lmbtl, int fuelh, int fuell, int gear);
void send_serial_frame_2(int shut, int fan, int lmbch, int lmbcl, int brakeh, int brakel, int aux1);
void send_serial_frame_3(int oilth, int oiltl, int oilph, int oilpl, int maph, int mapl, int dig1);
void send_serial_frame_4(int dig3, int dig4, int dig5, int dig6, int dig7, int dig8, int dig9);
//Métodos extras
void send_serial(byte type, unsigned int value);
char* process(std::vector<float> data);
private:
SdFat* _sd = nullptr;
SdFile* _logFile = nullptr;
uint32_t _last_sync_time = 0;
const uint32_t _sync_interval_ms = 1000;
void flushToSD();
};
#endif

View file

@ -1,6 +1,6 @@
/**
* @file can.cpp
* @author Raúl Arcos Herrera
* @author Raul Arcos Herrera
* @brief This file contains the implementation of the CAN Controller class for Link G4+ ECU.
*/
@ -8,8 +8,7 @@
// Volcado en crudo de TODOS los mensajes del bus, a la velocidad a la que
// llegan. Satura los 115200 baudios y frena la tarea de escucha, asi que solo
// debe activarse para depurar el bus. Con esto a 1 no se leen las lineas
// [TRAMA n] del data_processor, que son las utiles para localizar canales
// debe activarse para depurar el bus.
#define VOLCADO_CRUDO_CAN 0
static bool driver_installed = false;
@ -47,7 +46,6 @@ void CAN::start() {
return;
}
// TWAI driver is now successfully installed and started
driver_installed = true;
}
@ -69,7 +67,7 @@ void CAN::start_listening_task() {
"CAN_Listen_Task", // Task name
4096, // Stack size (words)
this, // Task parameter (this CAN instance)
1, // Priority (lowered from 5 to 1)
1, // Priority
&_listen_task_handle // Task handle
);
@ -88,7 +86,6 @@ void CAN::stop_listening_task() {
if (_listen_task_handle != NULL) {
_should_stop_listening = true;
// Wait for task to finish (max 1 second)
for (int i = 0; i < 100; i++) {
if (_listen_task_handle == NULL) {
break;
@ -96,7 +93,6 @@ void CAN::stop_listening_task() {
vTaskDelay(pdMS_TO_TICKS(10));
}
// Force delete if still running
if (_listen_task_handle != NULL) {
vTaskDelete(_listen_task_handle);
_listen_task_handle = NULL;
@ -128,21 +124,17 @@ twai_message_t CAN::createBoolMessage(bool b0, bool b1, bool b2, bool b3, bool b
void CAN::listen() {
Serial.println("CAN listening task started");
// Continuous loop for the thread
while (!_should_stop_listening) {
if (!driver_installed) {
// Driver not installed
vTaskDelay(pdMS_TO_TICKS(1000));
continue;
}
// Check if alert happened
uint32_t alerts_triggered;
twai_read_alerts(&alerts_triggered, 0); // Reduced timeout for more responsiveness
twai_read_alerts(&alerts_triggered, 0);
twai_status_info_t twaistatus;
twai_get_status_info(&twaistatus);
// Handle alerts
if (alerts_triggered & TWAI_ALERT_ERR_PASS) {
Serial.println("Alert: TWAI controller has become error passive.");
}
@ -190,8 +182,6 @@ void CAN::listen() {
Serial.println("");
#endif
if (!(message.rtr)) {
// Send to data processor based on first byte (maintaining original logic)
switch (message.data[0]) {
case 0:
_data_processor->send_serial_frame_0(message.data[1], message.data[2], message.data[3], message.data[4], message.data[5], message.data[6], message.data[7]);
@ -223,5 +213,5 @@ void CAN::listen() {
Serial.println("CAN listening task ending");
_listen_task_handle = NULL;
vTaskDelete(NULL); // Delete this task
vTaskDelete(NULL);
}

View file

@ -1,76 +1,90 @@
#include "../include/data_processor.hpp"
char* DataProcessor::process(std::vector<float> data) {
// Implementación del procesamiento de datos si es necesario
return nullptr; // Placeholder
return nullptr;
}
void DataProcessor::send_serial(byte type, unsigned int value) { //Como parámetros se pasan el ID (type), que es el ID establecido al inicio del código para el dato que se quiera enviar. Ej: RPM_ID -> 0x51; y se envía el valor de dicho dato.
byte dato[8] = { 0x5A, 0xA5, 0x05, 0x82, 0x00, 0x00, 0x00, 0x00 }; //Se establece un arreglo de bytes con los primeros datos necesarios para que la pantalla lo interprete como mensaje (En la Wiki hay tutoriales que lo explican a fondo), como ser la longitud y el tipo de mensaje.
dato[4] = type; //Se configura en el mensaje el ID correspondiente al dato a enviar.
dato[6] = (value >> 8) & 0xFF; //Se configura el dato en los últimos 2 bytes.
void DataProcessor::send_serial(byte type, unsigned int value) {
byte dato[8] = { 0x5A, 0xA5, 0x05, 0x82, 0x00, 0x00, 0x00, 0x00 };
dato[4] = type;
dato[6] = (value >> 8) & 0xFF;
dato[7] = value & 0xFF;
Serial.write(dato, 8); //Se envía serialmente el mensaje, indicando su longituden bytes para ello.
Serial.write(dato, 8);
}
//RPM + TPS + vBatt + ECT
void DataProcessor::send_serial_frame_0(int rpmh, int rpml, int tpsh, int tpsl, int vbatth, int vbattl, int ect){
//Calculos necesarios para obtener bien el formato de los valores necesarios
// RPM + TPS + vBatt + ECT
void DataProcessor::send_serial_frame_0(int rpmh, int rpml, int tpsh, int tpsl, int vbatth, int vbattl, int ect) {
int rpm = (rpmh * 256) + rpml;
double vbatt = ((vbatth * 256) + vbattl) / 100.0;
int tps = (tpsh * 256) + tpsl;
int tps = (tpsh * 256) + tpsl;
//Serial.printf("TRAMA: 0\n");
//Serial.printf("RPM: %d | VBATT: %f | TPS: %d | ECT: %d\n", rpm, vbatt, tps, ect);
// Actualizamos las variables globales para que puedan ser leidas por el protocolo UDP
this->current_ect_value = ect;
this->current_rpm_value = rpm;
this->current_vbatt_value = vbatt;
this->current_tps_value = tps;
flushToSD();
}
void DataProcessor::send_serial_frame_1(int lmbh, int lmbl, int lmbth, int lmbtl, int fuelh, int fuell, int gear){
void DataProcessor::send_serial_frame_1(int lmbh, int lmbl, int lmbth, int lmbtl, int fuelh, int fuell, int gear) {
float lambda = ((lmbh * 256) + lmbl) / 100.0;
float lambdaTarget = ((lmbth * 256) + lmbtl) / 100.0;
float presionComb = ((fuelh * 256) + fuell) / 100.0;
//Serial.printf("TRAMA: 1\n");
//Serial.printf("LAMBDA: %f | LAMBDA TARGET: %f | PRESION COMBUSTIBLE: %f\n", lambda, lambdaTarget, presionComb);
this->current_lambda_value = lambda;
this->current_lambda_obj_value = lambdaTarget;
this->current_pcomb_value = presionComb;
//this->current_marcha_value
flushToSD();
}
void DataProcessor::send_serial_frame_2(int shut, int fan, int lmbch, int lmbcl, int brakeh, int brakel, int aux1){
void DataProcessor::send_serial_frame_2(int shut, int fan, int lmbch, int lmbcl, int brakeh, int brakel, int aux1) {
float freno = (brakeh * 256) + brakel;
//Serial.printf("TRAMA: 2\n");
//Serial.printf("FRENO: %f\n", freno);
this->current_freno_del_value = freno;
flushToSD();
}
void DataProcessor::send_serial_frame_3(int oilth, int oiltl, int oilph, int oilpl, int maph, int mapl, int dig1){
void DataProcessor::send_serial_frame_3(int oilth, int oiltl, int oilph, int oilpl, int maph, int mapl, int dig1) {
float tempOil = ((oilth * 256) + oiltl) / 100.0;
float presionOil = ((oilph * 256) + oilpl) / 100.0;
float map = ((maph * 256) + mapl) / 100.0;
//Serial.printf("TRAMA: 3\n");
//Serial.printf("TEMP OIL: %f | PRESION OIL: %f | MAP: %f\n", tempOil, presionOil, map);
this->current_taceite_value = tempOil;
this->current_paceite_value = presionOil;
this->current_map_value = map;
flushToSD();
}
void DataProcessor::send_serial_frame_4(int dig3, int dig4, int dig5, int dig6, int dig7, int dig8, int dig9){
void DataProcessor::send_serial_frame_4(int dig3, int dig4, int dig5, int dig6, int dig7, int dig8, int dig9) {
}
// --- ESCRITURA SD ---
void DataProcessor::flushToSD() {
if (_sd && _logFile && _logFile->isOpen()) {
char buffer[256];
int len = snprintf(buffer, sizeof(buffer),
"%lu,%d,%d,%.1f,%.2f,%.1f,%.2f,%.1f,%.2f,%.1f,%.3f,%.3f\n",
millis(),
current_ect_value,
current_rpm_value,
current_tps_value,
current_vbatt_value,
current_freno_del_value,
current_pcomb_value,
current_taceite_value,
current_paceite_value,
current_map_value,
current_lambda_value,
current_lambda_obj_value);
_logFile->write(buffer, len);
if (millis() - _last_sync_time > _sync_interval_ms) {
_logFile->sync();
_last_sync_time = millis();
}
}
}