Conversation
…EADME, and CHANGELOG - Add src/eca_config.h: central compile-time configuration - Add src/eca_can.h: opt-in CAN bus module with MCP2515 support - Add eca.dbc: CAN message schema with all signals and error codes - Update src/eca.ino: integrate config header, CAN support, fix preprocessor guards - Expand README.md: full documentation with pinout, mapping tables, CAN guide, DBC usage - Add CHANGELOG.md: version history for v1.0.0 and v2.0.0 - Update LICENSE copyright to 2021-2026 Co-authored-by: nberlette <11234104+nberlette@users.noreply.github.com>
Open
There was a problem hiding this comment.
Pull request overview
Adds an opt-in CAN output path to the Arduino ECA firmware, centralizes compile-time configuration, and documents the full analog + CAN interface (including a DBC schema) for downstream integration.
Changes:
- Introduces centralized configuration (
src/eca_config.h) and a header-only MCP2515 CAN module (src/eca_can.h) gated byECA_ENABLE_CAN. - Updates the main firmware (
src/eca.ino) to use the config header, track status, and transmit CAN frames on an interval. - Adds/updates project documentation and artifacts:
eca.dbc, expandedREADME.md, newCHANGELOG.md, and LICENSE year update.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/eca_config.h | Centralizes pins, scaling, error voltages, and CAN settings/feature flags. |
| src/eca_can.h | Adds MCP2515 CAN wrapper + status code/flag mapping. |
| src/eca.ino | Wires config + CAN into the main loop and error paths; modernizes serial strings. |
| eca.dbc | Defines the CAN message schema (ECA_Data) for tool-based decoding. |
| README.md | Full documentation rewrite covering wiring, mappings, CAN payload, and DBC usage. |
| CHANGELOG.md | Adds a Keep-a-Changelog formatted version history including v2.0.0 entry. |
| LICENSE | Updates copyright years. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
219
to
+236
| unsigned long highTime = pulseIn(PIN_INPUT_SENSOR, HIGH); | ||
| unsigned long lowTime = pulseIn(PIN_INPUT_SENSOR, LOW); | ||
|
|
||
| unsigned long pulseTime = highTime + lowTime; | ||
| float frequency = float(1000000 / pulseTime); | ||
|
|
||
| eContent = getEthanol(pulseTime); | ||
| setVoltageFromEthanol(eContent); | ||
| int eContent = getEthanol(pulseTime); | ||
| float outputVoltage = setVoltageFromEthanol(eContent); | ||
|
|
||
| tempC = getTempC(highTime, lowTime); | ||
| tempF = cToF(tempC); | ||
| int tempC = getTempC(highTime, lowTime); | ||
| int tempF = cToF(tempC); | ||
|
|
||
| #if ECA_ENABLE_CAN | ||
| if (ecaCan.ready()) { | ||
| uint16_t voltageMv = (uint16_t)(outputVoltage * 1000); | ||
| ecaCan.send((uint8_t)eContent, frequency, voltageMv, | ||
| (int8_t)tempC, ecaStatus); | ||
| } |
Comment on lines
13
to
+60
|
|
||
| | Type | Pin | Description | | ||
| | ---------- | -------- | ------------------- | | ||
| | Sensor In | `D8` | `TIMER1 / ICP1` | | ||
| | PWM Output | `D3/D11` | Built-in PWM driver | | ||
| | DAC Output | `A4/A5` | MCP4725 12bit DAC | | ||
| - **FlexFuel sensor input** — reads the 50–150 Hz square-wave from any | ||
| standard GM-style FlexFuel sensor via Timer1 input capture. | ||
| - **Dual analog output** — simultaneous PWM and 12-bit MCP4725 I2C DAC output | ||
| for maximum compatibility. | ||
| - **Configurable voltage range** — default 0.50–4.50 V maps linearly to | ||
| 0–100% ethanol. | ||
| - **Error/fault signaling** — dedicated out-of-band voltages for sensor | ||
| disconnect, contaminated fuel, and high water content. | ||
| - **Optional CAN bus output** — opt-in at compile time; transmits ethanol | ||
| content, frequency, voltage, fuel temperature, and status over CAN using a | ||
| configurable arbitration ID. | ||
| - **Fuel temperature** — derived from the sensor's duty cycle, output via | ||
| serial and (optionally) CAN. | ||
| - **Serial debug output** — logs ethanol percentage and fuel temperature over | ||
| UART at configurable baud rate. | ||
| - **Single config header** — all compile-time options centralized in | ||
| `src/eca_config.h`. | ||
|
|
||
| ## `Hz -> E % -> V` | ||
| --- | ||
|
|
||
| | Input (Hz) | E (%) | Output (V) | | ||
| | :--------- | :----: | :------------------------- | | ||
| | `50 hz` | ` 0 %` | `0.50v` | | ||
| | `100 hz` | `50 %` | `2.25v` | | ||
| | `150 hz` | `100%` | `4.50v` | | ||
| | **Errors** | | | | ||
| | `< 50 hz` | `---` | `4.80v` - contaminated | | ||
| | `> 150 hz` | `---` | `4.90v` - high water level | | ||
| | `<= 0 hz` | `---` | `0.10v` - disconnected | | ||
| ## Hardware | ||
|
|
||
| ## `License` | ||
| ### Supported Boards | ||
|
|
||
| [MIT](https://mit-license.org) © [Nicholas Berlette](https://nick.berlette.com) | ||
| | Board | MCU | Notes | | ||
| |---|---|---| | ||
| | Arduino Nano | ATmega328P | Primary target | | ||
| | Arduino Uno | ATmega328P | Fully compatible | | ||
| | Other ATmega328P boards | ATmega328P | Should work with correct pin mapping | | ||
|
|
||
| ### Required Components | ||
|
|
||
| | Component | Purpose | | ||
| |---|---| | ||
| | FlexFuel sensor (GM-style) | Ethanol content input (50–150 Hz square-wave) | | ||
| | MCP4725 breakout board | 12-bit I2C DAC for precision analog output | | ||
| | MCP2515 CAN module _(optional)_ | CAN bus transceiver (only needed if CAN is enabled) | | ||
|
|
||
| --- | ||
|
|
||
| ## Pinout | ||
|
|
||
| | Type | Pin(s) | Description | | ||
| |---|---|---| | ||
| | Sensor Input | `D10` | FlexFuel sensor signal (Timer1 / ICP1) | | ||
| | PWM Output | `D9` | Built-in PWM analog output | | ||
| | DAC Output | `A4 / A5` | MCP4725 I2C (SDA / SCL) | |
Comment on lines
79
to
+89
| void setVoltage (double volts, bool init = false) | ||
| { | ||
| const int maxVolts = 5.0; | ||
| const int maxVolts = ECA_VOLTAGE_RAIL; | ||
|
|
||
| if (defined(ENABLE_PWM_OUT) && ENABLE_PWM_OUT == 1) | ||
| { | ||
| if (init) { | ||
| pinMode(PIN_OUTPUT_PWM, OUTPUT); | ||
| TCCR1B = TCCR1B & 0b11111000 | 0x01; | ||
| } | ||
| analogWrite(PIN_OUTPUT_PWM, int((PWM_MULTIPLIER * (volts / maxVolts)))); | ||
| #if ECA_ENABLE_PWM_OUT | ||
| if (init) { | ||
| pinMode(PIN_OUTPUT_PWM, OUTPUT); | ||
| TCCR1B = TCCR1B & 0b11111000 | 0x01; | ||
| } | ||
| if (defined(ENABLE_DAC_OUT) && ENABLE_DAC_OUT == 1) | ||
| { | ||
| if (init) { | ||
| dac.begin(0x60); | ||
| } | ||
| dac.setVoltage(int(DAC_MULTIPLIER * (volts / maxVolts)), false); | ||
| analogWrite(PIN_OUTPUT_PWM, int((PWM_MULTIPLIER * (volts / maxVolts)))); | ||
| #endif |
Comment on lines
+30
to
+33
| SG_ SensorFrequency : 8|16@1+ (0.1,0) [0|6553.5] "Hz" Vector__XXX | ||
|
|
||
| // Analog output voltage in millivolts | ||
| SG_ OutputVoltage : 24|16@1+ (1,0) [0|5000] "mV" Vector__XXX |
| // ------------------------------------------------------------------------- | ||
| // Pin assignments | ||
| // ------------------------------------------------------------------------- | ||
| #define PIN_INPUT_SENSOR 10 // FlexFuel sensor input (Timer1 / ICP1) |
Comment on lines
+86
to
+88
| // CAN chip-select pin for MCP2515 module | ||
| #define ECA_CAN_CS_PIN 10 // SPI chip select (change if shared) | ||
|
|
Comment on lines
+102
to
+104
| // CAN_500KBPS corresponds to ECA_CAN_BAUDRATE default of 500 kbps. | ||
| // MCP_ANY = accept all incoming messages (we only transmit, but set a sane default). | ||
| if (_can.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK) { |
Comment on lines
112
to
145
| int getEthanol (unsigned long pulseTime) | ||
| { | ||
| float frequency = float(1000000 / pulseTime); | ||
| // 20000 uS = 50 HZ - ~6667 uS = 150 HZ | ||
| if (pulseTime >= 20100 || pulseTime <= 6400) | ||
| { | ||
| if (pulseTime == 0) | ||
| { // sensor disconnected / short circuit | ||
| setVoltage(0.1); | ||
| setVoltage(ECA_ERROR_V_DISCONNECTED); | ||
| #if ECA_ENABLE_CAN | ||
| ecaStatus = ECA_STATUS_DISCONNECTED; | ||
| #endif | ||
| } | ||
| else if (pulseTime >= 20100) | ||
| { // contaminated fuel supply | ||
| setVoltage(4.8); | ||
| setVoltage(ECA_ERROR_V_CONTAMINATED); | ||
| #if ECA_ENABLE_CAN | ||
| ecaStatus = ECA_STATUS_CONTAMINATED; | ||
| #endif | ||
| } | ||
| else if ((pulseTime <= 6400) && (pulseTime >= 1)) | ||
| { // high water content in fuel | ||
| setVoltage(4.9); | ||
| setVoltage(ECA_ERROR_V_HIGH_WATER); | ||
| #if ECA_ENABLE_CAN | ||
| ecaStatus = ECA_STATUS_HIGH_WATER; | ||
| #endif | ||
| } | ||
| if (countTick < 2) | ||
| { | ||
| countTick++; | ||
| } | ||
| return; | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2.
Adds opt-in CAN bus output to the ECA firmware with compile-time feature gating, centralizes all configuration, and documents the full system including error condition mappings.
New files
src/eca_config.h— single config header replacing scattered#defines. All pins, voltages, error thresholds, and CAN parameters in one place.src/eca_can.h— header-only MCP2515 CAN module.EcaStatusenum + bit flags map 1:1 to existing analog error voltages. Compiles out entirely whenECA_ENABLE_CANis 0 (default).eca.dbc— canonical CAN message schema. DefinesECA_Data(ID0x0EC, 8 bytes) with signals for ethanol %, frequency, voltage, fuel temp, status code, and fault flags.CHANGELOG.md— v1.0.0 reconstructed from repo history; v2.0.0 for this work.Modified files
src/eca.ino— uses config header;#ifdef/defined()replaced with#ifguards; CAN status tracked throughgetEthanol()error paths and transmitted inloop(); serial output switched toF()flash strings; local variables properly scoped inloop()README.md— full rewrite: pinout, voltage mapping tables, analog error conditions, CAN payload layout, status/error code cross-reference, DBC usage, integration examplesLICENSE— copyright years → 2021–2026CAN error mapping
0.10 V0x01Disconnected≤ 0 Hz4.80 V0x02Contaminated< 50 Hz4.90 V0x03HighWater> 150 Hz0.50–4.50 V0x00OKEnabling CAN
When
ECA_ENABLE_CANremains0, no CAN headers are included and the binary is identical to pre-change behavior.Original prompt
You are working in the GitHub repository
nberlette/eca, a C++ Arduino project for an Ethanol Content Analyzer.Project context:
50–150 Hz) into an analog output for tuners, datalogs, and in-dash gauges.50 Hz -> 0% ethanol -> 0.50V100 Hz -> 50% ethanol -> 2.25V150 Hz -> 100% ethanol -> 4.50V< 50 Hz-> contaminated ->4.80V> 150 Hz-> high water level ->4.90V<= 0 Hz-> disconnected ->0.10VYour task is to implement a clean, production-quality feature update with the following goals:
Primary feature: optional CAN support
Add CAN bus output support as a strictly opt-in feature. This must not break existing analog-output-only behavior.
Requirements:
ECA_ENABLE_CAN.CAN configuration options
Support configurable CAN-related settings, including at minimum:
ARBID)0xEC(the de facto standard for this project/use case)If the project does not already include a CAN library abstraction, add one in the least invasive way possible. Keep the code modular so CAN support can be compiled out cleanly.
CAN error/status support
The CAN output must include support for the same error conditions currently represented by the analog 0–5V output.
Requirements:
< 50 Hz, currently4.80V)> 150 Hz, currently4.90V)<= 0 Hz, currently0.10V)Design this in a way that downstream ECUs, dashboards, loggers, or tuning software can reliably distinguish valid ethanol readings from sensor fault conditions.
CAN message schema / DBC
Create a small DBC file and add it to the repository as the canonical message schema for the new CAN output.
Requirements:
0xECAlso update the README to mention the DBC file and explain how it should be used by tuners, dashboards, loggers, or other downstream software.
Modernization / stale content cleanup
Audit the repo for anything obviously outdated and bring it up to date, including:
Be conservative: do not rewrite things unnecessarily, but fix obvious drift.
CHANGELOG
Create a
CHANGELOG.mdin the repo root.Requirements: