24 Commits

Author SHA1 Message Date
Emanuele Trabattoni
28a2bf75e3 start refactoring 2026-04-22 22:23:55 +02:00
c9887a563e more variables name refactoring 2026-04-22 15:14:25 +02:00
15ca82b6df task variable name refactoring 2026-04-22 14:17:35 +02:00
d700578256 disable interrupts in adc reading critical section 2026-04-22 13:43:41 +02:00
10f8026c6d enable disable interrupts on adc drdy only when needed (only for cycle read now) fixed useless delays 2026-04-22 12:07:39 +02:00
dc56990f1e fixed ws ping timer 2026-04-21 23:30:08 +02:00
a9d5bcfd66 fixed pinmap 2026-04-21 23:29:48 +02:00
9bb66a9459 re enable interrupt logic for ADC drdy 2026-04-21 22:32:01 +02:00
aa9935ef22 reorder upload and monitor ports 2026-04-21 22:25:56 +02:00
79dbd5db5d added some fake commands 2026-04-21 22:22:59 +02:00
94c5c7491a file cleanup 2026-04-21 22:22:47 +02:00
5ca3d3a46b Added module datasheet 2026-04-21 21:53:22 +02:00
6f372fcb49 Vhanged pin assignment to avoid 35,36,37 used in QSPI PSRAM 2026-04-21 21:51:58 +02:00
fec59815a6 Merge branch 'ioexpander' into debug 2026-04-21 16:16:16 +02:00
7e7d0a1c59 Second ADC debugging in process 2026-04-21 16:11:07 +02:00
59e4e955ff Merged for debug 2026-04-21 16:08:34 +02:00
Emanuele Trabattoni
dce6b0fd4f working on second adc 2026-04-17 13:24:43 +02:00
Emanuele Trabattoni
bea29dc8f5 ADC ok with interrupt or drdy 2026-04-17 12:21:35 +02:00
Emanuele Trabattoni
1b8ba88b05 ADC working ok in sync with system 2026-04-17 11:01:41 +02:00
5aa5aaa07a ADC Testing 2026-04-17 09:13:05 +02:00
1b7a531d54 Updated test instrument with cli commands 2026-04-17 09:11:41 +02:00
8171cab9cb adc ok 2026-04-14 14:16:11 +02:00
Emanuele Trabattoni
899c8cffbc io expander class ok , adc not working 2026-04-14 11:02:33 +02:00
Emanuele Trabattoni
782aa95ee6 Merge branch 'task-refactor' 2026-04-13 10:28:24 +02:00
29 changed files with 20432 additions and 1437 deletions

Binary file not shown.

View File

@@ -36,7 +36,6 @@
<div class="box-data"> <div class="box-data">
<p><strong>Timestamp:</strong> <span id="a_timestamp">-</span></p> <p><strong>Timestamp:</strong> <span id="a_timestamp">-</span></p>
<p><strong>Data Valid:</strong> <span id="a_datavalid">-</span></p> <p><strong>Data Valid:</strong> <span id="a_datavalid">-</span></p>
<p><strong>Generator voltage:</strong> <span id="a_volts_gen">-</span></p>
<p><strong>ADC read time:</strong> <span id="a_adc_read_time">-</span></p> <p><strong>ADC read time:</strong> <span id="a_adc_read_time">-</span></p>
<p><strong>Queue errors:</strong> <span id="a_n_queue_errors">-</span></p> <p><strong>Queue errors:</strong> <span id="a_n_queue_errors">-</span></p>
</div> </div>
@@ -111,7 +110,6 @@
<div class="box-data"> <div class="box-data">
<p><strong>Timestamp:</strong> <span id="b_timestamp">-</span></p> <p><strong>Timestamp:</strong> <span id="b_timestamp">-</span></p>
<p><strong>Data Valid:</strong> <span id="b_datavalid">-</span></p> <p><strong>Data Valid:</strong> <span id="b_datavalid">-</span></p>
<p><strong>Generator voltage:</strong> <span id="b_volts_gen">-</span></p>
<p><strong>ADC read time:</strong> <span id="b_adc_read_time">-</span></p> <p><strong>ADC read time:</strong> <span id="b_adc_read_time">-</span></p>
<p><strong>Queue errors:</strong> <span id="b_n_queue_errors">-</span></p> <p><strong>Queue errors:</strong> <span id="b_n_queue_errors">-</span></p>
</div> </div>

View File

