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;
29static char widebandRestart = 0;
30
31void handleWidebandCan(const size_t busIndex, const CANRxFrame& frame) {
32 // wrong bus
33 if (busIndex != getWidebandBus()) {
34 return;
35 }
36
37 // Bootloader acks with address 0x727573 aka ascii "rus"
38 if (CAN_EID(frame) != WB_ACK) {
39 return;
40 }
41
42 if (frame.DLC == 0)
43 {
44 // Ack reply
45 // Nop
46 } else if (frame.DLC == 8)
47 {
48 // Ping reply
49 #if EFI_TUNER_STUDIO
50 auto data = reinterpret_cast<const wbo::PongData*>(&frame.data8[0]);
51
56 #endif
57 } else {
58 // Unknown
59 return;
60 }
61
62 // Wake thread in any case
64 if (t) {
65 chEvtSignal(t, EVT_BOOTLOADER_ACK);
66 }
67}
68
69bool waitAck(int timeout = 1000) {
70 return chEvtWaitAnyTimeout(EVT_BOOTLOADER_ACK, TIME_MS2I(timeout)) != 0;
71}
72
73static void setStatus(can_wbo_re_status_e status)
74{
75#if EFI_TUNER_STUDIO
76 engine->outputChannels.canReWidebandCmdStatus = static_cast<uint8_t>(status);
77#endif
78}
79
80void setWidebandOffsetNoWait(uint8_t hwIndex, uint8_t index) {
81 size_t bus = getWidebandBus();
82
83 if (hwIndex == 0xff) {
84 CanTxMessage m(CanCategory::WBO_SERVICE, WB_MSG_SET_INDEX, 1, bus, true);
85 m[0] = index;
86 } else {
87 CanTxMessage m(CanCategory::WBO_SERVICE, WB_MSG_SET_INDEX, 2, bus, true);
88 m[0] = index;
89 m[1] = hwIndex;
90 }
91}
92
93void setWidebandOffset(uint8_t hwIndex, uint8_t index) {
94 setStatus(WBO_RE_BUSY);
95
96 // Clear any pending acks for this thread
97 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
98
99 // Send messages to the current thread when acks come in
100 waitingBootloaderThread = chThdGetSelfX();
101
102 efiPrintf("***************************************");
103 efiPrintf(" WIDEBAND INDEX SET");
104 efiPrintf("***************************************");
105
106 if (hwIndex == 0xff) {
107 efiPrintf("Setting all connected widebands to index %d...", index);
108 } else {
109 efiPrintf("Setting wideband with hwIndex %d to CAN index %d...", hwIndex, index);
110 }
111 setWidebandOffsetNoWait(hwIndex, index);
112
113 if (!waitAck()) {
114 efiPrintf("Wideband index set failed: no controller detected!");
115 setStatus(WBO_RE_FAILED);
116 } else {
117 setStatus(WBO_RE_DONE);
118 }
119
120 waitingBootloaderThread = nullptr;
121}
122
123void setWidebandSensorType(uint8_t hwIndex, uint8_t type) {
124 size_t bus = getWidebandBus();
125
126 setStatus(WBO_RE_BUSY);
127
128 // Clear any pending acks for this thread
129 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
130
131 // Send messages to the current thread when acks come in
132 waitingBootloaderThread = chThdGetSelfX();
133
134 {
135 // Note position of hwIndex, compared to WB_MSG_SET_INDEX
136 // TODO: replace madic number after updating WBO submodule
137 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF7'0000 /* WB_MSG_SET_SENS_TYPE */, 2, bus, true);
138 m[0] = hwIndex;
139 m[1] = type;
140 }
141
142 if (!waitAck()) {
143 efiPrintf("Wideband sensor type set failed: no controller detected!");
144 setStatus(WBO_RE_FAILED);
145 } else {
146 setStatus(WBO_RE_DONE);
147 }
148
149 waitingBootloaderThread = nullptr;
150}
151
152void pingWideband(uint8_t hwIndex) {
153 size_t bus = getWidebandBus();
154
155 setStatus(WBO_RE_BUSY);
156
157#if EFI_TUNER_STUDIO
162#endif
163
164 // Clear any pending acks for this thread
165 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
166
167 // Send messages to the current thread when acks come in
168 waitingBootloaderThread = chThdGetSelfX();
169
170 {
171 // TODO: replace magic number with WB_MSG_PING after updating Wideband submodule
172 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF6'0000, 1, bus, true);
173 m[0] = hwIndex;
174 }
175
176 // 25mS should be enought, lets do not block TS thread too long while waiting for WBO reply
177 if (!waitAck(25)) {
178 efiPrintf("Wideband ping failed: no controller detected!");
179 setStatus(WBO_RE_FAILED);
180 } else {
181 efiPrintf("WBO_RE_DONE");
182 setStatus(WBO_RE_DONE);
183 }
184
185 waitingBootloaderThread = nullptr;
186}
187
189 // 10 times of 50 mS
190 widebandRestart = 10;
191}
192
193// Called with 50mS interval and only if CAN and (on-boards) WBO(s) are enabled
195 static int counter = 0;
196
197 CanTxMessage m(CanCategory::WBO_SERVICE, WB_MSG_ECU_STATUS, /*dlc*/2, getWidebandBus(), /*isExtended*/true);
198
200
201 m[0] = vbatt;
202
203 // Offset 1 bit 0 = heater enable
204 if (widebandRestart) {
205 m[1] = 0x00;
207 } else {
208 m[1] = engine->engineState.heaterControlEnabled ? 0x01 : 0x00;
209 }
210
211 // 10 * 50 = 0.5S delay
212 if (counter == 10) {
213 for (size_t i = 0; i < CAN_WBO_COUNT; i++) {
215 (engineConfiguration->canWbo[i].type == RUSEFI)) {
216 // remap
218 }
219 }
220 }
221
222 counter++;
223}
224
225#if EFI_WIDEBAND_FIRMWARE_UPDATE
226
227// reboots to bootloader and erases FW area
228static int updateWidebandFirmwarePrepare(size_t bus, uint8_t hwIndex) {
229 int ret = -1;
230
231 setStatus(WBO_RE_BUSY);
232
233 // Clear any pending acks for this thread
234 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
235
236 // Send messages to the current thread when acks come in
237 waitingBootloaderThread = chThdGetSelfX();
238
239 efiPrintf("***************************************");
240 efiPrintf(" WIDEBAND FIRMWARE UPDATE");
241 efiPrintf("***************************************");
242 if (hwIndex != 0xff) {
243 efiPrintf("Wideband Update: Rebooting WBO hwIndex %d to bootloader...", hwIndex);
244 } else {
245 efiPrintf("Wideband Update: Rebooting any WBO to bootloader...");
246 }
247 // The first request will reboot the chip (if necessary), and the second one will enable bootloader mode
248 // If the chip was already in bootloader (aka manual mode), then that's ok - the second request will
249 // just be safely ignored (but acked)
250 for (int i = 0; i < 2; i++) {
251 // Send bootloader entry command
252 // First packet will ask main FW to reboot to bootloader
253 // Second will ask bootloader to stay in bootloader and wait for FW upload
254 // First packet can be new format - individually addressed
255 // Second one should have zero payload - bootloader expects no payload.
256 if ((hwIndex != 0xff) && (i == 0)) {
257 // New format - individually addressed
258 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ENTER, 1, bus, true);
259 m[0] = hwIndex;
260 } else {
261 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ENTER, 0, bus, true);
262 }
263
264 if (!waitAck()) {
265 efiPrintf("Wideband Update ERROR: Expected ACK from entry to bootloader, didn't get %s.",
266 i ? "second (from bootloader)" : "first (from app)");
267 setStatus(WBO_RE_FAILED);
268 goto exit;
269 }
270
271 // Let the controller reboot (and show blinky lights for a second before the update begins)
272 chThdSleepMilliseconds(200);
273 }
274
275 efiPrintf("Wideband Update: in update mode, erasing flash...");
276
277 {
278 // Erase flash - opcode 1, magic value 0x5A5A
279 // TODO: replace magic numer with WB_BL_ERASE when define is fixed in wideband_can.h, currently mess with shift and addition precedence
280 //CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ERASE, 0, bus, true);
281 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF1'5A5A, 0, bus, true);
282 }
283
284 if (!waitAck()) {
285 efiPrintf("Wideband Update ERROR: Expected ACK from flash erase command, didn't get one.");
286 setStatus(WBO_RE_FAILED);
287 goto exit;
288 }
289
290 ret = 0;
291
292exit:
293 return ret;
294}
295
296
297// reboots to bootloader and erases FW area
298static int updateWidebandFirmwareFinalize(size_t bus, uint8_t /* hwIndex */) {
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 if (!waitAck()) {
307 efiPrintf("Wideband Update ERROR: No ack on reboot command");
308 waitingBootloaderThread = nullptr;
309 return -1;
310 }
311
312 return 0;
313}
314
315#if EFI_PROD_CODE
316
317#if EFI_FILE_LOGGING
318#include "ff.h"
319#endif
320
321// Yes, this file also contains bootloader, but this is how it was historicaly released in 2023
322static const char wboFileName[] = "fw/wbo/wideband_image_with_bl.bin";
323// FW goes after 6K bootloader
324static const size_t wboFwOffset = 6 * 1024;
325static const size_t wboFwMaxSize = 32 * 1024;
326
328{
329#if EFI_FILE_LOGGING
330 int ret = 0;
331 size_t totalSize = 0;
332 size_t bus = 0;
333 FIL fil;
334
335 if (f_open(&fil, wboFileName, FA_READ) != FR_OK) {
336 efiPrintf("Wideband Update: filed to open %s", wboFileName);
337 return;
338 }
339
340 if (f_lseek(&fil, wboFwOffset) != FR_OK) {
341 efiPrintf("Wideband Update: filed to seek");
342 goto exit;
343 }
344
345 totalSize = f_size(&fil) - wboFwOffset;
346 bus = getWidebandBus();
347
348 // this also sets waitingBootloaderThread
349 ret = updateWidebandFirmwarePrepare(bus, hwIndex);
350 if (ret < 0) {
351 goto exit;
352 }
353
354 for (size_t i = 0; i < totalSize; i += 8) {
355 UINT br;
356 uint8_t tmp[8];
357
358 if (f_read(&fil, tmp, sizeof(tmp), &br) != FR_OK) {
359 efiPrintf("Wideband Update: filed to read");
360 setStatus(WBO_RE_FAILED);
361 goto exit;
362 }
363
364 if (br == 0) {
365 // EOF
366 break;
367 }
368
369 {
370 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_DATA_BASE | i, 8, bus, true);
371 memcpy(&m[0], tmp, 8);
372 }
373
374 if (!waitAck()) {
375 efiPrintf("Wideband Update ERROR: Expected ACK from data write, didn't get one.");
376 setStatus(WBO_RE_FAILED);
377 goto exit;
378 }
379 }
380
381 ret = updateWidebandFirmwareFinalize(bus, hwIndex);
382 if (ret) {
383 goto exit;
384 }
385
386 setStatus(WBO_RE_DONE);
387
388exit:
389 waitingBootloaderThread = nullptr;
390 f_close(&fil);
391#else
392 // Not supported
393#endif
394}
395#endif
396
397// This file contains an array called build_wideband_noboot_bin
398// This array contains the firmware image for the wideband contoller
399#include "wideband_firmware/for_rusefi/wideband_image.h"
400
401void updateWidebandFirmware(uint8_t hwIndex) {
402 size_t bus = getWidebandBus();
403 size_t totalSize = sizeof(build_wideband_image_bin);
404
405 // this also sets waitingBootloaderThread
406 int ret = updateWidebandFirmwarePrepare(bus, hwIndex);
407 if (ret < 0) {
408 goto exit;
409 }
410
411 efiPrintf("Wideband Update: Flash erased! Sending %d bytes...", totalSize);
412
413 // Send flash data 8 bytes at a time
414 for (size_t i = 0; i < totalSize; i += 8) {
415 {
416 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_DATA_BASE | i, 8, bus, true);
417 memcpy(&m[0], build_wideband_image_bin + i, 8);
418 }
419
420 if (!waitAck()) {
421 efiPrintf("Wideband Update ERROR: Expected ACK from data write, didn't get one.");
422 setStatus(WBO_RE_FAILED);
423 goto exit;
424 }
425 }
426
427 ret = updateWidebandFirmwareFinalize(bus, hwIndex);
428 if (ret) {
429 goto exit;
430 }
431
432 setStatus(WBO_RE_DONE);
433
434exit:
435 waitingBootloaderThread = nullptr;
436}
437
438#endif // EFI_WIDEBAND_FIRMWARE_UPDATE
439#endif // HAL_USE_CAN
EngineState engineState
Definition engine.h:352
TunerStudioOutputChannels outputChannels
Definition engine.h:113
static float getOrZero(SensorType type)
Definition sensor.h:83
static EngineAccessor engine
Definition engine.h:421
static constexpr engine_configuration_s * engineConfiguration
can_wbo_re_status_e
static int updateWidebandFirmwarePrepare(size_t bus, uint8_t hwIndex)
static const size_t wboFwMaxSize
static int updateWidebandFirmwareFinalize(size_t bus, uint8_t)
void handleWidebandCan(const size_t busIndex, const CANRxFrame &frame)
void updateWidebandFirmwareFromFile(uint8_t hwIndex)
static thread_t * waitingBootloaderThread
void setWidebandSensorType(uint8_t hwIndex, uint8_t type)
bool waitAck(int timeout=1000)
static const size_t wboFwOffset
static char widebandRestart
void setWidebandOffsetNoWait(uint8_t hwIndex, uint8_t index)
void pingWideband(uint8_t hwIndex)
static const char wboFileName[]
static void setStatus(can_wbo_re_status_e status)
void sendWidebandInfo()
void setWidebandOffset(uint8_t hwIndex, uint8_t index)
void restartWideband()
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