Compare commits

26 Commits

Author SHA1 Message Date
5c9ef7e93b Fast ADC readings ok, to verify timing and settling time 2026-04-05 17:05:10 +02:00
a2d0afa0c9 adc read ok, very slow 2026-04-05 11:16:10 +02:00
1109681eb5 Merge branch 'debug' 2026-04-05 10:32:08 +02:00
c5d80052e5 Thans to copilot, microsecond resolution to wait for spark.
no missing firings detected
2026-04-04 22:12:53 +02:00
38c595fd7b revert on double task wait, normal and soft start working 2026-04-04 20:00:30 +02:00
b0842aadef Sync 12 and 34 working 2026-04-04 16:27:52 +02:00
0dc5d1ce79 Debug Config OK, ready to realtime debugging 2026-04-04 03:25:33 +02:00
Emanuele Trabattoni
941a2b4eaa debug testing commit 2026-04-04 03:11:44 +02:00
Emanuele Trabattoni
48df6a509d Test OK Channel A 2026-04-01 12:31:29 +02:00
Emanuele Trabattoni
ebff6281af Test Softs start working 2026-04-01 12:00:07 +02:00
Emanuele Trabattoni
d7e0990e36 Test working at 100Khz 2026-04-01 11:43:44 +02:00
Emanuele Trabattoni
21e50bdca8 Tester first iteration 2026-03-31 17:44:00 +02:00
Emanuele Trabattoni
6072a603df First Attempt to print data async, fields not working 2026-03-31 13:07:02 +02:00
Emanuele Trabattoni
27ad612844 Mod to ISR and Tasks ok working 2026-03-31 09:19:11 +02:00
Emanuele Trabattoni
ec138553ad Separate code from headers 2026-03-30 16:29:40 +02:00
Emanuele Trabattoni
1adbf7fdb9 Tasks, ISR, Pin refactoring and renaming 2026-03-30 15:50:17 +02:00
Emanuele Trabattoni
e0af350b89 refactored real time task to general with configuration structure 2026-03-30 12:36:31 +02:00
Emanuele Trabattoni
b75014854d Working trigger + Spark recognition!!! 2026-03-27 17:53:05 +01:00
Emanuele Trabattoni
a210d808da First Test env 2026-03-27 12:49:20 +01:00
Emanuele Trabattoni
a0710f7ee7 Pins and devices refactoring in progress 2026-03-26 18:22:22 +01:00
Emanuele Trabattoni
68ed8a2282 Fixed spark timing compute 2026-03-26 14:09:55 +01:00
Emanuele Trabattoni
b573f64a39 Added datasheets 2026-03-25 20:17:45 +01:00
Emanuele Trabattoni
f84fcfffbb Tasks work in progress 2026-03-25 18:22:02 +01:00
Emanuele Trabattoni
bece80ad6c Fixed pin mappings and task logic 2026-03-25 17:19:53 +01:00
Emanuele Trabattoni
8c5b7d4a1c Updated pin definitions and trigger tasks from pickups 2026-03-25 14:16:23 +01:00
Emanuele Trabattoni
b42474fff5 Added custom board definition 2026-03-25 14:14:10 +01:00
31 changed files with 1666 additions and 162 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
Datasheet/PCA9555.pdf Normal file

Binary file not shown.

BIN
Datasheet/ad5291_5292.pdf Normal file

Binary file not shown.

BIN
Datasheet/ads1255.pdf Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

@@ -1,7 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"Jason2866.esp-decoder",
"pioarduino.pioarduino-ide",
"platformio.platformio-ide"
],
"unwantedRecommendations": [

View File

@@ -0,0 +1,52 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_ESP32S3_DEV",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_CDC_ON_BOOT=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"psram_type": "opi",
"hwids": [
["0x303A", "0x1001"]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": [
"esp-builtin"
],
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "ESP32-S3 WROOM (16MB Flash / 8MB PSRAM / USB HOST)",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"url": "https://www.pcb-hero.com/blogs/lickys-column/esp32-s3-chip-definition-pinout-processor-application-and-development-board",
"vendor": "Custom"
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x700000,
app1, app, ota_1, 0x710000,0x700000,
spiffs, data, spiffs, 0xE10000,0x1F0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x700000
5 app1 app ota_1 0x710000 0x700000
6 spiffs data spiffs 0xE10000 0x1F0000

View File

@@ -8,27 +8,61 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32-s3-devkitc1-n8r8]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip
board = esp32-s3-devkitc1-n8r8
[env:esp32-s3-devkitc1-n16r8]
board = esp32-s3-devkitc1-n16r8
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
lib_deps =
hideakitai/DebugLog@^0.8.4
bblanchon/ArduinoJson@^7.4.2
hideakitai/DebugLog@^0.8.4
bblanchon/ArduinoJson@^7.4.2
hideakitai/PCA95x5@^0.1.3
adafruit/Adafruit SSD1306@^2.5.16
garfius/Menu-UI@^1.2.0
;Upload protocol configuration
upload_protocol = esptool
upload_port = /dev/ttyACM2
upload_speed = 921600
;Monitor configuration
monitor_port = /dev/ttyACM2
monitor_speed = 921600
; Build configuration
build_type = release
build_flags =
-DARDUINO_USB_CDC_ON_BOOT=0
-DARDUINO_USB_MODE=0
-fstack-protector-all
-DCONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=1
-DCONFIG_FREERTOS_USE_TRACE_FACILITY=1
[env:esp32-s3-devkitc1-n8r8-debug]
platform = ${env:esp32-s3-devkitc1-n8r8.platform}
board = ${env:esp32-s3-devkitc1-n8r8.board}
framework = ${env:esp32-s3-devkitc1-n8r8.framework}
lib_deps = ${env:esp32-s3-devkitc1-n8r8.lib_deps}
[env:esp32-s3-devkitc1-n16r8-debug]
board = ${env:esp32-s3-devkitc1-n16r8.board}
platform = ${env:esp32-s3-devkitc1-n16r8.platform}
framework = ${env:esp32-s3-devkitc1-n16r8.framework}
lib_deps = ${env:esp32-s3-devkitc1-n16r8.lib_deps}
;Upload protocol configuration
upload_protocol = esptool
upload_port = /dev/ttyACM2
upload_speed = 921600
;Monitor configuration
monitor_port = /dev/ttyACM2
monitor_speed = 921600
; Debug configuration
debug_tool = esp-builtin
debug_speed = 15000
; Build configuration
build_type = debug
build_flags =
build_flags =
-O0
-g3
-ggdb
-fno-inline
-fno-ipa-sra
-fno-tree-sra
-fno-builtin
-ggdb3
-DCORE_DEBUG_LEVEL=5
-DARDUINO_USB_CDC_ON_BOOT=0
-DARDUINO_USB_MODE=0
-fstack-protector-all

View File

