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// reboots to bootloader and erases FW area
217static int updateWidebandFirmwarePrepare(size_t bus, uint8_t hwIndex) {
218 int ret = -1;
219
220 setStatus(WBO_RE_BUSY);
221
222 // Clear any pending acks for this thread
223 chEvtGetAndClearEvents(EVT_BOOTLOADER_ACK);
224
225 // Send messages to the current thread when acks come in
226 waitingBootloaderThread = chThdGetSelfX();
227
228 efiPrintf("***************************************");
229 efiPrintf(" WIDEBAND FIRMWARE UPDATE");
230 efiPrintf("***************************************");
231 if (hwIndex != 0xff) {
232 efiPrintf("Wideband Update: Rebooting WBO hwIndex %d to bootloader...", hwIndex);
233 } else {
234 efiPrintf("Wideband Update: Rebooting any WBO to bootloader...");
235 }
236 // The first request will reboot the chip (if necessary), and the second one will enable bootloader mode
237 // If the chip was already in bootloader (aka manual mode), then that's ok - the second request will
238 // just be safely ignored (but acked)
239 for (int i = 0; i < 2; i++) {
240 // Send bootloader entry command
241 // First packet will ask main FW to reboot to bootloader
242 // Second will ask bootloader to stay in bootloader and wait for FW upload
243 // First packet can be new format - individually addressed
244 // Second one should have zero payload - bootloader expects no payload.
245 if ((hwIndex != 0xff) && (i == 0)) {
246 // New format - individually addressed
247 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ENTER, 1, bus, true);
248 m[0] = hwIndex;
249 } else {
250 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ENTER, 0, bus, true);
251 }
252
253 if (!waitAck()) {
254 efiPrintf("Wideband Update ERROR: Expected ACK from entry to bootloader, didn't get %s.",
255 i ? "second (from bootloader)" : "first (from app)");
256 setStatus(WBO_RE_FAILED);
257 goto exit;
258 }
259
260 // Let the controller reboot (and show blinky lights for a second before the update begins)
261 chThdSleepMilliseconds(200);
262 }
263
264 efiPrintf("Wideband Update: in update mode, erasing flash...");
265
266 {
267 // Erase flash - opcode 1, magic value 0x5A5A
268 // TODO: replace magic numer with WB_BL_ERASE when define is fixed in wideband_can.h, currently mess with shift and addition precedence
269 //CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_ERASE, 0, bus, true);
270 CanTxMessage m(CanCategory::WBO_SERVICE, 0xEF1'5A5A, 0, bus, true);
271 }
272
273 if (!waitAck()) {
274 efiPrintf("Wideband Update ERROR: Expected ACK from flash erase command, didn't get one.");
275 setStatus(WBO_RE_FAILED);
276 goto exit;
277 }
278
279 ret = 0;
280
281exit:
282 return ret;
283}
284
285
286// reboots to bootloader and erases FW area
287static int updateWidebandFirmwareFinalize(size_t bus, uint8_t /* hwIndex */) {
288 efiPrintf("Wideband Update: Update complete! Rebooting controller.");
289
290 {
291 // Reboot to firmware!
292 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_REBOOT, 0, bus, true);
293 }
294
295 if (!waitAck()) {
296 efiPrintf("Wideband Update ERROR: No ack on reboot command");
297 waitingBootloaderThread = nullptr;
298 return -1;
299 }
300
301 return 0;
302}
303
304#if EFI_PROD_CODE
305
306#if EFI_FILE_LOGGING
307#include "ff.h"
308#endif
309
310// Yes, this file also contains bootloader, but this is how it was historicaly released in 2023
311static const char wboFileName[] = "fw/wbo/wideband_image_with_bl.bin";
312// FW goes after 6K bootloader
313static const size_t wboFwOffset = 6 * 1024;
314static const size_t wboFwMaxSize = 32 * 1024;
315
317{
318#if EFI_FILE_LOGGING
319 int ret = 0;
320 size_t totalSize = 0;
321 size_t bus = 0;
322 FIL fil;
323
324 if (f_open(&fil, wboFileName, FA_READ) != FR_OK) {
325 efiPrintf("Wideband Update: filed to open %s", wboFileName);
326 return;
327 }
328
329 if (f_lseek(&fil, wboFwOffset) != FR_OK) {
330 efiPrintf("Wideband Update: filed to seek");
331 goto exit;
332 }
333
334 totalSize = f_size(&fil) - wboFwOffset;
335 bus = getWidebandBus();
336
337 // this also sets waitingBootloaderThread
338 ret = updateWidebandFirmwarePrepare(bus, hwIndex);
339 if (ret < 0) {
340 goto exit;
341 }
342
343 for (size_t i = 0; i < totalSize; i += 8) {
344 UINT br;
345 uint8_t tmp[8];
346
347 if (f_read(&fil, tmp, sizeof(tmp), &br) != FR_OK) {
348 efiPrintf("Wideband Update: filed to read");
349 setStatus(WBO_RE_FAILED);
350 goto exit;
351 }
352
353 if (br == 0) {
354 // EOF
355 break;
356 }
357
358 {
359 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_DATA_BASE | i, 8, bus, true);
360 memcpy(&m[0], tmp, 8);
361 }
362
363 if (!waitAck()) {
364 efiPrintf("Wideband Update ERROR: Expected ACK from data write, didn't get one.");
365 setStatus(WBO_RE_FAILED);
366 goto exit;
367 }
368 }
369
370 ret = updateWidebandFirmwareFinalize(bus, hwIndex);
371 if (ret) {
372 goto exit;
373 }
374
375 setStatus(WBO_RE_DONE);
376
377exit:
378 waitingBootloaderThread = nullptr;
379 f_close(&fil);
380#else
381 // Not supported
382#endif
383}
384#endif
385
386// This file contains an array called build_wideband_noboot_bin
387// This array contains the firmware image for the wideband contoller
388#include "wideband_firmware/for_rusefi/wideband_image.h"
389
390void updateWidebandFirmware(uint8_t hwIndex) {
391 size_t bus = getWidebandBus();
392 size_t totalSize = sizeof(build_wideband_image_bin);
393
394 // this also sets waitingBootloaderThread
395 int ret = updateWidebandFirmwarePrepare(bus, hwIndex);
396 if (ret < 0) {
397 goto exit;
398 }
399
400 efiPrintf("Wideband Update: Flash erased! Sending %d bytes...", totalSize);
401
402 // Send flash data 8 bytes at a time
403 for (size_t i = 0; i < totalSize; i += 8) {
404 {
405 CanTxMessage m(CanCategory::WBO_SERVICE, WB_BL_DATA_BASE | i, 8, bus, true);
406 memcpy(&m[0], build_wideband_image_bin + i, 8);
407 }
408
409 if (!waitAck()) {
410 efiPrintf("Wideband Update ERROR: Expected ACK from data write, didn't get one.");
411 setStatus(WBO_RE_FAILED);
412 goto exit;
413 }
414 }
415
416 ret = updateWidebandFirmwareFinalize(bus, hwIndex);
417 if (ret) {
418 goto exit;
419 }
420
421 setStatus(WBO_RE_DONE);
422
423exit:
424 waitingBootloaderThread = nullptr;
425}
426
427#endif // EFI_WIDEBAND_FIRMWARE_UPDATE
428#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
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
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 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