Skip to content

Commit b968d88

Browse files
committed
Implemented EcuMaster SwitchBoard v3 protocol
1 parent 82581fe commit b968d88

6 files changed

Lines changed: 134 additions & 4 deletions

File tree

firmware/boards/port.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ enum class CanEgtProtocol : uint8_t {
9191
enum class CanIoProtocol : uint8_t {
9292
None = 0,
9393
Haltech = 3,
94+
EcuMaster = 4,
9495
Motec = 6,
9596
Emtron = 7,
9697
MsIoBox = 9,

firmware/can.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,11 @@ __attribute__((weak)) void SendCanData(uint16_t elapsedMs)
110110

111111
static uint16_t elapsedSinceIoExpanderTxMs = 0;
112112
const ProtocolHandler* ioExpanderHandler = nullptr;
113+
113114
switch (configuration->ioExpanderConfig.Protocol) {
115+
case CanIoProtocol::EcuMaster:
116+
ioExpanderHandler = &ecuMasterSwitchBoardTxHandler;
117+
break;
114118
case CanIoProtocol::Haltech:
115119
ioExpanderHandler = &haltechIoTxHandler;
116120
break;
@@ -129,6 +133,7 @@ __attribute__((weak)) void SendCanData(uint16_t elapsedMs)
129133

130134
#endif
131135

136+
// E888 combines EGT and IO expander data, so handle it separately
132137
#if (MOTEC_E888_ENABLED > 0)
133138
static uint16_t elapsedSinceMotecE888TxMs = 0;
134139
if (IsMotecE888Enabled(configuration)) {

firmware/can/can_ecumaster.cpp

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include "heater_control.h"
1414
#include "lambda_conversion.h"
1515
#include "max3185x.h"
16-
#include "../for_rusefi/wideband_can.h"
16+
#include "pwmout.h"
1717

1818
// EcuMaster protocol
1919
// CAN 1Mbps, big-endian
@@ -154,3 +154,125 @@ constexpr ProtocolHandler ecuMasterClassicEgtTxHandler = MakeProtocolHandler<&Se
154154
constexpr ProtocolHandler ecuMasterBlackEgtTxHandler = MakeProtocolHandler<&SendEcuMasterEgtFormat>(ECU_MASTER_EGT_TX_PERIOD_MS);
155155

156156
#endif
157+
158+
159+
#if (IO_EXPANDER_ENABLED > 0)
160+
161+
#define ECUMASTER_SWITCHBOARD_TX_PERIOD_MS 100
162+
#define ECUMASTER_SWITCHBOARD_BASE_ID 0x640
163+
#define ECUMASTER_SWITCHBOARD_DEVICE_ID(n) (ECUMASTER_SWITCHBOARD_BASE_ID + (n) * 4)
164+
#define ECUMASTER_SWITCHBOARD_RX_OFFSET 3
165+
166+
namespace ecumaster
167+
{
168+
// BASE: AV1-AV4, 0.001 V
169+
// BASE + 1: AV5-AV8, 0.001 V
170+
struct AVData
171+
{
172+
uint16_t Value[4];
173+
} __attribute__((packed));
174+
175+
static_assert(sizeof(AVData) == 8);
176+
177+
// BASE + 2
178+
struct OtherData
179+
{
180+
uint8_t Rotary1 : 4; // 0-15
181+
uint8_t Rotary2 : 4; // 0-15
182+
uint8_t Rotary3 : 4; // 0-15
183+
uint8_t Rotary4 : 4; // 0-15
184+
uint8_t Rotary5 : 4; // 0-15
185+
uint8_t Rotary6 : 4; // 0-15
186+
uint8_t Rotary7 : 4; // 0-15
187+
uint8_t Rotary8 : 4; // 0-15
188+
uint8_t Switch; // bit 0-7 represent switch 1-8 (0=open, 1=closed)
189+
uint8_t AnalogState; // bitmask of which analog inputs are above threshold (1) or below threshold (0)
190+
uint8_t LowSideState; // bitmask of which low-side outputs are active (1) or inactive (0)
191+
uint8_t Heartbeat; // Incremented on each message
192+
} __attribute__((packed));
193+
194+
static_assert(sizeof(OtherData) == 8);
195+
196+
// BASE + 3: Low-side output control messages from ECU to switchboard
197+
struct LowSideRxData
198+
{
199+
uint8_t State[4];
200+
uint8_t Reserved[4];
201+
} __attribute__((packed));
202+
203+
static_assert(sizeof(LowSideRxData) == 8);
204+
}
205+
206+
static uint8_t hearbeat = 0;
207+
static uint8_t analogStatus = 0;
208+
static uint8_t lowSideStatus = 0;
209+
210+
void SendEcuMasterSwitchBoardFormat(Configuration* configuration)
211+
{
212+
213+
auto id = ECUMASTER_SWITCHBOARD_BASE_ID + configuration->afr[0].ExtraCanIdOffset;
214+
215+
// Handle first 4 channels for now
216+
constexpr uint8_t channels = AUX_INPUT_CHANNELS > 4 ? 4 : AUX_INPUT_CHANNELS;
217+
218+
CanTxTyped<ecumaster::AVData> frame(id, true);
219+
for (uint8_t i = 0; i < channels; i++)
220+
{
221+
if (configuration->ioExpanderConfig.IOInputsEnabled & (1 << i))
222+
{
223+
float voltage = GetAuxInputVoltage(i);
224+
225+
// Some hysteresis
226+
if (voltage > 3.0f) analogStatus |= (1 << i);
227+
if (voltage < 2.0f) analogStatus &= ~(1 << i);
228+
229+
uint16_t raw = (uint16_t)(voltage * 1000);
230+
frame->Value[i] = raw;
231+
}
232+
else
233+
{
234+
frame->Value[i] = 0; // Input disabled, report as 0V
235+
}
236+
}
237+
238+
CanTxTyped<ecumaster::OtherData> otherFrame(id + 2, true);
239+
240+
otherFrame->AnalogState = analogStatus;
241+
otherFrame->LowSideState = lowSideStatus;
242+
otherFrame->Heartbeat = hearbeat++;
243+
}
244+
245+
void HandleEcuMasterCanMessage(const CANRxFrame* msg, Configuration* configuration)
246+
{
247+
auto msg_id = CAN_ID(*msg);
248+
249+
auto device_id = ECUMASTER_SWITCHBOARD_DEVICE_ID(configuration->ioExpanderConfig.Offset);
250+
251+
if (msg_id != device_id + ECUMASTER_SWITCHBOARD_RX_OFFSET) {
252+
return; // Not a low-side control message
253+
}
254+
255+
auto lowSideData = reinterpret_cast<const ecumaster::LowSideRxData*>(msg->data8);
256+
for (uint8_t i = 0; i < 4; i++)
257+
{
258+
if (configuration->ioExpanderConfig.IOOutputsEnabled & (1 << i))
259+
{
260+
if (lowSideData->State[i])
261+
{
262+
lowSideStatus |= (1 << i);
263+
SetAuxPwmDuty(i, 1.0); // Set PWM duty to 100% for active low-side output
264+
}
265+
else
266+
{
267+
lowSideStatus &= ~(1 << i);
268+
SetAuxPwmDuty(i, 0.0); // Set PWM duty to 0% for inactive low-side output
269+
}
270+
271+
272+
}
273+
}
274+
}
275+
276+
constexpr ProtocolHandler ecuMasterSwitchBoardTxHandler = MakeProtocolHandler<&SendEcuMasterSwitchBoardFormat>(ECUMASTER_SWITCHBOARD_TX_PERIOD_MS);
277+
278+
#endif

firmware/can/can_ecumaster.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@
1010

1111
extern const ProtocolHandler ecuMasterAfrTxHandler;
1212
extern const ProtocolHandler ecuMasterClassicEgtTxHandler;
13-
extern const ProtocolHandler ecuMasterBlackEgtTxHandler;
13+
extern const ProtocolHandler ecuMasterBlackEgtTxHandler;
14+
extern const ProtocolHandler ecuMasterSwitchBoardTxHandler;
15+
16+
void HandleEcuMasterCanMessage(const CANRxFrame* msg, Configuration* configuration);

firmware/can/can_haltech.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include "heater_control.h"
1515
#include "lambda_conversion.h"
1616
#include "max3185x.h"
17-
#include "../for_rusefi/wideband_can.h"
1817

1918
// Haltech protocol
2019
// 1Mbps, big endian, DLC 8

firmware/can/can_msiobox.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ void ProcessMsIoBoxCanMessage(const CANRxFrame* fr, Configuration* cfg)
195195
}
196196

197197
const uint32_t base = MS_IOBOX_DEVICE_ID(cfg->ioExpanderConfig.Offset);
198-
uint32_t frame_id = fr->SID;
198+
uint32_t frame_id = CAN_ID(*fr);
199199

200200
if ((frame_id < base) || (frame_id > base + MS_IOBOX_LAST_IN))
201201
return;

0 commit comments

Comments
 (0)