@@ -120,14 +120,14 @@ void ADS1256::setDRATE(uint8_t drate) //Setting DRATE (sampling frequency)
{
writeRegister(DRATE_REG, drate);
_DRATE = drate;
delay(200);
delayMicroseconds(500);
}
void ADS1256::setMUX(uint8_t mux) //Setting MUX (input channel)
{
writeRegister(MUX_REG, mux);
_MUX = mux;
delay(200);
//delayMicroseconds(500);
}
void ADS1256::setPGA(uint8_t pga) //Setting PGA (input voltage range)
@@ -138,7 +138,7 @@ void ADS1256::setPGA(uint8_t pga) //Setting PGA (input voltage range)
_ADCON = (_ADCON & 0b11111000) | (_PGA & 0b00000111); // Clearing and then setting bits 2-0 based on pga
writeRegister(ADCON_REG, _ADCON);
delay(200);
delayMicroseconds(1000); //Delay to allow the PGA to settle after changing its value
updateConversionParameter(); //Update the multiplier according top the new PGA value
}
@@ -501,8 +501,6 @@ void ADS1256::writeRegister(uint8_t registerAddress, uint8_t registerValueToWrit
CS_HIGH();
_spi->endTransaction();
delay(100);
}
long ADS1256::readRegister(uint8_t registerAddress) //Reading a register
@@ -524,7 +522,7 @@ long ADS1256::readRegister(uint8_t registerAddress) //Reading a register
CS_HIGH();
_spi->endTransaction();
delay(100);
return regValue;
}

View File

@@ -0,0 +1,39 @@
#pragma once
// Library defines
#define ADS1256_SPI_ALREADY_STARTED
// Device Libraries
#include <ADS1256.h>
#include <AD5292.h>
#include <Adafruit_SSD1306.h>
#include <PCA95x5.h>
// ADC Channel mapping
#define ADC_CH_PEAK_12P_IN SING_0
#define ADC_CH_PEAK_12N_IN SING_1
#define ADC_CH_PEAK_34P_IN SING_2
#define ADC_CH_PEAK_34N_IN SING_3
#define ADC_CH_PEAK_12P_OUT SING_4
#define ADC_CH_PEAK_12N_OUT SING_5
#define ADC_CH_PEAK_34P_OUT SING_6
#define ADC_CH_PEAK_34N_OUT SING_7
// Device Pointer structs for tasks
struct Devices {
AD5292 *pot_a = NULL, *pot_b = NULL;
ADS1256 *adc_a = NULL, *adc_b = NULL;
Adafruit_SSD1306* lcd = NULL;
PCA9555* io = NULL;
};
// Adc read channel wrapper to selet mux before reading
inline float adcReadChannel(ADS1256* adc, const uint8_t ch){
adc->setMUX(ch);
// scarta 3 conversioni
for (int i = 0; i < 3; i++) {
adc->readSingle();
}
// ora lettura valida a 30kSPS → ~100 µs di settling
return adc->convertToVoltage(adc->readSingle());
}

58
RotaxMonitor/src/isr.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "isr.h"
// =====================
// ISR (Pass return bitmask to ISR management function)
// one function for each wake up pin conncted to a trigger
// =====================
void trig_isr(void *arg)
{
const int64_t time_us = esp_timer_get_time();
// exit if invalid args
if (!arg)
return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
isrParams *params = (isrParams *)arg;
ignitionBoxStatus *box = params->ign_stat;
TaskHandle_t task_handle = params->rt_handle_ptr;
// exit if task not running
if (!task_handle)
return;
switch (params->flag)
{
case TRIG_FLAG_12P:
case TRIG_FLAG_12N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils12.trig_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break;
case TRIG_FLAG_34P:
case TRIG_FLAG_34N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils34.trig_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break;
case SPARK_FLAG_12:
box->coils34.spark_ok = false;
box->coils12.spark_ok = true;
box->coils12.spark_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
break;
case SPARK_FLAG_34:
box->coils12.spark_ok = false;
box->coils34.spark_ok = true;
box->coils34.spark_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
break;
default:
break;
}
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}

View File

