GCC Code Coverage Report


Directory: ./
File: firmware/console/status_loop.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 90.2% 238 0 264
Functions: 75.0% 18 0 24
Branches: 85.5% 112 0 131
Decisions: 62.5% 10 - 16

Line Branch Decision Exec Source
1 /**
2 * @file status_loop.cpp
3 * @brief Human-readable protocol status messages
4 *
5 * http://rusefi.com/forum/viewtopic.php?t=263 rusEfi console overview
6 * http://rusefi.com/forum/viewtopic.php?t=210 Commands overview
7 *
8 *
9 * @date Mar 15, 2013
10 * @author Andrey Belomutskiy, (c) 2012-2020
11 *
12 * This file is part of rusEfi - see http://rusefi.com
13 *
14 * rusEfi is free software; you can redistribute it and/or modify it under the terms of
15 * the GNU General Public License as published by the Free Software Foundation; either
16 * version 3 of the License, or (at your option) any later version.
17 *
18 * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
19 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with this program.
23 * If not, see <http://www.gnu.org/licenses/>.
24 *
25 */
26
27 #include "pch.h"
28 #include "status_loop.h"
29 #include "electronic_throttle.h"
30
31 #if EFI_LOGIC_ANALYZER
32 #include "logic_analyzer.h"
33 #endif /* EFI_LOGIC_ANALYZER */
34
35 #include "trigger_central.h"
36 #include "sensor_reader.h"
37 #include "mmc_card.h"
38 #include "console_io.h"
39 #include "malfunction_central.h"
40 #include "speed_density.h"
41
42 #include "tunerstudio.h"
43 #include "tunerstudio_calibration_channel.h"
44 #include "fuel_math.h"
45 #include "main_trigger_callback.h"
46 #include "spark_logic.h"
47 #include "idle_thread.h"
48 #include "can_hw.h"
49 #include "periodic_thread_controller.h"
50 #include "cdm_ion_sense.h"
51 #include "binary_mlg_logging.h"
52 #include "buffered_writer.h"
53 #include "dynoview.h"
54 #include "frequency_sensor.h"
55 #include "digital_input_exti.h"
56 #include "dc_motors.h"
57
58 #if EFI_PROD_CODE
59 // todo: move this logic to algo folder!
60 #include "rtc_helper.h"
61 #include "rusefi.h"
62 #include "pin_repository.h"
63 #include "max3185x.h"
64 #include "single_timer_executor.h"
65 #include "periodic_task.h"
66 #endif /* EFI_PROD_CODE */
67
68 #if EFI_CONFIGURATION_STORAGE
69 #include "storage.h"
70 #include "flash_main.h"
71 #endif
72
73 #if EFI_MAP_AVERAGING && defined (MODULE_MAP_AVERAGING)
74 #include "map_averaging.h"
75 #endif
76
77 #if (BOARD_TLE8888_COUNT > 0)
78 #include "tle8888.h"
79 #endif /* BOARD_TLE8888_COUNT */
80
81 #if EFI_ENGINE_SNIFFER
82 #include "engine_sniffer.h"
83 extern WaveChart waveChart;
84 #endif /* EFI_ENGINE_SNIFFER */
85
86 extern int maxTriggerReentrant;
87 extern uint32_t maxLockedDuration;
88
89 /**
90 * Time when the firmware version was last reported
91 * TODO: implement a request/response instead of just constantly sending this out
92 */
93 static Timer printVersionTimer;
94
95 // todo: unify with handleGetVersion?
96 static void printVersionForConsole(const char *engineTypeName, const char *firmwareBuildId) {
97 // VersionChecker in rusEFI console is parsing these version string, please follow the expected format
98 efiPrintfProto(PROTOCOL_VERSION_TAG, "%d@%u %s %s %ld",
99 getRusEfiVersion(), /*do we have a working way to print 64 bit values?!*/(unsigned int)SIGNATURE_HASH,
100 firmwareBuildId,
101 engineTypeName,
102 (uint32_t)getTimeNowS());
103 }
104
105 #if EFI_PROD_CODE
106 // Inform the console about the mapping between a pin's logical name (for example, injector 3)
107 // and the physical MCU pin backing that function (for example, PE3)
108 static void printOutPin(const char *pinName, brain_pin_e hwPin) {
109 if (hwPin == Gpio::Unassigned || hwPin == Gpio::Invalid) {
110 return;
111 }
112 const char *hwPinName;
113 if (isBrainPinValid(hwPin)) {
114 hwPinName = hwPortname(hwPin);
115 } else {
116 hwPinName = "smart";
117 }
118
119 efiPrintfProto(PROTOCOL_OUTPIN, "%s@%s", pinName, hwPinName);
120 }
121 #endif // EFI_PROD_CODE
122
123 // Print out the current mapping between logical and physical pins that
124 // the engine sniffer cares about, so we can display a physical pin
125 // in each engine sniffer row
126 static void printEngineSnifferPinMappings() {
127 #if EFI_PROD_CODE
128 printOutPin(PROTOCOL_CRANK1, engineConfiguration->triggerInputPins[0]);
129 printOutPin(PROTOCOL_CRANK2, engineConfiguration->triggerInputPins[1]);
130 for (int i = 0;i<CAM_INPUTS_COUNT;i++) {
131 extern const char *vvtNames[];
132 printOutPin(vvtNames[i], engineConfiguration->camInputs[i]);
133 }
134
135 int cylCount = minI(engineConfiguration->cylindersCount, MAX_CYLINDER_COUNT);
136 for (int i = 0; i < cylCount; i++) {
137 printOutPin(enginePins.coils[i].getShortName(), engineConfiguration->ignitionPins[i]);
138 printOutPin(enginePins.trailingCoils[i].getShortName(), engineConfiguration->trailingCoilPins[i]);
139 printOutPin(enginePins.injectors[i].getShortName(), engineConfiguration->injectionPins[i]);
140 printOutPin(enginePins.injectorsStage2[i].getShortName(), engineConfiguration->injectionPinsStage2[i]);
141 }
142 #endif /* EFI_PROD_CODE */
143 }
144
145 void printOverallStatus() {
146 #if EFI_ENGINE_SNIFFER
147 waveChart.publishIfFull();
148 #endif /* EFI_ENGINE_SNIFFER */
149
150
151 /**
152 * we report the version every second - this way the console does not need to
153 * request it and we will display it pretty soon
154 */
155 if (printVersionTimer.hasElapsedSec(1)) {
156 // we're sending, reset the timer
157 printVersionTimer.reset();
158
159 // Output the firmware version, board type, git hash, uptime in seconds, etc for rusEFI console
160 printVersionForConsole(getEngine_type_e(engineConfiguration->engineType), FIRMWARE_ID);
161
162 // Output the current engine sniffer pin mappings
163 printEngineSnifferPinMappings();
164 }
165 }
166
167 #if !defined(LOGIC_ANALYZER_BUFFER_SIZE)
168 // TODO: how small can this be?
169 #define LOGIC_ANALYZER_BUFFER_SIZE 1000
170 #endif /* LOGIC_ANALYZER_BUFFER_SIZE */
171
172 #if EFI_LOGIC_ANALYZER
173 static char logicAnalyzerBuffer[LOGIC_ANALYZER_BUFFER_SIZE];
174 static Logging logicAnalyzerLogger("logic analyzer", logicAnalyzerBuffer, sizeof(logicAnalyzerBuffer));
175 #endif // EFI_LOGIC_ANALYZER
176
177 /**
178 * @brief Sends all pending data to rusEfi console
179 *
180 * This method is periodically invoked by the main loop
181 * todo: is this mostly dead code?
182 */
183 void updateDevConsoleState() {
184 #if EFI_PROD_CODE
185 // todo: unify with simulator!
186 if (hasFirmwareError()) {
187 efiPrintf("%s error: %s", CRITICAL_PREFIX, getCriticalErrorMessage());
188 return;
189 }
190 #endif /* EFI_PROD_CODE */
191
192 #if EFI_RTC
193 engine->outputChannels.rtcUnixEpochTime = getEpochTime();
194 #endif
195
196 #if HAL_USE_ADC
197 printFullAdcReportIfNeeded();
198 #endif /* HAL_USE_ADC */
199
200 #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
201 int currentCkpEventCounter = engine->triggerCentral.triggerState.getTotalEventCounter();
202 systime_t nowSeconds = getTimeNowS();
203 static int prevCkpEventCounter = -1;
204 static systime_t timeOfPreviousReport = (systime_t) -1;
205 if (prevCkpEventCounter == currentCkpEventCounter && timeOfPreviousReport == nowSeconds) {
206 return;
207 }
208 timeOfPreviousReport = nowSeconds;
209
210 prevCkpEventCounter = currentCkpEventCounter;
211 #else
212 chThdSleepMilliseconds(200);
213 #endif
214
215 #if EFI_LOGIC_ANALYZER
216 printWave(&logicAnalyzerLogger);
217 scheduleLogging(&logicAnalyzerLogger);
218 #endif /* EFI_LOGIC_ANALYZER */
219 }
220
221 #if EFI_PROD_CODE
222 static OutputPin* leds[] = { &enginePins.warningLedPin, &enginePins.runningLedPin,
223 &enginePins.errorLedPin, &enginePins.communicationLedPin, &enginePins.checkEnginePin };
224 #endif // EFI_PROD_CODE
225
226 void initWarningRunningPins() {
227 #if EFI_PROD_CODE
228 // open question if we need warningLedPin and runningLedPin at all!
229 enginePins.warningLedPin.initPin("led: warning status", getWarningLedPin(), LED_PIN_MODE, true);
230 enginePins.runningLedPin.initPin("led: running status", getRunningLedPin(), LED_PIN_MODE, true);
231 #endif /* EFI_PROD_CODE */
232 }
233
234 #if EFI_PROD_CODE
235 static void initStatusLeds() {
236 enginePins.communicationLedPin.initPin("led: comm status", getCommsLedPin(), LED_PIN_MODE, true);
237 // checkEnginePin is already initialized by the time we get here
238 }
239
240 static bool isTriggerErrorNow() {
241 #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
242 bool justHadError = engine->triggerCentral.triggerState.someSortOfTriggerError();
243 return justHadError || engine->triggerCentral.isTriggerDecoderError();
244 #else
245 return false;
246 #endif /* EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT */
247 }
248
249 extern bool consoleByteArrived;
250
251 class CommunicationBlinkingTask : public PeriodicTimerController {
252
253 int getPeriodMs() override {
254 return counter % 2 == 0 ? onTimeMs : offTimeMs;
255 }
256
257 void setAllLeds(int value) {
258 // make sure we do not turn the critical LED off if already have
259 // critical error by now
260 for (size_t i = 0; !hasFirmwareError() && i < efi::size(leds); i++) {
261 leds[i]->setValue(value, /*force*/true);
262 }
263 }
264
265 void PeriodicTask() override {
266 counter++;
267
268 if (counter == 1) {
269 // first invocation of BlinkingTask
270 setAllLeds(1);
271 } else if (counter == 2) {
272 // second invocation of BlinkingTask
273 setAllLeds(0);
274 } else if (counter % 2 == 0) {
275 enginePins.communicationLedPin.setValue(0, /*force*/true);
276
277 #if EFI_FILE_LOGGING
278 extern bool needErrorReportFile;
279 #else
280 #define needErrorReportFile false
281 #endif // EFI_FILE_LOGGING
282 // todo: properly encapsulate warning LED logic!
283 if (!needErrorReportFile) {
284 enginePins.warningLedPin.setValue(0);
285 }
286 } else {
287 #define BLINKING_PERIOD_MS 33
288
289 if (hasFirmwareError()) {
290 // special behavior in case of critical error - not equal on/off time
291 // this special behavior helps to notice that something is not right, also
292 // differentiates software firmware error from critical interrupt error with CPU halt.
293 offTimeMs = 50;
294 onTimeMs = 450;
295 } else if (consoleByteArrived) {
296 offTimeMs = 100;
297 onTimeMs = 33;
298 #if EFI_CONFIGURATION_STORAGE
299 } else if (getNeedToWriteConfiguration()) {
300 offTimeMs = onTimeMs = 500;
301 #endif /* EFI_CONFIGURATION_STORAGE */
302 } else {
303 onTimeMs =
304 #if EFI_USB_SERIAL
305 is_usb_serial_ready() ? 3 * BLINKING_PERIOD_MS :
306 #endif // EFI_USB_SERIAL
307 BLINKING_PERIOD_MS;
308 offTimeMs = 0.6 * onTimeMs;
309 }
310
311 enginePins.communicationLedPin.setValue(1, /*force*/true);
312
313 #if EFI_ENGINE_CONTROL
314 if (isTriggerErrorNow()) {
315 // todo: at the moment warning codes do not affect warning LED?!
316 enginePins.warningLedPin.setValue(1);
317 }
318 #endif /* EFI_ENGINE_CONTROL */
319 }
320 }
321
322 private:
323 int counter = 0;
324 int onTimeMs = 100;
325 int offTimeMs = 100;
326 };
327
328 static CommunicationBlinkingTask communicationsBlinkingTask;
329
330 #endif /* EFI_PROD_CODE */
331
332
333 #if EFI_TUNER_STUDIO
334
335 /**
336 * This is useful if we are changing engine mode dynamically
337 * For example http://rusefi.com/forum/viewtopic.php?f=5&t=1085
338 */
339 522954 static int packEngineMode() {
340 522954 return (Enum2Underlying(engineConfiguration->fuelAlgorithm) << 4) +
341 522954 (engineConfiguration->injectionMode << 2) +
342 522954 engineConfiguration->ignitionMode;
343 }
344
345 522954 static void updateTempSensors() {
346
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult clt = Sensor::get(SensorType::Clt);
347 522954 engine->outputChannels.coolant = clt.value_or(0);
348 522954 engine->outputChannels.isCltError = !clt.Valid;
349
350
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult iat = Sensor::get(SensorType::Iat);
351 522954 engine->outputChannels.intake = iat.value_or(0);
352 522954 engine->outputChannels.isIatError = !iat.Valid;
353
354
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult auxTemp1 = Sensor::get(SensorType::AuxTemp1);
355 522954 engine->outputChannels.auxTemp1 = auxTemp1.value_or(0);
356
357
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult auxTemp2 = Sensor::get(SensorType::AuxTemp2);
358 522954 engine->outputChannels.auxTemp2 = auxTemp2.value_or(0);
359
360
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult oilTemp = Sensor::get(SensorType::OilTemperature);
361 522954 engine->outputChannels.oilTemp = oilTemp.value_or(0);
362
363 // see also updateFuelSensors()
364
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult fuelTemp = Sensor::get(SensorType::FuelTemperature);
365 522954 engine->outputChannels.fuelTemp = fuelTemp.value_or(0);
366
367
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult ambientTemp = Sensor::get(SensorType::AmbientTemperature);
368 522954 engine->outputChannels.ambientTemp = ambientTemp.value_or(0);
369
370
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult compressorDischargeTemp = Sensor::get(SensorType::CompressorDischargeTemperature);
371 522954 engine->outputChannels.compressorDischargeTemp = compressorDischargeTemp.value_or(0);
372 522954 }
373
374 void updateUnfilteredRawPedal();
375
376 522954 static void updateThrottles() {
377
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult tps1 = Sensor::get(SensorType::Tps1);
378 522954 engine->outputChannels.TPSValue = tps1.value_or(0);
379
2/3
✓ Branch 1 taken 522954 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 522954 times.
522954 engine->outputChannels.isTpsError = Sensor::hasSensor(SensorType::Tps1) ? !tps1.Valid : false;
380
1/1
✓ Branch 1 taken 522954 times.
522954 engine->outputChannels.tpsADC = convertVoltageTo10bitADC(Sensor::getRaw(SensorType::Tps1Primary));
381
382
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult tps2 = Sensor::get(SensorType::Tps2);
383 522954 engine->outputChannels.TPS2Value = tps2.value_or(0);
384 // If we don't have a TPS2 at all, don't turn on the failure light
385
2/3
✓ Branch 1 taken 522954 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 522954 times.
522954 engine->outputChannels.isTps2Error = Sensor::hasSensor(SensorType::Tps2Primary) ? !tps2.Valid : false;
386
387
1/1
✓ Branch 2 taken 522954 times.
522954 SensorResult pedal = Sensor::get(SensorType::AcceleratorPedal);
388 522954 engine->outputChannels.throttlePedalPosition = pedal.value_or(0);
389 // Only report fail if you have one (many people don't)
390
2/3
✓ Branch 1 taken 522954 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 522954 times.
522954 engine->outputChannels.isPedalError = Sensor::hasSensor(SensorType::AcceleratorPedal) ? !pedal.Valid : false;
391
392 // TPS 1 pri/sec split
393
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 5 taken 522954 times.
522954 engine->outputChannels.tps1Split = Sensor::getOrZero(SensorType::Tps1Primary) - Sensor::getOrZero(SensorType::Tps1Secondary);
394 // TPS 2 pri/sec split
395
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 5 taken 522954 times.
522954 engine->outputChannels.tps2Split = Sensor::getOrZero(SensorType::Tps2Primary) - Sensor::getOrZero(SensorType::Tps2Secondary);
396 // TPS1 - TPS2 split
397
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 5 taken 522954 times.
522954 engine->outputChannels.tps12Split = Sensor::getOrZero(SensorType::Tps1) - Sensor::getOrZero(SensorType::Tps2);
398 // Pedal pri/sec split
399
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 5 taken 522954 times.
522954 engine->outputChannels.accPedalSplit = Sensor::getOrZero(SensorType::AcceleratorPedalPrimary) - Sensor::getOrZero(SensorType::AcceleratorPedalSecondary);
400
401
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.accPedalUnfiltered = Sensor::getOrZero(SensorType::AcceleratorPedalUnfiltered);
402
1/1
✓ Branch 1 taken 522954 times.
522954 updateUnfilteredRawPedal();
403 522954 }
404
405 522954 static void updateLambda() {
406 522954 float lambdaValue = Sensor::getOrZero(SensorType::Lambda1);
407 522954 engine->outputChannels.lambdaValue = lambdaValue;
408 522954 engine->outputChannels.AFRValue = lambdaValue * engine->fuelComputer.stoichiometricRatio;
409 // TODO: this can be calculated on PC side!
410 522954 engine->outputChannels.afrGasolineScale = lambdaValue * STOICH_RATIO;
411
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.SmoothedAFRValue = Sensor::getOrZero(SensorType::SmoothedLambda1);
412
413 522954 float lambda2Value = Sensor::getOrZero(SensorType::Lambda2);
414 522954 engine->outputChannels.lambdaValue2 = lambda2Value;
415 522954 engine->outputChannels.AFRValue2 = lambda2Value * engine->fuelComputer.stoichiometricRatio;
416 // TODO: this can be calculated on PC side!
417 522954 engine->outputChannels.afr2GasolineScale = lambda2Value * STOICH_RATIO;
418
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.SmoothedAFRValue2 = Sensor::getOrZero(SensorType::SmoothedLambda2);
419 522954 }
420
421 522954 static void updateFuelSensors() {
422 // Low pressure is directly in kpa
423
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.lowFuelPressure = Sensor::getOrZero(SensorType::FuelPressureLow);
424 // High pressure is in bar, aka 100 kpa
425
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.highFuelPressure = KPA2BAR(Sensor::getOrZero(SensorType::FuelPressureHigh));
426
427
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.flexPercent = Sensor::getOrZero(SensorType::FuelEthanolPercent);
428
429
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.fuelTankLevel = Sensor::getOrZero(SensorType::FuelLevel);
430 522954 }
431
432 522954 static void updateEgtSensors() {
433
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[0] = Sensor::getOrZero(SensorType::EGT1);
434
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[1] = Sensor::getOrZero(SensorType::EGT2);
435
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[2] = Sensor::getOrZero(SensorType::EGT3);
436
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[3] = Sensor::getOrZero(SensorType::EGT4);
437
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[4] = Sensor::getOrZero(SensorType::EGT5);
438
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[5] = Sensor::getOrZero(SensorType::EGT6);
439
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[6] = Sensor::getOrZero(SensorType::EGT7);
440
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.egt[7] = Sensor::getOrZero(SensorType::EGT8);
441 522954 }
442
443 522954 static void updateVvtSensors() {
444 #if EFI_SHAFT_POSITION_INPUT
445 // 248
446
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.vvtPositionB1I = engine->triggerCentral.getVVTPosition(/*bankIndex*/0, /*camIndex*/0);
447
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.vvtPositionB1E = engine->triggerCentral.getVVTPosition(/*bankIndex*/0, /*camIndex*/1);
448
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.vvtPositionB2I = engine->triggerCentral.getVVTPosition(/*bankIndex*/1, /*camIndex*/0);
449
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.vvtPositionB2E = engine->triggerCentral.getVVTPosition(/*bankIndex*/1, /*camIndex*/1);
450 #endif
451 522954 }
452
453 522954 static void updateVehicleSpeed() {
454 #if EFI_VEHICLE_SPEED
455
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.vehicleSpeedKph = Sensor::getOrZero(SensorType::VehicleSpeed);
456 522954 engine->outputChannels.wheelSlipRatio = Sensor::getOrZero(SensorType::WheelSlipRatio);
457 #ifdef MODULE_GEAR_DETECTOR
458
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 6 taken 522954 times.
522954 engine->outputChannels.speedToRpmRatio = engine->module<GearDetector>()->getGearboxRatio();
459 522954 engine->outputChannels.detectedGear = Sensor::getOrZero(SensorType::DetectedGear);
460 #endif
461 #endif /* EFI_VEHICLE_SPEED */
462 522954 }
463
464 static SensorType luaGaugeTypes[] = {
465 SensorType::LuaGauge1,
466 SensorType::LuaGauge2,
467 SensorType::LuaGauge3,
468 SensorType::LuaGauge4,
469 SensorType::LuaGauge5,
470 SensorType::LuaGauge6,
471 SensorType::LuaGauge7,
472 SensorType::LuaGauge8
473 };
474
475 522954 static void updateRawSensors() {
476
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawTps1Primary = Sensor::getRaw(SensorType::Tps1Primary);
477
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawTps1Secondary = Sensor::getRaw(SensorType::Tps1Secondary);
478
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawTps2Primary = Sensor::getRaw(SensorType::Tps2Primary);
479
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawTps2Secondary = Sensor::getRaw(SensorType::Tps2Secondary);
480 522954 engine->outputChannels.rawPpsPrimary = Sensor::getRaw(SensorType::AcceleratorPedalPrimary);
481 522954 engine->outputChannels.rawPpsSecondary = Sensor::getRaw(SensorType::AcceleratorPedalSecondary);
482
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawBattery = Sensor::getRaw(SensorType::BatteryVoltage);
483
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawClt = Sensor::getRaw(SensorType::Clt);
484
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawIat = Sensor::getRaw(SensorType::Iat);
485
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAuxTemp1 = Sensor::getRaw(SensorType::AuxTemp1);
486
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAuxTemp2 = Sensor::getRaw(SensorType::AuxTemp2);
487
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAmbientTemp = Sensor::getRaw(SensorType::AmbientTemperature);
488
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawOilPressure = Sensor::getRaw(SensorType::OilPressure);
489
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawFuelLevel = Sensor::getRaw(SensorType::FuelLevel);
490
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAcPressure = Sensor::getRaw(SensorType::AcPressure);
491
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawLowFuelPressure = Sensor::getRaw(SensorType::FuelPressureLow);
492
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawHighFuelPressure = Sensor::getRaw(SensorType::FuelPressureHigh);
493
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawMaf = Sensor::getRaw(SensorType::Maf);
494
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawMaf2 = Sensor::getRaw(SensorType::Maf2);
495
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawMap = Sensor::getRaw(SensorType::MapSlow);
496
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawWastegatePosition = Sensor::getRaw(SensorType::WastegatePosition);
497
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawIdlePositionSensor = Sensor::getRaw(SensorType::IdlePosition);
498
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAuxAnalog1 = Sensor::getRaw(SensorType::AuxAnalog1);
499
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAuxAnalog2 = Sensor::getRaw(SensorType::AuxAnalog2);
500
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAuxAnalog3 = Sensor::getRaw(SensorType::AuxAnalog3);
501
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.rawAuxAnalog4 = Sensor::getRaw(SensorType::AuxAnalog4);
502
503
2/2
✓ Branch 0 taken 4183632 times.
✓ Branch 1 taken 522954 times.
2/2
✓ Decision 'true' taken 4183632 times.
✓ Decision 'false' taken 522954 times.
4706586 for (size_t i = 0;i<LUA_GAUGE_COUNT;i++) {
504 4183632 engine->outputChannels.luaGauges[i] = Sensor::getOrZero(luaGaugeTypes[i]);
505 }
506
507
2/2
✓ Branch 0 taken 4183632 times.
✓ Branch 1 taken 522954 times.
2/2
✓ Decision 'true' taken 4183632 times.
✓ Decision 'false' taken 522954 times.
4706586 for (int i = 0; i < LUA_ANALOG_INPUT_COUNT; i++) {
508 4183632 adc_channel_e channel = engineConfiguration->auxAnalogInputs[i];
509
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4183632 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 4183632 times.
4183632 if (isAdcChannelValid(channel)) {
510 engine->outputChannels.rawAnalogInput[i] = adcGetScaledVoltage("raw aux", channel).value_or(0);
511 }
512 }
513
514 // TODO: transition AFR to new sensor model
515
5/7
✓ Branch 1 taken 195464 times.
✓ Branch 2 taken 327490 times.
✓ Branch 5 taken 195464 times.
✓ Branch 9 taken 195464 times.
✓ Branch 10 taken 327490 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
522954 engine->outputChannels.rawAfr = (engineConfiguration->afr.hwChannel == EFI_ADC_NONE) ? 0 : adcGetScaledVoltage("ego", engineConfiguration->afr.hwChannel).value_or(0);
516 522954 }
517 522954 static void updatePressures() {
518
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.baroPressure = Sensor::getOrZero(SensorType::BarometricPressure);
519 // instantMAPValue is injected in a very different way
520 522954 float mapValue = Sensor::getOrZero(SensorType::Map);
521 522954 engine->outputChannels.MAPValue = mapValue;
522
523 522954 engine->outputChannels.mapFast = Sensor::getOrZero(SensorType::MapFast);
524
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.oilPressure = Sensor::getOrZero(SensorType::OilPressure);
525 522954 engine->outputChannels.acPressure = Sensor::getOrZero(SensorType::AcPressure);
526
527
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.compressorDischargePressure = Sensor::getOrZero(SensorType::CompressorDischargePressure);
528
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.throttleInletPressure = Sensor::getOrZero(SensorType::ThrottleInletPressure);
529 522954 engine->outputChannels.throttlePressureRatio = getThrottlePressureRatio(mapValue);
530
531 522954 engine->outputChannels.auxLinear1 = Sensor::getOrZero(SensorType::AuxLinear1);
532 522954 engine->outputChannels.auxLinear2 = Sensor::getOrZero(SensorType::AuxLinear2);
533 522954 engine->outputChannels.auxLinear3 = Sensor::getOrZero(SensorType::AuxLinear3);
534 522954 engine->outputChannels.auxLinear4 = Sensor::getOrZero(SensorType::AuxLinear4);
535 522954 }
536
537 522954 static void updateMiscSensors() {
538
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.VBatt = Sensor::getOrZero(SensorType::BatteryVoltage);
539
540
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.idlePositionSensor = Sensor::getOrZero(SensorType::IdlePosition);
541
542
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.wastegatePositionSensor = Sensor::getOrZero(SensorType::WastegatePosition);
543
544 522954 engine->outputChannels.ISSValue = Sensor::getOrZero(SensorType::InputShaftSpeed);
545 522954 engine->outputChannels.auxSpeed1 = Sensor::getOrZero(SensorType::AuxSpeed1);
546 522954 engine->outputChannels.auxSpeed2 = Sensor::getOrZero(SensorType::AuxSpeed2);
547
548 #if HAL_USE_ADC
549 engine->outputChannels.internalMcuTemperature = getMCUInternalTemperature();
550 engine->outputChannels.internalVref = getMCUVref();
551 #endif /* HAL_USE_ADC */
552 522954 }
553
554 522954 static void updateSensors() {
555 522954 updateTempSensors();
556 522954 updateThrottles();
557 522954 updateRawSensors();
558 522954 updateLambda();
559 522954 updateFuelSensors();
560 522954 updateEgtSensors();
561 522954 updateVvtSensors();
562 522954 updateVehicleSpeed();
563 522954 updatePressures();
564 522954 updateMiscSensors();
565 522954 }
566
567 522954 static void updateFuelCorrections() {
568 522954 engine->outputChannels.Gego = 100.0f * engine->engineState.stftCorrection[0];
569 522954 }
570
571 522954 static void updateFuelResults() {
572 #if EFI_VEHICLE_SPEED && defined (MODULE_ODOMETER)
573
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 6 taken 522954 times.
522954 engine->outputChannels.fuelFlowRate = engine->module<TripOdometer>()->getConsumptionGramPerSecond();
574 522954 engine->outputChannels.totalFuelConsumption = engine->module<TripOdometer>()->getConsumedGrams();
575 522954 engine->outputChannels.ignitionOnTime = engine->module<TripOdometer>()->getIgnitionOnTime();
576 522954 engine->outputChannels.engineRunTime = engine->module<TripOdometer>()->getEngineRunTime();
577
578 // output channel in km
579
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 6 taken 522954 times.
522954 engine->outputChannels.distanceTraveled = 0.001f * engine->module<TripOdometer>()->getDistanceMeters();
580 #endif // EFI_VEHICLE_SPEED MODULE_ODOMETER
581 522954 }
582
583 522954 static void updateFuelInfo() {
584 522954 updateFuelCorrections();
585 522954 updateFuelResults();
586 #if EFI_ENGINE_CONTROL
587 522954 const auto& wallFuel = engine->injectionEvents.elements[0].getWallFuel();
588
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.wallFuelAmount = wallFuel.getWallFuel() * 1000; // Convert grams to mg
589 522954 engine->outputChannels.wallFuelCorrectionValue = wallFuel.wallFuelCorrection * 1000; // Convert grams to mg
590
591 522954 engine->outputChannels.veValue = engine->engineState.currentVe;
592 #endif // EFI_ENGINE_CONTROL
593 522954 }
594
595 522954 static void updateIgnition(float rpm) {
596 #if EFI_ENGINE_CONTROL
597
1/1
✓ Branch 2 taken 522954 times.
522954 engine->outputChannels.coilDutyCycle = getCoilDutyCycle(rpm);
598 #endif // EFI_ENGINE_CONTROL
599 522954 }
600
601 522954 static void updateFlags() {
602 522954 engine->outputChannels.isO2HeaterOn = enginePins.o2heater.getLogicValue();
603 // todo: eliminate state copy logic by giving DfcoController it's owm xxx.txt and leveraging LiveData
604 522954 engine->outputChannels.dfcoActive = engine->module<DfcoController>()->cutFuel();
605
606 #if EFI_LAUNCH_CONTROL
607 522954 engine->outputChannels.launchTriggered = engine->launchController.isLaunchCondition;
608 #endif
609
610 #if EFI_PROD_CODE
611 engine->outputChannels.isTriggerError = isTriggerErrorNow();
612 #endif // EFI_PROD_CODE
613
614 #if EFI_CONFIGURATION_STORAGE
615 engine->outputChannels.needBurn = getNeedToWriteConfiguration();
616 #endif /* EFI_CONFIGURATION_STORAGE */
617 #if EFI_FILE_LOGGING
618 updateSdCardLiveFlags();
619 #endif
620 522954 }
621
622 // sensor state for EFI Analytics Tuner Studio
623 // todo: the 'let's copy internal state for external consumers' approach is DEPRECATED
624 // As of 2022 it's preferred to leverage LiveData where all state is exposed
625 // this method is invoked ONLY if we SD card log or have serial connection with some frontend app
626 522954 void updateTunerStudioState() {
627 522954 TunerStudioOutputChannels *tsOutputChannels = &engine->outputChannels;
628 #if EFI_USB_SERIAL
629 // pretty much SD card logs know if specifically USB serial is active
630 engine->outputChannels.isUsbConnected = is_usb_serial_ready();
631 #endif // EFI_USB_SERIAL
632
633
1/1
✓ Branch 2 taken 522954 times.
522954 float rpm = Sensor::get(SensorType::Rpm).value_or(0);
634
635
636 static Timer blinkIndicatorsTimer;
637 522954 constexpr float blinkHalfPeriod = 0.3;
638 522954 bool isBlinkPhase = blinkIndicatorsTimer.hasElapsedSec(blinkHalfPeriod);
639 #if EFI_ELECTRONIC_THROTTLE_BODY
640 522954 blinkEtbErrorCodes(isBlinkPhase);
641 #endif // EFI_ELECTRONIC_THROTTLE_BODY
642
2/2
✓ Branch 0 taken 259331 times.
✓ Branch 1 taken 263623 times.
2/2
✓ Decision 'true' taken 259331 times.
✓ Decision 'false' taken 263623 times.
522954 if (isBlinkPhase) {
643 259331 engine->outputChannels.sparkCutReasonBlinker = 0;
644 259331 engine->outputChannels.fuelCutReasonBlinker = 0;
645
646
2/2
✓ Branch 1 taken 1644 times.
✓ Branch 2 taken 257687 times.
2/2
✓ Decision 'true' taken 1644 times.
✓ Decision 'false' taken 257687 times.
259331 if (blinkIndicatorsTimer.hasElapsedSec(2 * blinkHalfPeriod)) {
647 1644 blinkIndicatorsTimer.reset();
648 }
649 } else {
650 263623 engine->outputChannels.sparkCutReasonBlinker = engine->outputChannels.sparkCutReason;
651 263623 engine->outputChannels.fuelCutReasonBlinker = engine->outputChannels.fuelCutReason;
652 }
653
654
655 #if EFI_PROD_CODE
656 executorStatistics();
657 #endif /* EFI_PROD_CODE */
658
659 522954 DcHardware *dc = getPrimaryDCHardwareForLogging();
660 522954 engine->dc_motors.dcOutput0 = dc->dcMotor.get();
661 522954 engine->dc_motors.isEnabled0_int = dc->msg() == nullptr;
662
663 522954 tsOutputChannels->RPMValue = rpm;
664 #if EFI_SHAFT_POSITION_INPUT
665 522954 tsOutputChannels->instantRpm = engine->triggerCentral.instantRpm.getInstantRpm();
666 522954 tsOutputChannels->totalTriggerErrorCounter = engine->triggerCentral.triggerState.totalTriggerErrorCounter;
667 522954 tsOutputChannels->rpmAcceleration = engine->rpmCalculator.getRpmAcceleration();
668
669 522954 tsOutputChannels->orderingErrorCounter = engine->triggerCentral.triggerState.orderingErrorCounter;
670 #endif // EFI_SHAFT_POSITION_INPUT
671
672 522954 updateSensors();
673 522954 updateFuelInfo();
674 522954 updateIgnition(rpm);
675 522954 updateFlags();
676 // update calibration channel, reset to None state after timeout
677 522954 tsCalibrationIsIdle();
678
679 // Output both the estimated air flow, and measured air flow (if available)
680
1/1
✓ Branch 2 taken 522954 times.
522954 tsOutputChannels->mafMeasured = Sensor::getOrZero(SensorType::Maf);
681
1/1
✓ Branch 2 taken 522954 times.
522954 tsOutputChannels->mafMeasured2 = Sensor::getOrZero(SensorType::Maf2);
682 522954 tsOutputChannels->mafEstimate = engine->engineState.airflowEstimate;
683
684 #if EFI_ENGINE_CONTROL
685
2/2
✓ Branch 2 taken 522954 times.
✓ Branch 5 taken 522954 times.
522954 tsOutputChannels->injectorDutyCycle = minF(/*let's avoid scaled "uint8_t, 2" overflow*/127, getInjectorDutyCycle(rpm));
686
1/1
✓ Branch 2 taken 522954 times.
522954 tsOutputChannels->injectorDutyCycleStage2 = getInjectorDutyCycleStage2(rpm);
687 #endif
688
689 522954 tsOutputChannels->seconds = getTimeNowS();
690
691 522954 tsOutputChannels->engineMode = packEngineMode();
692 522954 tsOutputChannels->firmwareVersion = getRusEfiVersion();
693
694 522954 tsOutputChannels->accelerationLat = engine->sensors.accelerometer.lat;
695 522954 tsOutputChannels->accelerationLon = engine->sensors.accelerometer.lon;
696 522954 tsOutputChannels->accelerationVert = engine->sensors.accelerometer.vert;
697 522954 tsOutputChannels->gyroYaw = engine->sensors.accelerometer.yawRate;
698
699 #if EFI_DYNO_VIEW
700 522954 tsOutputChannels->hp = getDynoviewHP();
701 522954 tsOutputChannels->torque = getDynoviewTorque();
702 #else
703 tsOutputChannels->hp = -1;
704 tsOutputChannels->torque = -1;
705 #endif
706
707 522954 tsOutputChannels->turboSpeed = Sensor::getOrZero(SensorType::TurbochargerSpeed);
708 extern FrequencySensor inputShaftSpeedSensor;
709 522954 tsOutputChannels->issEdgeCounter = inputShaftSpeedSensor.eventCounter;
710 extern FrequencySensor vehicleSpeedSensor;
711 522954 tsOutputChannels->vssEdgeCounter = vehicleSpeedSensor.eventCounter;
712
713
3/6
✓ Branch 0 taken 522954 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 522954 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 522954 times.
522954 tsOutputChannels->hasCriticalError = hasFirmwareError() || hasConfigError() || engine->engineState.warnings.hasWarningMessage();
714 522954 tsOutputChannels->hasFaultReportFile = hasErrorReportFile();
715
2/4
✓ Branch 1 taken 522954 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 522954 times.
522954 tsOutputChannels->triggerPageRefreshFlag = needToTriggerTsRefresh() || ltftNeedVeRefresh();
716
717 522954 tsOutputChannels->isWarnNow = engine->engineState.warnings.isWarningNow();
718
719 522954 tsOutputChannels->tpsAccelFuel = engine->engineState.tpsAccelEnrich;
720
721 522954 tsOutputChannels->checkEngine = hasErrorCodes();
722
723 522954 engine->engineState.warnings.refreshTs();
724
725 522954 tsOutputChannels->starterState = enginePins.starterControl.getLogicValue();
726 522954 tsOutputChannels->starterRelayDisable = enginePins.starterRelayDisable.getLogicValue();
727
728
729 522954 tsOutputChannels->revolutionCounterSinceStart = engine->rpmCalculator.getRevolutionCounterSinceStart();
730
731 #if EFI_CLOCK_LOCKS
732 tsOutputChannels->maxLockedDuration = NT2US(maxLockedDuration);
733 #endif /* EFI_CLOCK_LOCKS */
734
735 #if EFI_SHAFT_POSITION_INPUT
736 522954 tsOutputChannels->maxTriggerReentrant = maxTriggerReentrant;
737 522954 tsOutputChannels->triggerPrimaryFall = engine->triggerCentral.getHwEventCounter((int)SHAFT_PRIMARY_FALLING);
738 522954 tsOutputChannels->triggerPrimaryRise = engine->triggerCentral.getHwEventCounter((int)SHAFT_PRIMARY_RISING);
739
740 522954 tsOutputChannels->triggerSecondaryFall = engine->triggerCentral.getHwEventCounter((int)SHAFT_SECONDARY_FALLING);
741 522954 tsOutputChannels->triggerSecondaryRise = engine->triggerCentral.getHwEventCounter((int)SHAFT_SECONDARY_RISING);
742
743 #endif // EFI_SHAFT_POSITION_INPUT
744
745 #if HAL_USE_PAL && EFI_PROD_CODE
746 tsOutputChannels->extiOverflowCount = getExtiOverflowCounter();
747 #endif
748
749
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 522954 times.
522954 switch (engineConfiguration->debugMode) {
750 case DBG_LOGIC_ANALYZER:
751 #if EFI_LOGIC_ANALYZER
752 // used by HW CI
753 reportLogicAnalyzerToTS();
754 #endif /* EFI_LOGIC_ANALYZER */
755 break;
756
1/1
✓ Decision 'true' taken 522954 times.
522954 default:
757 ;
758 }
759 522954 }
760
761 #endif /* EFI_TUNER_STUDIO */
762
763 void startStatusThreads() {
764 // todo: refactoring needed, this file should probably be split into pieces
765 #if EFI_PROD_CODE
766 initStatusLeds();
767 communicationsBlinkingTask.start();
768 #endif /* EFI_PROD_CODE */
769 }
770