@@ -78,34 +78,31 @@ function connectWS() {
const boxA = data.box_a; const boxA = data.box_a;
document.getElementById("a_datavalid").textContent = boxA.datavalid ?? "-"; document.getElementById("a_datavalid").textContent = boxA.datavalid ?? "-";
document.getElementById("a_timestamp").textContent = boxA.timestamp ?? "-"; document.getElementById("a_timestamp").textContent = boxA.timestamp ?? "-";
document.getElementById("a_volts_gen").textContent = boxA.volts_gen ?? "-"; document.getElementById("a_eng_rpm").textContent = boxA.engRpm ?? "-";
document.getElementById("a_eng_rpm").textContent = boxA.eng_rpm ?? "-"; document.getElementById("a_adc_read_time").textContent = boxA.adcReadTime ?? "-";
document.getElementById("a_adc_read_time").textContent = boxA.adc_read_time ?? "-"; document.getElementById("a_n_queue_errors").textContent = boxA.nQueueErrors ?? "-";
document.getElementById("a_n_queue_errors").textContent = boxA.n_queue_errors ?? "-";
const coils12A = boxA.coils12 || {}; const coils12A = boxA.coils12 || {};
const coils34A = boxA.coils34 || {}; const coils34A = boxA.coils34 || {};
document.getElementById("a_coils12_spark_delay").textContent = coils12A.spark_delay ?? "-"; document.getElementById("a_coils12_spark_delay").textContent = coils12A.sparkDelay ?? "-";
document.getElementById("a_coils34_spark_delay").textContent = coils34A.spark_delay ?? "-"; document.getElementById("a_coils34_spark_delay").textContent = coils34A.sparkDelay ?? "-";
document.getElementById("a_coils12_spark_status").textContent = coils12A.spark_status ?? "-"; document.getElementById("a_coils12_spark_status").textContent = coils12A.sparkStatus ?? "-";
document.getElementById("a_coils34_spark_status").textContent = coils34A.spark_status ?? "-"; document.getElementById("a_coils34_spark_status").textContent = coils34A.sparkStatus ?? "-";
document.getElementById("a_coils12_sstart_status").textContent = coils12A.sstart_status ?? "-"; document.getElementById("a_coils12_sstart_status").textContent = coils12A.softStartStatus ?? "-";
document.getElementById("a_coils34_sstart_status").textContent = coils34A.sstart_status ?? "-"; document.getElementById("a_coils34_sstart_status").textContent = coils34A.softStartStatus ?? "-";
document.getElementById("a_coils12_peak_p_in").textContent = coils12A.peak_p_in ?? "-"; document.getElementById("a_coils12_peak_p_in").textContent = coils12A.peakPos ?? "-";
document.getElementById("a_coils34_peak_p_in").textContent = coils34A.peak_p_in ?? "-"; document.getElementById("a_coils34_peak_p_in").textContent = coils34A.peakPos ?? "-";
document.getElementById("a_coils12_peak_n_in").textContent = coils12A.peak_n_in ?? "-"; document.getElementById("a_coils12_peak_n_in").textContent = coils12A.peakNeg ?? "-";
document.getElementById("a_coils34_peak_n_in").textContent = coils34A.peak_n_in ?? "-"; document.getElementById("a_coils34_peak_n_in").textContent = coils34A.peakNeg ?? "-";
document.getElementById("a_coils12_peak_p_out").textContent = coils12A.peak_p_out ?? "-"; document.getElementById("a_coils12_peak_p_out").textContent = coils12A.trigLevelPos ?? "-";
document.getElementById("a_coils34_peak_p_out").textContent = coils34A.peak_p_out ?? "-"; document.getElementById("a_coils34_peak_p_out").textContent = coils34A.trigLevelPos ?? "-";
document.getElementById("a_coils12_peak_n_out").textContent = coils12A.peak_n_out ?? "-"; document.getElementById("a_coils12_peak_n_out").textContent = coils12A.trigLevelNeg ?? "-";
document.getElementById("a_coils34_peak_n_out").textContent = coils34A.peak_n_out ?? "-"; document.getElementById("a_coils34_peak_n_out").textContent = coils34A.trigLevelNeg ?? "-";
document.getElementById("a_coils12_level_spark").textContent = coils12A.level_spark ?? "-"; document.getElementById("a_coils12_n_events").textContent = coils12A.nEvents ?? "-";
document.getElementById("a_coils34_level_spark").textContent = coils34A.level_spark ?? "-"; document.getElementById("a_coils34_n_events").textContent = coils34A.nEvents ?? "-";
document.getElementById("a_coils12_n_events").textContent = coils12A.n_events ?? "-"; document.getElementById("a_coils12_n_missed_firing").textContent = coils12A.nMissedFiring ?? "-";
document.getElementById("a_coils34_n_events").textContent = coils34A.n_events ?? "-"; document.getElementById("a_coils34_n_missed_firing").textContent = coils34A.nMissedFiring ?? "-";
document.getElementById("a_coils12_n_missed_firing").textContent = coils12A.n_missed_firing ?? "-";
document.getElementById("a_coils34_n_missed_firing").textContent = coils34A.n_missed_firing ?? "-";
} }
// Update Box_B // Update Box_B
@@ -113,34 +110,31 @@ function connectWS() {
const boxB = data.box_b; const boxB = data.box_b;
document.getElementById("b_datavalid").textContent = boxB.datavalid ?? "-"; document.getElementById("b_datavalid").textContent = boxB.datavalid ?? "-";
document.getElementById("b_timestamp").textContent = boxB.timestamp ?? "-"; document.getElementById("b_timestamp").textContent = boxB.timestamp ?? "-";
document.getElementById("b_volts_gen").textContent = boxB.volts_gen ?? "-"; document.getElementById("b_eng_rpm").textContent = boxB.engRpm ?? "-";
document.getElementById("b_eng_rpm").textContent = boxB.eng_rpm ?? "-"; document.getElementById("b_adc_read_time").textContent = boxB.adcReadTime ?? "-";
document.getElementById("b_adc_read_time").textContent = boxB.adc_read_time ?? "-"; document.getElementById("b_n_queue_errors").textContent = boxB.nQueueErrors ?? "-";
document.getElementById("b_n_queue_errors").textContent = boxB.n_queue_errors ?? "-";
const coils12B = boxB.coils12 || {}; const coils12B = boxB.coils12 || {};
const coils34B = boxB.coils34 || {}; const coils34B = boxB.coils34 || {};
document.getElementById("b_coils12_spark_delay").textContent = coils12B.spark_delay ?? "-"; document.getElementById("a_coils12_spark_delay").textContent = coils12B.sparkDelay ?? "-";
document.getElementById("b_coils34_spark_delay").textContent = coils34B.spark_delay ?? "-"; document.getElementById("a_coils34_spark_delay").textContent = coils34B.sparkDelay ?? "-";
document.getElementById("b_coils12_spark_status").textContent = coils12B.spark_status ?? "-"; document.getElementById("a_coils12_spark_status").textContent = coils12B.sparkStatus ?? "-";
document.getElementById("b_coils34_spark_status").textContent = coils34B.spark_status ?? "-"; document.getElementById("a_coils34_spark_status").textContent = coils34B.sparkStatus ?? "-";
document.getElementById("b_coils12_sstart_status").textContent = coils12B.sstart_status ?? "-"; document.getElementById("a_coils12_sstart_status").textContent = coils12B.softStartStatus ?? "-";
document.getElementById("b_coils34_sstart_status").textContent = coils34B.sstart_status ?? "-"; document.getElementById("a_coils34_sstart_status").textContent = coils34B.softStartStatus ?? "-";
document.getElementById("b_coils12_peak_p_in").textContent = coils12B.peak_p_in ?? "-"; document.getElementById("a_coils12_peak_p_in").textContent = coils12B.peakPos ?? "-";
document.getElementById("b_coils34_peak_p_in").textContent = coils34B.peak_p_in ?? "-"; document.getElementById("a_coils34_peak_p_in").textContent = coils34B.peakPos ?? "-";
document.getElementById("b_coils12_peak_n_in").textContent = coils12B.peak_n_in ?? "-"; document.getElementById("a_coils12_peak_n_in").textContent = coils12B.peakNeg ?? "-";
document.getElementById("b_coils34_peak_n_in").textContent = coils34B.peak_n_in ?? "-"; document.getElementById("a_coils34_peak_n_in").textContent = coils34B.peakNeg ?? "-";
document.getElementById("b_coils12_peak_p_out").textContent = coils12B.peak_p_out ?? "-"; document.getElementById("a_coils12_peak_p_out").textContent = coils12B.trigLevelPos ?? "-";
document.getElementById("b_coils34_peak_p_out").textContent = coils34B.peak_p_out ?? "-"; document.getElementById("a_coils34_peak_p_out").textContent = coils34B.trigLevelPos ?? "-";
document.getElementById("b_coils12_peak_n_out").textContent = coils12B.peak_n_out ?? "-"; document.getElementById("a_coils12_peak_n_out").textContent = coils12B.trigLevelNeg ?? "-";
document.getElementById("b_coils34_peak_n_out").textContent = coils34B.peak_n_out ?? "-"; document.getElementById("a_coils34_peak_n_out").textContent = coils34B.trigLevelNeg ?? "-";
document.getElementById("b_coils12_level_spark").textContent = coils12B.level_spark ?? "-"; document.getElementById("a_coils12_n_events").textContent = coils12B.nEvents ?? "-";
document.getElementById("b_coils34_level_spark").textContent = coils34B.level_spark ?? "-"; document.getElementById("a_coils34_n_events").textContent = coils34B.nEvents ?? "-";
document.getElementById("b_coils12_n_events").textContent = coils12B.n_events ?? "-"; document.getElementById("a_coils12_n_missed_firing").textContent = coils12B.nMissedFiring ?? "-";
document.getElementById("b_coils34_n_events").textContent = coils34B.n_events ?? "-"; document.getElementById("a_coils34_n_missed_firing").textContent = coils34B.nMissedFiring ?? "-";
document.getElementById("b_coils12_n_missed_firing").textContent = coils12B.n_missed_firing ?? "-";
document.getElementById("b_coils34_n_missed_firing").textContent = coils34B.n_missed_firing ?? "-";
} }
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@@ -11,34 +11,71 @@
RadoMmm for suggesting an improvement on the ADC-to-Volts conversion RadoMmm for suggesting an improvement on the ADC-to-Volts conversion
*/ */
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
#include "Arduino.h" #include "Arduino.h"
#include "ADS1256.h" #include "ADS1256.h"
#include "SPI.h" #include "SPI.h"
#include <DebugLog.h>
#define convertSigned24BitToLong(value) ((value) & (1l << 23) ? (value) - 0x1000000 : value) #define convertSigned24BitToLong(value) ((value) & (1l << 23) ? (value) - 0x1000000 : value)
void IRAM_ATTR drdyCallback(void *arg)
{
auto cls = (ADS1256 *)arg;
if (!arg)
return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (digitalRead(cls->getDRDYpin())) // impose wait on low
{
xSemaphoreTakeFromISR(cls->getDRDYsemaphoreLow(), &xHigherPriorityTaskWoken);
xSemaphoreGiveFromISR(cls->getDRDYsemaphoreHigh(), &xHigherPriorityTaskWoken);
}
else // impose wait on high
{
xSemaphoreTakeFromISR(cls->getDRDYsemaphoreHigh(), &xHigherPriorityTaskWoken);
xSemaphoreGiveFromISR(cls->getDRDYsemaphoreLow(), &xHigherPriorityTaskWoken);
}
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}
// Constructor // Constructor
ADS1256::ADS1256(const int8_t DRDY_pin, const int8_t RESET_pin, const int8_t SYNC_pin, const int8_t CS_pin, float VREF, SPIClass *spi) : _spi(spi), ADS1256::ADS1256(const int8_t DRDY_pin, const int8_t RESET_pin, const int8_t SYNC_pin, const int8_t CS_pin, float VREF, SPIClass *spi) : _spi(spi),
_DRDY_pin(DRDY_pin), _RESET_pin(RESET_pin), _SYNC_pin(SYNC_pin), _CS_pin(CS_pin), _VREF(VREF), _PGA(0) m_DRDY_pin(DRDY_pin), m_RESET_pin(RESET_pin), m_SYNC_pin(SYNC_pin), m_CS_pin(CS_pin), m_VREF(VREF), m_PGA(0)
{ {
pinMode(_DRDY_pin, INPUT); pinMode(m_DRDY_pin, INPUT);
if (RESET_pin != PIN_UNUSED) if (RESET_pin != PIN_UNUSED)
{ {
pinMode(_RESET_pin, OUTPUT); pinMode(m_RESET_pin, OUTPUT);
} }
if (SYNC_pin != PIN_UNUSED) if (SYNC_pin != PIN_UNUSED)
{ {
pinMode(_SYNC_pin, OUTPUT); pinMode(m_SYNC_pin, OUTPUT);
} }
if (CS_pin != PIN_UNUSED) if (CS_pin != PIN_UNUSED)
{ {
pinMode(_CS_pin, OUTPUT); pinMode(m_CS_pin, OUTPUT);
} }
updateConversionParameter(); updateConversionParameter();
m_drdyHigh = xSemaphoreCreateBinary();
m_drdyLow = xSemaphoreCreateBinary();
if (!m_drdyHigh || !m_drdyLow)
{
LOG_ERROR("ADC Unable to create interrupt semaphores");
return;
}
xSemaphoreGive(m_drdyHigh);
xSemaphoreGive(m_drdyLow);
attachInterruptArg(DRDY_pin, drdyCallback, (void *)this, CHANGE);
disableInterrupt(DRDY_pin);
} }
// Initialization // Initialization
@@ -48,97 +85,100 @@ void ADS1256::InitializeADC()
CS_LOW(); CS_LOW();
// We do a manual chip reset on the ADS1256 - Datasheet Page 27/ RESET // We do a manual chip reset on the ADS1256 - Datasheet Page 27/ RESET
if(_RESET_pin != PIN_UNUSED) if (m_RESET_pin != PIN_UNUSED)
{ {
digitalWrite(_RESET_pin, LOW); digitalWrite(m_RESET_pin, LOW);
delay(200); delayMicroseconds(500);
digitalWrite(_RESET_pin, HIGH); //RESET is set to high digitalWrite(m_RESET_pin, HIGH); // RESET is set to high
delay(1000); delay(1000);
} }
// Sync pin is also treated if it is defined // Sync pin is also treated if it is defined
if(_SYNC_pin != PIN_UNUSED) if (m_SYNC_pin != PIN_UNUSED)
{ {
digitalWrite(_SYNC_pin, HIGH); //RESET is set to high digitalWrite(m_SYNC_pin, HIGH); // RESET is set to high
} }
#ifndef ADS1256_SPI_ALREADY_STARTED //Guard macro to allow external initialization of the SPI
_spi->begin();
#endif
// Applying arbitrary default values to speed up the starting procedure if the user just want to get quick readouts // Applying arbitrary default values to speed up the starting procedure if the user just want to get quick readouts
// We both pass values to the variables and then send those values to the corresponding registers // We both pass values to the variables and then send those values to the corresponding registers
delay(200); delayMicroseconds(500);
_STATUS = 0b00110110; //BUFEN and ACAL enabled, Order is MSB, rest is read only m_STATUS = 0b00110110; // BUFEN and ACAL enabled, Order is MSB, rest is read only
writeRegister(STATUS_REG, _STATUS); writeRegister(STATUS_REG, m_STATUS);
delay(200); delayMicroseconds(500);
_MUX = 0b00000001; //MUX AIN0+AIN1 m_MUX = DIFF_0_1; // MUX AIN0+AIN1
writeRegister(MUX_REG, _MUX); writeRegister(MUX_REG, m_MUX);
delay(200); delayMicroseconds(500);
_ADCON = 0b00000000; //ADCON - CLK: OFF, SDCS: OFF, PGA = 0 (+/- 5 V) m_ADCON = WAKEUP; // ADCON - CLK: OFF, SDCS: OFF, PGA = 0 (+/- 5 V)
writeRegister(ADCON_REG, _ADCON); writeRegister(ADCON_REG, m_ADCON);
delay(200); delayMicroseconds(500);
updateConversionParameter(); updateConversionParameter();
_DRATE = 0b10000010; //100SPS m_DRATE = DRATE_100SPS; // 100SPS
writeRegister(DRATE_REG, _DRATE); writeRegister(DRATE_REG, m_DRATE);
delay(200); delayMicroseconds(500);
sendDirectCommand(0b11110000); //Offset and self-gain calibration sendDirectCommand(SELFCAL); // Offset and self-gain calibration
delay(200); delayMicroseconds(500);
_isAcquisitionRunning = false; //MCU will be waiting to start a continuous acquisition m_isAcquisitionRunning = false; // MCU will be waiting to start a continuous acquisition
} }
void ADS1256::waitForLowDRDY() void ADS1256::waitForLowDRDY()
{ {
while (digitalRead(_DRDY_pin) == HIGH) {} if (!m_isAcquisitionRunning)
while (digitalRead(m_DRDY_pin) == HIGH)
; // wait in loop only for single shot modes
xSemaphoreTake(m_drdyLow, pdMS_TO_TICKS(10));
xSemaphoreGive(m_drdyLow);
} }
void ADS1256::waitForHighDRDY() void ADS1256::waitForHighDRDY()
{ {
#if F_CPU >= 48000000 //Fast MCUs need this protection to wait until DRDY goes high after a conversion if (!m_isAcquisitionRunning)
while (digitalRead(_DRDY_pin) == LOW) {} while (digitalRead(m_DRDY_pin) == LOW)
#endif ; // wait in loop only for single shot modes
xSemaphoreTake(m_drdyHigh, pdMS_TO_TICKS(10));
xSemaphoreGive(m_drdyHigh);
} }
void ADS1256::stopConversion() // Sending SDATAC to stop the continuous conversion void ADS1256::stopConversion() // Sending SDATAC to stop the continuous conversion
{ {
waitForLowDRDY(); // SDATAC should be called after DRDY goes LOW (p35. Figure 33) waitForLowDRDY(); // SDATAC should be called after DRDY goes LOW (p35. Figure 33)
_spi->transfer(0b00001111); //Send SDATAC to the ADC _spi->transfer(SDATAC); // Send SDATAC to the ADC
CS_HIGH(); // We finished the command sequence, so we switch it back to HIGH CS_HIGH(); // We finished the command sequence, so we switch it back to HIGH
_spi->endTransaction(); _spi->endTransaction();
_isAcquisitionRunning = false; //Reset to false, so the MCU will be able to start a new conversion m_isAcquisitionRunning = false; // Reset to false, so the MCU will be able to start a new conversion
disableDRDYinterrupt();
} }
void ADS1256::setDRATE(uint8_t drate) // Setting DRATE (sampling frequency) void ADS1256::setDRATE(uint8_t drate) // Setting DRATE (sampling frequency)
{ {
writeRegister(DRATE_REG, drate); writeRegister(DRATE_REG, drate);
_DRATE = drate; m_DRATE = drate;
delayMicroseconds(500); delayMicroseconds(500);
} }
void ADS1256::setMUX(uint8_t mux) // Setting MUX (input channel) void ADS1256::setMUX(uint8_t mux) // Setting MUX (input channel)
{ {
writeRegister(MUX_REG, mux); writeRegister(MUX_REG, mux);
_MUX = mux; m_MUX = mux;
//delayMicroseconds(500); delayMicroseconds(500);
} }
void ADS1256::setPGA(uint8_t pga) // Setting PGA (input voltage range) void ADS1256::setPGA(uint8_t pga) // Setting PGA (input voltage range)
{ {
_PGA = pga; m_PGA = pga;
_ADCON = readRegister(ADCON_REG); //Read the most recent value of the register m_ADCON = readRegister(ADCON_REG); // Read the most recent value of the register
_ADCON = (_ADCON & 0b11111000) | (_PGA & 0b00000111); // Clearing and then setting bits 2-0 based on pga m_ADCON = (m_ADCON & 0b11111000) | (m_PGA & 0b00000111); // Clearing and then setting bits 2-0 based on pga
writeRegister(ADCON_REG, _ADCON); writeRegister(ADCON_REG, m_ADCON);
delayMicroseconds(1000); //Delay to allow the PGA to settle after changing its value delayMicroseconds(500);
updateConversionParameter(); // Update the multiplier according top the new PGA value updateConversionParameter(); // Update the multiplier according top the new PGA value
} }
@@ -153,96 +193,99 @@ uint8_t ADS1256::getPGA() //Reading PGA from the ADCON register
void ADS1256::setCLKOUT(uint8_t clkout) // Setting CLKOUT void ADS1256::setCLKOUT(uint8_t clkout) // Setting CLKOUT
{ {
_ADCON = readRegister(ADCON_REG); //Read the most recent value of the register m_ADCON = readRegister(ADCON_REG); // Read the most recent value of the register
// Values: 0, 1, 2, 3 // Values: 0, 1, 2, 3
if (clkout == 0) if (clkout == 0)
{ {
// 00 // 00
bitWrite(_ADCON, 6, 0); bitWrite(m_ADCON, 6, 0);
bitWrite(_ADCON, 5, 0); bitWrite(m_ADCON, 5, 0);
} }
else if (clkout == 1) else if (clkout == 1)
{ {
// 01 (default) // 01 (default)
bitWrite(_ADCON, 6, 0); bitWrite(m_ADCON, 6, 0);
bitWrite(_ADCON, 5, 1); bitWrite(m_ADCON, 5, 1);
} }
else if (clkout == 2) else if (clkout == 2)
{ {
// 10 // 10
bitWrite(_ADCON, 6, 1); bitWrite(m_ADCON, 6, 1);
bitWrite(_ADCON, 5, 0); bitWrite(m_ADCON, 5, 0);
} }
else if (clkout == 3) else if (clkout == 3)
{ {
// 11 // 11
bitWrite(_ADCON, 6, 1); bitWrite(m_ADCON, 6, 1);
bitWrite(_ADCON, 5, 1); bitWrite(m_ADCON, 5, 1);
}
else
{
} }
else{}
writeRegister(ADCON_REG, _ADCON); writeRegister(ADCON_REG, m_ADCON);
delay(100);
} }
void ADS1256::setSDCS(uint8_t sdcs) // Setting SDCS void ADS1256::setSDCS(uint8_t sdcs) // Setting SDCS
{ {
_ADCON = readRegister(ADCON_REG); //Read the most recent value of the register m_ADCON = readRegister(ADCON_REG); // Read the most recent value of the register
// Values: 0, 1, 2, 3 // Values: 0, 1, 2, 3
if (sdcs == 0) if (sdcs == 0)
{ {
// 00 (default) // 00 (default)
bitWrite(_ADCON, 4, 0); bitWrite(m_ADCON, 4, 0);
bitWrite(_ADCON, 3, 0); bitWrite(m_ADCON, 3, 0);
} }
else if (sdcs == 1) else if (sdcs == 1)
{ {
// 01 // 01
bitWrite(_ADCON, 4, 0); bitWrite(m_ADCON, 4, 0);
bitWrite(_ADCON, 3, 1); bitWrite(m_ADCON, 3, 1);
} }
else if (sdcs == 2) else if (sdcs == 2)
{ {
// 10 // 10
bitWrite(_ADCON, 4, 1); bitWrite(m_ADCON, 4, 1);
bitWrite(_ADCON, 3, 0); bitWrite(m_ADCON, 3, 0);
} }
else if (sdcs == 3) else if (sdcs == 3)
{ {
// 11 // 11
bitWrite(_ADCON, 4, 1); bitWrite(m_ADCON, 4, 1);
bitWrite(_ADCON, 3, 1); bitWrite(m_ADCON, 3, 1);
}
else
{
} }
else{}
writeRegister(ADCON_REG, _ADCON); writeRegister(ADCON_REG, m_ADCON);
delay(100);
} }
void ADS1256::setByteOrder(uint8_t byteOrder) // Setting byte order (MSB/LSB) void ADS1256::setByteOrder(uint8_t byteOrder) // Setting byte order (MSB/LSB)
{ {
_STATUS = readRegister(STATUS_REG); //Read the most recent value of the register m_STATUS = readRegister(STATUS_REG); // Read the most recent value of the register
if (byteOrder == 0) if (byteOrder == 0)
{ {
// Byte order is MSB (default) // Byte order is MSB (default)
bitWrite(_STATUS, 3, 0); bitWrite(m_STATUS, 3, 0);
// Set value of _STATUS at the third bit to 0 // Set value of _STATUS at the third bit to 0
} }
else if (byteOrder == 1) else if (byteOrder == 1)
{ {
// Byte order is LSB // Byte order is LSB
bitWrite(_STATUS, 3, 1); bitWrite(m_STATUS, 3, 1);
// Set value of _STATUS at the third bit to 1 // Set value of _STATUS at the third bit to 1
} }
else{} else
{
}
writeRegister(STATUS_REG, _STATUS); writeRegister(STATUS_REG, m_STATUS);
delay(100);
} }
uint8_t ADS1256::getByteOrder() // Getting byte order (MSB/LSB) uint8_t ADS1256::getByteOrder() // Getting byte order (MSB/LSB)
@@ -254,24 +297,25 @@ uint8_t ADS1256::getByteOrder() //Getting byte order (MSB/LSB)
void ADS1256::setAutoCal(uint8_t acal) // Setting ACAL (Automatic SYSCAL) void ADS1256::setAutoCal(uint8_t acal) // Setting ACAL (Automatic SYSCAL)
{ {
_STATUS = readRegister(STATUS_REG); //Read the most recent value of the register m_STATUS = readRegister(STATUS_REG); // Read the most recent value of the register
if (acal == 0) if (acal == 0)
{ {
// Auto-calibration is disabled (default) // Auto-calibration is disabled (default)
bitWrite(_STATUS, 2, 0); bitWrite(m_STATUS, 2, 0);
//_STATUS |= B00000000; //_STATUS |= B00000000;
} }
else if (acal == 1) else if (acal == 1)
{ {
// Auto-calibration is enabled // Auto-calibration is enabled
bitWrite(_STATUS, 2, 1); bitWrite(m_STATUS, 2, 1);
//_STATUS |= B00000100; //_STATUS |= B00000100;
} }
else{} else
{
}
writeRegister(STATUS_REG, _STATUS); writeRegister(STATUS_REG, m_STATUS);
delay(100);
} }
uint8_t ADS1256::getAutoCal() // Getting ACAL (Automatic SYSCAL) uint8_t ADS1256::getAutoCal() // Getting ACAL (Automatic SYSCAL)
@@ -283,24 +327,25 @@ uint8_t ADS1256::getAutoCal() //Getting ACAL (Automatic SYSCAL)
void ADS1256::setBuffer(uint8_t bufen) // Setting input buffer (Input impedance) void ADS1256::setBuffer(uint8_t bufen) // Setting input buffer (Input impedance)
{ {
_STATUS = readRegister(STATUS_REG); //Read the most recent value of the register m_STATUS = readRegister(STATUS_REG); // Read the most recent value of the register
if (bufen == 0) if (bufen == 0)
{ {
// Analog input buffer is disabled (default) // Analog input buffer is disabled (default)
//_STATUS |= B00000000; //_STATUS |= B00000000;
bitWrite(_STATUS, 1, 0); bitWrite(m_STATUS, 1, 0);
} }
else if (bufen == 1) else if (bufen == 1)
{ {
// Analog input buffer is enabled (recommended) // Analog input buffer is enabled (recommended)
//_STATUS |= B00000010; //_STATUS |= B00000010;
bitWrite(_STATUS, 1, 1); bitWrite(m_STATUS, 1, 1);
}
else
{
} }
else{}
writeRegister(STATUS_REG, _STATUS); writeRegister(STATUS_REG, m_STATUS);
delay(100);
} }
uint8_t ADS1256::getBuffer() // Getting input buffer (Input impedance) uint8_t ADS1256::getBuffer() // Getting input buffer (Input impedance)
@@ -312,7 +357,7 @@ uint8_t ADS1256::getBuffer() //Getting input buffer (Input impedance)
void ADS1256::setGPIO(uint8_t dir0, uint8_t dir1, uint8_t dir2, uint8_t dir3) // Setting GPIO void ADS1256::setGPIO(uint8_t dir0, uint8_t dir1, uint8_t dir2, uint8_t dir3) // Setting GPIO
{ {
_GPIO = readRegister(IO_REG); //Read the most recent value of the register m_GPIO = readRegister(IO_REG); // Read the most recent value of the register
// Default: 11100000 - DEC: 224 - Ref: p32 I/O section // Default: 11100000 - DEC: 224 - Ref: p32 I/O section
// Sets D3-D0 as input or output // Sets D3-D0 as input or output
@@ -327,7 +372,7 @@ void ADS1256::setGPIO(uint8_t dir0, uint8_t dir1, uint8_t dir2, uint8_t dir3) //
{ {
GPIO_bit7 = 0; // D3 is output GPIO_bit7 = 0; // D3 is output
} }
bitWrite(_GPIO, 7, GPIO_bit7); bitWrite(m_GPIO, 7, GPIO_bit7);
//----------------------------------------------------- //-----------------------------------------------------
// Bit6: DIR2 // Bit6: DIR2
if (dir2 == 1) if (dir2 == 1)
@@ -338,7 +383,7 @@ void ADS1256::setGPIO(uint8_t dir0, uint8_t dir1, uint8_t dir2, uint8_t dir3) //
{ {
GPIO_bit6 = 0; // D2 is output GPIO_bit6 = 0; // D2 is output
} }
bitWrite(_GPIO, 6, GPIO_bit6); bitWrite(m_GPIO, 6, GPIO_bit6);
//----------------------------------------------------- //-----------------------------------------------------
// Bit5: DIR1 // Bit5: DIR1
if (dir1 == 1) if (dir1 == 1)
@@ -349,7 +394,7 @@ void ADS1256::setGPIO(uint8_t dir0, uint8_t dir1, uint8_t dir2, uint8_t dir3) //
{ {
GPIO_bit5 = 0; // D1 is output GPIO_bit5 = 0; // D1 is output
} }
bitWrite(_GPIO, 5, GPIO_bit5); bitWrite(m_GPIO, 5, GPIO_bit5);
//----------------------------------------------------- //-----------------------------------------------------
// Bit4: DIR0 // Bit4: DIR0
if (dir0 == 1) if (dir0 == 1)
@@ -360,16 +405,15 @@ void ADS1256::setGPIO(uint8_t dir0, uint8_t dir1, uint8_t dir2, uint8_t dir3) //
{ {
GPIO_bit4 = 0; // D0 is output (default) GPIO_bit4 = 0; // D0 is output (default)
} }
bitWrite(_GPIO, 4, GPIO_bit4); bitWrite(m_GPIO, 4, GPIO_bit4);
//----------------------------------------------------- //-----------------------------------------------------
writeRegister(IO_REG, _GPIO); writeRegister(IO_REG, m_GPIO);
delay(100);
} }
void ADS1256::writeGPIO(uint8_t dir0value, uint8_t dir1value, uint8_t dir2value, uint8_t dir3value) // Writing GPIO void ADS1256::writeGPIO(uint8_t dir0value, uint8_t dir1value, uint8_t dir2value, uint8_t dir3value) // Writing GPIO
{ {
_GPIO = readRegister(IO_REG); m_GPIO = readRegister(IO_REG);
// Sets D3-D0 output values // Sets D3-D0 output values
// It is important that first one must use setGPIO, then writeGPIO // It is important that first one must use setGPIO, then writeGPIO
@@ -385,7 +429,7 @@ void ADS1256::writeGPIO(uint8_t dir0value, uint8_t dir1value, uint8_t dir2value,
{ {
GPIO_bit3 = 0; GPIO_bit3 = 0;
} }
bitWrite(_GPIO, 3, GPIO_bit3); bitWrite(m_GPIO, 3, GPIO_bit3);
//----------------------------------------------------- //-----------------------------------------------------
// Bit2: DIR2 // Bit2: DIR2
if (dir2value == 1) if (dir2value == 1)
@@ -396,7 +440,7 @@ void ADS1256::writeGPIO(uint8_t dir0value, uint8_t dir1value, uint8_t dir2value,
{ {
GPIO_bit2 = 0; GPIO_bit2 = 0;
} }
bitWrite(_GPIO, 2, GPIO_bit2); bitWrite(m_GPIO, 2, GPIO_bit2);
//----------------------------------------------------- //-----------------------------------------------------
// Bit1: DIR1 // Bit1: DIR1
if (dir1value == 1) if (dir1value == 1)
@@ -407,7 +451,7 @@ void ADS1256::writeGPIO(uint8_t dir0value, uint8_t dir1value, uint8_t dir2value,
{ {
GPIO_bit1 = 0; GPIO_bit1 = 0;
} }
bitWrite(_GPIO, 1, GPIO_bit1); bitWrite(m_GPIO, 1, GPIO_bit1);
//----------------------------------------------------- //-----------------------------------------------------
// Bit0: DIR0 // Bit0: DIR0
if (dir0value == 1) if (dir0value == 1)
@@ -418,26 +462,23 @@ void ADS1256::writeGPIO(uint8_t dir0value, uint8_t dir1value, uint8_t dir2value,
{ {
GPIO_bit0 = 0; GPIO_bit0 = 0;
} }
bitWrite(_GPIO, 0, GPIO_bit0); bitWrite(m_GPIO, 0, GPIO_bit0);
//----------------------------------------------------- //-----------------------------------------------------
writeRegister(IO_REG, _GPIO); writeRegister(IO_REG, m_GPIO);
delay(100);
} }
uint8_t ADS1256::readGPIO(uint8_t gpioPin) // Reading GPIO uint8_t ADS1256::readGPIO(uint8_t gpioPin) // Reading GPIO
{ {
uint8_t GPIO_bit3, GPIO_bit2, GPIO_bit1, GPIO_bit0, GPIO_return; uint8_t GPIO_bit3, GPIO_bit2, GPIO_bit1, GPIO_bit0, GPIO_return;
_GPIO = readRegister(IO_REG); //Read the GPIO register m_GPIO = readRegister(IO_REG); // Read the GPIO register
// Save each bit values in a variable // Save each bit values in a variable
GPIO_bit3 = bitRead(_GPIO, 3); GPIO_bit3 = bitRead(m_GPIO, 3);
GPIO_bit2 = bitRead(_GPIO, 2); GPIO_bit2 = bitRead(m_GPIO, 2);
GPIO_bit1 = bitRead(_GPIO, 1); GPIO_bit1 = bitRead(m_GPIO, 1);
GPIO_bit0 = bitRead(_GPIO, 0); GPIO_bit0 = bitRead(m_GPIO, 0);
delay(100);
switch (gpioPin) // Selecting which value should be returned switch (gpioPin) // Selecting which value should be returned
{ {
@@ -459,13 +500,12 @@ uint8_t ADS1256::readGPIO(uint8_t gpioPin) //Reading GPIO
} }
return GPIO_return; return GPIO_return;
} }
void ADS1256::sendDirectCommand(uint8_t directCommand) void ADS1256::sendDirectCommand(uint8_t directCommand)
{ {
// Direct commands can be found in the datasheet Page 34, Table 24. // Direct commands can be found in the datasheet Page 34, Table 24.
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); _spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1));
CS_LOW(); // REF: P34: "CS must stay low during the entire command sequence" CS_LOW(); // REF: P34: "CS must stay low during the entire command sequence"
delayMicroseconds(5); delayMicroseconds(5);
@@ -476,27 +516,20 @@ void ADS1256::sendDirectCommand(uint8_t directCommand)
_spi->endTransaction(); _spi->endTransaction();
} }
float ADS1256::convertToVoltage(int32_t rawData) // Converting the 24-bit data into a voltage value float ADS1256::convertToVoltage(int32_t rawData) // Converting the 24-bit data into a voltage value
{ {
return(conversionParameter * rawData); return (m_conversionParameter * rawData);
} }
void ADS1256::writeRegister(uint8_t registerAddress, uint8_t registerValueToWrite) void ADS1256::writeRegister(uint8_t registerAddress, uint8_t registerValueToWrite)
{ {
waitForLowDRDY(); waitForLowDRDY();
_spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1)); // SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0, clock phase: 1.
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
//SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0, clock phase: 1.
CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24] CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24]
delayMicroseconds(5); // see t6 in the datasheet delayMicroseconds(5); // see t6 in the datasheet
_spi->transfer(WREG | registerAddress); // 0x50 = 01010000 = WREG
_spi->transfer(0x50 | registerAddress); // 0x50 = 01010000 = WREG
_spi->transfer(0x00); // 2nd (empty) command byte _spi->transfer(0x00); // 2nd (empty) command byte
_spi->transfer(registerValueToWrite); // pass the value to the register _spi->transfer(registerValueToWrite); // pass the value to the register
CS_HIGH(); CS_HIGH();
@@ -506,19 +539,13 @@ void ADS1256::writeRegister(uint8_t registerAddress, uint8_t registerValueToWrit
long ADS1256::readRegister(uint8_t registerAddress) // Reading a register long ADS1256::readRegister(uint8_t registerAddress) // Reading a register
{ {
waitForLowDRDY(); waitForLowDRDY();
_spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1)); // SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0, clock phase: 1.
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1));
//SPI_MODE1 = output edge: rising, data capture: falling; clock polarity: 0, clock phase: 1.
CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24] CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24]
_spi->transfer(0x10 | registerAddress); //0x10 = 0001000 = RREG - OR together the two numbers (command + address) _spi->transfer(RREG | registerAddress); // 0x10 = 0001000 = RREG - OR together the two numbers (command + address)
_spi->transfer(0x00); // 2nd (empty) command byte _spi->transfer(0x00); // 2nd (empty) command byte
delayMicroseconds(5); // see t6 in the datasheet delayMicroseconds(5); // see t6 in the datasheet
uint8_t regValue = _spi->transfer(0x00); // read out the register value
uint8_t regValue = _spi->transfer(0xFF); //read out the register value
CS_HIGH(); CS_HIGH();
_spi->endTransaction(); _spi->endTransaction();
@@ -526,38 +553,38 @@ long ADS1256::readRegister(uint8_t registerAddress) //Reading a register
return regValue; return regValue;
} }
long ADS1256::readSingle() // Reading a single value ONCE using the RDATA command long ADS1256::readSingle() // Reading a single value ONCE using the RDATA command
{ {
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); _spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1));
CS_LOW(); // REF: P34: "CS must stay low during the entire command sequence" CS_LOW(); // REF: P34: "CS must stay low during the entire command sequence"
waitForLowDRDY(); waitForLowDRDY();
_spi->transfer(0b00000001); //Issue RDATA (0000 0001) command _spi->transfer(RDATA); // Issue RDATA (0000 0001) command
delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30.
_outputBuffer[0] = _spi->transfer(0); // MSB m_outputBuffer[0] = _spi->transfer(0); // MSB
_outputBuffer[1] = _spi->transfer(0); // Mid-byte m_outputBuffer[1] = _spi->transfer(0); // Mid-byte
_outputBuffer[2] = _spi->transfer(0); // LSB m_outputBuffer[2] = _spi->transfer(0); // LSB
// Shifting and combining the above three items into a single, 24-bit number // Shifting and combining the above three items into a single, 24-bit number
_outputValue = ((long)_outputBuffer[0]<<16) | ((long)_outputBuffer[1]<<8) | (_outputBuffer[2]); m_outputValue = ((long)m_outputBuffer[0] << 16) | ((long)m_outputBuffer[1] << 8) | (m_outputBuffer[2]);
_outputValue = convertSigned24BitToLong(_outputValue); m_outputValue = convertSigned24BitToLong(m_outputValue);
CS_HIGH(); // We finished the command sequence, so we set CS to HIGH CS_HIGH(); // We finished the command sequence, so we set CS to HIGH
_spi->endTransaction(); _spi->endTransaction();
return(_outputValue); return (m_outputValue);
} }
long ADS1256::readSingleContinuous() // Reads the recently selected input channel using RDATAC long ADS1256::readSingleContinuous() // Reads the recently selected input channel using RDATAC
{ {
if(_isAcquisitionRunning == false) if (m_isAcquisitionRunning == false)
{ {
_isAcquisitionRunning = true; enableDRDYinterrupt();
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); m_isAcquisitionRunning = true;
_spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1));
CS_LOW(); // REF: P34: "CS must stay low during the entire command sequence" CS_LOW(); // REF: P34: "CS must stay low during the entire command sequence"
waitForLowDRDY(); waitForLowDRDY();
_spi->transfer(0b00000011); //Issue RDATAC (0000 0011) _spi->transfer(RDATAC); // Issue RDATAC (0000 0011)
delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30.
} }
else else
@@ -565,42 +592,39 @@ long ADS1256::readSingleContinuous() //Reads the recently selected input channel
waitForLowDRDY(); waitForLowDRDY();
} }
_outputBuffer[0] = _spi->transfer(0); // MSB m_outputBuffer[0] = _spi->transfer(0); // MSB
_outputBuffer[1] = _spi->transfer(0); // Mid-byte m_outputBuffer[1] = _spi->transfer(0); // Mid-byte
_outputBuffer[2] = _spi->transfer(0); // LSB m_outputBuffer[2] = _spi->transfer(0); // LSB
_outputValue = ((long)_outputBuffer[0]<<16) | ((long)_outputBuffer[1]<<8) | (_outputBuffer[2]); m_outputValue = ((long)m_outputBuffer[0] << 16) | ((long)m_outputBuffer[1] << 8) | (m_outputBuffer[2]);
_outputValue = convertSigned24BitToLong(_outputValue); m_outputValue = convertSigned24BitToLong(m_outputValue);
waitForHighDRDY(); waitForHighDRDY();
return _outputValue; return m_outputValue;
} }
long ADS1256::cycleSingle() long ADS1256::cycleSingle()
{ {
if(_isAcquisitionRunning == false) if (m_isAcquisitionRunning == false)
{ {
_isAcquisitionRunning = true; enableDRDYinterrupt();
_cycle = 0; m_isAcquisitionRunning = true;
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); m_cycle = 0;
_spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1));
CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24] CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24]
_spi->transfer(0x50 | 1); // 0x50 = WREG //1 = MUX _spi->transfer(WREG | MUX_REG); // 0x50 = WREG //1 = MUX
_spi->transfer(0x00); _spi->transfer(0x00);
_spi->transfer(SING_0); // AIN0+AINCOM _spi->transfer(SING_0); // AIN0+AINCOM
CS_HIGH(); delayMicroseconds(250);
delay(50);
CS_LOW(); //CS must stay LOW during the entire sequence [Ref: P34, T24]
} }
else
{}
if(_cycle < 8) if (m_cycle < 8)
{ {
_outputValue = 0; m_outputValue = 0;
waitForLowDRDY(); waitForLowDRDY();
// Step 1. - Updating MUX // Step 1. - Updating MUX
switch (_cycle) switch (m_cycle)
{ {
// Channels are written manually // Channels are written manually
case 0: // Channel 2 case 0: // Channel 2
@@ -636,60 +660,55 @@ long ADS1256::cycleSingle()
break; break;
} }
// Step 2. // Step 2.
_spi->transfer(0b11111100); //SYNC _spi->transfer(SYNC); // SYNC
delayMicroseconds(4); // t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us delayMicroseconds(4); // t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us
_spi->transfer(0b11111111); //WAKEUP _spi->transfer(WAKEUP); // WAKEUP
// Step 3. // Step 3.
// Issue RDATA (0000 0001) command // Issue RDATA (0000 0001) command
_spi->transfer(0b00000001); _spi->transfer(RDATA);
delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30.
_outputBuffer[0] = _spi->transfer(0x0F); // MSB m_outputBuffer[0] = _spi->transfer(0); // MSB
_outputBuffer[1] = _spi->transfer(0x0F); // Mid-byte m_outputBuffer[1] = _spi->transfer(0); // Mid-byte
_outputBuffer[2] = _spi->transfer(0x0F); // LSB m_outputBuffer[2] = _spi->transfer(0); // LSB
_outputValue = ((long)_outputBuffer[0]<<16) | ((long)_outputBuffer[1]<<8) | (_outputBuffer[2]); m_outputValue = ((long)m_outputBuffer[0] << 16) | ((long)m_outputBuffer[1] << 8) | (m_outputBuffer[2]);
_outputValue = convertSigned24BitToLong(_outputValue); m_outputValue = convertSigned24BitToLong(m_outputValue);
_cycle++; //Increase cycle - This will move to the next MUX input channel m_cycle++; // Increase cycle - This will move to the next MUX input channel
if(_cycle == 8) if (m_cycle == 8)
{ {
_cycle = 0; //Reset to 0 - Restart conversion from the 1st input channel m_cycle = 0; // Reset to 0 - Restart conversion from the 1st input channel
} }
} }
return _outputValue; return m_outputValue;
} }
long ADS1256::cycleDifferential() long ADS1256::cycleDifferential()
{ {
if(_isAcquisitionRunning == false) if (m_isAcquisitionRunning == false)
{ {
_cycle = 0; enableDRDYinterrupt();
_isAcquisitionRunning = true; m_cycle = 0;
_spi->beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); m_isAcquisitionRunning = true;
_spi->beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE1));
//Set the AIN0+AIN1 as inputs manually
CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24] CS_LOW(); // CS must stay LOW during the entire sequence [Ref: P34, T24]
_spi->transfer(0x50 | 1); // 0x50 = WREG //1 = MUX _spi->transfer(WREG | MUX_REG); // 0x50 = WREG //1 = MUX
_spi->transfer(0x00); _spi->transfer(0x00);
_spi->transfer(DIFF_0_1); //AIN0+AIN1 _spi->transfer(DIFF_0_1); // Set the AIN0+AIN1 as inputs manually
CS_HIGH(); delayMicroseconds(250);
delay(50);
CS_LOW(); //CS must stay LOW during the entire sequence [Ref: P34, T24]
} }
else
{}
if(_cycle < 4) if (m_cycle < 4)
{ {
_outputValue = 0; m_outputValue = 0;
// DRDY has to go low // DRDY has to go low
waitForLowDRDY(); waitForLowDRDY();
// Step 1. - Updating MUX // Step 1. - Updating MUX
switch (_cycle) switch (m_cycle)
{ {
case 0: // Channel 2 case 0: // Channel 2
updateMUX(DIFF_2_3); // AIN2+AIN3 updateMUX(DIFF_2_3); // AIN2+AIN3
@@ -708,57 +727,89 @@ long ADS1256::cycleDifferential()
break; break;
} }
_spi->transfer(0b11111100); //SYNC _spi->transfer(SYNC); // SYNC
delayMicroseconds(4); // t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us delayMicroseconds(4); // t11 delay 24*tau = 3.125 us //delay should be larger, so we delay by 4 us
_spi->transfer(0b11111111); //WAKEUP _spi->transfer(WAKEUP); // WAKEUP
// Step 3. // Step 3.
_spi->transfer(0b00000001); //Issue RDATA (0000 0001) command _spi->transfer(RDATA); // Issue RDATA (0000 0001) command
delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30. delayMicroseconds(7); // Wait t6 time (~6.51 us) REF: P34, FIG:30.
_outputBuffer[0] = _spi->transfer(0); // MSB m_outputBuffer[0] = _spi->transfer(0); // MSB
_outputBuffer[1] = _spi->transfer(0); // Mid-byte m_outputBuffer[1] = _spi->transfer(0); // Mid-byte
_outputBuffer[2] = _spi->transfer(0); // LSB m_outputBuffer[2] = _spi->transfer(0); // LSB
_outputValue = ((long)_outputBuffer[0]<<16) | ((long)_outputBuffer[1]<<8) | (_outputBuffer[2]); m_outputValue = ((long)m_outputBuffer[0] << 16) | ((long)m_outputBuffer[1] << 8) | (m_outputBuffer[2]);
_outputValue = convertSigned24BitToLong(_outputValue); m_outputValue = convertSigned24BitToLong(m_outputValue);
_cycle++; m_cycle++;
if(_cycle == 4) if (m_cycle == 4)
{ {
_cycle = 0; m_cycle = 0;
// After the 4th cycle, we reset to zero so the next iteration reads the 1st MUX again // After the 4th cycle, we reset to zero so the next iteration reads the 1st MUX again
} }
} }
return _outputValue; return m_outputValue;
} }
void ADS1256::updateConversionParameter() void ADS1256::updateConversionParameter()
{ {
conversionParameter = ((2.0 * _VREF) / 8388608.0) / (pow(2, _PGA)); //Calculate the "bit to Volts" multiplier m_conversionParameter = ((2.0 * m_VREF) / 8388608.0) / (pow(2, m_PGA)); // Calculate the "bit to Volts" multiplier
// 8388608 = 2^{23} - 1, REF: p23, Table 16. // 8388608 = 2^{23} - 1, REF: p23, Table 16.
} }
void ADS1256::updateMUX(uint8_t muxValue) void ADS1256::updateMUX(uint8_t muxValue)
{ {
_spi->transfer(0x50 | MUX_REG); //Write to the MUX register (0x50 is the WREG command) _spi->transfer(WREG | MUX_REG); // Write to the MUX register (0x50 is the WREG command)
_spi->transfer(0x00); _spi->transfer(0x00);
_spi->transfer(muxValue); // Write the new MUX value _spi->transfer(muxValue); // Write the new MUX value
} }
inline void ADS1256::CS_LOW() inline void ADS1256::CS_LOW()
{ {
if (_CS_pin != PIN_UNUSED) //Sets CS LOW if it is not an unused pin if (m_CS_pin != PIN_UNUSED) // Sets CS LOW if it is not an unused pin
{ {
digitalWrite(_CS_pin, LOW); digitalWrite(m_CS_pin, LOW);
} }
} }
inline void ADS1256::CS_HIGH() inline void ADS1256::CS_HIGH()
{ {
if (_CS_pin != PIN_UNUSED) //Sets CS HIGH if it is not an unused pin if (m_CS_pin != PIN_UNUSED) // Sets CS HIGH if it is not an unused pin
{ {
digitalWrite(_CS_pin, HIGH); digitalWrite(m_CS_pin, HIGH);
} }
} }
// functions for callback
inline uint8_t ADS1256::getDRDYpin()
{
return m_DRDY_pin;
}
inline SemaphoreHandle_t ADS1256::getDRDYsemaphoreHigh()
{
return m_drdyHigh;
}
inline SemaphoreHandle_t ADS1256::getDRDYsemaphoreLow()
{
return m_drdyLow;
}
inline void ADS1256::enableDRDYinterrupt()
{
// release semaphores to avoid deadlock
xSemaphoreGive(m_drdyHigh);
xSemaphoreGive(m_drdyLow);
enableInterrupt(m_DRDY_pin);
}
inline void ADS1256::disableDRDYinterrupt()
{
// release semaphores to avoid deadlock
disableInterrupt(m_DRDY_pin);
xSemaphoreGive(m_drdyHigh);
xSemaphoreGive(m_drdyLow);
}