@@ -1,40 +1,112 @@
#pragma once
// Test device Flag
// #define TEST
// Arduino Libraries
#include <Arduino.h>
#include "soc/gpio_struct.h"
#include <map>
#ifndef TEST
#include "pins.h"
#else
#include "pins_test.h"
#endif
#define CORE_0 0
#define CORE_1 1
#define TASK_STACK 4096 // in words
#define TASK_PRIORITY (configMAX_PRIORITIES - 4) // highest priority after wifi tasks
// =====================
// Event Flags (bitmask)
// =====================
#define PKDT_FLAG_AP (1 << 0)
#define PKDT_FLAG_AN (1 << 1)
#define PKDT_FLAG_BP (1 << 2)
#define PKDT_FLAG_BN (1 << 3)
static const uint32_t TRIG_FLAG_12P = (1 << 0);
static const uint32_t TRIG_FLAG_12N = (1 << 1);
static const uint32_t TRIG_FLAG_34P = (1 << 2);
static const uint32_t TRIG_FLAG_34N = (1 << 3);
// Task handle
TaskHandle_t pkdtTaskHandle = NULL;
static const uint32_t SPARK_FLAG_NIL = (1 << 8);
static const uint32_t SPARK_FLAG_12 = (1 << 9);
static const uint32_t SPARK_FLAG_34 = (1 << 10);
static const uint32_t SPARK_FLAG_TIMEOUT = (1 << 11);
// =====================
// ISR (Pass return bitmask to ISR management function)
// one function for each wake up pin conncted to a trigger
// =====================
void IRAM_ATTR pkdt_isr_ap() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(pkdtTaskHandle, PKDT_FLAG_AP, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// Spark Status
enum sparkStatus
{
SPARK_POS_OK,
SPARK_NEG_OK,
SPARK_POS_SKIP,
SPARK_NEG_SKIP,
SPARK_POS_WAIT,
SPARK_NEG_WAIT,
SPARK_POS_FAIL,
SPARK_NEG_FAIL,
SPARK_POS_UNEXPECTED,
SPARK_NEG_UNEXPECTED,
SPARK_SYNC_FAIL,
};
void IRAM_ATTR pkdt_isr_an() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(pkdtTaskHandle, PKDT_FLAG_AN, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
static const std::map<const sparkStatus, const char *> sparkStatusNames = {
{SPARK_POS_OK, "SPARK_POS_OK"},
{SPARK_NEG_OK, "SPARK_NEG_OK"},
{SPARK_POS_SKIP, "SPARK_POS_SKIP"},
{SPARK_NEG_SKIP, "SPARK_NEG_SKIP"},
{SPARK_POS_WAIT, "SPARK_POS_WAIT"},
{SPARK_NEG_WAIT, "SPARK_NEG_WAIT"},
{SPARK_POS_FAIL, "SPARK_POS_FAIL"},
{SPARK_NEG_FAIL, "SPARK_NEG_FAIL"},
{SPARK_POS_UNEXPECTED, "SPARK_POS_UNEXPECTED"},
{SPARK_NEG_UNEXPECTED, "SPARK_NEG_UNEXPECTED"},
{SPARK_SYNC_FAIL, "SPARK_SYNC_FAIL"},
};
void IRAM_ATTR pkdt_isr_bp() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(pkdtTaskHandle, PKDT_FLAG_BP, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
enum softStartStatus
{
NORMAL,
SOFT_START,
ERROR,
};
void IRAM_ATTR pkdt_isr_bn() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(pkdtTaskHandle, PKDT_FLAG_BN, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
const std::map<const softStartStatus, const char *> softStartStatusNames = {
{NORMAL, "NORMAL"},
{SOFT_START, "SOFT_START"},
{ERROR, "ERROR"},
};
struct coilsStatus
{
int64_t trig_time = 0;
int64_t spark_time = 0;
int64_t spark_delay = 0; // in microseconds
sparkStatus spark_status = sparkStatus::SPARK_POS_OK;
softStartStatus sstart_status = softStartStatus::NORMAL;
float peak_p_in = 0.0, peak_n_in = 0.0;
float peak_p_out = 0.0, peak_n_out = 0.0;
float trigger_spark = 0.0;
bool spark_ok = false;
uint32_t n_events = 0;
};
// Task internal Status
struct ignitionBoxStatus
{
int64_t timestamp = 0;
// coils pairs for each ignition
coilsStatus coils12;
coilsStatus coils34;
// voltage from generator
float volts_gen = 0.0;
uint32_t n_queue_errors = 0;
uint32_t adc_read_time = 0;
};
struct isrParams
{
const uint32_t flag;
ignitionBoxStatus *ign_stat;
TaskHandle_t rt_handle_ptr;
};
void IRAM_ATTR trig_isr(void *arg);

View File

@@ -7,82 +7,238 @@
#include <SPI.h>
// Definitions
#include <isr.h>
#include <pins.h>
#include <tasks.h>
#include <channels.h>
#include <devices.h>
#include <ui.h>
// Device Libraries
#include <ADS1256.h>
#include <AD5292.h>
// FreeRTOS directives
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void pkdtTask(void *pvParameters) {
uint32_t notifiedValue;
// #define CH_B_ENABLE
#define TEST
while (true) {
// attende eventi
xTaskNotifyWait(
0x00, // non pulire all'ingresso
0xFFFFFFFF, // pulisci tutti i bit all'uscita
&notifiedValue, // valore ricevuto
portMAX_DELAY
);
float freqToRPM(float freq)
{
return freq * 60.0f; // 1 pulse per revolution
}
// 🔥 QUI GIRA SU CORE 0
switch (notifiedValue)
case PKDT_FLAG_AP: {
handlePKDT(PKDT_AP);
break;
}
default:
LOG_ERROR("Invalid Interrupt: ", notifiedValue);
void printTaskStats()
{
char buffer[1024];
vTaskGetRunTimeStats(buffer);
Serial.println(buffer);
}
void setup()
{
Serial.begin(921600);
delay(250);
// Setup Logger
LOG_ATTACH_SERIAL(Serial);
LOG_SET_LEVEL(DebugLogLevel::LVL_INFO);
// Print Processor Info
LOG_INFO("ESP32 Chip:", ESP.getChipModel());
if (psramFound())
{
LOG_INFO("ESP32 PSram Found");
LOG_INFO("ESP32 PSram:", ESP.getPsramSize());
psramInit();
}
LOG_INFO("ESP32 Flash:", ESP.getFlashChipSize());
LOG_INFO("ESP32 Heap:", ESP.getHeapSize());
LOG_INFO("ESP32 Sketch:", ESP.getFreeSketchSpace());
// Initialize Interrupt pins on PICKUP detectors
initTriggerPinsInputs();
// Initialize Interrupt pins on SPARK detectors
initSparkPinInputs();
}
void setup() {
Serial.begin(9600);
LOG_ATTACH_SERIAL(Serial);
LOG_SET_LEVEL(DebugLogLevel::LVL_INFO);
LOG_INFO("ESP32 Chip:", ESP.getChipModel());
LOG_INFO("ESP32 PSram:", ESP.getPsramSize());
LOG_INFO("ESP32 Flash:", ESP.getFlashChipSize());
LOG_INFO("ESP32 Heap:", ESP.getHeapSize());
LOG_INFO("ESP32 Sketch:", ESP.getFreeSketchSpace());
void loop()
{
// global variables
bool running = true;
static Devices dev;
// Initialize Interrupt pins on peak detectors
pinMode(PKDT_AP, INPUT_PULLDOWN);
pinMode(PKDT_AN, INPUT_PULLDOWN);
pinMode(PKDT_BP, INPUT_PULLDOWN);
pinMode(PKDT_BN, INPUT_PULLDOWN);
// interrupt
attachInterrupt(PKDT_AP, pkdt_isr_ap, RISING);
attachInterrupt(PKDT_AN, pkdt_isr_an, RISING);
attachInterrupt(PKDT_BP, pkdt_isr_bp, RISING);
attachInterrupt(PKDT_BN, pkdt_isr_bn, RISING);
// Task handle
static TaskHandle_t trigA_TaskHandle = NULL;
static TaskHandle_t trigB_TaskHandle = NULL;
// Init SPI interface
SPI.begin();
// Init ADC
auto adc = ADS1256(ADC_DRDY, ADC_RST, ADC_SYNC, ADC_CS, 0.0, SPI);
ADS1256.
}
static QueueHandle_t rt_taskA_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
static QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
static rtTaskParams taskA_params{
.rt_running = true,
.dev = &dev,
.rt_handle_ptr = &trigA_TaskHandle,
.rt_queue = rt_taskA_queue,
.rt_int = rtTaskInterrupts{
.isr_ptr = trig_isr,
.trig_pin_12p = TRIG_PIN_A12P,
.trig_pin_12n = TRIG_PIN_A12N,
.trig_pin_34p = TRIG_PIN_A34P,
.trig_pin_34n = TRIG_PIN_A34N,
.spark_pin_12 = SPARK_PIN_A12,
.spark_pin_34 = SPARK_PIN_A34},
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_A12P, .rst_io_12n = RST_EXT_A12N, .rst_io_34p = RST_EXT_A34P, .rst_io_34n = RST_EXT_A34N}};
void loop() {
// task su core 0
auto isrTask = xTaskCreatePinnedToCore(
pkdtTask,
"pkdtTask",
4096,
NULL,
2, // priorità leggermente più alta
&pkdtTaskHandle,
0
);
LOG_INFO("Task Variables OK");
if (isrTask != pdPASS){
LOG_ERROR("Unble to initialize ISR task");
#ifdef CH_B_ENABLE
QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
rtTaskParams taskB_params{
.rt_running = true,
.dev = &dev,
.rt_handle_ptr = &trigB_TaskHandle,
.rt_queue = rt_taskB_queue,
.rt_int = rtTaskInterrupts{
.isr_ptr = trig_isr,
.trig_pin_12p = TRIG_PIN_B12P,
.trig_pin_12n = TRIG_PIN_B12N,
.trig_pin_34p = TRIG_PIN_B34P,
.trig_pin_34n = TRIG_PIN_B34N,
.spark_pin_12 = SPARK_PIN_B12,
.spark_pin_34 = SPARK_PIN_B34},
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_B12P, .rst_io_12n = RST_EXT_B12N, .rst_io_34p = RST_EXT_B34P, .rst_io_34n = RST_EXT_B34N}};
#endif
bool spiA_ok = true;
bool spiB_ok = true;
// Init 2 SPI interfaces
SPIClass SPI_A(FSPI);
spiA_ok = SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI);
SPI_A.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
#ifndef TEST
SPIClass SPI_B(HSPI);
spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI);
SPI_B.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
#endif
if (!spiA_ok || !spiB_ok)
{
LOG_ERROR("Unable to Initialize SPI Busses");
LOG_ERROR("5 seconds to restart...");
vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart();
}
LOG_INFO("Init SPI OK");
// Init ADC_A
dev.adc_a = new ADS1256(ADC_A_DRDY, ADS1256::PIN_UNUSED, ADC_A_SYNC, ADC_A_CS, 2.5, &SPI_A);
dev.adc_a->InitializeADC();
dev.adc_a->setPGA(PGA_1);
dev.adc_a->setDRATE(DRATE_7500SPS);
#ifndef TEST
// Init ADC_B
dev.adc_a = new ADS1256(ADC_B_DRDY, ADC_B_RST, ADC_B_SYNC, ADC_B_CS, 2.5, &SPI_B);
dev.adc_a->InitializeADC();
dev.adc_a->setPGA(PGA_1);
dev.adc_a->setDRATE(DRATE_1000SPS);
#endif
LOG_INFO("Init ADC OK");
// Ignition A on Core 0
auto ignA_task_success = pdPASS;
ignA_task_success = xTaskCreatePinnedToCore(
rtIgnitionTask,
"rtIgnitionTask_boxA",
TASK_STACK,
(void *)&taskA_params,
TASK_PRIORITY,
&trigA_TaskHandle,
CORE_0);
// Ignition B on Core 1
auto ignB_task_success = pdPASS;
#ifdef CH_B_ENABLE
ignB_task_success = xTaskCreatePinnedToCore(
rtIgnitionTask,
"rtIgnitionTask_boxB",
TASK_STACK,
(void *)&taskB_params,
TASK_PRIORITY, // priorità leggermente più alta
&trigB_TaskHandle,
CORE_1);
#endif
if ((ignA_task_success && ignB_task_success) != pdPASS)
{
LOG_ERROR("Unble to initialize ISR task");
LOG_ERROR("5 seconds to restart...");
vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart();
}
LOG_INFO("Real Time Tasks A & B initialized");
////////////////////// MAIN LOOP //////////////////////
clearScreen();
setCursor(0, 0);
ignitionBoxStatus ignA;
int64_t last = esp_timer_get_time();
uint32_t missed_firings12 = 0;
uint32_t missed_firings34 = 0;
uint32_t counter = 0;
while (running)
{
if (xQueueReceive(rt_taskA_queue, &ignA, pdMS_TO_TICKS(1000)) == pdTRUE)
{
float freq = (esp_timer_get_time() - last) / 1000000.0f; // in seconds
freq = freq > 0 ? 1.0f / freq : 0; // Calculate frequency (Hz)
last = esp_timer_get_time();
if (ignA.coils12.spark_status == sparkStatus::SPARK_POS_FAIL || ignA.coils12.spark_status == sparkStatus::SPARK_NEG_FAIL)
missed_firings12++;
if (ignA.coils34.spark_status == sparkStatus::SPARK_POS_FAIL || ignA.coils34.spark_status == sparkStatus::SPARK_NEG_FAIL)
missed_firings34++;
clearScreen();
setCursor(0, 0);
printField("++ Timestamp", (uint32_t)ignA.timestamp);
Serial.println("========== Coils 12 =============");
printField("Events", (uint32_t)ignA.coils12.n_events);
printField("Missed Firing", missed_firings12);
printField("Spark Dly", (uint32_t)ignA.coils12.spark_delay);
printField("Spark Sts", sparkStatusNames.at(ignA.coils12.spark_status));
printField("Peak P_IN", ignA.coils12.peak_p_in);
printField("Peak N_IN", ignA.coils12.peak_n_in);
printField("Peak P_OUT", ignA.coils12.peak_p_out);
printField("Peak N_OUT", ignA.coils12.peak_n_out);
printField("Soft Start ", softStartStatusNames.at(ignA.coils12.sstart_status));
Serial.println("========== Coils 34 =============");
printField("Events", (uint32_t)ignA.coils34.n_events);
printField("Missed Firing", missed_firings34);
printField("Spark Dly", (uint32_t)ignA.coils34.spark_delay);
printField("Spark Sts", sparkStatusNames.at(ignA.coils34.spark_status));
printField("Peak P_IN", ignA.coils34.peak_p_in);
printField("Peak N_IN", ignA.coils34.peak_n_in);
printField("Peak P_OUT", ignA.coils34.peak_p_out);
printField("Peak N_OUT", ignA.coils34.peak_n_out);
printField("Soft Start ", softStartStatusNames.at(ignA.coils34.sstart_status));
Serial.println("========== END =============");
Serial.println();
printField("Engine RPM", freqToRPM(freq));
printField("ADC Read Time", (uint32_t)ignA.adc_read_time);
printField("Queue Errors", (uint32_t)ignA.n_queue_errors);
}
else
{
Serial.println("Waiting for data... ");
delay(500);
}
}
if (trigA_TaskHandle)
vTaskDelete(trigA_TaskHandle);
if (trigB_TaskHandle)
vTaskDelete(trigB_TaskHandle);
////////////////////// MAIN LOOP //////////////////////
}

View File

@@ -1,67 +1,128 @@
// =====================
// SPI BUS
// =====================
#define SPI_MOSI 11
#define SPI_MISO 13
#define SPI_SCK 12
#pragma once
#include <Arduino.h>
// =====================
// CHIP SELECT
// USB (RISERVATA)
// =====================
#define ADC_CS 10
#define POT_CS 9
#define USB_DM 19
#define USB_DP 20
// =====================
// UART DEBUG (RISERVATA)
// =====================
#define UART_TX 43
#define UART_RX 44
// =====================
// RGB Led
// =====================
#define LED 48
// =====================
// STRAPPING CRITICI (NON USARE)
// =====================
// 0, 3
// =====================
// SPI BUS ADC1 (VSPI)
// =====================
#define SPI_A_MOSI 11
#define SPI_A_MISO 13
#define SPI_A_SCK 12
// =====================
// SPI BUS ADC2 (HSPI)
// =====================
#define SPI_B_MOSI 35
#define SPI_B_MISO 37
#define SPI_B_SCK 36
// =====================
// I2C BUS (PCA9555)
// =====================
#define SDA 8
#define SCL 9
// =====================
// ADC CONTROL
// =====================
#define ADC_DRDY 4
#define ADC_RST 5
#define ADC_SYNC 6
#define ADC_A_CS 4
#define ADC_A_DRDY 5
#define ADC_A_SYNC 6
#define ADC_B_CS 14
#define ADC_B_DRDY 15
#define ADC_B_SYNC 16
// =====================
// DIGITAL POT
// =====================
#define POT_DRDY 7
#define POT_A_CS 7
#define POT_B_CS 17
// =====================
// RELAY
// TRIGGER INPUT INTERRUPTS
// =====================
#define PICK_RELAY 8
#define TRIG_PIN_A12P 18
#define TRIG_PIN_A12N 21
#define TRIG_PIN_A34P 1
#define TRIG_PIN_A34N 2
#define TRIG_PIN_B12P 38
#define TRIG_PIN_B12N 39
#define TRIG_PIN_B34P 40
#define TRIG_PIN_B34N 41
// =====================
// PEAK DETECTORS (DIGITAL INPUT)
// SPARK DETECT INPUTS
// =====================
#define PKDT_AP 1
#define PKDT_AN 2
#define PKDT_BP 3
#define PKDT_BN 14
#define SPARK_PIN_A12 42
#define SPARK_PIN_A34 45 // OK (strapping ma consentito) 45
#define SPARK_PIN_B12 46 // OK (strapping ma consentito) 46
#define SPARK_PIN_B34 47
// =====================
// TRIGGER INPUTS
// PCA9555 (I2C EXPANDER)
// =====================
#define TRIG_AP 15
#define TRIG_AN 16
#define TRIG_BP 17
#define TRIG_BN 18
// =====================
// SOFT START DETECT
// =====================
#define SOFT_A 21
#define SOFT_B 47
// --- RESET LINES ---
#define RST_EXT_A12P 0
#define RST_EXT_A12N 1
#define RST_EXT_A34P 2
#define RST_EXT_A34N 3
#define RST_EXT_B12P 4
#define RST_EXT_B12N 5
#define RST_EXT_B34P 6
#define RST_EXT_B34N 7
// =====================
// STATUS OUTPUT
// =====================
#define STA_1 35
#define STA_2 36
#define STA_3 37
#define STA_4 38
// --- RELAY ---
#define A_EXT_RELAY 8
#define B_EXT_RELAY 9
// =====================
// BUTTON INPUT
// =====================
#define BTN_1 39
#define BTN_2 40
#define BTN_3 41
#define BTN_4 42
// --- STATUS / BUTTON ---
#define BTN_3 10
#define BTN_4 11
#define STA_1 12
#define STA_2 13
#define STA_3 14
#define STA_4 15
// Init Pin Functions
inline void initTriggerPinsInputs()
{
pinMode(TRIG_PIN_A12P, INPUT_PULLDOWN);
pinMode(TRIG_PIN_A12N, INPUT_PULLDOWN);
pinMode(TRIG_PIN_A34P, INPUT_PULLDOWN);
pinMode(TRIG_PIN_A34N, INPUT_PULLDOWN);
pinMode(TRIG_PIN_B12P, INPUT_PULLDOWN);
pinMode(TRIG_PIN_B12N, INPUT_PULLDOWN);
pinMode(TRIG_PIN_B34P, INPUT_PULLDOWN);
pinMode(TRIG_PIN_B34N, INPUT_PULLDOWN);
}
inline void initSparkPinInputs()
{
pinMode(SPARK_PIN_A12, INPUT_PULLDOWN);
pinMode(SPARK_PIN_A34, INPUT_PULLDOWN);
pinMode(SPARK_PIN_B12, INPUT_PULLDOWN);
pinMode(SPARK_PIN_B34, INPUT_PULLDOWN);
}

View File

@@ -0,0 +1,84 @@
#pragma once
#include <Arduino.h>
// =====================
// UART DEBUG
// =====================
#define UART_TX 1 // TX0 (USB seriale)
#define UART_RX 3 // RX0
// =====================
// SPI BUS
// =====================
#define SPI_A_MOSI 23
#define SPI_A_MISO 19
#define SPI_A_SCK 18
// =====================
// I2C BUS
// =====================
#define SDA 21
#define SCL 22
// =====================
// ADC CONTROL (SPI + interrupt safe)
// =====================
#define ADC_A_CS 5 // chip select
#define ADC_A_DRDY 34 // input only + interrupt perfetto
#define ADC_A_RST 27 // output
#define ADC_A_SYNC 26 // output
// =====================
// DIGITAL OUT
// =====================
#define POT_A_CS 25
#define POT_B_CS 33
// =====================
// TRIGGER INPUT INTERRUPTS
// =====================
#define TRIG_PIN_A12P 35
#define TRIG_PIN_A12N 32
#define TRIG_PIN_A34P 39
#define TRIG_PIN_A34N 36
// =====================
// SPARK DETECT INTERRUPTS
// =====================
#define SPARK_PIN_A12 4
#define SPARK_PIN_A34 2
// =====================
// PCA9555 (I2C EXPANDER)
// =====================
// --- RESET LINES ---
#define RST_EXT_A12P 0
#define RST_EXT_A12N 1
#define RST_EXT_A34P 2
#define RST_EXT_A34N 3
// --- RELAY ---
#define A_EXT_RELAY 8
// Init Pin Functions
inline void initTriggerPinsInputs()
{
pinMode(TRIG_PIN_A12P, INPUT_PULLDOWN);
pinMode(TRIG_PIN_A12N, INPUT_PULLDOWN);
pinMode(TRIG_PIN_A34P, INPUT_PULLDOWN);
pinMode(TRIG_PIN_A34N, INPUT_PULLDOWN);
}
inline void initSparkPinInputs()
{
pinMode(SPARK_PIN_A12, INPUT_PULLDOWN);
pinMode(SPARK_PIN_A34, INPUT_PULLDOWN);
}

308
RotaxMonitor/src/tasks.cpp Normal file
View File

@@ -0,0 +1,308 @@
#include "tasks.h"
#include <esp_timer.h>
// Timeout callback for microsecond precision
void spark_timeout_callback(void* arg) {
TaskHandle_t handle = (TaskHandle_t)arg;
xTaskNotify(handle, SPARK_FLAG_TIMEOUT, eSetValueWithOverwrite);
}
void rtIgnitionTask(void *pvParameters)
{
// Invalid real time rt_task_ptr parameters, exit immediate
if (!pvParameters)
{
LOG_ERROR("Null rt_task_ptr parameters");
vTaskDelete(NULL);
}
LOG_INFO("rtTask Params OK");
// Task Parameters and Devices
rtTaskParams *params = (rtTaskParams *)pvParameters;
const rtTaskInterrupts rt_int = params->rt_int; // copy to avoid external override
const rtTaskResets rt_rst = params->rt_resets; // copy to avoid external override
QueueHandle_t rt_queue = params->rt_queue;
TaskHandle_t rt_handle_ptr = *params->rt_handle_ptr;
Devices *dev = params->dev;
ADS1256 *adc = dev->adc_a;
PCA9555 *io = dev->io;
ignitionBoxStatus ign_box_sts;
// Variables for ISR, static to be fixed in memory locations
static isrParams isr_params_t12p{
.flag = TRIG_FLAG_12P,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_t12n{
.flag = TRIG_FLAG_12N,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_t34p{
.flag = TRIG_FLAG_34P,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_t34n{
.flag = TRIG_FLAG_34N,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_sp12{
.flag = SPARK_FLAG_12,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_sp34{
.flag = SPARK_FLAG_34,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
LOG_INFO("rtTask ISR Params OK");
// Create esp_timer for microsecond precision timeout
esp_timer_handle_t timeout_timer;
esp_timer_create_args_t timer_args = {
.callback = spark_timeout_callback,
.arg = (void*)rt_handle_ptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "spark_timeout"
};
esp_timer_create(&timer_args, &timeout_timer);
// Attach Pin Interrupts
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12p), rt_int.isr_ptr, (void *)&isr_params_t12p, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12n), rt_int.isr_ptr, (void *)&isr_params_t12n, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_34p), rt_int.isr_ptr, (void *)&isr_params_t34p, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_34n), rt_int.isr_ptr, (void *)&isr_params_t34n, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.spark_pin_12), rt_int.isr_ptr, (void *)&isr_params_sp12, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.spark_pin_34), rt_int.isr_ptr, (void *)&isr_params_sp34, RISING);
LOG_INFO("rtTask ISR Attach OK");
// Compute Reset Pin Bitmask
const uint16_t rst_bitmask = (1 << rt_rst.rst_io_12p) |
(1 << rt_rst.rst_io_12n) |
(1 << rt_rst.rst_io_34p) |
(1 << rt_rst.rst_io_34n);
LOG_WARN("rtTask Init Correct");
// Global rt_task_ptr variables
bool first_cycle = true;
bool cycle12 = false;
bool cycle34 = false;
while (params->rt_running)
{
uint32_t pickup_flag = 0;
uint32_t spark_flag = 0;
// WAIT FOR PICKUP SIGNAL
xTaskNotifyWait(
0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit
&pickup_flag, // valore ricevuto
portMAX_DELAY);
if (first_cycle && pickup_flag != TRIG_FLAG_12P) // skip first cycle because of possible initial noise on pickup signals at startu
{
continue;
}
#ifdef DEBUG
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
LOG_INFO("Iteration [", it++, "]");
if (!names.contains(pickup_flag))
{
LOG_ERROR("Wrong Pickup Flag");
LOG_ERROR("Pickup Flags: ", printBits(pickup_flag).c_str());
continue;
}
else
{
LOG_INFO("Pickup Trigger: ", names.at(pickup_flag));
}
#endif
// Start microsecond precision timeout timer
esp_timer_stop(timeout_timer); // stop timer in case it was running from previous cycle
esp_timer_start_once(timeout_timer, spark_timeout_max);
spark_flag = SPARK_FLAG_NIL; // default value in case of timeout, to be set by ISR if spark event occours
// WAIT FOR SPARK TO HAPPEN OR TIMEOUT
BaseType_t sp = pdFALSE;
sp = xTaskNotifyWait(
0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit
&spark_flag, // valore ricevuto
portMAX_DELAY); // wait indefinitely, timeout handled by esp_timer
// Handle timeout or spark event
if (spark_flag == SPARK_FLAG_TIMEOUT) {
spark_flag = SPARK_FLAG_NIL;
} else {
// Spark occurred, stop the timer
esp_timer_stop(timeout_timer);
}
#ifdef DEBUG
// LOG_INFO("Spark Flags: ", printBits(spark_flag).c_str());
LOG_INFO("Spark12:", ign_box_sts.coils12.spark_ok ? "TRUE" : "FALSE");
LOG_INFO("Spark34:", ign_box_sts.coils34.spark_ok ? "TRUE" : "FALSE");
if (names.contains(spark_flag))
LOG_INFO("Spark Trigger:", names.at(spark_flag));
#endif
xTaskNotifyStateClear(NULL);
ulTaskNotifyValueClear(NULL, 0xFFFFFFFF);
// A trigger from pickup 12 is followed by a spark event on 34 or vice versa pickup 34 triggers spark on 12
if ((pickup_flag == TRIG_FLAG_12P || pickup_flag == TRIG_FLAG_12N) && (spark_flag != SPARK_FLAG_12 && spark_flag != SPARK_FLAG_NIL))
{
ign_box_sts.coils12.spark_status = ign_box_sts.coils34.spark_status = sparkStatus::SPARK_SYNC_FAIL;
continue;
}
// Select coil status reference based on pickup_flag
coilsStatus *coils;
switch (pickup_flag)
{
case TRIG_FLAG_12P:
first_cycle = false;
case TRIG_FLAG_12N:
coils = &ign_box_sts.coils12;
break;
case TRIG_FLAG_34P:
case TRIG_FLAG_34N:
coils = &ign_box_sts.coils34;
break;
}
// Select logic based on pickup and spark flags
switch (pickup_flag)
{
case TRIG_FLAG_12P:
case TRIG_FLAG_34P:
{
// Timeout not occourred, expected POSITIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_NIL)
{
coils->spark_delay = coils->spark_time - coils->trig_time;
coils->sstart_status = softStartStatus::NORMAL; // because spark on positive edge
coils->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge
#ifdef DEBUG
LOG_INFO("Spark on POSITIVE pulse");
LOG_INFO("Spark Delay Time: ", (int32_t)coils->spark_delay);
#endif
}
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_NIL)
{
coils->spark_status = sparkStatus::SPARK_NEG_WAIT;
coils->sstart_status = softStartStatus::NORMAL;
}
continue; // Do nothing more on positive pulse
}
// CASES for NEGATIVE cycle triggering of pickup and sparks 12 & 34
case TRIG_FLAG_12N:
case TRIG_FLAG_34N:
{
const bool expected_negative = coils->spark_status == sparkStatus::SPARK_NEG_WAIT;
// Timeout not occourred, expected NEGATIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_NIL && expected_negative)
{
coils->spark_delay = coils->spark_time - coils->trig_time;
coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_OK;
#ifdef DEBUG
LOG_INFO("Spark on NEGATIVE pulse");
LOG_INFO("Spark Delay Time: ", (int32_t)coils->spark_delay);
#endif
}
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_NIL && expected_negative)
{
coils->sstart_status = softStartStatus::ERROR;
coils->spark_status = sparkStatus::SPARK_NEG_FAIL;
}
// Timeout not occouured, unexpected negative edge spark
else if (spark_flag != SPARK_FLAG_NIL && !expected_negative)
{
coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_UNEXPECTED;
}
// Wait for finish of negative pulse to save data to buffer
coils->n_events++;
if (pickup_flag == TRIG_FLAG_12N)
cycle12 = true;
else
cycle34 = true;
break;
}
default:
#ifdef DEUG
LOG_ERROR("Invalid Interrupt");
LOG_ERROR("Pickup Flags: ", printBits(pickup_flag).c_str());
LOG_ERROR("Spark Flags: ", printBits(spark_flag).c_str());
#endif
break;
}
if (cycle12 && cycle34) // wait for both 12 and 34 cycles to complete before sending data to main loop and resetting peak detectors
{
cycle12 = false;
cycle34 = false;
// read adc channels: pickup12, out12 [ pos + neg ]
if (adc) // read only if adc initialized
{
uint32_t start_adc_read = esp_timer_get_time();
// from peak detector circuits
ign_box_sts.coils12.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_12P_IN);
ign_box_sts.coils12.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_12N_IN);
ign_box_sts.coils34.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_34P_IN);
ign_box_sts.coils34.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_34N_IN);
ign_box_sts.coils12.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_12P_OUT);
ign_box_sts.coils12.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_12N_OUT);
ign_box_sts.coils34.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_34P_OUT);
ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT);
ign_box_sts.adc_read_time = (uint32_t)(esp_timer_get_time() - start_adc_read);
}
else // simulate adc read timig
vTaskDelay(pdMS_TO_TICKS(1));
// reset peak detectors + sample and hold
// outputs on io expander
if (io)
{
const uint16_t iostat = io->read();
io->write(iostat | rst_bitmask);
vTaskDelay(pdMS_TO_TICKS(1));
io->write(iostat & ~rst_bitmask);
}
else
vTaskDelay(pdMS_TO_TICKS(1));
// send essage to main loop with ignition info, by copy so local static variable is ok
if (rt_queue)
ign_box_sts.timestamp = esp_timer_get_time(); // update data timestamp
if (xQueueSendToBack(rt_queue, (void *)&ign_box_sts, 0) != pdPASS)
{
static uint32_t n_errors = 0;
n_errors++;
ign_box_sts.n_queue_errors = n_errors;
LOG_ERROR("Failed to send to rt_queue");
}
}
}
// Delete the timeout timer
esp_timer_delete(timeout_timer);
LOG_WARN("Ending realTime Task");
// Ignition A Interrupts DETACH
detachInterrupt(rt_int.trig_pin_12p);
detachInterrupt(rt_int.trig_pin_12n);
detachInterrupt(rt_int.trig_pin_34p);
detachInterrupt(rt_int.trig_pin_34n);
detachInterrupt(rt_int.spark_pin_12);
detachInterrupt(rt_int.spark_pin_34);
// delete present task
vTaskDelete(NULL);
}

