2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
28 #if defined(USE_ESC_SENSOR)
30 #include "build/debug.h"
32 #include "common/time.h"
34 #include "config/feature.h"
36 #include "pg/pg_ids.h"
39 #include "common/maths.h"
40 #include "common/utils.h"
42 #include "drivers/timer.h"
43 #include "drivers/motor.h"
44 #include "drivers/dshot.h"
45 #include "drivers/dshot_dpwm.h"
46 #include "drivers/serial.h"
47 #include "drivers/serial_uart.h"
49 #include "esc_sensor.h"
51 #include "config/config.h"
53 #include "flight/mixer.h"
55 #include "io/serial.h"
58 KISS ESC TELEMETRY PROTOCOL
59 ---------------------------
61 One transmission will have 10 times 8-bit bytes sent with 115200 baud and 3.6V.
64 Byte 1: Voltage high byte
65 Byte 2: Voltage low byte
66 Byte 3: Current high byte
67 Byte 4: Current low byte
68 Byte 5: Consumption high byte
69 Byte 6: Consumption low byte
76 PG_REGISTER_WITH_RESET_TEMPLATE(escSensorConfig_t
, escSensorConfig
, PG_ESC_SENSOR_CONFIG
, 0);
78 PG_RESET_TEMPLATE(escSensorConfig_t
, escSensorConfig
,
86 set debug_mode = DEBUG_ESC_SENSOR in cli
91 DEBUG_ESC_MOTOR_INDEX
= 0,
92 DEBUG_ESC_NUM_TIMEOUTS
= 1,
93 DEBUG_ESC_NUM_CRC_ERRORS
= 2,
94 DEBUG_ESC_DATA_AGE
= 3,
98 ESC_SENSOR_FRAME_PENDING
= 0,
99 ESC_SENSOR_FRAME_COMPLETE
= 1,
100 ESC_SENSOR_FRAME_FAILED
= 2
101 } escTlmFrameState_t
;
104 ESC_SENSOR_TRIGGER_STARTUP
= 0,
105 ESC_SENSOR_TRIGGER_READY
= 1,
106 ESC_SENSOR_TRIGGER_PENDING
= 2
107 } escSensorTriggerState_t
;
109 #define ESC_SENSOR_BAUDRATE 115200
110 #define ESC_BOOTTIME 5000 // 5 seconds
111 #define ESC_REQUEST_TIMEOUT 100 // 100 ms (data transfer takes only 900us)
113 #define TELEMETRY_FRAME_SIZE 10
114 static uint8_t telemetryBuffer
[TELEMETRY_FRAME_SIZE
] = { 0, };
116 static volatile uint8_t *buffer
;
117 static volatile uint8_t bufferSize
= 0;
118 static volatile uint8_t bufferPosition
= 0;
120 static serialPort_t
*escSensorPort
= NULL
;
122 static escSensorData_t escSensorData
[MAX_SUPPORTED_MOTORS
];
124 static escSensorTriggerState_t escSensorTriggerState
= ESC_SENSOR_TRIGGER_STARTUP
;
125 static uint32_t escTriggerTimestamp
;
126 static uint8_t escSensorMotor
= 0; // motor index
128 static escSensorData_t combinedEscSensorData
;
129 static bool combinedDataNeedsUpdate
= true;
131 static uint16_t totalTimeoutCount
= 0;
132 static uint16_t totalCrcErrorCount
= 0;
134 void startEscDataRead(uint8_t *frameBuffer
, uint8_t frameLength
)
136 buffer
= frameBuffer
;
138 bufferSize
= frameLength
;
141 uint8_t getNumberEscBytesRead(void)
143 return bufferPosition
;
146 static bool isFrameComplete(void)
148 return bufferPosition
== bufferSize
;
151 bool isEscSensorActive(void)
153 return escSensorPort
!= NULL
;
156 escSensorData_t
*getEscSensorData(uint8_t motorNumber
)
158 if (!featureIsEnabled(FEATURE_ESC_SENSOR
)) {
162 if (motorNumber
< getMotorCount()) {
163 return &escSensorData
[motorNumber
];
164 } else if (motorNumber
== ESC_SENSOR_COMBINED
) {
165 if (combinedDataNeedsUpdate
) {
166 combinedEscSensorData
.dataAge
= 0;
167 combinedEscSensorData
.temperature
= 0;
168 combinedEscSensorData
.voltage
= 0;
169 combinedEscSensorData
.current
= 0;
170 combinedEscSensorData
.consumption
= 0;
171 combinedEscSensorData
.rpm
= 0;
173 for (int i
= 0; i
< getMotorCount(); i
= i
+ 1) {
174 combinedEscSensorData
.dataAge
= MAX(combinedEscSensorData
.dataAge
, escSensorData
[i
].dataAge
);
175 combinedEscSensorData
.temperature
= MAX(combinedEscSensorData
.temperature
, escSensorData
[i
].temperature
);
176 combinedEscSensorData
.voltage
+= escSensorData
[i
].voltage
;
177 combinedEscSensorData
.current
+= escSensorData
[i
].current
;
178 combinedEscSensorData
.consumption
+= escSensorData
[i
].consumption
;
179 combinedEscSensorData
.rpm
+= escSensorData
[i
].rpm
;
182 combinedEscSensorData
.voltage
= combinedEscSensorData
.voltage
/ getMotorCount();
183 combinedEscSensorData
.rpm
= combinedEscSensorData
.rpm
/ getMotorCount();
185 combinedDataNeedsUpdate
= false;
187 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_DATA_AGE
, combinedEscSensorData
.dataAge
);
190 return &combinedEscSensorData
;
196 // Receive ISR callback
197 static void escSensorDataReceive(uint16_t c
, void *data
)
201 // KISS ESC sends some data during startup, ignore this for now (maybe future use)
202 // startup data could be firmware version and serialnumber
204 if (isFrameComplete()) {
208 buffer
[bufferPosition
++] = (uint8_t)c
;
211 bool escSensorInit(void)
213 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_ESC_SENSOR
);
218 portOptions_e options
= SERIAL_NOT_INVERTED
| (escSensorConfig()->halfDuplex
? SERIAL_BIDIR
: 0);
220 // Initialize serial port
221 escSensorPort
= openSerialPort(portConfig
->identifier
, FUNCTION_ESC_SENSOR
, escSensorDataReceive
, NULL
, ESC_SENSOR_BAUDRATE
, MODE_RX
, options
);
223 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
= i
+ 1) {
224 escSensorData
[i
].dataAge
= ESC_DATA_INVALID
;
227 return escSensorPort
!= NULL
;
230 static uint8_t updateCrc8(uint8_t crc
, uint8_t crc_seed
)
235 for (int i
=0; i
<8; i
++) {
236 crc_u
= ( crc_u
& 0x80 ) ? 0x7 ^ ( crc_u
<< 1 ) : ( crc_u
<< 1 );
242 uint8_t calculateCrc8(const uint8_t *Buf
, const uint8_t BufLen
)
245 for (int i
= 0; i
< BufLen
; i
++) {
246 crc
= updateCrc8(Buf
[i
], crc
);
252 static uint8_t decodeEscFrame(void)
254 if (!isFrameComplete()) {
255 return ESC_SENSOR_FRAME_PENDING
;
259 uint16_t chksum
= calculateCrc8(telemetryBuffer
, TELEMETRY_FRAME_SIZE
- 1);
260 uint16_t tlmsum
= telemetryBuffer
[TELEMETRY_FRAME_SIZE
- 1]; // last byte contains CRC value
262 if (chksum
== tlmsum
) {
263 escSensorData
[escSensorMotor
].dataAge
= 0;
264 escSensorData
[escSensorMotor
].temperature
= telemetryBuffer
[0];
265 escSensorData
[escSensorMotor
].voltage
= telemetryBuffer
[1] << 8 | telemetryBuffer
[2];
266 escSensorData
[escSensorMotor
].current
= telemetryBuffer
[3] << 8 | telemetryBuffer
[4];
267 escSensorData
[escSensorMotor
].consumption
= telemetryBuffer
[5] << 8 | telemetryBuffer
[6];
268 escSensorData
[escSensorMotor
].rpm
= telemetryBuffer
[7] << 8 | telemetryBuffer
[8];
270 combinedDataNeedsUpdate
= true;
272 frameStatus
= ESC_SENSOR_FRAME_COMPLETE
;
274 if (escSensorMotor
< 4) {
275 DEBUG_SET(DEBUG_ESC_SENSOR_RPM
, escSensorMotor
, lrintf(erpmToRpm(escSensorData
[escSensorMotor
].rpm
) / 10.0f
)); // output actual rpm/10 to fit in 16bit signed.
276 DEBUG_SET(DEBUG_ESC_SENSOR_TMP
, escSensorMotor
, escSensorData
[escSensorMotor
].temperature
);
279 frameStatus
= ESC_SENSOR_FRAME_FAILED
;
285 static void increaseDataAge(void)
287 if (escSensorData
[escSensorMotor
].dataAge
< ESC_DATA_INVALID
) {
288 escSensorData
[escSensorMotor
].dataAge
++;
290 combinedDataNeedsUpdate
= true;
294 static void selectNextMotor(void)
297 if (escSensorMotor
== getMotorCount()) {
302 // XXX Review ESC sensor under refactored motor handling
304 void escSensorProcess(timeUs_t currentTimeUs
)
306 const timeMs_t currentTimeMs
= currentTimeUs
/ 1000;
308 if (!escSensorPort
|| !motorIsEnabled()) {
312 switch (escSensorTriggerState
) {
313 case ESC_SENSOR_TRIGGER_STARTUP
:
314 // Wait period of time before requesting telemetry (let the system boot first)
315 if (currentTimeMs
>= ESC_BOOTTIME
) {
316 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
320 case ESC_SENSOR_TRIGGER_READY
:
321 escTriggerTimestamp
= currentTimeMs
;
323 startEscDataRead(telemetryBuffer
, TELEMETRY_FRAME_SIZE
);
324 motorDmaOutput_t
* const motor
= getMotorDmaOutput(escSensorMotor
);
325 motor
->protocolControl
.requestTelemetry
= true;
326 escSensorTriggerState
= ESC_SENSOR_TRIGGER_PENDING
;
328 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_MOTOR_INDEX
, escSensorMotor
+ 1);
331 case ESC_SENSOR_TRIGGER_PENDING
:
332 if (currentTimeMs
< escTriggerTimestamp
+ ESC_REQUEST_TIMEOUT
) {
333 uint8_t state
= decodeEscFrame();
335 case ESC_SENSOR_FRAME_COMPLETE
:
337 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
340 case ESC_SENSOR_FRAME_FAILED
:
344 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
346 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_NUM_CRC_ERRORS
, ++totalCrcErrorCount
);
348 case ESC_SENSOR_FRAME_PENDING
:
352 // Move on to next ESC, we'll come back to this one
356 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
358 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_NUM_TIMEOUTS
, ++totalTimeoutCount
);