View File

@@ -10,10 +10,13 @@
Benjamin Pelletier for pointing out and fixing an issue around the handling of the DRDY signal Benjamin Pelletier for pointing out and fixing an issue around the handling of the DRDY signal
*/ */
#ifndef _ADS1256_h #pragma once
#define _ADS1256_h
#include <SPI.h> #include <SPI.h>
#include <Arduino.h>
// SPI Frequency
#define SPI_FREQ 1920000
// Differential inputs // Differential inputs
#define DIFF_0_1 0b00000001 // A0 + A1 as differential input #define DIFF_0_1 0b00000001 // A0 + A1 as differential input
@@ -96,7 +99,6 @@
#define RESET 0b11111110 #define RESET 0b11111110
//---------------------------------------------------------------- //----------------------------------------------------------------
class ADS1256 class ADS1256
{ {
public: public:
@@ -104,6 +106,11 @@ static constexpr int8_t PIN_UNUSED = -1;
// Constructor // Constructor
ADS1256(const int8_t DRDY_pin, const int8_t RESET_pin, const int8_t SYNC_pin, const int8_t CS_pin, float VREF, SPIClass *spi = &SPI); ADS1256(const int8_t DRDY_pin, const int8_t RESET_pin, const int8_t SYNC_pin, const int8_t CS_pin, float VREF, SPIClass *spi = &SPI);
~ADS1256()
{
vSemaphoreDelete(m_drdyHigh);
vSemaphoreDelete(m_drdyLow);
}
// Initializing function // Initializing function
void InitializeADC(); void InitializeADC();
@@ -151,8 +158,12 @@ static constexpr int8_t PIN_UNUSED = -1;
// Stop AD // Stop AD
void stopConversion(); void stopConversion();
private: // functions for callback, public to be accessed by static callback
inline uint8_t getDRDYpin();
inline SemaphoreHandle_t getDRDYsemaphoreHigh();
inline SemaphoreHandle_t getDRDYsemaphoreLow();
private:
SPIClass *_spi; // Pointer to an SPIClass object SPIClass *_spi; // Pointer to an SPIClass object
void waitForLowDRDY(); // Block until DRDY is low void waitForLowDRDY(); // Block until DRDY is low
@@ -160,30 +171,34 @@ void waitForHighDRDY(); // Block until DRDY is high
void updateMUX(uint8_t muxValue); void updateMUX(uint8_t muxValue);
inline void CS_LOW(); inline void CS_LOW();
inline void CS_HIGH(); inline void CS_HIGH();
inline void enableDRDYinterrupt();
inline void disableDRDYinterrupt();
void updateConversionParameter(); // Refresh the conversion parameter based on the PGA void updateConversionParameter(); // Refresh the conversion parameter based on the PGA
float _VREF = 0; //Value of the reference voltage float m_VREF = 0; // Value of the reference voltage
float conversionParameter = 0; //PGA-dependent multiplier float m_conversionParameter = 0; // PGA-dependent multiplier
// Pins // Pins
int8_t _DRDY_pin; //Pin assigned for DRDY int8_t m_DRDY_pin; // Pin assigned for DRDY
int8_t _RESET_pin; //Pin assigned for RESET int8_t m_RESET_pin; // Pin assigned for RESET
int8_t _SYNC_pin; //Pin assigned for SYNC int8_t m_SYNC_pin; // Pin assigned for SYNC
int8_t _CS_pin; //Pin assigned for CS int8_t m_CS_pin; // Pin assigned for CS
// Register values // Register values
byte _DRATE; //Value of the DRATE register uint8_t m_DRATE; // Value of the DRATE register
byte _ADCON; //Value of the ADCON register uint8_t m_ADCON; // Value of the ADCON register
byte _MUX; //Value of the MUX register uint8_t m_MUX; // Value of the MUX register
byte _PGA; //Value of the PGA (within ADCON) uint8_t m_PGA; // Value of the PGA (within ADCON)
byte _GPIO; //Value of the GPIO register uint8_t m_GPIO; // Value of the GPIO register
byte _STATUS; //Value of the status register uint8_t m_STATUS; // Value of the status register
byte _GPIOvalue; //GPIO value uint8_t m_GPIOvalue; // GPIO value
byte _ByteOrder; //Byte order uint8_t m_ByteOrder; // Byte order
byte _outputBuffer[3]; //3-byte (24-bit) buffer for the fast acquisition - Single-channel, continuous uint8_t m_outputBuffer[3]; // 3-byte (24-bit) buffer for the fast acquisition - Single-channel, continuous
long _outputValue; //Combined value of the _outputBuffer[3] int32_t m_outputValue; // Combined value of the m_outputBuffer[3]
bool _isAcquisitionRunning; //bool that keeps track of the acquisition (running or not) bool m_isAcquisitionRunning; // bool that keeps track of the acquisition (running or not)
uint8_t _cycle; //Tracks the cycles as the MUX is cycling through the input channels uint8_t m_cycle; // Tracks the cycles as the MUX is cycling through the input channels
SemaphoreHandle_t m_drdyHigh;
SemaphoreHandle_t m_drdyLow;
}; };
#endif

View File

@@ -4,6 +4,7 @@ RGBled::RGBled(const uint8_t pin) : m_led(pin)
{ {
pinMode(m_led, OUTPUT); pinMode(m_led, OUTPUT);
writeStatus(RGBled::ERROR); writeStatus(RGBled::ERROR);
m_brightness = 1.0f;
} }
RGBled::~RGBled() RGBled::~RGBled()
@@ -11,6 +12,11 @@ RGBled::~RGBled()
pinMode(m_led, INPUT); pinMode(m_led, INPUT);
} }
void RGBled::setBrightness(const float b)
{
m_brightness = b;
}
void RGBled::setStatus(const LedStatus s) void RGBled::setStatus(const LedStatus s)
{ {
if (m_status == s) if (m_status == s)
@@ -27,6 +33,6 @@ const RGBled::LedStatus RGBled::getSatus(void)
void RGBled::writeStatus(const RGBled::LedStatus s) void RGBled::writeStatus(const RGBled::LedStatus s)
{ {
RGBled::color_u u{.status = s}; const RGBled::color_u u{.status = s};
rgbLedWrite(m_led, u.color.r, u.color.g, u.color.b); rgbLedWrite(m_led, (uint8_t)(m_brightness*u.color.r), (uint8_t)(m_brightness*u.color.g), (uint8_t)(m_brightness*u.color.b));
} }

View File

@@ -50,6 +50,7 @@ public:
RGBled(const uint8_t pin = 48); RGBled(const uint8_t pin = 48);
~RGBled(); ~RGBled();
void setBrightness(const float b);
void setStatus(const LedStatus s); void setStatus(const LedStatus s);
const LedStatus getSatus(void); const LedStatus getSatus(void);
@@ -59,5 +60,6 @@ private:
private: private:
LedStatus m_status = LedStatus::IDLE; LedStatus m_status = LedStatus::IDLE;
std::mutex m_mutex; std::mutex m_mutex;
float m_brightness;
const uint8_t m_led; const uint8_t m_led;
}; };

View File

@@ -20,11 +20,10 @@ lib_deps =
hideakitai/PCA95x5@^0.1.3 hideakitai/PCA95x5@^0.1.3
me-no-dev/AsyncTCP@^3.3.2 me-no-dev/AsyncTCP@^3.3.2
me-no-dev/ESPAsyncWebServer@^3.6.0 me-no-dev/ESPAsyncWebServer@^3.6.0
adafruit/Adafruit NeoPixel@^1.15.4
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM1 upload_port = /dev/ttyACM0
upload_speed = 921600 upload_speed = 921600
monitor_port = /dev/ttyACM0 monitor_port = /dev/ttyACM1
monitor_speed = 921600 monitor_speed = 921600
build_type = release build_type = release
build_flags = build_flags =
@@ -36,7 +35,7 @@ build_flags =
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=64 -DCONFIG_ASYNC_TCP_QUEUE_SIZE=64
-DCONFIG_ASYNC_TCP_RUNNING_CORE=1 -DCONFIG_ASYNC_TCP_RUNNING_CORE=1
-DCONFIG_ASYNC_TCP_STACK_SIZE=4096 -DCONFIG_ASYNC_TCP_STACK_SIZE=4096
-fstack-protector-all -fstack-protector-strong
[env:esp32-s3-devkitc1-n16r8-debug] [env:esp32-s3-devkitc1-n16r8-debug]
board = ${env:esp32-s3-devkitc1-n16r8.board} board = ${env:esp32-s3-devkitc1-n16r8.board}
@@ -46,11 +45,10 @@ platform = ${env:esp32-s3-devkitc1-n16r8.platform}
framework = ${env:esp32-s3-devkitc1-n16r8.framework} framework = ${env:esp32-s3-devkitc1-n16r8.framework}
lib_deps = lib_deps =
${env:esp32-s3-devkitc1-n16r8.lib_deps} ${env:esp32-s3-devkitc1-n16r8.lib_deps}
adafruit/Adafruit NeoPixel@^1.15.4
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM1 upload_port = /dev/ttyACM0
upload_speed = 921600 upload_speed = 921600
monitor_port = /dev/ttyACM0 monitor_port = /dev/ttyACM1
monitor_speed = 921600 monitor_speed = 921600
debug_tool = esp-builtin debug_tool = esp-builtin
debug_speed = 15000 debug_speed = 15000
@@ -64,7 +62,6 @@ build_flags =
-DARDUINO_USB_MODE=0 -DARDUINO_USB_MODE=0
-DCONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 -DCONFIG_ASYNC_TCP_MAX_ACK_TIME=5000
-DCONFIG_ASYNC_TCP_PRIORITY=21 -DCONFIG_ASYNC_TCP_PRIORITY=21
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128 -DCONFIG_ASYNC_TCP_QUEUE_SIZE=64
-DCONFIG_ASYNC_TCP_RUNNING_CORE=1 -DCONFIG_ASYNC_TCP_RUNNING_CORE=1
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192 -DCONFIG_ASYNC_TCP_STACK_SIZE=4096
-fstack-protector-all

View File