66
RotaxMonitor/src/tasks.h Normal file
View File

@@ -0,0 +1,66 @@
#pragma once
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
// Serial debug flag
//#define DEBUG
// Arduino Libraries
#include <Arduino.h>
#include <DebugLog.h>
#include "utils.h"
// ISR
#include "isr.h"
// DEVICES
#include "devices.h"
// Global Variables and Flags
const uint32_t spark_timeout_max = 500; // in microseconds
// Debug Variables
#ifdef DEBUG
static const std::map<const uint32_t, const char *> names = {
{TRIG_FLAG_12P, "TRIG_FLAG_12P"},
{TRIG_FLAG_12N, "TRIG_FLAG_12N"},
{TRIG_FLAG_34P, "TRIG_FLAG_34P"},
{TRIG_FLAG_34N, "TRIG_FLAG_34N"},
{SPARK_FLAG_12, "SPARK_FLAG_12"},
{SPARK_FLAG_34, "SPARK_FLAG_34"},
{SPARK_FLAG_TIMEOUT, "SPARK_FLAG_TIMEOUT"},
};
#endif
// RT task Interrupt parameters
struct rtTaskInterrupts
{
void (*isr_ptr)(void *);
const uint8_t trig_pin_12p;
const uint8_t trig_pin_12n;
const uint8_t trig_pin_34p;
const uint8_t trig_pin_34n;
const uint8_t spark_pin_12;
const uint8_t spark_pin_34;
};
// RT Task Peak Detector Reset pins
struct rtTaskResets
{
const uint8_t rst_io_12p;
const uint8_t rst_io_12n;
const uint8_t rst_io_34p;
const uint8_t rst_io_34n;
};
// RT task parameters
struct rtTaskParams
{
bool rt_running; // run flag, false to terminate
Devices *dev;
TaskHandle_t* rt_handle_ptr;
const QueueHandle_t rt_queue;
const rtTaskInterrupts rt_int; // interrupt pins to attach
const rtTaskResets rt_resets; // reset ping for peak detectors
};
void rtIgnitionTask(void *pvParameters);

