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/>.
27 #if defined(USE_ESC_SENSOR)
29 #include "build/debug.h"
31 #include "common/time.h"
33 #include "config/feature.h"
35 #include "pg/pg_ids.h"
38 #include "common/maths.h"
39 #include "common/utils.h"
41 #include "drivers/timer.h"
42 #include "drivers/motor.h"
43 #include "drivers/dshot.h"
44 #include "drivers/dshot_dpwm.h"
45 #include "drivers/serial.h"
46 #include "drivers/serial_uart.h"
48 #include "esc_sensor.h"
50 #include "config/config.h"
52 #include "flight/mixer.h"
54 #include "io/serial.h"
57 KISS ESC TELEMETRY PROTOCOL
58 ---------------------------
60 One transmission will have 10 times 8-bit bytes sent with 115200 baud and 3.6V.
63 Byte 1: Voltage high byte
64 Byte 2: Voltage low byte
65 Byte 3: Current high byte
66 Byte 4: Current low byte
67 Byte 5: Consumption high byte
68 Byte 6: Consumption low byte
75 PG_REGISTER_WITH_RESET_TEMPLATE(escSensorConfig_t
, escSensorConfig
, PG_ESC_SENSOR_CONFIG
, 0);
77 PG_RESET_TEMPLATE(escSensorConfig_t
, escSensorConfig
,
85 set debug_mode = DEBUG_ESC_SENSOR in cli
90 DEBUG_ESC_MOTOR_INDEX
= 0,
91 DEBUG_ESC_NUM_TIMEOUTS
= 1,
92 DEBUG_ESC_NUM_CRC_ERRORS
= 2,
93 DEBUG_ESC_DATA_AGE
= 3,
97 ESC_SENSOR_FRAME_PENDING
= 0,
98 ESC_SENSOR_FRAME_COMPLETE
= 1,
99 ESC_SENSOR_FRAME_FAILED
= 2
100 } escTlmFrameState_t
;
103 ESC_SENSOR_TRIGGER_STARTUP
= 0,
104 ESC_SENSOR_TRIGGER_READY
= 1,
105 ESC_SENSOR_TRIGGER_PENDING
= 2
106 } escSensorTriggerState_t
;
108 #define ESC_SENSOR_BAUDRATE 115200
109 #define ESC_BOOTTIME 5000 // 5 seconds
110 #define ESC_REQUEST_TIMEOUT 100 // 100 ms (data transfer takes only 900us)
112 #define TELEMETRY_FRAME_SIZE 10
113 static uint8_t telemetryBuffer
[TELEMETRY_FRAME_SIZE
] = { 0, };
115 static volatile uint8_t *buffer
;
116 static volatile uint8_t bufferSize
= 0;
117 static volatile uint8_t bufferPosition
= 0;
119 static serialPort_t
*escSensorPort
= NULL
;
121 static escSensorData_t escSensorData
[MAX_SUPPORTED_MOTORS
];
123 static escSensorTriggerState_t escSensorTriggerState
= ESC_SENSOR_TRIGGER_STARTUP
;
124 static uint32_t escTriggerTimestamp
;
125 static uint8_t escSensorMotor
= 0; // motor index
127 static escSensorData_t combinedEscSensorData
;
128 static bool combinedDataNeedsUpdate
= true;
130 static uint16_t totalTimeoutCount
= 0;
131 static uint16_t totalCrcErrorCount
= 0;
133 void startEscDataRead(uint8_t *frameBuffer
, uint8_t frameLength
)
135 buffer
= frameBuffer
;
137 bufferSize
= frameLength
;
140 uint8_t getNumberEscBytesRead(void)
142 return bufferPosition
;
145 static bool isFrameComplete(void)
147 return bufferPosition
== bufferSize
;
150 bool isEscSensorActive(void)
152 return escSensorPort
!= NULL
;
155 escSensorData_t
*getEscSensorData(uint8_t motorNumber
)
157 if (!featureIsEnabled(FEATURE_ESC_SENSOR
)) {
161 if (motorNumber
< getMotorCount()) {
162 return &escSensorData
[motorNumber
];
163 } else if (motorNumber
== ESC_SENSOR_COMBINED
) {
164 if (combinedDataNeedsUpdate
) {
165 combinedEscSensorData
.dataAge
= 0;
166 combinedEscSensorData
.temperature
= 0;
167 combinedEscSensorData
.voltage
= 0;
168 combinedEscSensorData
.current
= 0;
169 combinedEscSensorData
.consumption
= 0;
170 combinedEscSensorData
.rpm
= 0;
172 for (int i
= 0; i
< getMotorCount(); i
= i
+ 1) {
173 combinedEscSensorData
.dataAge
= MAX(combinedEscSensorData
.dataAge
, escSensorData
[i
].dataAge
);
174 combinedEscSensorData
.temperature
= MAX(combinedEscSensorData
.temperature
, escSensorData
[i
].temperature
);
175 combinedEscSensorData
.voltage
+= escSensorData
[i
].voltage
;
176 combinedEscSensorData
.current
+= escSensorData
[i
].current
;
177 combinedEscSensorData
.consumption
+= escSensorData
[i
].consumption
;
178 combinedEscSensorData
.rpm
+= escSensorData
[i
].rpm
;
181 combinedEscSensorData
.voltage
= combinedEscSensorData
.voltage
/ getMotorCount();
182 combinedEscSensorData
.rpm
= combinedEscSensorData
.rpm
/ getMotorCount();
184 combinedDataNeedsUpdate
= false;
186 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_DATA_AGE
, combinedEscSensorData
.dataAge
);
189 return &combinedEscSensorData
;
195 // Receive ISR callback
196 static void escSensorDataReceive(uint16_t c
, void *data
)
200 // KISS ESC sends some data during startup, ignore this for now (maybe future use)
201 // startup data could be firmware version and serialnumber
203 if (isFrameComplete()) {
207 buffer
[bufferPosition
++] = (uint8_t)c
;
210 bool escSensorInit(void)
212 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_ESC_SENSOR
);
217 portOptions_e options
= SERIAL_NOT_INVERTED
| (escSensorConfig()->halfDuplex
? SERIAL_BIDIR
: 0);
219 // Initialize serial port
220 escSensorPort
= openSerialPort(portConfig
->identifier
, FUNCTION_ESC_SENSOR
, escSensorDataReceive
, NULL
, ESC_SENSOR_BAUDRATE
, MODE_RX
, options
);
222 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
= i
+ 1) {
223 escSensorData
[i
].dataAge
= ESC_DATA_INVALID
;
226 return escSensorPort
!= NULL
;
229 static uint8_t updateCrc8(uint8_t crc
, uint8_t crc_seed
)
234 for (int i
=0; i
<8; i
++) {
235 crc_u
= ( crc_u
& 0x80 ) ? 0x7 ^ ( crc_u
<< 1 ) : ( crc_u
<< 1 );
241 uint8_t calculateCrc8(const uint8_t *Buf
, const uint8_t BufLen
)
244 for (int i
= 0; i
< BufLen
; i
++) {
245 crc
= updateCrc8(Buf
[i
], crc
);
251 static uint8_t decodeEscFrame(void)
253 if (!isFrameComplete()) {
254 return ESC_SENSOR_FRAME_PENDING
;
258 uint16_t chksum
= calculateCrc8(telemetryBuffer
, TELEMETRY_FRAME_SIZE
- 1);
259 uint16_t tlmsum
= telemetryBuffer
[TELEMETRY_FRAME_SIZE
- 1]; // last byte contains CRC value
261 if (chksum
== tlmsum
) {
262 escSensorData
[escSensorMotor
].dataAge
= 0;
263 escSensorData
[escSensorMotor
].temperature
= telemetryBuffer
[0];
264 escSensorData
[escSensorMotor
].voltage
= telemetryBuffer
[1] << 8 | telemetryBuffer
[2];
265 escSensorData
[escSensorMotor
].current
= telemetryBuffer
[3] << 8 | telemetryBuffer
[4];
266 escSensorData
[escSensorMotor
].consumption
= telemetryBuffer
[5] << 8 | telemetryBuffer
[6];
267 escSensorData
[escSensorMotor
].rpm
= telemetryBuffer
[7] << 8 | telemetryBuffer
[8];
269 combinedDataNeedsUpdate
= true;
271 frameStatus
= ESC_SENSOR_FRAME_COMPLETE
;
273 if (escSensorMotor
< 4) {
274 DEBUG_SET(DEBUG_ESC_SENSOR_RPM
, escSensorMotor
, erpmToRpm(escSensorData
[escSensorMotor
].rpm
) / 10); // output actual rpm/10 to fit in 16bit signed.
275 DEBUG_SET(DEBUG_ESC_SENSOR_TMP
, escSensorMotor
, escSensorData
[escSensorMotor
].temperature
);
278 frameStatus
= ESC_SENSOR_FRAME_FAILED
;
284 static void increaseDataAge(void)
286 if (escSensorData
[escSensorMotor
].dataAge
< ESC_DATA_INVALID
) {
287 escSensorData
[escSensorMotor
].dataAge
++;
289 combinedDataNeedsUpdate
= true;
293 static void selectNextMotor(void)
296 if (escSensorMotor
== getMotorCount()) {
301 // XXX Review ESC sensor under refactored motor handling
303 void escSensorProcess(timeUs_t currentTimeUs
)
305 const timeMs_t currentTimeMs
= currentTimeUs
/ 1000;
307 if (!escSensorPort
|| !motorIsEnabled()) {
311 switch (escSensorTriggerState
) {
312 case ESC_SENSOR_TRIGGER_STARTUP
:
313 // Wait period of time before requesting telemetry (let the system boot first)
314 if (currentTimeMs
>= ESC_BOOTTIME
) {
315 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
319 case ESC_SENSOR_TRIGGER_READY
:
320 escTriggerTimestamp
= currentTimeMs
;
322 startEscDataRead(telemetryBuffer
, TELEMETRY_FRAME_SIZE
);
323 motorDmaOutput_t
* const motor
= getMotorDmaOutput(escSensorMotor
);
324 motor
->protocolControl
.requestTelemetry
= true;
325 escSensorTriggerState
= ESC_SENSOR_TRIGGER_PENDING
;
327 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_MOTOR_INDEX
, escSensorMotor
+ 1);
330 case ESC_SENSOR_TRIGGER_PENDING
:
331 if (currentTimeMs
< escTriggerTimestamp
+ ESC_REQUEST_TIMEOUT
) {
332 uint8_t state
= decodeEscFrame();
334 case ESC_SENSOR_FRAME_COMPLETE
:
336 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
339 case ESC_SENSOR_FRAME_FAILED
:
343 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
345 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_NUM_CRC_ERRORS
, ++totalCrcErrorCount
);
347 case ESC_SENSOR_FRAME_PENDING
:
351 // Move on to next ESC, we'll come back to this one
355 escSensorTriggerState
= ESC_SENSOR_TRIGGER_READY
;
357 DEBUG_SET(DEBUG_ESC_SENSOR
, DEBUG_ESC_NUM_TIMEOUTS
, ++totalTimeoutCount
);