@@ -1,8 +1,6 @@
#include "datasave.h" #include "datasave.h"
#include <math.h> #include <math.h>
LITTLEFSGuard::LITTLEFSGuard() LITTLEFSGuard::LITTLEFSGuard()
{ {
if (!LittleFS.begin(true, "/littlefs", 10, "littlefs")) if (!LittleFS.begin(true, "/littlefs", 10, "littlefs"))
@@ -50,29 +48,28 @@ void ignitionBoxStatusFiltered::update(const ignitionBoxStatus &new_status)
m_count++; m_count++;
// simple moving average calculation // simple moving average calculation
m_last.timestamp = new_status.timestamp; // keep timestamp of latest status m_last.timestamp = new_status.timestamp; // keep timestamp of latest status
m_last.coils12.nEvents = new_status.coils12.nEvents; // sum events instead of averaging
m_last.coils12.nMissedFiring = new_status.coils12.nMissedFiring; // sum missed firings instead of averaging
m_last.coils12.sparkStatus = new_status.coils12.sparkStatus; // take latest spark status
m_last.coils12.softStartStatus = new_status.coils12.softStartStatus; // take latest soft start status
filter(m_last.coils12.sparkDelay, new_status.coils12.sparkDelay, m_max_count); // incremental average calculation
filter(m_last.coils12.peakPos, new_status.coils12.peakPos, m_max_count); // incremental average calculation
filter(m_last.coils12.peakNeg, new_status.coils12.peakNeg, m_max_count); // incremental average calculation
filter(m_last.coils12.trigLevelPos, new_status.coils12.trigLevelPos, m_max_count); // incremental average calculation
filter(m_last.coils12.trigLevelNeg, new_status.coils12.trigLevelNeg, m_max_count); // incremental average calculation
m_last.coils12.n_events = new_status.coils12.n_events; // sum events instead of averaging m_last.coils34.nEvents = new_status.coils34.nEvents; // sum events instead of averaging
m_last.coils12.n_missed_firing = new_status.coils12.n_missed_firing; // sum missed firings instead of averaging m_last.coils34.nMissedFiring = new_status.coils34.nMissedFiring; // sum missed firings instead of averaging
m_last.coils12.spark_status = new_status.coils12.spark_status; // take latest spark status m_last.coils34.sparkStatus = new_status.coils34.sparkStatus; // take latest spark status
m_last.coils12.sstart_status = new_status.coils12.sstart_status; // take latest soft start status m_last.coils34.softStartStatus = new_status.coils34.softStartStatus; // take latest soft start status
filter(m_last.coils12.spark_delay, new_status.coils12.spark_delay, m_max_count); // incremental average calculation filter(m_last.coils34.sparkDelay, new_status.coils34.sparkDelay, m_max_count); // incremental average calculation
filter(m_last.coils12.peak_p_in, new_status.coils12.peak_p_in, m_max_count); // incremental average calculation filter(m_last.coils34.peakPos, new_status.coils34.peakPos, m_max_count); // incremental average calculation
filter(m_last.coils12.peak_n_in, new_status.coils12.peak_n_in, m_max_count); // incremental average calculation filter(m_last.coils34.peakNeg, new_status.coils34.peakNeg, m_max_count); // incremental average calculation
filter(m_last.coils12.peak_p_out, new_status.coils12.peak_p_out, m_max_count); // incremental average calculation filter(m_last.coils34.trigLevelPos, new_status.coils34.trigLevelPos, m_max_count); // incremental average calculation
filter(m_last.coils12.peak_n_out, new_status.coils12.peak_n_out, m_max_count); // incremental average calculation filter(m_last.coils34.trigLevelNeg, new_status.coils34.trigLevelNeg, m_max_count); // incremental average calculation
filter(m_last.engRpm, new_status.engRpm, m_max_count); // incremental average calculation // incremental average calculation
m_last.coils34.n_events = new_status.coils34.n_events; // sum events instead of averaging filter(m_last.adcReadTime, m_last.adcReadTime, m_max_count); // incremental average calculation
m_last.coils34.n_missed_firing = new_status.coils34.n_missed_firing; // sum missed firings instead of averaging m_last.nQueueErrors = new_status.nQueueErrors;
m_last.coils34.spark_status = new_status.coils34.spark_status; // take latest spark status
m_last.coils34.sstart_status = new_status.coils34.sstart_status; // take latest soft start status
filter(m_last.coils34.spark_delay, new_status.coils34.spark_delay, m_max_count); // incremental average calculation
filter(m_last.coils34.peak_p_in, new_status.coils34.peak_p_in, m_max_count); // incremental average calculation
filter(m_last.coils34.peak_n_in, new_status.coils34.peak_n_in, m_max_count); // incremental average calculation
filter(m_last.coils34.peak_p_out, new_status.coils34.peak_p_out, m_max_count); // incremental average calculation
filter(m_last.coils34.peak_n_out, new_status.coils34.peak_n_out, m_max_count); // incremental average calculation
filter(m_last.eng_rpm, new_status.eng_rpm, m_max_count); // incremental average calculation // incremental average calculation
filter(m_last.adc_read_time, m_last.adc_read_time, m_max_count); // incremental average calculation
m_last.n_queue_errors = new_status.n_queue_errors; // take last of queue errors since it's a cumulative count of errors in the queue, not an average value
if (m_count >= m_max_count) if (m_count >= m_max_count)
{ {
@@ -98,30 +95,29 @@ const ArduinoJson::JsonDocument ignitionBoxStatusFiltered::toJson() const
doc["timestamp"] = m_last.timestamp; doc["timestamp"] = m_last.timestamp;
doc["datavalid"] = m_data_valid ? "TRUE" : "FALSE"; doc["datavalid"] = m_data_valid ? "TRUE" : "FALSE";
doc["coils12"]["n_events"] = m_last.coils12.n_events; doc["coils12"]["nEvents"] = m_last.coils12.nEvents;
doc["coils12"]["n_missed_firing"] = m_last.coils12.n_missed_firing; doc["coils12"]["nMissedFiring"] = m_last.coils12.nMissedFiring;
doc["coils12"]["spark_delay"] = m_last.coils12.spark_delay; doc["coils12"]["sparkDelay"] = m_last.coils12.sparkDelay;
doc["coils12"]["spark_status"] = sparkStatusNames.at(m_last.coils12.spark_status); doc["coils12"]["sparkStatus"] = sparkStatusNames.at(m_last.coils12.sparkStatus);
doc["coils12"]["peak_p_in"] = m_last.coils12.peak_p_in; doc["coils12"]["peakPos"] = m_last.coils12.peakPos;
doc["coils12"]["peak_n_in"] = m_last.coils12.peak_n_in; doc["coils12"]["peakNeg"] = m_last.coils12.peakNeg;
doc["coils12"]["peak_p_out"] = m_last.coils12.peak_p_out; doc["coils12"]["trigLevelPos"] = m_last.coils12.trigLevelPos;
doc["coils12"]["peak_n_out"] = m_last.coils12.peak_n_out; doc["coils12"]["trigLevelNeg"] = m_last.coils12.trigLevelNeg;
doc["coils12"]["sstart_status"] = softStartStatusNames.at(m_last.coils12.sstart_status); doc["coils12"]["softStartStatus"] = softStartStatusNames.at(m_last.coils12.softStartStatus);
doc["coils34"]["n_events"] = m_last.coils34.n_events; doc["coils34"]["nEvents"] = m_last.coils34.nEvents;
doc["coils34"]["n_missed_firing"] = m_last.coils34.n_missed_firing; doc["coils34"]["nMissedFiring"] = m_last.coils34.nMissedFiring;
doc["coils34"]["spark_delay"] = m_last.coils34.spark_delay; doc["coils34"]["sparkDelay"] = m_last.coils34.sparkDelay;
doc["coils34"]["spark_status"] = sparkStatusNames.at(m_last.coils34.spark_status); doc["coils34"]["sparkStatus"] = sparkStatusNames.at(m_last.coils34.sparkStatus);
doc["coils34"]["peak_p_in"] = m_last.coils34.peak_p_in; doc["coils34"]["peakPos"] = m_last.coils34.peakPos;
doc["coils34"]["peak_n_in"] = m_last.coils34.peak_n_in; doc["coils34"]["peakNeg"] = m_last.coils34.peakNeg;
doc["coils34"]["peak_p_out"] = m_last.coils34.peak_p_out; doc["coils34"]["trigLevelPos"] = m_last.coils34.trigLevelPos;
doc["coils34"]["peak_n_out"] = m_last.coils34.peak_n_out; doc["coils34"]["trigLevelNeg"] = m_last.coils34.trigLevelNeg;
doc["coils34"]["sstart_status"] = softStartStatusNames.at(m_last.coils34.sstart_status); doc["coils34"]["softStartStatus"] = softStartStatusNames.at(m_last.coils34.softStartStatus);
doc["eng_rpm"] = m_last.eng_rpm; doc["engRpm"] = m_last.engRpm;
doc["adc_read_time"] = m_last.adc_read_time; doc["adcReadTime"] = m_last.adcReadTime;
doc["n_queue_errors"] = m_last.n_queue_errors; doc["nQueueErrors"] = m_last.nQueueErrors;
} }
return doc; return doc;
} }

View File