36
RotaxMonitor/src/ui.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <Arduino.h>
void clearScreen()
{
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
Serial.flush();
}
void setCursor(const uint8_t x, const uint8_t y)
{
Serial.printf("\033[%d;%d", y, x + 1);
Serial.flush();
}
void printField(const char name[], const uint32_t val)
{
Serial.printf("%15s: %06d\n", name, val);
}
void printField(const char name[], const int64_t val)
{
Serial.printf("%15s: %06u\n", name, (uint64_t)val);
}
void printField(const char name[], const float val)
{
Serial.printf("%15s: %4.2f\n", name, val);
}
void printField(const char name[], const char *val)
{
Serial.printf("%15s: %s\n", name, val);
}

View File

@@ -0,0 +1,14 @@
#include "utils.h"
std::string printBits(uint32_t value) {
std::string result;
for (int i = 31; i >= 0; i--) {
// ottieni il singolo bit
result += ((value >> i) & 1) ? '1' : '0';
// aggiungi uno spazio ogni 8 bit, tranne dopo l'ultimo
if (i % 8 == 0 && i != 0) {
result += ' ';
}
}
return result;
}

6
RotaxMonitor/src/utils.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <Arduino.h>
#include <string>
std::string printBits(uint32_t value);

5
RotaxMonitorTester/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,10 @@
{
"recommendations": [
"Jason2866.esp-decoder",
"pioarduino.pioarduino-ide",
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,39 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32-devtest-release]
board = esp32dev
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
lib_deps =
hideakitai/DebugLog@^0.8.4
board_build.flash_size = 4MB
board_build.partitions = default.csv
monitor_speed = 921600
build_type = release
[env:esp32-devtest-debug]
board = esp32dev
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
lib_deps =
hideakitai/DebugLog@^0.8.4
board_build.flash_size = 4MB
board_build.partitions = default.csv
monitor_speed = 921600
build_type = debug
build_flags =
-O0
-g3
-ggdb
-fno-inline
-fno-ipa-sra
-fno-tree-sra
-fno-builtin

View File

@@ -0,0 +1,111 @@
#include <Arduino.h>
#include <DebugLog.h>
#include "timer.h"
#include <map>
static hw_timer_t *timerA = NULL;
static hw_timer_t *timerB = NULL;
TaskHandle_t main_t = NULL;
static uint32_t count = 0;
#define FREQUENCY 100000 // 100 KHz
#define PERIOD_US 10
#define SPARK_DLY_MIN 10
#define SPARK_DLY_MAX 490
#define PAUSE_LONG_MIN 5000
#define PAUSE_LONG_MAX PAUSE_LONG_MIN*100
#define RPM_MIN 800
#define RPM_MAX 5500
void clearScreen(){
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
Serial.flush();
}
static double filtered_rpm = 0;
static const std::map<const uint32_t, const char *> pin2Name = {
{PIN_TRIG_A12P, "HIGH_PIN_TRIG_A12P"},
{~PIN_TRIG_A12P, "LOW_PIN_TRIG_A12P"},
{PIN_TRIG_A12N, "HIGH_PIN_TRIG_A12N"},
{~PIN_TRIG_A12N, "LOW_PIN_TRIG_A12N"},
{PIN_TRIG_A34P, "HIGH_PIN_TRIG_A34P"},
{~PIN_TRIG_A34P, "LOW_PIN_TRIG_A34P"},
{PIN_TRIG_A34N, "HIGH_PIN_TRIG_A34N"},
{~PIN_TRIG_A34N, "LOW_PIN_TRIG_A34N"},
{SPARK_A12, "HIGH_SPARK_A12"},
{~SPARK_A12, "LOW_SPARK_A12"},
{SPARK_A34, "HIGH_SPARK_A34"},
{~SPARK_A34, "LOW_SPARK_A34"},
{State::S_WAIT_10MS_END, "S_WAIT_10MS_END"},
{State::S_WAIT_10MS, "S_WAIT_10MS"}};
static timerStatus stsA = {
.clock_period_us = (uint32_t)PERIOD_US,
.pause_long_us = 10000,
.pause_short_us = 1000,
.coil_pulse_us = 1000,
.spark_pulse_us = 100,
.spark_delay_us = 50,
.main_task = NULL};
void setup()
{
Serial.begin(921600);
delay(1000);
LOG_ATTACH_SERIAL(Serial);
pinMode(PIN_TRIG_A12P, OUTPUT);
pinMode(PIN_TRIG_A12N, OUTPUT);
pinMode(PIN_TRIG_A34P, OUTPUT);
pinMode(PIN_TRIG_A34N, OUTPUT);
pinMode(SPARK_A12, OUTPUT);
pinMode(SPARK_A34, OUTPUT);
pinMode(PIN_TRIG_B12P, OUTPUT);
pinMode(PIN_TRIG_B12N, OUTPUT);
pinMode(PIN_TRIG_B34P, OUTPUT);
pinMode(PIN_TRIG_B34N, OUTPUT);
pinMode(SPARK_B12, OUTPUT);
pinMode(SPARK_B34, OUTPUT);
pinMode(SPARK_DELAY_POT, ANALOG);
stsA.main_task = xTaskGetCurrentTaskHandleForCore(1);
timerA = timerBegin(FREQUENCY);
timerAttachInterruptArg(timerA, &onTimer, (void *)&stsA);
timerAlarm(timerA, 1, true, 0);
LOG_INFO("Setup Complete");
}
void loop()
{
LOG_INFO("Loop: ", count++);
uint32_t spark_delay = (uint32_t)(map(analogRead(SPARK_DELAY_POT), 0, 4096, SPARK_DLY_MIN, SPARK_DLY_MAX) / PERIOD_US);
stsA.spark_delay_us = spark_delay * PERIOD_US;
if (stsA.spark_delay_us > (SPARK_DLY_MIN + SPARK_DLY_MAX) / 2) {
stsA.soft_start = true;
stsA.spark_delay_us -= (SPARK_DLY_MIN + SPARK_DLY_MAX) / 2;
} else {
stsA.soft_start = false;
}
double new_rpm = (double)(map(analogRead(FREQ_POT), 0, 4096, RPM_MIN, RPM_MAX));
filtered_rpm = filtered_rpm + 0.1 * (new_rpm - filtered_rpm);
stsA.pause_long_us = (uint32_t)(60000000.0f / filtered_rpm / 2.0f);
LOG_INFO("Spark Delay uS: ", stsA.spark_delay_us, "\tSoft Start: ", stsA.soft_start ? "TRUE" : "FALSE");
LOG_INFO("Engine Rpm: ", (uint32_t)(filtered_rpm));
LOG_INFO("Coil Pulse: ", stsA.coil_pulse_us, "us");
LOG_INFO("Spark Pulse: ", stsA.spark_pulse_us, "us");
delay(100);
clearScreen();
}

View File

@@ -0,0 +1,23 @@
#pragma once
///// Ignition Box A /////
#define PIN_TRIG_A12P 18
#define PIN_TRIG_A12N 19
#define PIN_TRIG_A34P 21
#define PIN_TRIG_A34N 22
#define SPARK_A12 23
#define SPARK_A34 25
///// Ignition Box /////
#define PIN_TRIG_B12P 26
#define PIN_TRIG_B12N 27
#define PIN_TRIG_B34P 32
#define PIN_TRIG_B34N 33
#define SPARK_B12 4
#define SPARK_B34 5
// Pot
#define SPARK_DELAY_POT 13
#define FREQ_POT 14

View File

@@ -0,0 +1,192 @@
#include "timer.h"
volatile static bool wait_sent = false;
void onTimer(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
timerStatus *params = (timerStatus *)(arg);
TaskHandle_t task = params->main_task;
// increment state time
params->state_time += params->clock_period_us;
digitalWrite(PIN_TRIG_B12P, HIGH);
switch (params->state)
{
case S_12P:
if (params->state_time == params->clock_period_us && !params->coil12p_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A12P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12P, HIGH);
params->coil12p_high = true;
wait_sent = false;
}
if (!params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, HIGH);
}
if (params->state_time == (params->spark_delay_us + params->spark_pulse_us))
{
// xTaskNotifyFromISR(task, ~SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil12p_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A12P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12P, LOW);
params->coil12p_high = false;
}
if (params->state_time >= params->pause_short_us)
{
params->state = S_12N;
params->state_time = 0;
}
break;
case S_12N:
if (params->state_time == params->clock_period_us && !params->coil12n_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A12N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12N, HIGH);
params->coil12n_high = true;
}
if (params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, HIGH);
}
if (params->state_time == (params->spark_delay_us + params->spark_pulse_us))
{
// xTaskNotifyFromISR(task, ~SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil12n_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A12N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12N, LOW);
params->coil12n_high = false;
params->state = S_WAIT_10MS;
params->state_time = 0;
}
break;
case S_WAIT_10MS:
if (!wait_sent)
{
// xTaskNotifyFromISR(task, S_WAIT_10MS, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
wait_sent = true;
}
if (params->state_time >= params->pause_long_us)
{
params->state = S_34P;
params->state_time = 0;
}
break;
case S_34P:
if (params->state_time == params->clock_period_us && !params->coil34p_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A34P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34P, HIGH);
params->coil34p_high = true;;
wait_sent = false;
}
if (!params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, HIGH);
}
if (params->state_time == params->spark_delay_us + params->spark_pulse_us)
{
// xTaskNotifyFromISR(task, ~SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil34p_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A34P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34P, LOW);
params->coil34p_high = false;
}
if (params->state_time >= params->pause_short_us)
{
params->state = S_34N;
params->state_time = 0;
}
break;
case S_34N:
if (params->state_time == params->clock_period_us && !params->coil34n_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A34N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34N, HIGH);
params->coil34n_high = true;
}
if (params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, HIGH);
}
if (params->state_time == params->spark_delay_us + params->spark_pulse_us)
{
// xTaskNotifyFromISR(task, ~SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil34n_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A34N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34N, LOW);
params->coil34n_high = false;
params->state = S_WAIT_10MS_END;
params->state_time = 0;
}
break;
case S_WAIT_10MS_END:
if (!wait_sent)
{
// xTaskNotifyFromISR(task, S_WAIT_10MS_END, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
wait_sent = true;
}
if (params->state_time >= params->pause_long_us)
{
params->state = S_12P;
params->state_time = 0;
}
break;
}
digitalWrite(PIN_TRIG_B12P, LOW);
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <Arduino.h>
#include <DebugLog.h>
#include "pins.h"
#include "driver/gpio.h"
enum State
{
S_12P,
S_12N_DELAY,
S_12N,
S_WAIT_10MS,
S_34P,
S_34N_DELAY,
S_34N,
S_WAIT_1MS,
S_WAIT_10MS_END
};
struct timerStatus
{
State state = State::S_12P;
uint32_t state_time = 0;
uint32_t clock_period_us;
uint32_t pause_long_us;
uint32_t pause_short_us;
uint32_t coil_pulse_us;
uint32_t spark_pulse_us;
uint32_t spark_delay_us;
bool soft_start = false;
bool coil12p_high = false;
bool coil34p_high = false;
bool coil12n_high = false;
bool coil34n_high = false;
TaskHandle_t main_task;
};
void IRAM_ATTR onTimer(void *arg);

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html