rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
rusefi_wideband.cpp
Go to the documentation of this file.
1#include "pch.h"
2
3/*
4 * - we have many board with one or two on-board F0 WBO module
5 * - we have a requirement to use external AEM controller instead of onboard module ('Genesis use-case')
6 * - we also have requirement for CANbus ID conflict resolution in case of vehicle integration ('Camaro use-case')
7 */
8
9// do we use some sort of a custom bootloader protocol in rusEFI WBO?
10// todo: should we move to any widely used protocol like OpenBLT or else?
11
12#if EFI_CAN_SUPPORT
13
14#include "ch.h"
15#include "can_msg_tx.h"
16#include "rusefi_wideband.h"
17#pragma GCC diagnostic push
18#pragma GCC diagnostic ignored "-Wunused-function"
19#include "wideband_firmware/for_rusefi/wideband_can.h"
20#pragma GCC diagnostic pop
21
24}
25
26#define EVT_BOOTLOADER_ACK EVENT_MASK(0)
27
28static thread_t* waitingBootloaderThread = nullptr;
29
30void handleWidebandCan(const size_t busIndex, const CANRxFrame& frame) {
31 // wrong bus
32 if (busIndex != getWidebandBus()) {
33 return;
34 }
35
36 // Bootloader acks with address 0x727573 aka ascii "rus"
37 if (CAN_EID(frame) != WB_ACK) {
38 return;
39 }
40
41 if (frame.DLC == 0)
42 {
43 // Ack reply
44 // Nop
45 } else if (frame.DLC == 8)
46 {
47 // Ping reply
48 #if EFI_TUNER_STUDIO
49 auto data = reinterpret_cast<const wbo::PongData*>(&frame.data8[0]);
50
55 #endif
56 } else {
57 // Unknown
58 return;
59 }
60
61 // Wake thread in any case
63 if (t) {
64 chEvtSignal(t, EVT_BOOTLOADER_ACK);
65 }
66}
67
68bool waitAck(int timeout = 1000) {
69 return chEvtWaitAnyTimeout(EVT_BOOTLOADER_ACK, TIME_MS2I(timeout)) != 0;
70}
71
72static void setStatus(can_wbo_re_status_e status)
73{
74#if EFI_TUNER_STUDIO
75 engine->outputChannels.canReWidebandCmdStatus = static_cast<uint8_t>(status);
76#endif
77}
78
79void setWidebandOffsetNoWait(uint8_t hwIndex, uint8_t index) {
80 size_t bus = getWidebandBus();
81
82 if (hwIndex == 0xff) {
83 CanTxMessage m(CanCategory::WBO_SERVICE, WB_MSG_SET_INDEX, 1, bus, true);
84 m[0] = index;
85 } else {
86 CanTxMessage m(CanCategory::WBO_SERVICE, WB_MSG_SET_INDEX, 2, bus, true);
87 m[0] = index;
88 m[1] = hwIndex;
89 }
90}
91
92void setWidebandOffset(uint8_t hwIndex, uint8_t index) {
93 setStatus(WBO_RE_BUSY);
94
95 // Clear any pending acks for this thread
96 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
97
98 // Send messages to the current thread when acks come in
99 waitingBootloaderThread = chThdGetSelfX();
100
101 efiPrintf("***************************************");
102 efiPrintf(" WIDEBAND INDEX SET");
103 efiPrintf("***************************************");
104
105 if (hwIndex == 0xff) {
106 efiPrintf("Setting all connected widebands to index %d...", index);
107 } else {
108 efiPrintf("Setting wideband with hwIndex %d to CAN index %d...", hwIndex, index);
109 }
110 setWidebandOffsetNoWait(hwIndex, index);
111
112 if (!waitAck()) {
113 efiPrintf("Wideband index set failed: no controller detected!");
114 setStatus(WBO_RE_FAILED);
115 } else {
116 setStatus(WBO_RE_DONE);
117 }
118
119 waitingBootloaderThread = nullptr;
120}
121
122void setWidebandSensorType(uint8_t hwIndex, uint8_t type) {
123 size_t bus = getWidebandBus();
124
125 setStatus(WBO_RE_BUSY);
126
127 // Clear any pending acks for this thread
128 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
129
130 // Send messages to the current thread when acks come in
131 waitingBootloaderThread = chThdGetSelfX();
132
133 {
134 // Note position of hwIndex, compared to WB_MSG_SET_INDEX
135 // TODO: replace madic number after updating WBO submodule
136 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF7'0000 /* WB_MSG_SET_SENS_TYPE */, 2, bus, true);
137 m[0] = hwIndex;
138 m[1] = type;
139 }
140
141 if (!waitAck()) {
142 efiPrintf("Wideband sensor type set failed: no controller detected!");
143 setStatus(WBO_RE_FAILED);
144 } else {
145 setStatus(WBO_RE_DONE);
146 }
147
148 waitingBootloaderThread = nullptr;
149}
150
151void pingWideband(uint8_t hwIndex) {
152 size_t bus = getWidebandBus();
153
154 setStatus(WBO_RE_BUSY);
155
156#if EFI_TUNER_STUDIO
161#endif
162
163 // Clear any pending acks for this thread
164 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
165
166 // Send messages to the current thread when acks come in
167 waitingBootloaderThread = chThdGetSelfX();
168
169 {
170 // TODO: replace magic number with WB_MSG_PING after updating Wideband submodule
171 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF6'0000, 1, bus, true);
172 m[0] = hwIndex;
173 }
174
175 // 25mS should be enought, lets do not block TS thread too long while waiting for WBO reply
176 if (!waitAck(25)) {
177 efiPrintf("Wideband ping failed: no controller detected!");
178 setStatus(WBO_RE_FAILED);
179 } else {
180 efiPrintf("WBO_RE_DONE");
181 setStatus(WBO_RE_DONE);
182 }
183
184 waitingBootloaderThread = nullptr;
185}
186
187// Called with 50mS interval and only if CAN and (on-boards) WBO(s) are enabled
189 static int counter = 0;
190
191 CanTxMessage m(CanCategory::WBO_SERVICE, WB_MGS_ECU_STATUS, /*dlc*/2, getWidebandBus(), /*isExtended*/true);
192
194
195 m[0] = vbatt;
196
197 // Offset 1 bit 0 = heater enable
198 m[1] = engine->engineState.heaterControlEnabled ? 0x01 : 0x00;
199
200 // 10 * 50 = 0.5S delay
201 if (counter == 10) {
202 for (size_t i = 0; i < CAN_WBO_COUNT; i++) {
204 (engineConfiguration->canWbo[i].type == RUSEFI)) {
205 // remap
207 }
208 }
209 }
210
211 counter++;
212}
213
214#if EFI_WIDEBAND_FIRMWARE_UPDATE
215
216// This file contains an array called build_wideband_noboot_bin
217// This array contains the firmware image for the wideband contoller
218#include "wideband_firmware/for_rusefi/wideband_image.h"
219
220void updateWidebandFirmware(uint8_t hwIndex) {
221 size_t bus = getWidebandBus();
222 size_t totalSize = sizeof(build_wideband_image_bin);
223
224 setStatus(WBO_RE_BUSY);
225
226 // Clear any pending acks for this thread
227 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
228
229 // Send messages to the current thread when acks come in
230 waitingBootloaderThread = chThdGetSelfX();
231
232 efiPrintf("***************************************");
233 efiPrintf(" WIDEBAND FIRMWARE UPDATE");
234 efiPrintf("***************************************");
235 if (hwIndex != 0xff) {
236 efiPrintf("Wideband Update: Rebooting WBO hwIndex %d to bootloader...", hwIndex);
237 } else {
238 efiPrintf("Wideband Update: Rebooting any WBO to bootloader...");
239 }
240 // The first request will reboot the chip (if necessary), and the second one will enable bootloader mode
241 // If the chip was already in bootloader (aka manual mode), then that's ok - the second request will
242 // just be safely ignored (but acked)
243 for (int i = 0; i < 2; i++) {
244 // Send bootloader entry command
245 // First packet will ask main FW to reboot to bootloader
246 // Second will ask bootloader to stay in bootloader and wait for FW upload
247 // First packet can be new format - individually addressed
248 // Second one should have zero payload - bootloader expects no payload.
249 if ((hwIndex != 0xff) && (i == 0)) {
250 // New format - individually addressed
251 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ENTER, 1, bus, true);
252 m[0] = hwIndex;
253 } else {
254 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ENTER, 0, bus, true);
255 }
256
257 if (!waitAck()) {
258 efiPrintf("Wideband Update ERROR: Expected ACK from entry to bootloader, didn't get %s.",
259 i ? "second (from bootloader)" : "first (from app)");
260 setStatus(WBO_RE_FAILED);
261 goto exit;
262 }
263
264 // Let the controller reboot (and show blinky lights for a second before the update begins)
265 chThdSleepMilliseconds(200);
266 }
267
268 efiPrintf("Wideband Update: in update mode, erasing flash...");
269
270 {
271 // Erase flash - opcode 1, magic value 0x5A5A
272 // TODO: replace magic numer with WB_BL_ERASE when define is fixed in wideband_can.h, currently mess with shift and addition precedence
273 //CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ERASE, 0, bus, true);
274 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF1'5A5A, 0, bus, true);
275 }
276
277 if (!waitAck()) {
278 efiPrintf("Wideband Update ERROR: Expected ACK from flash erase command, didn't get one.");
279 setStatus(WBO_RE_FAILED);
280 goto exit;
281 }
282
283 efiPrintf("Wideband Update: Flash erased! Sending %d bytes...", totalSize);
284
285 // Send flash data 8 bytes at a time
286 for (size_t i = 0; i < totalSize; i += 8) {
287 {
288 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_DATA_BASE | i, 8, bus, true);
289 memcpy(&m[0], build_wideband_image_bin + i, 8);
290 }
291
292 if (!waitAck()) {
293 efiPrintf("Wideband Update ERROR: Expected ACK from data write, didn't get one.");
294 setStatus(WBO_RE_FAILED);
295 goto exit;
296 }
297 }
298
299 efiPrintf("Wideband Update: Update complete! Rebooting controller.");
300
301 {
302 // Reboot to firmware!
303 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_REBOOT, 0, bus, true);
304 }
305
306 // TODO:
307 waitAck();
308
309 setStatus(WBO_RE_DONE);
310
311exit:
312 waitingBootloaderThread = nullptr;
313}
314
315#endif // EFI_WIDEBAND_FIRMWARE_UPDATE
316#endif // HAL_USE_CAN
EngineState engineState
Definition engine.h:344
TunerStudioOutputChannels outputChannels
Definition engine.h:109
static float getOrZero(SensorType type)
Definition sensor.h:83
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
can_wbo_re_status_e
void handleWidebandCan(const size_t busIndex, const CANRxFrame &frame)
static thread_t * waitingBootloaderThread
void setWidebandSensorType(uint8_t hwIndex, uint8_t type)
bool waitAck(int timeout=1000)
void setWidebandOffsetNoWait(uint8_t hwIndex, uint8_t index)
void pingWideband(uint8_t hwIndex)
static void setStatus(can_wbo_re_status_e status)
void sendWidebandInfo()
void setWidebandOffset(uint8_t hwIndex, uint8_t index)
void updateWidebandFirmware(uint8_t hwIndex)
size_t getWidebandBus()
uint8_t data8[8]
Frame data.
Definition can_mocks.h:55
uint8_t DLC
Data length.
Definition can_mocks.h:42