@@ -14,8 +14,6 @@
#include "isr.h" #include "isr.h"
#include "psvector.h" #include "psvector.h"
const uint32_t max_history = 256;
class LITTLEFSGuard class LITTLEFSGuard
{ {
public: public:

View File

@@ -16,7 +16,7 @@ static const uint32_t SPARK_FLAG_12 = (1 << 9);
static const uint32_t SPARK_FLAG_34 = (1 << 10); static const uint32_t SPARK_FLAG_34 = (1 << 10);
// Spark Status // Spark Status
enum sparkStatus enum sparkStatusEnum
{ {
SPARK_POS_OK, SPARK_POS_OK,
SPARK_NEG_OK, SPARK_NEG_OK,
@@ -31,7 +31,7 @@ enum sparkStatus
SPARK_SYNC_FAIL, SPARK_SYNC_FAIL,
}; };
static const std::map<const sparkStatus, const char *> sparkStatusNames = { static const std::map<const sparkStatusEnum, const char *> sparkStatusNames = {
{SPARK_POS_OK, "SPARK_POS_OK"}, {SPARK_POS_OK, "SPARK_POS_OK"},
{SPARK_NEG_OK, "SPARK_NEG_OK"}, {SPARK_NEG_OK, "SPARK_NEG_OK"},
{SPARK_POS_SKIP, "SPARK_POS_SKIP"}, {SPARK_POS_SKIP, "SPARK_POS_SKIP"},
@@ -45,14 +45,14 @@ static const std::map<const sparkStatus, const char *> sparkStatusNames = {
{SPARK_SYNC_FAIL, "SPARK_SYNC_FAIL"}, {SPARK_SYNC_FAIL, "SPARK_SYNC_FAIL"},
}; };
enum softStartStatus enum softStartStatusEnum
{ {
NORMAL, NORMAL,
SOFT_START, SOFT_START,
ERROR, ERROR,
}; };
const std::map<const softStartStatus, const char *> softStartStatusNames = { const std::map<const softStartStatusEnum, const char *> softStartStatusNames = {
{NORMAL, "NORMAL"}, {NORMAL, "NORMAL"},
{SOFT_START, "SOFT_START"}, {SOFT_START, "SOFT_START"},
{ERROR, "ERROR"}, {ERROR, "ERROR"},
@@ -60,18 +60,17 @@ const std::map<const softStartStatus, const char *> softStartStatusNames = {
struct coilsStatus struct coilsStatus
{ {
int64_t trig_time = 0; int64_t coilTime = 0;
int64_t spark_time = 0; int64_t sparkTime = 0;
int32_t spark_delay = 0; // in microseconds int32_t sparkDelay = 0; // in microseconds
sparkStatus spark_status = sparkStatus::SPARK_POS_OK; sparkStatusEnum sparkStatus = sparkStatusEnum::SPARK_POS_OK;
softStartStatus sstart_status = softStartStatus::NORMAL; softStartStatusEnum softStartStatus = softStartStatusEnum::NORMAL;
float peak_p_in = 0.0; float peakPos = 0.0;
float peak_n_in = 0.0; float peakNeg = 0.0;
float peak_p_out = 0.0; float trigLevelPos = 0.0;
float peak_n_out = 0.0; float trigLevelNeg = 0.0;
float level_spark = 0.0; uint32_t nEvents = 0;
uint32_t n_events = 0; uint32_t nMissedFiring = 0;
uint32_t n_missed_firing = 0;
}; };
// Task internal Status // Task internal Status
@@ -81,13 +80,12 @@ struct ignitionBoxStatus
// coils pairs for each ignition // coils pairs for each ignition
coilsStatus coils12; coilsStatus coils12;
coilsStatus coils34; coilsStatus coils34;
// voltage from generator
float volts_gen = 0.0;
// enine rpm // enine rpm
int32_t eng_rpm = 0; int32_t engRpm = 0;
// debug values // debug values
uint32_t n_queue_errors = 0; uint32_t nQueueErrors = 0;
int32_t adc_read_time = 0; int32_t adcReadTime = 0;
int32_t ioReadWriteTime = 0;
}; };

View File

@@ -9,7 +9,8 @@
// Device Libraries // Device Libraries
#include <ADS1256.h> #include <ADS1256.h>
#include <AD5292.h> #include <AD5292.h>
#include <PCA95x5.h> #include <extio.h>
#include <Wire.h>
// ADC Channel mapping // ADC Channel mapping
#define ADC_CH_PEAK_12P_IN SING_0 #define ADC_CH_PEAK_12P_IN SING_0
@@ -24,34 +25,31 @@
// Device Pointer structs for tasks // Device Pointer structs for tasks
struct Devices struct Devices
{ {
std::unique_ptr<SPIClass> m_spi_a = nullptr; // Busses
std::unique_ptr<SPIClass> m_spi_b = nullptr; TwoWire *m_i2c = NULL;
SPIClass *m_spi_a = NULL;
std::unique_ptr<AD5292> m_pot_a = nullptr; SPIClass *m_spi_b = NULL;
std::unique_ptr<AD5292> m_pot_b = nullptr;
std::unique_ptr<ADS1256> m_adc_a = nullptr;
std::unique_ptr<ADS1256> m_adc_b = nullptr;
std::unique_ptr<PCA9555> m_expander_a = nullptr;
std::unique_ptr<PCA9555> m_expander_b = nullptr;
std::unique_ptr<PCA9555> m_expander_inputs_ab = nullptr;
// Bus Mutextes
std::mutex m_spi_a_mutex; std::mutex m_spi_a_mutex;
std::mutex m_spi_b_mutex; std::mutex m_spi_b_mutex;
std::mutex m_i2c_mutex; std::mutex m_i2c_mutex;
// Device Pointers
AD5292 *m_pot_a = NULL;
AD5292 *m_pot_b = NULL;
ADS1256 *m_adc_a = NULL;
ADS1256 *m_adc_b = NULL;
ExternalIO *m_ext_io = NULL;
}; };
// Adc read channel wrapper to selet mux before reading // Adc read channel wrapper to selet mux before reading
inline float adcReadChannel(ADS1256 *adc, const uint8_t ch) inline float adcReadChannel(ADS1256 *adc, const uint8_t ch)
{ {
adc->setMUX(ch); adc->setMUX(ch);
// scarta 3 conversioni
for (int i = 0; i < 3; i++)
{
adc->readSingle(); adc->readSingle();
}
// ora lettura valida a 30kSPS → ~100 µs di settling // ora lettura valida a 30kSPS → ~100 µs di settling
return adc->convertToVoltage(adc->readSingle()); return adc->convertToVoltage(adc->readSingle());
} }

129
RotaxMonitor/src/extio.cpp Normal file
View File

@@ -0,0 +1,129 @@
#include <extio.h>
// Static interrupt callback
static void onExpanderInterrupt(void *arg)
{
auto cls = (ExternalIO *)(arg);
if (!cls) // invalid args
return;
cls->extReadInterrupt();
}
ExternalIO::ExternalIO(TwoWire &i2c, std::mutex &i2c_mutex, const uint8_t int_pin) : m_i2cMutex(i2c_mutex), m_i2c(i2c), m_intPin(int_pin)
{
std::lock_guard<std::mutex> lock(m_i2cMutex);
// Attach OUT expanders on BUS
m_outMap[EXPANDER_A_OUT_ADDR] = std::make_unique<PCA9555>();
m_outMap[EXPANDER_A_OUT_ADDR]->attach(m_i2c, EXPANDER_A_OUT_ADDR);
m_outMap[EXPANDER_B_OUT_ADDR] = std::make_unique<PCA9555>();
m_outMap[EXPANDER_B_OUT_ADDR]->attach(m_i2c, EXPANDER_B_OUT_ADDR);
for (auto &[a, e] : m_outMap)
{
e->direction(PCA95x5::Direction::OUT_ALL);
e->polarity(PCA95x5::Polarity::ORIGINAL_ALL);
};
// Attach IN Expanders on Bus
m_inMap[EXPANDER_A_IN_ADDR] = std::make_unique<PCA9555>();
m_inMap[EXPANDER_A_IN_ADDR]->attach(m_i2c, EXPANDER_A_IN_ADDR);
m_inMap[EXPANDER_B_IN_ADDR] = std::make_unique<PCA9555>();
m_inMap[EXPANDER_B_IN_ADDR]->attach(m_i2c, EXPANDER_B_IN_ADDR);
for (auto &[a, e] : m_inMap)
{
e->direction(PCA95x5::Direction::IN_ALL);
e->polarity(PCA95x5::Polarity::ORIGINAL_ALL);
m_lastInputState[a] = e->read(); /// initialize input state to collect interrupts
};
}
ExternalIO::~ExternalIO() {
}
void ExternalIO::extDigitalWrite(const uint32_t mappedPin, const bool val)
{
std::lock_guard<std::mutex> lock(m_i2cMutex);
const io_t pa = map2pin(mappedPin);
if (!m_outMap.contains(pa.addr))
{
LOG_ERROR("Undefined IO Expander addr: [", pa.addr, "]");
return;
}
auto &io = m_outMap.at(pa.addr);
if (!io->write(static_cast<PCA95x5::Port::Port>(pa.pin), val ? PCA95x5::Level::H : PCA95x5::Level::L))
{
LOG_ERROR("IO Expander [", pa.addr, "] Unable to WRITE Port [", pa.pin, "] to [", val ? "HIGH" : "LOW");
LOG_ERROR("IO Expander Error [", io->i2c_error(), "]");
}
}
const bool ExternalIO::extDigitalRead(const uint32_t mappedPin)
{
std::lock_guard<std::mutex> lock(m_i2cMutex);
const io_t pa = map2pin(mappedPin);
if (!m_inMap.contains(pa.addr))
{
LOG_ERROR("Undefined IO Expander addr: [", pa.addr, "]");
return false;
}
auto &io = m_inMap.at(pa.addr);
const bool rv = io->read(static_cast<PCA95x5::Port::Port>(pa.pin)) == PCA95x5::Level::H ? true : false; // read value
const uint8_t err = io->i2c_error();
if (err)
{
LOG_ERROR("IO Expander [", pa.addr, "] Unable to READ Port [", pa.pin, "]");
LOG_ERROR("IO Expander Error [", err, "]");
}
return rv;
}
void ExternalIO::extAttachInterrupt(ExtInterruptCb cb)
{
attachInterruptArg(EXPANDER_ALL_INTERRUPT, onExpanderInterrupt, (void *)(this), FALLING);
m_extInterruptCb = cb;
}
void ExternalIO::extDetachInterrupt()
{
detachInterrupt(EXPANDER_ALL_INTERRUPT);
}
void ExternalIO::extReadInterrupt()
{
std::lock_guard<std::mutex> lock(m_i2cMutex);
disableInterrupt(EXPANDER_ALL_INTERRUPT);
// read all registers and collect
IOstate interruptState;
for (auto &[a, e] : m_inMap)
{
interruptState[a] = e->read();
}
m_lastInputState = interruptState; // restore to current values
// compare to last state to see the difference
if (m_extInterruptCb)
{
for (auto &[a, v] : interruptState)
{
if (v)
m_extInterruptCb(stat2map(a, v));
}
}
enableInterrupt(EXPANDER_ALL_INTERRUPT);
}
const ExternalIO::io_t ExternalIO::map2pin(const uint32_t mappedIO)
{
return io_t{
.addr = (uint8_t)((mappedIO >> 16) & (uint8_t)0xFF),
.pin = (uint8_t)(mappedIO && (uint32_t)0xFF),
};
}
const uint32_t ExternalIO::stat2map(const uint8_t addr, const uint16_t stat)
{
if (!stat)
return 0;
return (uint32_t)(addr << 16) | (1UL << __builtin_ctz(stat));
}

49
RotaxMonitor/src/extio.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
#include <Arduino.h>
#include <DebugLog.h>
#include <PCA95x5.h>
#include <pins.h>
#include <memory>
#include <map>
class ExternalIO
{
using IOptr = std::unique_ptr<PCA9555>;
using IOmap = std::map<const uint8_t, IOptr>;
using IOstate = std::map<const uint8_t, uint16_t>;
using ExtInterruptCb = std::function<void(const uint32_t)>;
struct io_t
{
uint8_t addr;
uint8_t pin;
};
public:
ExternalIO(TwoWire &i2c, std::mutex &i2c_mutex, const uint8_t int_pin);
~ExternalIO();
void extDigitalWrite(const uint32_t mappedPin, const bool val);
const bool extDigitalRead(const uint32_t mappedPin);
void extAttachInterrupt(ExtInterruptCb cb = nullptr);
void extDetachInterrupt();
void extReadInterrupt();
private:
const io_t map2pin(const uint32_t mappedIO);
const uint32_t stat2map(const uint8_t addr, const uint16_t stat);
private:
const uint8_t m_intPin;
IOmap m_inMap;
IOmap m_outMap;
uint8_t m_intPinChanged;
IOstate m_lastInputState;
ExtInterruptCb m_extInterruptCb = nullptr;
std::mutex &m_i2cMutex;
TwoWire &m_i2c;
};

View File

@@ -26,22 +26,22 @@ void trig_isr_A(void *arg)
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce // only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils12.trig_time = time_us; box->coils12.coilTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case TRIG_FLAG_34P: case TRIG_FLAG_34P:
case TRIG_FLAG_34N: case TRIG_FLAG_34N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce // only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils34.trig_time = time_us; box->coils34.coilTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case SPARK_FLAG_12: case SPARK_FLAG_12:
box->coils12.spark_time = time_us; box->coils12.sparkTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case SPARK_FLAG_34: case SPARK_FLAG_34:
box->coils34.spark_time = time_us; box->coils34.sparkTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
default: default:
@@ -75,22 +75,22 @@ void trig_isr_B(void *arg)
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce // only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils12.trig_time = time_us; box->coils12.coilTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case TRIG_FLAG_34P: case TRIG_FLAG_34P:
case TRIG_FLAG_34N: case TRIG_FLAG_34N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce // only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils34.trig_time = time_us; box->coils34.coilTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case SPARK_FLAG_12: case SPARK_FLAG_12:
box->coils12.spark_time = time_us; box->coils12.sparkTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case SPARK_FLAG_34: case SPARK_FLAG_34:
box->coils34.spark_time = time_us; box->coils34.sparkTime = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
default: default:

View File

@@ -1,22 +1,16 @@
#pragma once #pragma once
// Test device Flag
// #define TEST
// Arduino Libraries // Arduino Libraries
#include <Arduino.h> #include <Arduino.h>
#include "soc/gpio_struct.h" #include "soc/gpio_struct.h"
#include <map> #include <map>
#ifndef TEST
#include "pins.h" #include "pins.h"
#else
#include "pins_test.h"
#endif
#include "datastruct.h" #include "datastruct.h"
#define CORE_0 0 #define CORE_0 0
#define CORE_1 1 #define CORE_1 1
#define RT_TASK_STACK 2048 // in words #define RT_TASK_STACK 4096 // in words
#define RT_TASK_PRIORITY (configMAX_PRIORITIES - 5) // highest priority after wifi tasks #define RT_TASK_PRIORITY (configMAX_PRIORITIES - 5) // highest priority after wifi tasks
struct isrParams struct isrParams

View File

@@ -16,13 +16,19 @@
#include <ui.h> #include <ui.h>
#include <led.h> #include <led.h>
// Defines to enable channel B // #define CH_A_ENABLE
#define CH_B_ENABLE // #define CH_B_ENABLE
// #define TEST #define CH_A_RT_ENABLE
#define CH_B_RT_ENABLE
// #define I2C_ENABLE
#define WEB_ENABLE
// Debug Defines // Debug Defines
#define WIFI_SSID "AstroRotaxMonitor" #define WIFI_SSID "AstroRotaxMonitor"
#define WIFI_PASSWORD "maledettirotax" #define WIFI_PASSWORD "maledettirotax"
#define PSRAM_MAX 4096
#define QUEUE_MAX 128
#define HTOP_DELAY 2000
void setup() void setup()
{ {
@@ -46,13 +52,14 @@ void setup()
LOG_DEBUG("ESP32 Sketch:", ESP.getFreeSketchSpace()); LOG_DEBUG("ESP32 Sketch:", ESP.getFreeSketchSpace());
// Init Wifi station // Init Wifi station
#ifdef WEB_ENABLE
LOG_INFO("Initializing WiFi..."); LOG_INFO("Initializing WiFi...");
WiFi.mode(WIFI_AP); WiFi.mode(WIFI_AP);
IPAddress local_IP(10, 11, 12, 1); IPAddress local_IP(10, 11, 12, 1);
IPAddress gateway(10, 11, 12, 1); IPAddress gateway(10, 11, 12, 1);
IPAddress subnet(255, 255, 255, 0); IPAddress subnet(255, 255, 255, 0);
WiFi.softAPConfig(local_IP, gateway, subnet); WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.setTxPower(WIFI_POWER_13dBm); // reduce wifi power WiFi.setTxPower(WIFI_POWER_5dBm); // reduce wifi power
if (WiFi.softAP(WIFI_SSID, WIFI_PASSWORD)) if (WiFi.softAP(WIFI_SSID, WIFI_PASSWORD))
{ {
LOG_INFO("WiFi AP Mode Started"); LOG_INFO("WiFi AP Mode Started");
@@ -67,6 +74,7 @@ void setup()
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart(); esp_restart();
} }
#endif
// Initialize Interrupt pins on PICKUP detectors // Initialize Interrupt pins on PICKUP detectors
initTriggerPinsInputs(); initTriggerPinsInputs();
@@ -79,23 +87,55 @@ void loop()
{ {
// global variables // global variables
RGBled led; RGBled led;
led.setBrightness(0.025f);
led.setStatus(RGBled::LedStatus::INIT); led.setStatus(RGBled::LedStatus::INIT);
Devices dev;
bool running = true; bool running = true;
std::mutex fs_mutex; std::mutex fs_mutex;
LITTLEFSGuard fsGuard; LITTLEFSGuard fsGuard;
//////// INIT SPI PORTS //////// //////// INIT SPI INTERFACES ////////
bool spiA_ok = true; bool spiA_ok = true;
bool spiB_ok = true; bool spiB_ok = true;
// Init 2 SPI interfaces //////// INIT SPI INTERFACES ////////
// SPIClass SPI_A(FSPI); LOG_DEBUG("Init SPI Interfaces");
// spiA_ok = SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI); #ifdef CH_A_ENABLE
// SPI_A.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1 LOG_DEBUG("Begin Init SPI_A");
// #ifdef CH_B_ENABLE SPIClass SPI_A(HSPI);
// SPIClass SPI_B(HSPI); spiA_ok = SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI);
// spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI); SPI_A.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
// SPI_B.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1 LOG_DEBUG("Init SPI_A -> OK");
// #endif delay(100);
LOG_DEBUG("Begin Init ADC_A");
ADS1256 ADC_A(ADC_A_DRDY, ADS1256::PIN_UNUSED, ADS1256::PIN_UNUSED, ADC_A_CS, 2.5, &SPI_A);
ADC_A.InitializeADC();
ADC_A.setPGA(PGA_1);
ADC_A.setDRATE(DRATE_7500SPS);
dev.m_adc_a = &ADC_A;
dev.m_spi_a = &SPI_A;
LOG_DEBUG("Init ADC_A -> OK");
delay(100);
#endif
#ifdef CH_B_ENABLE
LOG_DEBUG("Begin Init SPI_B");
SPIClass SPI_B(FSPI);
spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI);
SPI_B.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
LOG_DEBUG("Init SPI_B -> OK");
delay(100);
LOG_DEBUG("Begin Init ADC_B");
ADS1256 ADC_B(ADC_B_DRDY, ADS1256::PIN_UNUSED, ADS1256::PIN_UNUSED, ADC_B_CS, 2.5, &SPI_B);
ADC_B.InitializeADC();
ADC_B.setPGA(PGA_1);
ADC_B.setDRATE(DRATE_7500SPS);
dev.m_adc_b = &ADC_B;
dev.m_spi_b = &SPI_B;
LOG_DEBUG("Init ADC_B -> OK");
delay(100);
#endif
if (!spiA_ok || !spiB_ok) if (!spiA_ok || !spiB_ok)
{ {
LOG_ERROR("Unable to Initialize SPI Busses"); LOG_ERROR("Unable to Initialize SPI Busses");
@@ -103,38 +143,43 @@ void loop()
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart(); esp_restart();
} }
LOG_DEBUG("Init SPI OK");
// Resources Initialization LOG_DEBUG("Init SPI -> OK");
std::shared_ptr<Devices> dev = std::make_shared<Devices>();
// dev->m_spi_a = std::make_unique<SPIClass>(SPI_A);
// dev->m_spi_b = std::make_unique<SPIClass>(SPI_B);
// // Init ADC_A //////// INIT I2C INTERFACES ////////
// dev->m_adc_a = std::make_unique<ADS1256>(ADC_A_DRDY, ADS1256::PIN_UNUSED, ADS1256::PIN_UNUSED, ADC_A_CS, 2.5, &SPI_A); #ifdef I2C_ENABLE
// dev->m_adc_b = std::make_unique<ADS1256>(ADC_B_DRDY, ADS1256::PIN_UNUSED, ADS1256::PIN_UNUSED, ADC_B_CS, 2.5, &SPI_B); LOG_DEBUG("Init I2C Interfaces");
bool i2c_ok = true;
i2c_ok = Wire.begin(SDA, SCL, 100000);
if (!i2c_ok)
{
LOG_ERROR("Unable to Initialize I2C Bus");
LOG_ERROR("5 seconds to restart...");
vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart();
}
LOG_DEBUG("Init I2c ok");
// dev->m_adc_a->InitializeADC(); // Init IO Expanders
// dev->m_adc_a->setPGA(PGA_1); ExternalIO extIo(Wire, dev.m_i2c_mutex, EXPANDER_ALL_INTERRUPT);
// dev->m_adc_a->setDRATE(DRATE_7500SPS); dev.m_ext_io = &extIo;
#endif
// dev->m_adc_b->InitializeADC();
// dev->m_adc_b->setPGA(PGA_1);
// dev->m_adc_b->setDRATE(DRATE_7500SPS);
//////// INIT REALTIME TASKS PARAMETERS ////////
#ifdef CH_A_RT_ENABLE
const rtIgnitionTask::rtTaskParams taskA_params{ const rtIgnitionTask::rtTaskParams taskA_params{
.rt_running = true, .rt_running = true,
.name = "rtIgnTask_A", .name = "rtIgnTask_A",
.rt_stack_size = RT_TASK_STACK, .rt_stack_size = RT_TASK_STACK,
.rt_priority = RT_TASK_PRIORITY, .rt_priority = RT_TASK_PRIORITY,
.rt_int = rtIgnitionTask::rtTaskInterruptParams{ .rt_int = rtIgnitionTask::rtTaskInterruptParams{
.isr_ptr = &trig_isr_A, .isrPtr = &trig_isr_A,
.trig_pin_12p = TRIG_PIN_A12P, .trigPin_12p = TRIG_PIN_A12P,
.trig_pin_12n = TRIG_PIN_A12N, .trigPin_12n = TRIG_PIN_A12N,
.trig_pin_34p = TRIG_PIN_A34P, .trigPin_34p = TRIG_PIN_A34P,
.trig_pin_34n = TRIG_PIN_A34N, .trigPin_34n = TRIG_PIN_A34N,
.spark_pin_12 = SPARK_PIN_A12, .sparkPin_12 = SPARK_PIN_A12,
.spark_pin_34 = SPARK_PIN_A34}, .sparkPin_34 = SPARK_PIN_A34},
.rt_io = rtIgnitionTask::rtTaskIOParams{ .rt_io = rtIgnitionTask::rtTaskIOParams{
.pot_cs_12 = POT_CS_A12, .pot_cs_12 = POT_CS_A12,
.pot_cs_34 = POT_CS_A34, .pot_cs_34 = POT_CS_A34,
@@ -151,21 +196,22 @@ void loop()
.relay_out_34 = RELAY_OUT_A34, .relay_out_34 = RELAY_OUT_A34,
}, },
.rt_queue = nullptr, .rt_queue = nullptr,
.dev = dev}; .dev = &dev};
#endif
#ifdef CH_B_RT_ENABLE
const rtIgnitionTask::rtTaskParams taskB_params{ const rtIgnitionTask::rtTaskParams taskB_params{
.rt_running = true, .rt_running = true,
.name = "rtIgnTask_B", .name = "rtIgnTask_B",
.rt_stack_size = RT_TASK_STACK, .rt_stack_size = RT_TASK_STACK,
.rt_priority = RT_TASK_PRIORITY, .rt_priority = RT_TASK_PRIORITY,
.rt_int = rtIgnitionTask::rtTaskInterruptParams{ .rt_int = rtIgnitionTask::rtTaskInterruptParams{
.isr_ptr = &trig_isr_B, .isrPtr = &trig_isr_B,
.trig_pin_12p = TRIG_PIN_B12P, .trigPin_12p = TRIG_PIN_B12P,
.trig_pin_12n = TRIG_PIN_B12N, .trigPin_12n = TRIG_PIN_B12N,
.trig_pin_34p = TRIG_PIN_B34P, .trigPin_34p = TRIG_PIN_B34P,
.trig_pin_34n = TRIG_PIN_B34N, .trigPin_34n = TRIG_PIN_B34N,
.spark_pin_12 = SPARK_PIN_B12, .sparkPin_12 = SPARK_PIN_B12,
.spark_pin_34 = SPARK_PIN_B34}, .sparkPin_34 = SPARK_PIN_B34},
.rt_io = rtIgnitionTask::rtTaskIOParams{ .rt_io = rtIgnitionTask::rtTaskIOParams{
.pot_cs_12 = POT_CS_B12, .pot_cs_12 = POT_CS_B12,
.pot_cs_34 = POT_CS_B34, .pot_cs_34 = POT_CS_B34,
@@ -182,15 +228,30 @@ void loop()
.relay_out_34 = RELAY_OUT_B34, .relay_out_34 = RELAY_OUT_B34,
}, },
.rt_queue = nullptr, .rt_queue = nullptr,
.dev = dev}; .dev = &dev};
#endif
auto task_A = rtIgnitionTask(taskA_params, 4096, 256, CORE_0, fs_mutex); //////// SPAWN REALTIME TASKS ////////
delay(50); bool tasK_A_rt = true;
auto task_B = rtIgnitionTask(taskB_params, 4096, 256, CORE_1, fs_mutex); bool task_B_rt = true;
BaseType_t ignA_task_success = pdPASS;
BaseType_t ignB_task_success = pdPASS;
#ifdef CH_A_RT_ENABLE
auto task_A = rtIgnitionTask(taskA_params, PSRAM_MAX, QUEUE_MAX, CORE_0, fs_mutex);
ignA_task_success = task_A.getStatus() == rtIgnitionTask::OK ? pdPASS : pdFAIL;
tasK_A_rt = task_A.start();
delay(100);
#endif
#ifdef CH_B_RT_ENABLE
auto task_B = rtIgnitionTask(taskB_params, PSRAM_MAX, QUEUE_MAX, CORE_1, fs_mutex);
ignB_task_success = task_B.getStatus() == rtIgnitionTask::OK ? pdPASS : pdFAIL;
task_B_rt = task_B.start();
delay(100);
#endif
// Ignition A on Core 0 // Ignition A on Core 0
auto ignA_task_success = task_A.getStatus() == rtIgnitionTask::OK ? pdPASS : pdFAIL;
auto ignB_task_success = task_B.getStatus() == rtIgnitionTask::OK ? pdPASS : pdFAIL;
if (ignA_task_success != pdPASS || ignB_task_success != pdPASS) if (ignA_task_success != pdPASS || ignB_task_success != pdPASS)
{ {
LOG_ERROR("Unable to initialize ISR task"); LOG_ERROR("Unable to initialize ISR task");
@@ -198,33 +259,70 @@ void loop()
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart(); esp_restart();
} }
const bool tasK_A_rt = task_A.start();
delay(50);
const bool task_B_rt = task_B.start();
if (tasK_A_rt != true || task_B_rt != true) if (tasK_A_rt != true || task_B_rt != true)
{ {
led.setStatus(RGBled::LedStatus::ERROR); led.setStatus(RGBled::LedStatus::ERROR);
LOG_ERROR("Unable to start realtime tasks"); LOG_ERROR("Unable to start realtime tasks");
} else }
else
{
LOG_DEBUG("Real Time Tasks A & B initialized"); LOG_DEBUG("Real Time Tasks A & B initialized");
led.setStatus(RGBled::LedStatus::OK); led.setStatus(RGBled::LedStatus::OK);
}
AstroWebServer webPage(80, LittleFS); // Initialize webserver and Websocket //////// SPAWN WEBSERVER and WEBSOCKET ////////
ArduinoJson::JsonDocument json_data; ArduinoJson::JsonDocument json_data;
bool data_a, data_b; bool data_a = false, data_b = false;
task_A.onMessage([&webPage, &json_data, &data_a](ignitionBoxStatusFiltered sts){ #ifdef WEB_ENABLE
AstroWebServer webPage(80, LittleFS);
delay(100);
#ifdef CH_A_RT_ENABLE
task_A.onMessage([&webPage, &json_data, &data_a](ignitionBoxStatusFiltered sts)
{
json_data["box_a"] = sts.toJson(); json_data["box_a"] = sts.toJson();
data_a = true; data_a = true; });
}); #endif
task_B.onMessage([&webPage, &json_data, &data_b](ignitionBoxStatusFiltered sts){ #ifdef CH_B_RT_ENABLE
task_B.onMessage([&webPage, &json_data, &data_b](ignitionBoxStatusFiltered sts)
{
json_data["box_b"] = sts.toJson(); json_data["box_b"] = sts.toJson();
data_b = true; data_b = true; });
#endif
webPage.registerWsCommand("saveEnable", [&task_A, &task_B](const ArduinoJson::JsonDocument &doc) {
if(!doc["params"].is<ArduinoJson::JsonObject>()) return;
if(!doc["filename_a"].is<std::string>() ||!doc["filename_b"].is<std::string>()){
LOG_ERROR("saveEnable invalid or missing filenames");
return;
}
task_A.enableSave(true, doc["filename_a"].as<std::string>());
task_B.enableSave(true, doc["filename_a"].as<std::string>());
return; });
webPage.registerWsCommand("saveDisable", [&task_A, &task_B](const ArduinoJson::JsonDocument &doc) {
task_A.enableSave(false, "");
task_B.enableSave(false, ""); });
webPage.registerWsCommand("downloadHistory", [](const ArduinoJson::JsonDocument &doc) {
LOG_WARN("Command downloadHistory not Implemented");
}); });
// task_A.enableSave(true, "ignitionA_test.csv"); webPage.registerWsCommand("clearHistory", [](const ArduinoJson::JsonDocument &doc) {
// task_B.enableSave(true, "ignitionB_test.csv"); LOG_WARN("Command clearHistory not Implemented");
});
webPage.registerWsCommand("startTest", [](const ArduinoJson::JsonDocument &doc) {
LOG_WARN("Command startTest not Implemented");
});
webPage.registerWsCommand("stopTest", [](const ArduinoJson::JsonDocument &doc) {
LOG_WARN("Command stopTest not Implemented");
});
#endif
uint32_t monitor_loop = millis(); uint32_t monitor_loop = millis();
uint32_t data_loop = monitor_loop; uint32_t data_loop = monitor_loop;
@@ -232,18 +330,22 @@ void loop()
while (running) while (running)
{ {
uint32_t this_loop = millis(); uint32_t this_loop = millis();
if (this_loop - monitor_loop > 2000) if (this_loop - monitor_loop > HTOP_DELAY)
{ {
clearScreen(); clearScreen();
printRunningTasksMod(Serial); printRunningTasksMod(Serial);
monitor_loop = millis(); monitor_loop = millis();
} }
if ((data_a && data_b) || (this_loop - data_loop > 500)) { #ifdef WEB_ENABLE
if ((data_a && data_b) || ((this_loop - data_loop > 500) && (data_b || data_b)))
{
webPage.sendWsData(json_data.as<String>()); webPage.sendWsData(json_data.as<String>());
json_data.clear(); json_data.clear();
data_a = data_b = false; data_a = data_b = false;
data_loop = millis(); data_loop = millis();
} }
vTaskDelay(pdMS_TO_TICKS(10));
#endif
} //////////////// INNER LOOP ///////////////////// } //////////////// INNER LOOP /////////////////////
} ////////////////////// MAIN LOOP ////////////////////// } ////////////////////// MAIN LOOP //////////////////////

View File

@@ -33,16 +33,15 @@
// ===================== // =====================
// SPI BUS ADC2 (HSPI) // SPI BUS ADC2 (HSPI)
// ===================== // =====================
#define SPI_B_MOSI 36 #define SPI_B_MOSI 17
#define SPI_B_SCK 37 #define SPI_B_SCK 18
#define SPI_B_MISO 38 #define SPI_B_MISO 8
// ===================== // =====================
// I2C BUS (PCA9555) // I2C BUS (PCA9555)
// ===================== // =====================
#define SDA 8 #define SDA 21
#define SCL 9 #define SCL 47
#define I2C_INT 17
// ===================== // =====================
// ADC CONTROL // ADC CONTROL
@@ -50,14 +49,8 @@
#define ADC_A_CS 14 #define ADC_A_CS 14
#define ADC_A_DRDY 13 #define ADC_A_DRDY 13
#define ADC_B_CS 21 #define ADC_B_CS 3
#define ADC_B_DRDY 47 #define ADC_B_DRDY 46
// =====================
// DIGITAL POT
// =====================
#define POT_A_CS 18
#define POT_B_CS 35
// ===================== // =====================
// TRIGGER INPUT INTERRUPTS // TRIGGER INPUT INTERRUPTS
@@ -79,86 +72,87 @@
#define SPARK_PIN_B12 1 #define SPARK_PIN_B12 1
#define SPARK_PIN_B34 2 #define SPARK_PIN_B34 2
// ===================== // +++++++++++++++++++++
// PCA9555 I/O EXPANDER BOX_A // MACRO TO COMBINE PIN NUMBER AND ADDRESS
// ===================== #define PIN2ADDR(p, a) ((1UL << p) | ((uint32_t)(a) << 16))
// +++++++++++++++++++++
#define EXPANDER_A_ADDR 0x010101 // =====================
// PCA9555 I/O EXPANDER INTERRUPT (Common)
// =====================
#define EXPANDER_ALL_INTERRUPT 45
// =====================
// PCA9555 I/O EXPANDER BOX_A (OUT)
// =====================
#define EXPANDER_A_OUT_ADDR 0x7F
// --- DIGITAL POT CHIP SELECT LINES --- // --- DIGITAL POT CHIP SELECT LINES ---
#define POT_CS_A12 0 #define POT_CS_A12 PIN2ADDR(0, EXPANDER_A_OUT_ADDR)
#define POT_CS_A34 1 #define POT_CS_A34 PIN2ADDR(1, EXPANDER_A_OUT_ADDR)
// --- SOFT START FORCE LINES --- // --- SOFT START FORCE LINES ---
#define SS_FORCE_A 2 #define SS_FORCE_A PIN2ADDR(2, EXPANDER_A_OUT_ADDR)
#define SS_INIBHIT_A12 3 #define SS_INIBHIT_A12 PIN2ADDR(3, EXPANDER_A_OUT_ADDR)
#define SS_INHIBIT_A34 4 #define SS_INHIBIT_A34 PIN2ADDR(4, EXPANDER_A_OUT_ADDR)
// --- SAMPLE AND HOLD ARM AND DISCHARGE --- // --- SAMPLE AND HOLD ARM AND DISCHARGE ---
#define SH_DISCH_A12 5 #define SH_DISCH_A12 PIN2ADDR(5, EXPANDER_A_OUT_ADDR)
#define SH_DISCH_A34 6 #define SH_DISCH_A34 PIN2ADDR(6, EXPANDER_A_OUT_ADDR)
#define SH_ARM_A12 7 #define SH_ARM_A12 PIN2ADDR(7, EXPANDER_A_OUT_ADDR)
#define SH_ARM_A34 8 #define SH_ARM_A34 PIN2ADDR(8, EXPANDER_A_OUT_ADDR)
// --- RELAY --- // --- RELAY ---
#define RELAY_IN_A12 9 #define RELAY_IN_A12 PIN2ADDR(9, EXPANDER_A_OUT_ADDR)
#define RELAY_OUT_A12 10 #define RELAY_OUT_A12 PIN2ADDR(10, EXPANDER_A_OUT_ADDR)
#define RELAY_IN_A34 11 #define RELAY_IN_A34 PIN2ADDR(11, EXPANDER_A_OUT_ADDR)
#define RELAY_OUT_A34 12 #define RELAY_OUT_A34 PIN2ADDR(12, EXPANDER_A_OUT_ADDR)
// --- STATUS / BUTTON ---
#define STA_2 13
#define STA_3 14
#define STA_4 15
// ===================== // =====================
// PCA9555 I/O EXPANDER BOX_B // PCA9555 I/O EXPANDER BOX_A (IN)
// ===================== // =====================
#define EXPANDER_A_IN_ADDR 0x7F
#define EXPANDER_B_ADDR 0x101010 #define SS_A12_ON PIN2ADDR(0, EXPANDER_A_IN_ADDR)
#define SS_A12_OFF PIN2ADDR(1, EXPANDER_A_IN_ADDR)
#define SS_A34_ON PIN2ADDR(2, EXPANDER_A_IN_ADDR)
#define SS_A34_OFF PIN2ADDR(3, EXPANDER_A_IN_ADDR)
// =====================
// PCA9555 I/O EXPANDER BOX_B (OUT)
// =====================
#define EXPANDER_B_OUT_ADDR 0x7F
// --- DIGITAL POT CHIP SELECT LINES --- // --- DIGITAL POT CHIP SELECT LINES ---
#define POT_CS_B12 0 #define POT_CS_B12 PIN2ADDR(0, EXPANDER_B_OUT_ADDR)
#define POT_CS_B34 1 #define POT_CS_B34 PIN2ADDR(1, EXPANDER_B_OUT_ADDR)
// --- SOFT START FORCE LINES --- // --- SOFT START FORCE LINES ---
#define SS_FORCE_B 2 #define SS_FORCE_B PIN2ADDR(2, EXPANDER_B_OUT_ADDR)
#define SS_INIBHIT_B12 3 #define SS_INIBHIT_B12 PIN2ADDR(3, EXPANDER_B_OUT_ADDR)
#define SS_INHIBIT_B34 4 #define SS_INHIBIT_B34 PIN2ADDR(4, EXPANDER_B_OUT_ADDR)
// --- SAMPLE AND HOLD ARM AND DISCHARGE --- // --- SAMPLE AND HOLD ARM AND DISCHARGE ---
#define SH_DISCH_B12 5 #define SH_DISCH_B12 PIN2ADDR(5, EXPANDER_B_OUT_ADDR)
#define SH_DISCH_B34 6 #define SH_DISCH_B34 PIN2ADDR(6, EXPANDER_B_OUT_ADDR)
#define SH_ARM_B12 7 #define SH_ARM_B12 PIN2ADDR(7, EXPANDER_B_OUT_ADDR)
#define SH_ARM_B34 8 #define SH_ARM_B34 PIN2ADDR(8, EXPANDER_B_OUT_ADDR)
// --- RELAY --- // --- RELAY ---
#define RELAY_IN_B12 9 #define RELAY_IN_B12 PIN2ADDR(9, EXPANDER_B_OUT_ADDR)
#define RELAY_OUT_B12 10 #define RELAY_OUT_B12 PIN2ADDR(10, EXPANDER_B_OUT_ADDR)
#define RELAY_IN_B34 11 #define RELAY_IN_B34 PIN2ADDR(11, EXPANDER_B_OUT_ADDR)
#define RELAY_OUT_B34 12 #define RELAY_OUT_B34 PIN2ADDR(12, EXPANDER_B_OUT_ADDR)
// --- STATUS / BUTTON ---
#define STA_2 13
#define STA_3 14
#define STA_4 15
// ===================== // =====================
// PCA9555 I/O EXPANDER INPUTS A+B // PCA9555 I/O EXPANDER BOX_B (IN)
// ===================== // =====================
#define EXPANDER_B_IN_ADDR 0x7F
#define EXPANDER_IN_ADDR 0x0a0a0a #define SS_B12_ON PIN2ADDR(0, EXPANDER_B_IN_ADDR)
#define SS_B12_OFF PIN2ADDR(1, EXPANDER_B_IN_ADDR)
#define SS_A12_ON #define SS_B34_ON PIN2ADDR(2, EXPANDER_B_IN_ADDR)
#define SS_A12_OFF #define SS_B34_OFF PIN2ADDR(3, EXPANDER_B_IN_ADDR)
#define SS_A34_ON
#define SS_A34_OFF
#define SS_B12_ON
#define SS_B12_OFF
#define SS_B34_ON
#define SS_B34_OFF
// Init Pin Functions // Init Pin Functions
inline void initTriggerPinsInputs() inline void initTriggerPinsInputs()

View File

@@ -1,84 +0,0 @@
#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 SH_ARM_A34 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);
}

View File

@@ -1,11 +1,12 @@
#include "tasks.h" #include "tasks.h"
#include <esp_timer.h> #include <esp_timer.h>
#include <datasave.h> #include <datasave.h>
#include <mutex>
//// GLOBAL STATIC FUNCTIONS //// GLOBAL STATIC FUNCTIONS
// Timeout callback for microsecond precision // Timeout callback for microsecond precision
void spark_timeout_callback(void *arg) void IRAM_ATTR spark_timeout_callback(void *arg)
{ {
TaskHandle_t handle = (TaskHandle_t)arg; TaskHandle_t handle = (TaskHandle_t)arg;
xTaskNotify(handle, SPARK_FLAG_TIMEOUT, eSetValueWithOverwrite); xTaskNotify(handle, SPARK_FLAG_TIMEOUT, eSetValueWithOverwrite);
@@ -15,9 +16,12 @@ void spark_timeout_callback(void *arg)
void rtIgnitionTask::rtIgnitionTask_manager(void *pvParameters) void rtIgnitionTask::rtIgnitionTask_manager(void *pvParameters)
{ {
rtIgnitionTask *cls = (rtIgnitionTask *)pvParameters; rtIgnitionTask *cls = (rtIgnitionTask *)pvParameters;
auto last_loop = millis();
uint32_t count(0);
while (cls->m_running) while (cls->m_running)
{ {
cls->run(); cls->run();
vTaskDelay(pdMS_TO_TICKS(1));
} }
} }
@@ -33,155 +37,156 @@ void rtIgnitionTask::rtIgnitionTask_realtime(void *pvParameters)
} }
// Task Parameters and Devices // Task Parameters and Devices
rtTaskParams *params = (rtTaskParams *)pvParameters; const rtTaskParams *params = (const rtTaskParams *)pvParameters;
const rtTaskInterruptParams rt_int = params->rt_int; // copy to avoid external override const rtTaskInterruptParams rtInterrupts = params->rt_int; // copy to avoid external override
const rtTaskIOParams rt_rst = params->rt_io; // copy to avoid external override const rtTaskIOParams rtResets = params->rt_io; // copy to avoid external override
QueueHandle_t rt_queue = params->rt_queue; QueueHandle_t rtQueue = params->rt_queue;
Devices *dev = params->dev.get(); Devices *dev = params->dev;
ADS1256 *adc = dev->m_adc_a.get(); ExternalIO *io = dev->m_ext_io;
PCA9555 *io = dev->m_expander_a.get(); ADS1256 *adc = params->name == "rtIgnTask_A" ? dev->m_adc_a : dev->m_adc_b;
std::mutex &spi_mutex = params->name == "rtIgnTask_A" ? dev->m_spi_a_mutex : dev->m_spi_b_mutex;
TaskStatus_t rt_task_info; // Geta task name and additiona info for debug messages
vTaskGetInfo(NULL, &rt_task_info, pdFALSE, eInvalid); TaskStatus_t rtTaskInfo;
vTaskGetInfo(NULL, &rtTaskInfo, pdFALSE, eInvalid);
LOG_INFO("rtTask Params OK [", params->name.c_str(), "]");
const auto rt_task_name = pcTaskGetName(rt_task_info.xHandle); // Status of ignition box for this task, to be used locally and passed to isr to get timing
LOG_INFO("rtTask Params OK [", rt_task_name, "]"); ignitionBoxStatus ignBoxStatus;
ignitionBoxStatus ign_box_sts;
// Variables for ISR, static to be fixed in memory locations // Variables for ISR, static to be fixed in memory locations
isrParams isr_params_t12p{ isrParams isrParams_t12p{
.flag = TRIG_FLAG_12P, .flag = TRIG_FLAG_12P,
.ign_stat = &ign_box_sts, .ign_stat = &ignBoxStatus,
.rt_handle_ptr = rt_task_info.xHandle}; .rt_handle_ptr = rtTaskInfo.xHandle};
isrParams isr_params_t12n{ isrParams isrParams_t12n{
.flag = TRIG_FLAG_12N, .flag = TRIG_FLAG_12N,
.ign_stat = &ign_box_sts, .ign_stat = &ignBoxStatus,
.rt_handle_ptr = rt_task_info.xHandle}; .rt_handle_ptr = rtTaskInfo.xHandle};
isrParams isr_params_t34p{ isrParams isrParams_t34p{
.flag = TRIG_FLAG_34P, .flag = TRIG_FLAG_34P,
.ign_stat = &ign_box_sts, .ign_stat = &ignBoxStatus,
.rt_handle_ptr = rt_task_info.xHandle}; .rt_handle_ptr = rtTaskInfo.xHandle};
isrParams isr_params_t34n{ isrParams isrParams_t34n{
.flag = TRIG_FLAG_34N, .flag = TRIG_FLAG_34N,
.ign_stat = &ign_box_sts, .ign_stat = &ignBoxStatus,
.rt_handle_ptr = rt_task_info.xHandle}; .rt_handle_ptr = rtTaskInfo.xHandle};
isrParams isr_params_sp12{ isrParams isrParams_sp12{
.flag = SPARK_FLAG_12, .flag = SPARK_FLAG_12,
.ign_stat = &ign_box_sts, .ign_stat = &ignBoxStatus,
.rt_handle_ptr = rt_task_info.xHandle}; .rt_handle_ptr = rtTaskInfo.xHandle};
isrParams isr_params_sp34{ isrParams isrParams_sp34{
.flag = SPARK_FLAG_34, .flag = SPARK_FLAG_34,
.ign_stat = &ign_box_sts, .ign_stat = &ignBoxStatus,
.rt_handle_ptr = rt_task_info.xHandle}; .rt_handle_ptr = rtTaskInfo.xHandle};
LOG_DEBUG("rtTask HDL Params OK, HDL* [", (uint32_t)rt_task_info.xHandle, "]");
LOG_DEBUG("rtTask ISR Params OK, ISR* [", (uint32_t)rt_int.isr_ptr, "]");
LOG_DEBUG("rtTask QUE Params OK, QUE* [", (uint32_t)rt_queue, "]");
// Create esp_timer for microsecond precision timeout // Create esp_timer for microsecond precision timeout
esp_timer_handle_t timeout_timer; esp_timer_handle_t timeoutTimer;
esp_timer_create_args_t timer_args = { esp_timer_create_args_t timeoutTimerArgs = {
.callback = spark_timeout_callback, .callback = spark_timeout_callback,
.arg = (void *)rt_task_info.xHandle, .arg = (void *)rtTaskInfo.xHandle,
.dispatch_method = ESP_TIMER_TASK, .dispatch_method = ESP_TIMER_TASK,
.name = "spark_timeout"}; .name = "spark_timeout"};
esp_timer_create(&timer_args, &timeout_timer); if (esp_timer_create(&timeoutTimerArgs, &timeoutTimer) != ESP_OK)
{
LOG_INFO("rtTask [", params->name.c_str(), "] Fail to allocate timeoutTimer");
vTaskDelete(NULL);
}
// Attach Pin Interrupts // Attach Pin Interrupts
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12p), rt_int.isr_ptr, (void *)&isr_params_t12p, RISING); attachInterruptArg(digitalPinToInterrupt(rtInterrupts.trigPin_12p), rtInterrupts.isrPtr, (void *)&isrParams_t12p, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12n), rt_int.isr_ptr, (void *)&isr_params_t12n, RISING); attachInterruptArg(digitalPinToInterrupt(rtInterrupts.trigPin_12n), rtInterrupts.isrPtr, (void *)&isrParams_t12n, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_34p), rt_int.isr_ptr, (void *)&isr_params_t34p, RISING); attachInterruptArg(digitalPinToInterrupt(rtInterrupts.trigPin_34p), rtInterrupts.isrPtr, (void *)&isrParams_t34p, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_34n), rt_int.isr_ptr, (void *)&isr_params_t34n, RISING); attachInterruptArg(digitalPinToInterrupt(rtInterrupts.trigPin_34n), rtInterrupts.isrPtr, (void *)&isrParams_t34n, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.spark_pin_12), rt_int.isr_ptr, (void *)&isr_params_sp12, RISING); attachInterruptArg(digitalPinToInterrupt(rtInterrupts.sparkPin_12), rtInterrupts.isrPtr, (void *)&isrParams_sp12, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.spark_pin_34), rt_int.isr_ptr, (void *)&isr_params_sp34, RISING); attachInterruptArg(digitalPinToInterrupt(rtInterrupts.sparkPin_34), rtInterrupts.isrPtr, (void *)&isrParams_sp34, RISING);
LOG_INFO("rtTask ISR Attach OK [", rt_task_name, "]"); LOG_INFO("rtTask ISR Attach OK [", params->name.c_str(), "]");
// Global rt_task_ptr variables // Global rt_task_ptr variables
bool first_cycle = true; bool firstCycle = true;
bool cycle12 = false; bool cycle12 = false;
bool cycle34 = false; bool cycle34 = false;
int64_t last_cycle_time = 0; int64_t lastCycleTime = 0;
uint32_t n_errors = 0; uint32_t nErrors = 0;
while (params->rt_running) while (params->rt_running)
{ {
uint32_t pickup_flag = 0; uint32_t pickupFlag = 0;
uint32_t spark_flag = 0; uint32_t sparkFlag = 0;
// WAIT FOR PICKUP SIGNAL // WAIT FOR PICKUP SIGNAL
xTaskNotifyWait( xTaskNotifyWait(
0x00, // non pulire all'ingresso 0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit ULONG_MAX, // pulisci i primi 8 bit
&pickup_flag, // valore ricevuto &pickupFlag, // valore ricevuto
portMAX_DELAY); portMAX_DELAY);
if (first_cycle && pickup_flag != TRIG_FLAG_12P) // skip first cycle because of possible initial noise on pickup signals at startu if (firstCycle && pickupFlag != TRIG_FLAG_12P) // skip first cycle because of possible initial noise on pickup signals at startu
continue; continue;
// Start microsecond precision timeout timer // Start microsecond precision timeout timer
esp_timer_stop(timeout_timer); // stop timer in case it was running from previous cycle esp_timer_stop(timeoutTimer); // stop timer in case it was running from previous cycle
esp_timer_start_once(timeout_timer, spark_timeout_max); esp_timer_start_once(timeoutTimer, c_sparkTimeoutMax);
// WAIT FOR SPARK TO HAPPEN OR TIMEOUT // WAIT FOR SPARK TO HAPPEN OR TIMEOUT
xTaskNotifyWait( xTaskNotifyWait(
0x00, // non pulire all'ingresso 0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit ULONG_MAX, // pulisci i primi 8 bit
&spark_flag, // valore ricevuto &sparkFlag, // valore ricevuto
portMAX_DELAY); // wait indefinitely, timeout handled by esp_timer portMAX_DELAY); // wait indefinitely, timeout handled by esp_timer
// Handle timeout or spark event // Handle timeout or spark event
if (spark_flag != SPARK_FLAG_TIMEOUT) if (sparkFlag != SPARK_FLAG_TIMEOUT)
esp_timer_stop(timeout_timer); esp_timer_stop(timeoutTimer);
// A trigger from pickup 12 is followed by a spark event on 34 or vice versa pickup 34 triggers spark on 12 // 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_TIMEOUT)) if ((pickupFlag == TRIG_FLAG_12P || pickupFlag == TRIG_FLAG_12N) && (sparkFlag != SPARK_FLAG_12 && sparkFlag != SPARK_FLAG_TIMEOUT))
{ {
ign_box_sts.coils12.spark_status = ign_box_sts.coils34.spark_status = sparkStatus::SPARK_SYNC_FAIL; ignBoxStatus.coils12.sparkStatus = ignBoxStatus.coils34.sparkStatus = sparkStatusEnum::SPARK_SYNC_FAIL;
continue; continue;
} }
// Select coil status reference based on pickup_flag // Select coil status reference based on pickupFlag
coilsStatus *coils; coilsStatus *coils;
switch (pickup_flag) switch (pickupFlag)
{ {
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
{ {
first_cycle = false; firstCycle = false;
// compute engine rpm from cycle time // compute engine rpm from cycle time
auto current_time = esp_timer_get_time(); auto currentTime = esp_timer_get_time();
auto cycle_time = current_time - last_cycle_time; auto cycleTime = currentTime - lastCycleTime;
last_cycle_time = current_time; lastCycleTime = currentTime;
ign_box_sts.eng_rpm = (int32_t)(60.0f / (cycle_time / 1000000.0f)); ignBoxStatus.engRpm = (int32_t)(60.0f / (cycleTime / 1000000.0f));
} }
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
coils = &ign_box_sts.coils12; coils = &ignBoxStatus.coils12;
break; break;
case TRIG_FLAG_34P: case TRIG_FLAG_34P:
case TRIG_FLAG_34N: case TRIG_FLAG_34N:
coils = &ign_box_sts.coils34; coils = &ignBoxStatus.coils34;
break; break;
} }
// Select logic based on pickup and spark flags // Select logic based on pickup and spark flags
switch (pickup_flag) switch (pickupFlag)
{ {
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
case TRIG_FLAG_34P: case TRIG_FLAG_34P:
{ {
// Timeout not occourred, expected POSITIVE edge spark OCCOURRED // Timeout not occourred, expected POSITIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_TIMEOUT) if (sparkFlag != SPARK_FLAG_TIMEOUT)
{ {
coils->spark_delay = (int32_t)(coils->spark_time - coils->trig_time); coils->sparkDelay = (int32_t)(coils->sparkTime - coils->coilTime);
coils->sstart_status = softStartStatus::NORMAL; // because spark on positive edge coils->softStartStatus = softStartStatusEnum::NORMAL; // because spark on positive edge
coils->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge coils->sparkStatus = sparkStatusEnum::SPARK_POS_OK; // do not wait for spark on negative edge
} }
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED // Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_TIMEOUT) else if (sparkFlag == SPARK_FLAG_TIMEOUT)
{ {
coils->spark_status = sparkStatus::SPARK_NEG_WAIT; coils->sparkStatus = sparkStatusEnum::SPARK_NEG_WAIT;
coils->sstart_status = softStartStatus::NORMAL; coils->softStartStatus = softStartStatusEnum::NORMAL;
} }
continue; // Do nothing more on positive pulse continue; // Do nothing more on positive pulse
} }
@@ -189,29 +194,29 @@ void rtIgnitionTask::rtIgnitionTask_realtime(void *pvParameters)
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
case TRIG_FLAG_34N: case TRIG_FLAG_34N:
{ {
const bool expected_negative = coils->spark_status == sparkStatus::SPARK_NEG_WAIT; const bool negativeSparkExpected = coils->sparkStatus == sparkStatusEnum::SPARK_NEG_WAIT;
// Timeout not occourred, expected NEGATIVE edge spark OCCOURRED // Timeout not occourred, expected NEGATIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_TIMEOUT && expected_negative) if (sparkFlag != SPARK_FLAG_TIMEOUT && negativeSparkExpected)
{ {
coils->spark_delay = (int32_t)(coils->spark_time - coils->trig_time); coils->sparkDelay = (int32_t)(coils->sparkTime - coils->coilTime);
coils->sstart_status = softStartStatus::SOFT_START; coils->softStartStatus = softStartStatusEnum::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_OK; coils->sparkStatus = sparkStatusEnum::SPARK_NEG_OK;
} }
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED // Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_TIMEOUT && expected_negative) else if (sparkFlag == SPARK_FLAG_TIMEOUT && negativeSparkExpected)
{ {
coils->sstart_status = softStartStatus::ERROR; coils->softStartStatus = softStartStatusEnum::ERROR;
coils->spark_status = sparkStatus::SPARK_NEG_FAIL; coils->sparkStatus = sparkStatusEnum::SPARK_NEG_FAIL;
} }
// Timeout not occouured, unexpected negative edge spark // Timeout not occouured, unexpected negative edge spark
else if (spark_flag != SPARK_FLAG_TIMEOUT && !expected_negative) else if (sparkFlag != SPARK_FLAG_TIMEOUT && !negativeSparkExpected)
{ {
coils->sstart_status = softStartStatus::SOFT_START; coils->softStartStatus = softStartStatusEnum::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_UNEXPECTED; coils->sparkStatus = sparkStatusEnum::SPARK_NEG_UNEXPECTED;
} }
// Wait for finish of negative pulse to save data to buffer // Wait for finish of negative pulse to save data to buffer
coils->n_events++; coils->nEvents++;
if (pickup_flag == TRIG_FLAG_12N) if (pickupFlag == TRIG_FLAG_12N)
cycle12 = true; cycle12 = true;
else else
cycle34 = true; cycle34 = true;
@@ -223,167 +228,211 @@ void rtIgnitionTask::rtIgnitionTask_realtime(void *pvParameters)
if (cycle12 && cycle34) // wait for both 12 and 34 cycles to complete before sending data to main loop and resetting peak detectors if (cycle12 && cycle34) // wait for both 12 and 34 cycles to complete before sending data to main loop and resetting peak detectors
{ {
// disable interrupts during adc samples
disableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_12p));
disableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_12n));
disableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_34p));
disableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_34n));
disableInterrupt(digitalPinToInterrupt(rtInterrupts.sparkPin_12));
disableInterrupt(digitalPinToInterrupt(rtInterrupts.sparkPin_34));
// reset coils 12 and 34 cycles
cycle12 = false; cycle12 = false;
cycle34 = false; cycle34 = false;
if (ign_box_sts.coils12.spark_status == sparkStatus::SPARK_POS_FAIL || ign_box_sts.coils12.spark_status == sparkStatus::SPARK_NEG_FAIL) if (ignBoxStatus.coils12.sparkStatus == sparkStatusEnum::SPARK_POS_FAIL || ignBoxStatus.coils12.sparkStatus == sparkStatusEnum::SPARK_NEG_FAIL)
ign_box_sts.coils12.n_missed_firing++; ignBoxStatus.coils12.nMissedFiring++;
if (ign_box_sts.coils34.spark_status == sparkStatus::SPARK_POS_FAIL || ign_box_sts.coils34.spark_status == sparkStatus::SPARK_NEG_FAIL) if (ignBoxStatus.coils34.sparkStatus == sparkStatusEnum::SPARK_POS_FAIL || ignBoxStatus.coils34.sparkStatus == sparkStatusEnum::SPARK_NEG_FAIL)
ign_box_sts.coils34.n_missed_firing++; ignBoxStatus.coils34.nMissedFiring++;
// read adc channels: pickup12, out12 [ pos + neg ] // read adc channels: pickup12, out12 [ pos + neg ]
if (adc) // read only if adc initialized if (adc) // read only if adc initialized
{ {
uint32_t start_adc_read = esp_timer_get_time(); std::lock_guard<std::mutex> lock(spi_mutex);
uint32_t startAdcReadTime = esp_timer_get_time();
// from peak detector circuits // from peak detector circuits
ign_box_sts.coils12.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_12P_IN); ignBoxStatus.coils12.peakPos = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils12.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_12N_IN); ignBoxStatus.coils12.peakNeg = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils34.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_34P_IN); ignBoxStatus.coils34.peakPos = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils34.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_34N_IN); ignBoxStatus.coils34.peakNeg = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils12.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_12P_OUT); ignBoxStatus.coils12.trigLevelPos = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils12.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_12N_OUT); ignBoxStatus.coils12.trigLevelNeg = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils34.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_34P_OUT); ignBoxStatus.coils34.trigLevelPos = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT); ignBoxStatus.coils34.trigLevelNeg = adc->convertToVoltage(adc->cycleSingle());
ign_box_sts.adc_read_time = (int32_t)(esp_timer_get_time() - start_adc_read); adc->stopConversion();
ignBoxStatus.adcReadTime = (int32_t)(esp_timer_get_time() - startAdcReadTime);
} }
else // simulate adc read timig else // simulate adc read timig
vTaskDelay(pdMS_TO_TICKS(c_adc_time)); vTaskDelay(pdMS_TO_TICKS(c_adcTime));
// reset peak detectors + sample and hold // reset peak detectors + sample and hold
// outputs on io expander // outputs on io expander
if (io) if (io)
{ {
// [TODO] code to reset sample and hold and arm trigger level detectors uint32_t startIoReadWriteTime = esp_timer_get_time();
// Discharge Pulse
io->extDigitalWrite(rtResets.sh_disch_12, true);
io->extDigitalWrite(rtResets.sh_disch_34, true);
delayMicroseconds(250);
io->extDigitalWrite(rtResets.sh_disch_12, false);
io->extDigitalWrite(rtResets.sh_disch_34, false);
// Safety delay
delayMicroseconds(500);
// Re-Arm Pulse
io->extDigitalWrite(rtResets.sh_arm_12, true);
io->extDigitalWrite(rtResets.sh_arm_34, true);
delayMicroseconds(250);
io->extDigitalWrite(rtResets.sh_arm_12, false);
io->extDigitalWrite(rtResets.sh_arm_34, false);
ignBoxStatus.ioReadWriteTime = (int32_t)(esp_timer_get_time() - startIoReadWriteTime);
} }
else else
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(c_ioTime));
// send essage to main loop with ignition info, by copy so local static variable is ok // send essage to main loop with ignition info, by copy so local static variable is ok
if (rt_queue) if (rtQueue)
{ {
ign_box_sts.timestamp = esp_timer_get_time(); // update data timestamp ignBoxStatus.timestamp = esp_timer_get_time(); // update data timestamp
if (xQueueSendToBack(rt_queue, (void *)&ign_box_sts, 0) != pdPASS) if (xQueueSendToBack(rtQueue, (void *)&ignBoxStatus, 0) != pdPASS)
ign_box_sts.n_queue_errors = ++n_errors; ignBoxStatus.nQueueErrors = ++nErrors;
} }
// enable interrupts ready for a new cycle
enableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_12p));
enableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_12n));
enableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_34p));
enableInterrupt(digitalPinToInterrupt(rtInterrupts.trigPin_34n));
enableInterrupt(digitalPinToInterrupt(rtInterrupts.sparkPin_12));
enableInterrupt(digitalPinToInterrupt(rtInterrupts.sparkPin_34));
} }
} }
// Delete the timeout timer // Delete the timeout timer
esp_timer_delete(timeout_timer); esp_timer_stop(timeoutTimer);
LOG_WARN("rtTask Ending [", rt_task_name, "]"); esp_timer_delete(timeoutTimer);
LOG_WARN("rtTask Ending [", params->name.c_str(), "]");
// Ignition A Interrupts DETACH // Ignition A Interrupts DETACH
detachInterrupt(rt_int.trig_pin_12p); detachInterrupt(rtInterrupts.trigPin_12p);
detachInterrupt(rt_int.trig_pin_12n); detachInterrupt(rtInterrupts.trigPin_12n);
detachInterrupt(rt_int.trig_pin_34p); detachInterrupt(rtInterrupts.trigPin_34p);
detachInterrupt(rt_int.trig_pin_34n); detachInterrupt(rtInterrupts.trigPin_34n);
detachInterrupt(rt_int.spark_pin_12); detachInterrupt(rtInterrupts.sparkPin_12);
detachInterrupt(rt_int.spark_pin_34); detachInterrupt(rtInterrupts.sparkPin_34);
// delete present task // delete present task
vTaskDelete(NULL); vTaskDelete(NULL);
} }
///////////// CLASS MEMBER DEFINITIONS ///////////// ///////////// CLASS MEMBER DEFINITIONS /////////////
rtIgnitionTask::rtIgnitionTask(const rtTaskParams params, const uint32_t history_size, const uint32_t queue_size, const uint8_t core, std::mutex &fs_mutex, fs::FS &filesystem) : m_params(params), m_filesystem(filesystem), m_fs_mutex(fs_mutex), m_core(core), m_max_history(history_size) rtIgnitionTask::rtIgnitionTask(const rtTaskParams params, const uint32_t history_size, const uint32_t queue_size, const uint8_t core, std::mutex &fs_mutex, fs::FS &filesystem) : m_params(params), m_filesystem(filesystem), m_filesystemMutex(fs_mutex), m_core(core), m_historyMax(history_size)
{ {
LOG_WARN("Starting Manager for [", m_params.name.c_str(), "]");
// create queue buffers // create queue buffers
m_queue = xQueueCreate(queue_size, sizeof(ignitionBoxStatus)); m_rtQueueHandle = xQueueCreate(queue_size, sizeof(ignitionBoxStatus));
if (!m_queue) if (!m_rtQueueHandle)
{ {
LOG_ERROR("Unable To Create Task [", params.name.c_str(), "] queues"); LOG_ERROR("Unable To Create Task [", params.name.c_str(), "] queues");
m_manager_status = rtTaskStatus::ERROR; m_managerStatus = rtTaskStatus::ERROR;
return; return;
} }
else else
m_params.rt_queue = m_queue; m_params.rt_queue = m_rtQueueHandle;
try
{
// create PSram history vectors // create PSram history vectors
m_history_0 = PSHistory(history_size); m_historyBuf0 = PSHistory(history_size);
m_history_1 = PSHistory(history_size); m_historyBuf1 = PSHistory(history_size);
// assing active and writable history // assing active and writable history
m_active_history = std::unique_ptr<PSHistory>(&m_history_0); m_historyActive = std::unique_ptr<PSHistory>(&m_historyBuf0);
m_save_history = std::unique_ptr<PSHistory>(&m_history_1); m_historyInactive = std::unique_ptr<PSHistory>(&m_historyBuf1);
}
catch (std::bad_alloc &e)
{
LOG_ERROR("Task [", params.name.c_str(), "] Unable to allocate history PSRAM: ", e.what());
return;
}
LOG_WARN("Starting Manager for [", m_params.name.c_str(), "]"); m_managerTaskName = (std::string("man_") + m_params.name).c_str();
// auto task_success = pdPASS;
auto task_success = xTaskCreatePinnedToCore( auto task_success = xTaskCreatePinnedToCore(
rtIgnitionTask_manager, rtIgnitionTask_manager,
(std::string("man_") + m_params.name).c_str(), m_managerTaskName.c_str(),
8192, RT_TASK_STACK,
(void *)this, (void *)this,
m_params.rt_priority >> 2, m_params.rt_priority >> 2,
&m_manager_handle, &m_managerHandle,
m_core); m_core);
if (task_success != pdPASS) if (task_success != pdPASS)
{ {
LOG_ERROR("Unable To Create Manager for [", params.name.c_str(), "]"); LOG_ERROR("Unable To Create Manager for [", params.name.c_str(), "]");
m_manager_status = rtTaskStatus::ERROR; m_managerStatus = rtTaskStatus::ERROR;
return; return;
} }
// average every 10 samples // average every 10 samples
m_info_filtered = ignitionBoxStatusFiltered(10); m_statusFiltered = ignitionBoxStatusFiltered(m_filterSize);
m_last_data = millis(); m_dataLast = millis();
m_manager_status = rtTaskStatus::OK; m_managerStatus = rtTaskStatus::OK;
} }
rtIgnitionTask::~rtIgnitionTask() rtIgnitionTask::~rtIgnitionTask()
{ {
if (m_rt_handle) if (m_rtHandle)
vTaskDelete(m_rt_handle); vTaskDelete(m_rtHandle);
if (m_manager_handle) if (m_managerHandle)
vTaskDelete(m_manager_handle); vTaskDelete(m_managerHandle);
if (m_queue) if (m_rtQueueHandle)
vQueueDelete(m_queue); vQueueDelete(m_rtQueueHandle);
} }
void rtIgnitionTask::run() void rtIgnitionTask::run()
{ {
// receive new data from the queue // receive new data from the queue
auto new_data = xQueueReceive(m_queue, &m_last_status, 0); // non blocking receive auto new_data = xQueueReceive(m_rtQueueHandle, &m_statusLast, 0); // non blocking receive
if (new_data == pdPASS) if (new_data == pdPASS)
{ {
m_last_data = millis(); m_dataLast = millis();
m_manager_status = rtTaskStatus::RUNNING; m_managerStatus = rtTaskStatus::RUNNING;
// if history buffer is full swap buffers and if enabled save history buffer // if history buffer is full swap buffers and if enabled save history buffer
if (m_counter_status >= m_active_history->size()) if (m_statusCounter >= m_historyMax)
{ {
LOG_DEBUG("Save for Buffer Full: ", m_counter_status); LOG_DEBUG("Save for Buffer Full: ", m_statusCounter);
m_counter_status = 0; m_statusCounter = 0;
m_partial_save = false; // reset partial save flag on new data cycle m_savePartial = false; // reset partial save flag on new data cycle
std::swap(m_active_history, m_save_history); std::swap(m_historyActive, m_historyInactive);
if (m_enable_save) if (m_historySaveEnable)
saveHistory(*m_save_history, m_history_path); // directly call the save task function to save without delay saveHistory(*m_historyInactive, m_historyPath); // directly call the save task function to save without delay
LOG_INFO("Save History");
} }
// update filtered data // update filtered data
m_info_filtered.update(m_last_status); m_statusFiltered.update(m_statusLast);
(*m_active_history)[m_counter_status] = m_last_status; (*m_historyActive)[m_statusCounter] = m_statusLast;
if (m_on_message_cb && m_counter_status % 10 == 0) // callback
if (m_onFilteredStatusUpdate && m_statusCounter % m_filterSize == 0)
{ {
m_on_message_cb(m_info_filtered); m_onFilteredStatusUpdate(m_statusFiltered);
} }
// update data counter // update data counter
m_counter_status++; m_statusCounter++;
} }
else else
{ {
if (millis() - m_last_data > c_idle_time) if (millis() - m_dataLast > c_idleTime)
{ {
if (m_counter_status > 0 && !m_partial_save) if (m_statusCounter > 0 && !m_savePartial)
{ {
LOG_DEBUG("Save Partial: ", m_counter_status); LOG_DEBUG("Save Partial: ", m_statusCounter);
m_active_history->resize(m_counter_status); m_historyActive->resize(m_statusCounter);
saveHistory(*m_active_history, m_history_path); saveHistory(*m_historyActive, m_historyPath);
m_active_history->resize(m_max_history); m_historyActive->resize(m_historyMax);
m_counter_status = 0; m_statusCounter = 0;
m_partial_save = true; m_savePartial = true;
} }
m_manager_status = rtTaskStatus::IDLE; m_managerStatus = rtTaskStatus::IDLE;
} }
delay(5); // yeld to another task
} }
} }
@@ -396,22 +445,22 @@ const bool rtIgnitionTask::start()
m_params.rt_stack_size, m_params.rt_stack_size,
(void *)&m_params, (void *)&m_params,
m_params.rt_priority, m_params.rt_priority,
&m_rt_handle, &m_rtHandle,
m_core); m_core);
const bool success = task_success == pdPASS && m_rt_handle != nullptr; const bool success = task_success == pdPASS && m_rtHandle != nullptr;
if (success) if (success)
m_manager_status = rtTaskStatus::IDLE; m_managerStatus = rtTaskStatus::IDLE;
return success; return success;
} }
const bool rtIgnitionTask::stop() const bool rtIgnitionTask::stop()
{ {
LOG_WARN("Ending Task [", m_params.name.c_str(), "]"); LOG_WARN("Ending Task [", m_params.name.c_str(), "]");
if (m_rt_handle) if (m_rtHandle)
{ {
m_params.rt_running = false; m_params.rt_running = false;
m_rt_handle = nullptr; m_rtHandle = nullptr;
m_manager_status = rtTaskStatus::STOPPED; m_managerStatus = rtTaskStatus::STOPPED;
return true; return true;
} }
return false; return false;
@@ -419,26 +468,26 @@ const bool rtIgnitionTask::stop()
const ignitionBoxStatus rtIgnitionTask::getLast() const const ignitionBoxStatus rtIgnitionTask::getLast() const
{ {
return m_last_status; return m_statusLast;
} }
const ignitionBoxStatusFiltered rtIgnitionTask::getFiltered() const const ignitionBoxStatusFiltered rtIgnitionTask::getFiltered() const
{ {
return m_info_filtered; return m_statusFiltered;
} }
const rtIgnitionTask::rtTaskStatus rtIgnitionTask::getStatus() const const rtIgnitionTask::rtTaskStatus rtIgnitionTask::getStatus() const
{ {
return m_manager_status; return m_managerStatus;
} }
void rtIgnitionTask::enableSave(const bool enable, const std::filesystem::path filename) void rtIgnitionTask::enableSave(const bool enable, const std::filesystem::path filename)
{ {
m_enable_save = enable; m_historySaveEnable = enable;
if (enable && !filename.empty()) if (enable && !filename.empty())
{ {
LOG_WARN("Save History Enabled Task [", m_params.name.c_str(), "]"); LOG_WARN("Save History Enabled Task [", m_params.name.c_str(), "]");
m_history_path = m_filesystem.mountpoint() / filename; m_historyPath = m_filesystem.mountpoint() / filename;
} }
else else
{ {
@@ -448,13 +497,13 @@ void rtIgnitionTask::enableSave(const bool enable, const std::filesystem::path f
void rtIgnitionTask::onMessage(std::function<void(ignitionBoxStatusFiltered)> callaback) void rtIgnitionTask::onMessage(std::function<void(ignitionBoxStatusFiltered)> callaback)
{ {
m_on_message_cb = callaback; m_onFilteredStatusUpdate = callaback;
} }
void rtIgnitionTask::saveHistory(const rtIgnitionTask::PSHistory &history, const std::filesystem::path &file_name) void rtIgnitionTask::saveHistory(const rtIgnitionTask::PSHistory &history, const std::filesystem::path &fileName)
{ {
// Lock filesystem mutex to avoid concurrent access // Lock filesystem mutex to avoid concurrent access
std::lock_guard<std::mutex> fs_lock(m_fs_mutex); std::lock_guard<std::mutex> fs_lock(m_filesystemMutex);
// Check for free space // Check for free space
if (LittleFS.totalBytes() - LittleFS.usedBytes() < history.size() * sizeof(ignitionBoxStatus)) // check if at least 1MB is free for saving history if (LittleFS.totalBytes() - LittleFS.usedBytes() < history.size() * sizeof(ignitionBoxStatus)) // check if at least 1MB is free for saving history
@@ -464,26 +513,26 @@ void rtIgnitionTask::saveHistory(const rtIgnitionTask::PSHistory &history, const
} }
// create complete file path // create complete file path
const std::filesystem::path mount_point = std::filesystem::path(m_filesystem.mountpoint()); const std::filesystem::path mountPoint = std::filesystem::path(m_filesystem.mountpoint());
std::filesystem::path file_path = file_name; std::filesystem::path filePath = fileName;
if (file_name.root_path() != mount_point) if (fileName.root_path() != mountPoint)
file_path = mount_point / file_name; filePath = mountPoint / fileName;
// if firt save remove old file and create new // if firt save remove old file and create new
auto save_flags = std::ios::out; auto saveFlags = std::ios::out;
if (m_first_save) if (m_saveFirst)
{ {
save_flags |= std::ios::trunc; // overwrite existing file saveFlags |= std::ios::trunc; // overwrite existing file
m_filesystem.remove(file_path.c_str()); // ensure file is removed before saving to avoid issues with appending to existing file in SPIFFS m_filesystem.remove(filePath.c_str()); // ensure file is removed before saving to avoid issues with appending to existing file in SPIFFS
LOG_INFO("Saving history to Flash, new file:", file_path.c_str()); LOG_INFO("Saving history to Flash, new file:", filePath.c_str());
} }
else // else append to existing file else // else append to existing file
{ {
save_flags |= std::ios::app; // append to new file saveFlags |= std::ios::app; // append to new file
LOG_INFO("Saving history to Flash, appending to existing file:", file_path.c_str()); LOG_INFO("Saving history to Flash, appending to existing file:", filePath.c_str());
} }
std::ofstream ofs(file_path, save_flags); std::ofstream ofs(filePath, saveFlags);
if (ofs.fail()) if (ofs.fail())
{ {
LOG_ERROR("Failed to open file for writing"); LOG_ERROR("Failed to open file for writing");
@@ -491,38 +540,38 @@ void rtIgnitionTask::saveHistory(const rtIgnitionTask::PSHistory &history, const
} }
// write csv header // write csv header
if (m_first_save) if (m_saveFirst)
{ {
ofs << "TS,EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12," ofs << "TS,EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12,"
<< "EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34," << "EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34,"
<< "ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS" << "ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS"
<< std::endl; << std::endl;
ofs.flush(); ofs.flush();
m_first_save = false; m_saveFirst = false;
} }
for (const auto &entry : history) for (const auto &entry : history)
{ {
ofs << std::to_string(entry.timestamp) << "," ofs << std::to_string(entry.timestamp) << ","
<< std::to_string(entry.coils12.n_events) << "," << std::to_string(entry.coils12.nEvents) << ","
<< std::to_string(entry.coils12.spark_delay) << "," << std::to_string(entry.coils12.sparkDelay) << ","
<< std::string(sparkStatusNames.at(entry.coils12.spark_status)) << "," << std::string(sparkStatusNames.at(entry.coils12.sparkStatus)) << ","
<< std::to_string(entry.coils12.peak_p_in) << "," << std::to_string(entry.coils12.peakPos) << ","
<< std::to_string(entry.coils12.peak_n_in) << "," << std::to_string(entry.coils12.peakNeg) << ","
<< std::to_string(entry.coils12.peak_p_out) << "," << std::to_string(entry.coils12.trigLevelPos) << ","
<< std::to_string(entry.coils12.peak_n_out) << "," << std::to_string(entry.coils12.trigLevelNeg) << ","
<< std::string(softStartStatusNames.at(entry.coils12.sstart_status)) << "," << std::string(softStartStatusNames.at(entry.coils12.softStartStatus)) << ","
<< std::to_string(entry.coils34.n_events) << "," << std::to_string(entry.coils34.nEvents) << ","
<< std::to_string(entry.coils34.spark_delay) << "," << std::to_string(entry.coils34.sparkDelay) << ","
<< std::string(sparkStatusNames.at(entry.coils34.spark_status)) << "," << std::string(sparkStatusNames.at(entry.coils34.sparkStatus)) << ","
<< std::to_string(entry.coils34.peak_p_in) << "," << std::to_string(entry.coils34.peakPos) << ","
<< std::to_string(entry.coils34.peak_n_in) << "," << std::to_string(entry.coils34.peakNeg) << ","
<< std::to_string(entry.coils34.peak_p_out) << "," << std::to_string(entry.coils34.trigLevelPos) << ","
<< std::to_string(entry.coils34.peak_n_out) << "," << std::to_string(entry.coils34.trigLevelNeg) << ","
<< std::string(softStartStatusNames.at(entry.coils34.sstart_status)) << "," << std::string(softStartStatusNames.at(entry.coils34.softStartStatus)) << ","
<< std::to_string(entry.eng_rpm) << "," << std::to_string(entry.engRpm) << ","
<< std::to_string(entry.adc_read_time) << "," << std::to_string(entry.adcReadTime) << ","
<< std::to_string(entry.n_queue_errors); << std::to_string(entry.nQueueErrors);
ofs << std::endl; ofs << std::endl;
ofs.flush(); ofs.flush();
} }

View File

@@ -22,9 +22,6 @@
// DEVICES // DEVICES
#include "devices.h" #include "devices.h"
// Global Variables and Flags
const uint32_t spark_timeout_max = 500; // in microseconds
// Debug Variables // Debug Variables
#ifdef DEBUG #ifdef DEBUG
static const std::map<const uint32_t, const char *> names = { static const std::map<const uint32_t, const char *> names = {
@@ -46,32 +43,32 @@ public:
// RT task Interrupt parameters // RT task Interrupt parameters
struct rtTaskInterruptParams struct rtTaskInterruptParams
{ {
void (*isr_ptr)(void *); void (*isrPtr)(void *);
const uint8_t trig_pin_12p; const uint8_t trigPin_12p;
const uint8_t trig_pin_12n; const uint8_t trigPin_12n;
const uint8_t trig_pin_34p; const uint8_t trigPin_34p;
const uint8_t trig_pin_34n; const uint8_t trigPin_34n;
const uint8_t spark_pin_12; const uint8_t sparkPin_12;
const uint8_t spark_pin_34; const uint8_t sparkPin_34;
}; };
// RT Task Peak Detector Reset pins // RT Task Peak Detector Reset pins
struct rtTaskIOParams struct rtTaskIOParams
{ {
const uint32_t expander_addr; const uint32_t expander_addr;
const uint8_t pot_cs_12; const uint32_t pot_cs_12;
const uint8_t pot_cs_34; const uint32_t pot_cs_34;
const uint8_t ss_force; const uint32_t ss_force;
const uint8_t ss_inhibit_12; const uint32_t ss_inhibit_12;
const uint8_t ss_inhibit_34; const uint32_t ss_inhibit_34;
const uint8_t sh_disch_12; const uint32_t sh_disch_12;
const uint8_t sh_disch_34; const uint32_t sh_disch_34;
const uint8_t sh_arm_12; const uint32_t sh_arm_12;
const uint8_t sh_arm_34; const uint32_t sh_arm_34;
const uint8_t relay_in_12; const uint32_t relay_in_12;
const uint8_t relay_in_34; const uint32_t relay_in_34;
const uint8_t relay_out_12; const uint32_t relay_out_12;
const uint8_t relay_out_34; const uint32_t relay_out_34;
}; };
// RT task parameters // RT task parameters
@@ -84,7 +81,7 @@ public:
const rtTaskInterruptParams rt_int; // interrupt pins to attach const rtTaskInterruptParams rt_int; // interrupt pins to attach
const rtTaskIOParams rt_io; // reset ping for peak detectors const rtTaskIOParams rt_io; // reset ping for peak detectors
QueueHandle_t rt_queue; // queue for task io QueueHandle_t rt_queue; // queue for task io
const std::shared_ptr<Devices> dev; Devices *dev;
}; };
enum rtTaskStatus enum rtTaskStatus
@@ -123,37 +120,39 @@ private: // static functions for FreeRTOS
private: private:
bool m_running = true; bool m_running = true;
rtTaskStatus m_manager_status = INIT; rtTaskStatus m_managerStatus = INIT;
rtTaskParams m_params; rtTaskParams m_params;
const uint8_t m_core; const uint8_t m_core;
std::string m_managerTaskName;
TaskHandle_t m_rtHandle = nullptr;
TaskHandle_t m_managerHandle = nullptr;
QueueHandle_t m_rtQueueHandle = nullptr;
TaskHandle_t m_rt_handle = nullptr; const uint32_t m_historyMax;
TaskHandle_t m_manager_handle = nullptr; bool m_historySaveEnable = false;
QueueHandle_t m_queue = nullptr; std::filesystem::path m_historyPath;
PSHistory m_historyBuf0;
PSHistory m_historyBuf1;
std::unique_ptr<PSHistory> m_historyActive;
std::unique_ptr<PSHistory> m_historyInactive;
bool m_enable_save = false; bool m_savePartial = false;
std::filesystem::path m_history_path; bool m_saveFirst = true;
const uint32_t m_max_history;
PSHistory m_history_0;
PSHistory m_history_1;
std::unique_ptr<PSHistory> m_active_history;
std::unique_ptr<PSHistory> m_save_history;
fs::FS &m_filesystem; fs::FS &m_filesystem;
std::mutex &m_fs_mutex; std::mutex &m_filesystemMutex;
bool m_partial_save = false; uint8_t m_filterSize = 10;
bool m_first_save = true; uint32_t m_statusCounter = 0;
uint32_t m_dataLast = 0;
ignitionBoxStatus m_statusLast;
ignitionBoxStatusFiltered m_statusFiltered;
uint32_t m_counter_status = 0; std::function<void(ignitionBoxStatusFiltered)> m_onFilteredStatusUpdate = nullptr;
uint32_t m_last_data = 0;
ignitionBoxStatus m_last_status;
ignitionBoxStatusFiltered m_info_filtered;
std::function<void(ignitionBoxStatusFiltered)> m_on_message_cb = nullptr; // Global Variables and Flags
static const uint32_t c_sparkTimeoutMax = 500; // in microseconds
static const uint32_t c_idle_time = 10000; // in mS static const uint32_t c_idleTime = 10000; // in mS
static const uint32_t c_spark_timeout_max = 500; // uS static const uint8_t c_adcTime = 4; // in mS
static const uint8_t c_adc_time = 4; // in mS static const uint8_t c_ioTime = 2; // in mS
static const uint8_t c_io_time = 2; // in mS
}; };

View File

@@ -44,30 +44,30 @@ void printInfo(const ignitionBoxStatus &info)
setCursor(0, 0); setCursor(0, 0);
printField("++ Timestamp ++", (uint32_t)info.timestamp); printField("++ Timestamp ++", (uint32_t)info.timestamp);
Serial.println("========== Coils 12 ============="); Serial.println("========== Coils 12 =============");
printField("Events", info.coils12.n_events); printField("Events", info.coils12.nEvents);
printField("Events Missed", info.coils12.n_missed_firing); printField("Events Missed", info.coils12.nMissedFiring);
printField("Spark Dly", (uint32_t)info.coils12.spark_delay); printField("Spark Dly", (uint32_t)info.coils12.sparkDelay);
printField("Spark Sts", sparkStatusNames.at(info.coils12.spark_status)); printField("Spark Sts", sparkStatusNames.at(info.coils12.sparkStatus));
printField("Peak P_IN", info.coils12.peak_p_in); printField("Peak P_IN", info.coils12.peakPos);
printField("Peak N_IN", info.coils12.peak_n_in); printField("Peak N_IN", info.coils12.peakNeg);
printField("Peak P_OUT", info.coils12.peak_p_out); printField("Peak P_OUT", info.coils12.trigLevelPos);
printField("Peak N_OUT", info.coils12.peak_n_out); printField("Peak N_OUT", info.coils12.trigLevelNeg);
printField("Soft Start ", softStartStatusNames.at(info.coils12.sstart_status)); printField("Soft Start ", softStartStatusNames.at(info.coils12.softStartStatus));
Serial.println("========== Coils 34 ============="); Serial.println("========== Coils 34 =============");
printField("Events", info.coils34.n_events); printField("Events", info.coils34.nEvents);
printField("Events Missed", info.coils34.n_missed_firing); printField("Events Missed", info.coils34.nMissedFiring);
printField("Spark Dly", (uint32_t)info.coils34.spark_delay); printField("Spark Dly", (uint32_t)info.coils34.sparkDelay);
printField("Spark Sts", sparkStatusNames.at(info.coils34.spark_status)); printField("Spark Sts", sparkStatusNames.at(info.coils34.sparkStatus));
printField("Peak P_IN", info.coils34.peak_p_in); printField("Peak P_IN", info.coils34.peakPos);
printField("Peak N_IN", info.coils34.peak_n_in); printField("Peak N_IN", info.coils34.peakNeg);
printField("Peak P_OUT", info.coils34.peak_p_out); printField("Peak P_OUT", info.coils34.trigLevelPos);
printField("Peak N_OUT", info.coils34.peak_n_out); printField("Peak N_OUT", info.coils34.trigLevelNeg);
printField("Soft Start ", softStartStatusNames.at(info.coils34.sstart_status)); printField("Soft Start ", softStartStatusNames.at(info.coils34.softStartStatus));
Serial.println("============ END ==============="); Serial.println("============ END ===============");
Serial.println(); Serial.println();
printField("Engine RPM", info.eng_rpm); printField("Engine RPM", info.engRpm);
printField("ADC Read Time", info.adc_read_time); printField("ADC Read Time", info.adcReadTime);
printField("Queue Errors", info.n_queue_errors); printField("Queue Errors", info.nQueueErrors);
} }

View File

@@ -7,7 +7,7 @@
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_spi_flash.h" #include "spi_flash_mmap.h"
#include "esp_partition.h" #include "esp_partition.h"
#include "LittleFS.h" #include "LittleFS.h"
@@ -49,23 +49,27 @@ void printBar(Print &printer, const char *label, size_t used, size_t total, cons
{ {
float perc = total > 0 ? ((float)used / total) : 0; float perc = total > 0 ? ((float)used / total) : 0;
int filled = perc * BAR_WIDTH; int filled = perc * BAR_WIDTH;
char str[256] = {0};
uint16_t k(0);
printer.printf("%s%-12s [" COLOR_RESET, color, label); k += sprintf(str, "%s%-12s [" COLOR_RESET, color, label);
for (int i = 0; i < BAR_WIDTH; i++) for (int i = 0; i < BAR_WIDTH; i++)
{ {
if (i < filled) if (i < filled)
printer.printf("%s#%s", color, COLOR_RESET); k += sprintf(&str[k], "%s#%s", color, COLOR_RESET);
else else
printer.printf("-"); k += sprintf(&str[k], "-");
} }
printer.printf("] %s%6.2f%%%s (%5.3f/%5.3f)MB\n", sprintf(&str[k], "] %s%6.2f%%%s (%5.3f/%5.3f)MB",
color, color,
perc * 100.0, perc * 100.0,
COLOR_RESET, COLOR_RESET,
(used / 1024.0f / 1024.0f), (used / 1024.0f / 1024.0f),
(total / 1024.0f / 1024.0f)); (total / 1024.0f / 1024.0f));
printer.println(str);
} }
void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t &a, const TaskStatus_t &b)> orderBy) void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t &a, const TaskStatus_t &b)> orderBy)
@@ -95,18 +99,19 @@ void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t
// Compute system total runtime // Compute system total runtime
ulCurrentRunTime = ulTotalRunTime - ulLastRunTime; ulCurrentRunTime = ulTotalRunTime - ulLastRunTime;
ulCurrentRunTime = ulCurrentRunTime > 0 ? ulCurrentRunTime : 1;
ulLastRunTime = ulTotalRunTime; ulLastRunTime = ulTotalRunTime;
// PRINT MEMORY INFO // PRINT MEMORY INFO
printer.printf("\033[H"); printer.printf("\033[H");
printer.printf(COLOR_LBLUE "=================== ESP32 SYSTEM MONITOR ===================\n" COLOR_RESET); printer.printf(COLOR_WHITE "====================== ESP32 SYSTEM MONITOR ======================\n" COLOR_RESET);
std::string buffer; std::string buffer;
time_t now = time(nullptr); time_t now = time(nullptr);
struct tm *t = localtime(&now); struct tm *t = localtime(&now);
buffer.resize(64); buffer.resize(64);
strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t); strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
printer.printf(COLOR_YELLOW "=============== Datetime: %s ===============\n\n" COLOR_RESET, buffer.c_str()); printer.printf(COLOR_WHITE "=================== Datetime: %s ==================\n\n" COLOR_RESET, buffer.c_str());
// ===== HEAP ===== // ===== HEAP =====
size_t freeHeap = esp_get_free_heap_size(); size_t freeHeap = esp_get_free_heap_size();
@@ -116,7 +121,7 @@ void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t
// ===== RAM INTERNA ===== // ===== RAM INTERNA =====
size_t freeInternal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); size_t freeInternal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
size_t totalInternal = heap_caps_get_total_size(MALLOC_CAP_INTERNAL); size_t totalInternal = heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
printBar(printer, "INTERNAL", totalInternal - freeInternal, totalInternal, COLOR_BLUE); printBar(printer, "INTERNAL", totalInternal - freeInternal, totalInternal, COLOR_CYAN);
// ===== PSRAM ===== // ===== PSRAM =====
size_t totalPsram = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); size_t totalPsram = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
@@ -134,17 +139,6 @@ void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t
ESP_PARTITION_SUBTYPE_APP_FACTORY, ESP_PARTITION_SUBTYPE_APP_FACTORY,
NULL); NULL);
if (app_partition)
{
size_t totalAPP = app_partition->size; // dimensione reale partizione
size_t sketchSize = ESP.getSketchSize();
printBar(printer, "FLASH APP", sketchSize, totalAPP, COLOR_CYAN);
}
else
{
printer.printf(COLOR_YELLOW "%-12s [NOT FOUND]\n" COLOR_RESET, "FLASH APP");
}
// ===== LITTLEFS (corretto con partition table) ===== // ===== LITTLEFS (corretto con partition table) =====
const esp_partition_t *fs_partition = const esp_partition_t *fs_partition =
esp_partition_find_first(ESP_PARTITION_TYPE_DATA, esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
@@ -164,13 +158,15 @@ void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t
// ===== MIN HEAP ===== // ===== MIN HEAP =====
size_t minHeap = esp_get_minimum_free_heap_size(); size_t minHeap = esp_get_minimum_free_heap_size();
printer.printf("%s\nMin Heap Ever:%s %u KB\n\n", COLOR_RED, COLOR_RESET, minHeap / 1024); printer.printf("%s\nMin Heap Ever:%s %u KB\n", COLOR_RED, COLOR_RESET, minHeap / 1024);
size_t max_block = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
printer.printf("%sMax PSRAM Block:%s %u KB\n\n", COLOR_RED, COLOR_RESET, max_block / 1024);
// Print Runtime Information // Print Runtime Information
printer.printf("Tasks: %u, Runtime: %lus, Period: %luus\r\n", uxArraySize, ulTotalRunTime / 1000000, ulCurrentRunTime); printer.printf("Tasks: %u, Runtime: %lus, Period: %luus\n", uxArraySize, ulTotalRunTime / 1000000, ulCurrentRunTime);
// Print Task Headers // Print Task Headers
printer.printf("Num\t Name\tLoad\tPrio\t Free\tCore\tState\r\n"); printer.printf("Num\t Name\tLoad\tPrio\t Free\tCore\tState\n");
for (const auto &task : pxTaskStatusArray) for (const auto &task : pxTaskStatusArray)
{ {
@@ -183,7 +179,7 @@ void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t
"\t%3lu%%" "\t%3lu%%"
"\t%4u\t%5lu" "\t%4u\t%5lu"
"\t%4c" "\t%4c"
"\t%s\r\n", "\t%s\n",
task.xTaskNumber, task.pcTaskName, task.xTaskNumber, task.pcTaskName,
ulTaskRunTime, ulTaskRunTime,
task.uxCurrentPriority, task.usStackHighWaterMark, task.uxCurrentPriority, task.usStackHighWaterMark,

View File

@@ -1,9 +1,4 @@
#include <webserver.h> #include <webserver.h>
#include <ArduinoJson.h>
static std::map<const std::string, AstroWebServer::c_commandEnum> s_webserverCommands = {
{"setTime", AstroWebServer::SET_TIME},
};
void on_ping(TimerHandle_t xTimer) void on_ping(TimerHandle_t xTimer)
{ {
@@ -14,7 +9,7 @@ void on_ping(TimerHandle_t xTimer)
ws->cleanupClients(); ws->cleanupClients();
} }
AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem) AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : c_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem)
{ {
LOG_DEBUG("Initializing Web Server"); LOG_DEBUG("Initializing Web Server");
m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client, m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client,
@@ -31,12 +26,18 @@ AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : m_port(
m_webserver.begin(); m_webserver.begin();
m_websocket.enable(true); m_websocket.enable(true);
m_pingTimer = xTimerCreate("wsPingTimer", pdMS_TO_TICKS(2000), pdTRUE, (void *)&m_websocket, on_ping); m_pingTimer = xTimerCreate("wsPingTimer", pdMS_TO_TICKS(c_pingTime), pdTRUE, (void *)&m_websocket, on_ping);
xTimerStart(m_pingTimer, pdMS_TO_TICKS(10));
registerWsCommand("setTime", [this](const ArduinoJson::JsonDocument &doc)
{ onSetTme(doc); });
LOG_DEBUG("Webserver Init OK"); LOG_DEBUG("Webserver Init OK");
} }
AstroWebServer::~AstroWebServer() AstroWebServer::~AstroWebServer()
{ {
xTimerStop(m_pingTimer, 0);
xTimerDelete(m_pingTimer, pdMS_TO_TICKS(10)); xTimerDelete(m_pingTimer, pdMS_TO_TICKS(10));
m_webserver.removeHandler(&m_websocket); m_webserver.removeHandler(&m_websocket);
m_webserver.end(); m_webserver.end();
@@ -50,6 +51,21 @@ void AstroWebServer::sendWsData(const String &data)
} }
} }
void AstroWebServer::registerWsCommand(const std::string &cmd, const WScommand func)
{
if (cmd.empty() || m_webserverCommands.contains(cmd))
return;
if (!func)
return;
m_webserverCommands[cmd] = func;
}
void AstroWebServer::unRegisterWsCommand(const std::string &cmd)
{
if (m_webserverCommands.contains(cmd))
m_webserverCommands.erase(cmd);
}
void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
{ {
switch (type) switch (type)
@@ -75,38 +91,13 @@ void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *cli
LOG_ERROR("WS Client unable to deserialize Json"); LOG_ERROR("WS Client unable to deserialize Json");
return; return;
} }
if (!doc["cmd"].is<std::string>() || !s_webserverCommands.contains(doc["cmd"])) if (!doc["cmd"].is<std::string>() || !m_webserverCommands.contains(doc["cmd"]))
{ {
LOG_WARN("WS Client Invalid Json command [", doc["cmd"].as<std::string>().c_str(), "]"); LOG_WARN("WS Client Invalid Json command [", doc["cmd"].as<std::string>().c_str(), "]");
return; return;
} }
std::string buffer; // execute callback function
switch (s_webserverCommands.at(doc["cmd"])) m_webserverCommands[doc["cmd"]](doc);
{
case SET_TIME:
{
auto epoch = doc["time"].as<time_t>();
timeval te{
.tv_sec = epoch,
.tv_usec = 0,
};
timezone tz{
.tz_minuteswest = 0,
.tz_dsttime = DST_MET,
};
settimeofday(&te, &tz);
time_t now = time(nullptr);
struct tm *t = localtime(&now);
buffer.resize(64);
strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
LOG_DEBUG("WS Client set Datetime to: ", buffer.c_str());
break;
}
default:
// call external command callback
break;
}
} }
} }
} }
@@ -164,3 +155,23 @@ void AstroWebServer::onUploadHandler(AsyncWebServerRequest *request, const Strin
LOG_INFO("Uploaded file to LittleFS:", filename.c_str()); LOG_INFO("Uploaded file to LittleFS:", filename.c_str());
} }
} }
void AstroWebServer::onSetTme(const ArduinoJson::JsonDocument &doc)
{
std::string buffer;
auto epoch = doc["time"].as<time_t>();
timeval te{
.tv_sec = epoch,
.tv_usec = 0,
};
timezone tz{
.tz_minuteswest = 0,
.tz_dsttime = DST_MET,
};
settimeofday(&te, &tz);
time_t now = time(nullptr);
struct tm *t = localtime(&now);
buffer.resize(64);
strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
LOG_DEBUG("WS Client set Datetime to: ", buffer.c_str());
}

View File

@@ -8,11 +8,13 @@
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <filesystem> #include <filesystem>
#include <map> #include <map>
#include <FS.h> #include <FS.h>
#include <ArduinoJson.h>
class AstroWebServer class AstroWebServer
{ {
public:
using WScommand = std::function<void(const ArduinoJson::JsonDocument &)>;
public: public:
AstroWebServer(const uint8_t port, fs::FS &filesystem); AstroWebServer(const uint8_t port, fs::FS &filesystem);
@@ -20,6 +22,9 @@ public:
void sendWsData(const String &data); void sendWsData(const String &data);
void registerWsCommand(const std::string &cmd, const WScommand func);
void unRegisterWsCommand(const std::string &cmd);
private: private:
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client,
AwsEventType type, void *arg, uint8_t *data, size_t len); AwsEventType type, void *arg, uint8_t *data, size_t len);
@@ -27,22 +32,16 @@ private:
void onUploadRequest(AsyncWebServerRequest *request); void onUploadRequest(AsyncWebServerRequest *request);
void onUploadHandler(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final); void onUploadHandler(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
void onStart(AsyncWebServerRequest *request); void onSetTme(const ArduinoJson::JsonDocument &doc);
void onStop(AsyncWebServerRequest *request);
void onDownload(AsyncWebServerRequest *request);
private: private:
const uint8_t m_port = 80; const uint8_t c_port = 80;
const uint32_t c_pingTime = 5000;
fs::FS &m_filesystem; fs::FS &m_filesystem;
AsyncWebServer m_webserver; AsyncWebServer m_webserver;
AsyncWebSocket m_websocket; AsyncWebSocket m_websocket;
bool m_uploadFailed = false; bool m_uploadFailed = false;
fs::File m_uploadFile; fs::File m_uploadFile;
TimerHandle_t m_pingTimer = NULL; TimerHandle_t m_pingTimer = NULL;
std::map<const std::string, AstroWebServer::WScommand> m_webserverCommands;
public:
enum c_commandEnum
{
SET_TIME
};
}; };

View File

@@ -22,7 +22,7 @@ build_type = release
[env:esp32-devtest-debug] [env:esp32-devtest-debug]
board = esp32dev board = esp32dev
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
lib_deps = lib_deps =
hideakitai/DebugLog@^0.8.4 hideakitai/DebugLog@^0.8.4
board_build.flash_size = 4MB board_build.flash_size = 4MB

View File

@@ -0,0 +1,12 @@
#pragma once
// ANSI colors
#define COLOR_RESET "\033[0m"
#define COLOR_RED "\033[31m"
#define COLOR_GREEN "\033[32m"
#define COLOR_BLUE "\033[34m"
#define COLOR_MAGENTA "\033[35m"
#define COLOR_CYAN "\033[36m"
#define COLOR_YELLOW "\033[33m"
#define COLOR_WHITE "\033[37m"
#define COLOR_LBLUE "\033[94m"

View File

@@ -4,6 +4,8 @@
#include <DebugLog.h> #include <DebugLog.h>
#include "timer.h" #include "timer.h"
#include "colors.h"
#include <map> #include <map>
static hw_timer_t *timerA = NULL; static hw_timer_t *timerA = NULL;
@@ -17,6 +19,12 @@ static uint32_t count = 0;
#define SPARK_DLY_MIN 10 #define SPARK_DLY_MIN 10
#define SPARK_DLY_MAX 490 #define SPARK_DLY_MAX 490
#define COIL_PULSE_MIN 100
#define COIL_PULSE_MAX 1000
#define SPARK_PULSE_MIN 10
#define SPARK_PULSE_MAX 500
#define PAUSE_LONG_MIN 5000 #define PAUSE_LONG_MIN 5000
#define PAUSE_LONG_MAX PAUSE_LONG_MIN * 100 #define PAUSE_LONG_MAX PAUSE_LONG_MIN * 100
@@ -30,7 +38,8 @@ void clearScreen()
Serial.flush(); Serial.flush();
} }
static double filtered_rpm = 0; static uint32_t set_rpm = 500;
static uint32_t set_delay = 100;
static const std::map<const uint32_t, const char *> pin2Name = { static const std::map<const uint32_t, const char *> pin2Name = {
{PIN_TRIG_A12P, "HIGH_PIN_TRIG_A12P"}, {PIN_TRIG_A12P, "HIGH_PIN_TRIG_A12P"},
@@ -68,7 +77,7 @@ static timerStatus stsB = {
.clock_period_us = (uint32_t)PERIOD_US, .clock_period_us = (uint32_t)PERIOD_US,
.pause_long_us = 10000, .pause_long_us = 10000,
.pause_short_us = 1000, .pause_short_us = 1000,
.coil_pulse_us = 1000, .coil_pulse_us = 500,
.spark_pulse_us = 100, .spark_pulse_us = 100,
.spark_delay_us = 50, .spark_delay_us = 50,
.pins = { .pins = {
@@ -83,11 +92,14 @@ static timerStatus stsB = {
static bool isEnabled_A = false; static bool isEnabled_A = false;
static bool isEnabled_B = false; static bool isEnabled_B = false;
static String last_command;
void setup() void setup()
{ {
Serial.begin(115200); Serial.begin(115200);
delay(1000); delay(1000);
Serial.setTimeout(100);
LOG_ATTACH_SERIAL(Serial); LOG_ATTACH_SERIAL(Serial);
pinMode(PIN_TRIG_A12P, OUTPUT); pinMode(PIN_TRIG_A12P, OUTPUT);
@@ -133,9 +145,89 @@ void setup()
void loop() void loop()
{ {
LOG_INFO("Loop: ", count++); clearScreen();
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; Serial.printf("\t++++ Loop: %u ++++\n", count++);
if (isEnabled_A)
Serial.println("==== System A is" COLOR_GREEN " ENABLED" COLOR_RESET " ====");
else
Serial.println("==== System A is" COLOR_RED " DISABLED" COLOR_RESET " ====");
if (isEnabled_B)
Serial.println("==== System B is" COLOR_GREEN " ENABLED" COLOR_RESET " ====");
else
Serial.println("==== System B is" COLOR_RED " DISABLED" COLOR_RESET " ====");
Serial.printf("Spark Delay uS: %u\n", stsA.spark_delay_us);
Serial.printf("Soft Start: %s\n", stsA.soft_start ? "ENABLED" : "DISABLED");
Serial.printf("Engine Rpm: %u\n", (uint32_t)(set_rpm));
Serial.printf("Coil Pulse: %u uS\n", stsA.coil_pulse_us);
Serial.printf("Spark Pulse: %u uS\n", stsA.spark_pulse_us);
Serial.println(COLOR_CYAN "-------------------------------------");
Serial.println("E[a/b] > Enable Box a/b | D[a/b] > Disable a/b");
Serial.println("S[ddd] > Spark Delay | R[dddd] > Engine RPM");
Serial.println("C[ddd] > Spark Pulse | P[ddd] > Coil Pulse");
Serial.println("-------------------------------------" COLOR_RESET);
Serial.printf("Last Command: %s\n", last_command.c_str());
auto str = Serial.readStringUntil('\n');
if (!str.isEmpty())
{
last_command = str;
const auto cmd = str.charAt(0);
char c;
switch (cmd)
{
case 'E':
{
char box;
sscanf(str.c_str(), "%c%c\n", &c, &box);
if (box == 'a' && !isEnabled_A)
{
timerStart(timerA);
isEnabled_A = true;
}
else if (box == 'b' && !isEnabled_B)
{
timerStart(timerB);
isEnabled_B = true;
}
break;
}
case 'D':
{
char c;
char box;
sscanf(str.c_str(), "%c%c\n", &c, &box);
if (box == 'a' && isEnabled_A)
{
timerStop(timerA);
isEnabled_A = false;
}
else if (box == 'b' && isEnabled_B)
{
timerStop(timerB);
isEnabled_B = false;
}
break;
}
case 'R':
{
int new_rpm;
sscanf(str.c_str(), "%c%d\n", &c, &new_rpm);
new_rpm = min(RPM_MAX, max(RPM_MIN, new_rpm));
stsA.pause_long_us = (uint32_t)(60000000.0f / (float)new_rpm / 2.0f);
stsB.pause_long_us = stsA.pause_long_us;
set_rpm = (uint32_t)new_rpm;
break;
}
case 'S':
{
int new_delay;
sscanf(str.c_str(), "%c%d\n", &c, &new_delay);
new_delay = min(SPARK_DLY_MAX, max(SPARK_DLY_MIN, new_delay));
stsA.spark_delay_us = (uint32_t)(new_delay);
if (stsA.spark_delay_us > (SPARK_DLY_MIN + SPARK_DLY_MAX) / 2) if (stsA.spark_delay_us > (SPARK_DLY_MIN + SPARK_DLY_MAX) / 2)
{ {
stsA.soft_start = true; stsA.soft_start = true;
@@ -147,49 +239,30 @@ void loop()
} }
stsB.soft_start = stsA.soft_start; stsB.soft_start = stsA.soft_start;
stsB.spark_delay_us = stsA.spark_delay_us; stsB.spark_delay_us = stsA.spark_delay_us;
break;
double new_rpm = (double)(map(analogRead(FREQ_POT), 0, 4096, RPM_MIN, RPM_MAX)); }
filtered_rpm = filtered_rpm + 0.1 * (new_rpm - filtered_rpm); case 'P':
stsA.pause_long_us = (uint32_t)(60000000.0f / filtered_rpm / 2.0f);
stsB.pause_long_us = stsA.pause_long_us;
if (isEnabled_A)
LOG_INFO("==== System A is ENABLED ====");
else
LOG_INFO("==== System A is DISABLED ====");
if (isEnabled_B)
LOG_INFO("==== System B is ENABLED ====");
else
LOG_INFO("==== System B is DISABLED ====");
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");
if (digitalRead(ENABLE_PIN_A) == LOW && !isEnabled_A)
{ {
timerStart(timerA); int new_pulse;
isEnabled_A = true; sscanf(str.c_str(), "%c%d\n", &c, &new_pulse);
new_pulse = min(COIL_PULSE_MAX, max(COIL_PULSE_MIN, new_pulse));
stsA.coil_pulse_us = stsB.coil_pulse_us = (uint32_t)new_pulse;
break;
} }
else if (digitalRead(ENABLE_PIN_A) == HIGH && isEnabled_A) case 'C':
{ {
timerStop(timerA); int new_pulse;
isEnabled_A = false; sscanf(str.c_str(), "%c%d\n", &c, &new_pulse);
new_pulse = min(SPARK_PULSE_MAX, max(SPARK_PULSE_MIN, new_pulse));
stsA.spark_pulse_us = stsB.spark_pulse_us = (uint32_t)new_pulse;
break;
}
default:
break;
}
Serial.read();
} }
if (digitalRead(ENABLE_PIN_B) == LOW && !isEnabled_B) str.clear();
{ delay(1000);
timerStart(timerB);
isEnabled_B = true;
}
else if (digitalRead(ENABLE_PIN_B) == HIGH && isEnabled_B)
{
timerStop(timerB);
isEnabled_B = false;
}
delay(100);
clearScreen();
} }