diff --git a/conf/modules/esc32.xml b/conf/modules/esc32.xml
new file mode 100644
index 0000000000..4fc27001f9
--- /dev/null
+++ b/conf/modules/esc32.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ Driver for the ESC32v3 speed controller
+
+ Motor is controlled with PWM.
+ Data (voltage, current, RPM) are received from the serial link.
+
+ The driver should work with v2 and v3.
+
+ TBD:
+ - send commands to change ESC settings through serial link.
+ - support multiple ESC
+
+ For more information see:
+ - http://autoquad.org/esc32/
+ - https://github.com/svn2github/esc32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/telemetry/fixedwing_flight_recorder.xml b/conf/telemetry/fixedwing_flight_recorder.xml
index 8ce7fdb4cd..811b6d5fe3 100644
--- a/conf/telemetry/fixedwing_flight_recorder.xml
+++ b/conf/telemetry/fixedwing_flight_recorder.xml
@@ -34,6 +34,7 @@
+
@@ -134,6 +135,7 @@
+
diff --git a/sw/airborne/modules/esc32/esc32.c b/sw/airborne/modules/esc32/esc32.c
new file mode 100644
index 0000000000..8e8747429d
--- /dev/null
+++ b/sw/airborne/modules/esc32/esc32.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) Murat Bronz and Xavier Paris
+ *
+ * This file is part of paparazzi
+ *
+ * paparazzi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with paparazzi; see the file COPYING. If not, see
+ * .
+ */
+/**
+ * @file "modules/esc32/esc32.c"
+ * @author Murat Bronz
+ * Serial Connection module between esc32v3 and ap
+ * Mainly from Bill Nesbitt's code parts from AutoQuad project
+ */
+
+#include "modules/esc32/esc32.h"
+#include "mcu_periph/uart.h"
+#include "mcu_periph/sys_time.h"
+#include "subsystems/datalink/downlink.h"
+#include
+#include
+
+#if PERIODIC_TELEMETRY
+#include "subsystems/datalink/telemetry.h"
+#endif
+
+struct esc32 esc32;
+
+#define ESC32_BUF_IN_LEN 64
+#define ESC32_BUF_OUT_LEN 32
+#define ESC32_PARAMS_LEN 10
+#define ESC32_TELEMETRY_RATE 100.0
+
+struct esc32_ck {
+ uint8_t ck_a;
+ uint8_t ck_b;
+};
+
+struct esc32_private {
+ struct uart_periph *dev;
+ struct esc32_ck ck_in;
+ struct esc32_ck ck_out;
+ uint8_t buf_in[ESC32_BUF_IN_LEN];
+ uint8_t buf_out[ESC32_BUF_OUT_LEN];
+ uint8_t in_idx;
+ uint8_t out_idx;
+ uint16_t cmd_seq_id;
+ bool msg_available;
+ uint8_t state;
+ uint8_t initialized;
+ uint8_t params_idx;
+ uint8_t in_rows;
+ uint8_t in_cols;
+ struct esc32_parameter params[ESC32_PARAMS_LEN];
+};
+
+static struct esc32_private esc32_priv;
+
+
+static void esc32_send(struct esc32_private *esc) {
+ int i;
+ for (i = 0; i < esc->out_idx; i++) {
+ uart_put_byte(esc->dev, 0, esc->buf_out[i]);
+ }
+}
+
+static void esc32_compute_ck(struct esc32_ck *ck, uint8_t c) {
+ ck->ck_a += c;
+ ck->ck_b += ck->ck_a;
+}
+
+static void esc32_init_ck(struct esc32_ck *ck) {
+ ck->ck_a = 0;
+ ck->ck_b = 0;
+}
+
+static float esc32_get_float(struct esc32_private *esc, int idx) {
+ float f;
+ unsigned char *c = (unsigned char *)&f;
+ unsigned int i;
+ for (i = 0; i < sizeof(float); i++)
+ *c++ = esc->buf_in[idx + i];
+
+ return f;
+}
+
+static void esc32_put_char(struct esc32_private *esc, unsigned char c) {
+ esc->buf_out[esc->out_idx++] = c;
+ esc32_compute_ck(&(esc->ck_out), c);
+}
+
+static void esc32_put_short(struct esc32_private *esc, unsigned short i) {
+ unsigned int j;
+ unsigned char *c = (unsigned char *)&i;
+
+ for (j = 0; j < sizeof(short); j++)
+ esc32_put_char(esc, *c++);
+}
+
+static void esc32_put_float(struct esc32_private *esc, float f) {
+ unsigned int j;
+ unsigned char *c = (unsigned char *)&f;
+
+ for (j = 0; j < sizeof(float); j++)
+ esc32_put_char(esc, *c++);
+}
+
+static uint16_t esc32_send_command(struct esc32_private *esc, enum binaryCommands command, float param1, float param2, int n) {
+ esc32_init_ck(&(esc->ck_out));
+ esc->out_idx = 0;
+
+ esc->buf_out[esc->out_idx++] = 'A';
+ esc->buf_out[esc->out_idx++] = 'q';
+ esc32_put_char(esc, 1 + 2 + n*sizeof(float));
+ esc32_put_char(esc, command);
+ esc32_put_short(esc, esc->cmd_seq_id++);
+ if (n > 0)
+ esc32_put_float(esc, param1);
+ if (n > 1)
+ esc32_put_float(esc, param2);
+ esc->buf_out[esc->out_idx++] = esc->ck_out.ck_a;
+ esc->buf_out[esc->out_idx++] = esc->ck_out.ck_b;
+ esc32_send(esc);
+
+ return (esc->cmd_seq_id - 1);
+}
+
+#define UINIT 0
+#define GOT_SYNC 1
+#define SEARCH_COMMAND 2
+#define GET_CMD_ROWS 3
+#define GET_CMD_COLS 4
+#define GET_COMMAND 5
+#define CHECK_CK_A 6
+#define CHECK_CK_B 7
+
+static void parse_esc32(struct esc32_private *esc, uint8_t c) {
+
+ switch (esc->state) {
+ case UINIT:
+ if (c == 'A') {
+ esc->state = GOT_SYNC;
+ }
+ break;
+ case GOT_SYNC:
+ if (c == 'q') {
+ esc->state = SEARCH_COMMAND;
+ }
+ else {
+ esc->state = UINIT;
+ }
+ break;
+ case SEARCH_COMMAND:
+ if (c == 'T') {
+ esc32_init_ck(&(esc->ck_in));
+ esc->in_idx = 0;
+ esc->state = GET_CMD_ROWS;
+ }
+ else {
+ esc->state = UINIT;
+ }
+ break;
+ case GET_CMD_ROWS:
+ esc32_compute_ck(&(esc->ck_in), c);
+ esc->in_rows = c;
+ esc->state = GET_CMD_COLS;
+ break;
+ case GET_CMD_COLS:
+ esc32_compute_ck(&(esc->ck_in), c);
+ esc->in_cols = c;
+ esc->state = GET_COMMAND;
+ break;
+ case GET_COMMAND:
+ esc32_compute_ck(&(esc->ck_in), c);
+ esc->buf_in[esc->in_idx++] = c;
+ if (esc->in_idx >= esc->in_rows * esc->in_cols * sizeof(float)) {
+ esc->state = CHECK_CK_A;
+ }
+ break;
+ case CHECK_CK_A:
+ if (esc->ck_in.ck_a == c) {
+ esc->state = CHECK_CK_B;
+ }
+ else {
+ esc->state = UINIT;
+ }
+ break;
+ case CHECK_CK_B:
+ if (esc->ck_in.ck_b == c) {
+ esc->msg_available = true;
+ }
+ esc->state = UINIT;
+ break;
+ default:
+ esc->state = UINIT;
+ break;
+ }
+}
+
+static void esc32_msg_send(struct transport_tx *trans, struct link_device *dev) {
+
+ if (esc32.data_available == true){
+ pprz_msg_send_ESC(trans, dev, AC_ID,
+ &esc32.params.amps,
+ &esc32.params.volts_bat,
+ &esc32.power,
+ &esc32.params.rpm,
+ &esc32.params.volts_motor,
+ &esc32.energy,
+ 0); // only one motor handled for now
+
+ esc32.data_available = false;
+ };
+}
+
+void esc32_init(void) {
+ memset(&esc32, 0, sizeof(struct esc32));
+ memset(&esc32_priv, 0, sizeof(struct esc32_private));
+
+ // uart device
+ esc32_priv.dev = &(ESC32_DEV);
+
+ // set command sequence id to 1
+ esc32_priv.cmd_seq_id = 1;
+
+#if PERIODIC_TELEMETRY
+ register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_ESC, esc32_msg_send);
+#endif
+
+};
+
+#if ESC32_DEBUG
+static int debug_start = 0;
+#endif
+
+void esc32_periodic(void) {
+ if (esc32_priv.initialized == false) {
+ uart_put_byte(esc32_priv.dev, 0, '\r');
+ // enter binary mode
+ uint8_t data[] = "binary\r";
+ int i = 0;
+ while (i < (int) sizeof(data)) {
+ uart_put_byte(esc32_priv.dev, 0, data[i]);
+ i++;
+ }
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_TELEM_RATE, 0.0, 0.0, 1);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_TELEM_VALUE, 0.0, BINARY_VALUE_AMPS, 2);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_TELEM_VALUE, 1.0, BINARY_VALUE_VOLTS_BAT, 2);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_TELEM_VALUE, 2.0, BINARY_VALUE_RPM, 2);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_TELEM_VALUE, 3.0, BINARY_VALUE_VOLTS_MOTOR, 2);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_TELEM_RATE, ESC32_TELEMETRY_RATE, 0.0, 1);
+
+ esc32_priv.initialized = true;
+ }
+ else {
+#if ESC32_DEBUG
+ if (!debug_start) {
+ // motor spining for debug
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_ARM, 0.0, 0.0, 0);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_STOP, 0.0, 0.0, 0);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_START, 0.0, 0.0, 0);
+ esc32_send_command(&esc32_priv, BINARY_COMMAND_DUTY, 8.0, 0.0, 1);
+ debug_start = 1;
+ }
+#endif
+ }
+
+}
+
+static void esc32_filter_data(struct esc32_parameter *in, struct esc32_parameter *out, struct esc32_parameter *min, struct esc32_parameter *max) {
+ // amps
+ out->amps += in->amps;
+ if (in->amps > max->amps) max->amps = in->amps;
+ if (in->amps < min->amps) min->amps = in->amps;
+ // volts_bat
+ out->volts_bat += in->volts_bat;
+ if (in->volts_bat > max->volts_bat) max->volts_bat = in->volts_bat;
+ if (in->volts_bat < min->volts_bat) min->volts_bat = in->volts_bat;
+ // volts_motor
+ out->volts_motor += in->volts_motor;
+ if (in->volts_motor > max->volts_motor) max->volts_motor = in->volts_motor;
+ if (in->volts_motor < min->volts_motor) min->volts_motor = in->volts_motor;
+ // rpm
+ out->rpm += in->rpm;
+ if (in->rpm > max->rpm) max->rpm = in->rpm;
+ if (in->rpm < min->rpm) min->rpm = in->rpm;
+}
+
+static void esc32_parse_msg(struct esc32_private *esc_priv, struct esc32 *esc) {
+
+ if (esc_priv->in_cols >= 4 && esc_priv->in_rows > 0) {
+ // store new values
+ esc_priv->params[esc_priv->params_idx].amps = esc32_get_float(esc_priv, 0);
+ esc_priv->params[esc_priv->params_idx].volts_bat = esc32_get_float(esc_priv, 4);
+ esc_priv->params[esc_priv->params_idx].rpm = esc32_get_float(esc_priv, 8);
+ esc_priv->params[esc_priv->params_idx].volts_motor = esc32_get_float(esc_priv, 12);
+ // increment index and average/filter when input array is full
+ esc_priv->params_idx++;
+ if (esc_priv->params_idx == ESC32_PARAMS_LEN) {
+ int i;
+ struct esc32_parameter tmp = esc_priv->params[0]; // cumulated values
+ struct esc32_parameter min = esc_priv->params[0]; // min value
+ struct esc32_parameter max = esc_priv->params[0]; // max value
+ for (i = 1; i < ESC32_PARAMS_LEN; i++) {
+ esc32_filter_data(&(esc_priv->params[i]), &tmp, &min, &max);
+ }
+ tmp.amps = (tmp.amps - min.amps - max.amps) / (ESC32_PARAMS_LEN - 2);
+ tmp.volts_bat = (tmp.volts_bat - min.volts_bat - max.volts_bat) / (ESC32_PARAMS_LEN - 2);
+ tmp.volts_motor = (tmp.volts_motor - min.volts_motor - max.volts_motor) / (ESC32_PARAMS_LEN - 2);
+ tmp.rpm = (tmp.rpm - min.rpm - max.rpm) / (ESC32_PARAMS_LEN - 2);
+ esc->params = tmp;
+ // compute power
+ esc->power = tmp.amps * tmp.volts_bat;
+ // accumulate energy
+ esc->energy += esc->power * ESC32_PARAMS_LEN / ESC32_TELEMETRY_RATE / 3600.f;
+ // new data available
+ esc->data_available = true;
+ // reset input array
+ esc_priv->params_idx = 0;
+ }
+ }
+ esc_priv->msg_available = false;
+}
+
+
+void esc32_event(void) {
+ while (uart_char_available(esc32_priv.dev)) {
+ parse_esc32(&esc32_priv, uart_getch(esc32_priv.dev));
+ if (esc32_priv.msg_available) {
+ esc32_parse_msg(&esc32_priv, &esc32);
+ }
+ }
+}
+
diff --git a/sw/airborne/modules/esc32/esc32.h b/sw/airborne/modules/esc32/esc32.h
new file mode 100644
index 0000000000..65611f3c3c
--- /dev/null
+++ b/sw/airborne/modules/esc32/esc32.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) Murat Bronz
+ *
+ * This file is part of paparazzi
+ *
+ * paparazzi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with paparazzi; see the file COPYING. If not, see
+ * .
+ */
+/**
+ * @file "modules/esc32/esc32.h"
+ * @author Murat Bronz
+ * Connection between esc32v3
+ */
+
+#ifndef ESC32_H
+#define ESC32_H
+
+#include "std.h"
+
+extern void esc32_init(void);
+extern void esc32_periodic(void);
+extern void esc32_event(void);
+
+struct esc32_parameter {
+ float amps; ///< current consumption
+ float volts_bat; ///< input battery voltage
+ float volts_motor; ///< motor voltage (bat voltage * throttle in % in fact)
+ float rpm; ///< motor rotation speed
+ float duty; ///< motor duty cycle (more or less throttle in %)
+};
+
+struct esc32 {
+ struct esc32_parameter params; ///< filtered data from the esc
+ float energy; ///< accumulated energy
+ float power; ///< computed battery power
+ bool data_available; ///< data updated
+};
+
+extern struct esc32 esc32;
+
+enum binaryCommands {
+ BINARY_COMMAND_NOP = 0,
+ BINARY_COMMAND_ARM,
+ BINARY_COMMAND_CLI,
+ BINARY_COMMAND_CONFIG,
+ BINARY_COMMAND_DISARM,
+ BINARY_COMMAND_DUTY,
+ BINARY_COMMAND_PWM,
+ BINARY_COMMAND_RPM,
+ BINARY_COMMAND_SET,
+ BINARY_COMMAND_START,
+ BINARY_COMMAND_STATUS,
+ BINARY_COMMAND_STOP,
+ BINARY_COMMAND_TELEM_RATE,
+ BINARY_COMMAND_VERSION,
+ BINARY_COMMAND_TELEM_VALUE,
+ BINARY_COMMAND_GET_PARAM_ID,
+ BINARY_COMMAND_ACK = 250,
+ BINARY_COMMAND_NACK
+};
+
+enum binaryValues {
+ BINARY_VALUE_NONE = 0,
+ BINARY_VALUE_AMPS,
+ BINARY_VALUE_VOLTS_BAT,
+ BINARY_VALUE_VOLTS_MOTOR,
+ BINARY_VALUE_RPM,
+ BINARY_VALUE_DUTY,
+ BINARY_VALUE_COMM_PERIOD,
+ BINARY_VALUE_BAD_DETECTS,
+ BINARY_VALUE_ADC_WINDOW,
+ BINARY_VALUE_IDLE_PERCENT,
+ BINARY_VALUE_STATE,
+ BINARY_VALUE_AVGA,
+ BINARY_VALUE_AVGB,
+ BINARY_VALUE_AVGC,
+ BINARY_VALUE_AVGCOMP,
+ BINARY_VALUE_FETSTEP,
+ BINARY_VALUE_NUM
+};
+
+enum configParameters {
+ CONFIG_VERSION = 0,
+ STARTUP_MODE,
+ BAUD_RATE,
+ PTERM,
+ ITERM,
+ FF1TERM,
+ FF2TERM,
+ CL1TERM,
+ CL2TERM,
+ CL3TERM,
+ CL4TERM,
+ CL5TERM,
+ SHUNT_RESISTANCE,
+ MIN_PERIOD,
+ MAX_PERIOD,
+ BLANKING_MICROS,
+ ADVANCE,
+ START_VOLTAGE,
+ GOOD_DETECTS_START,
+ BAD_DETECTS_DISARM,
+ MAX_CURRENT,
+ SWITCH_FREQ,
+ MOTOR_POLES,
+ PWM_MIN_PERIOD,
+ PWM_MAX_PERIOD,
+ PWM_MIN_VALUE,
+ PWM_LO_VALUE,
+ PWM_HI_VALUE,
+ PWM_MAX_VALUE,
+ PWM_MIN_START,
+ PWM_RPM_SCALE,
+ FET_BRAKING,
+ PNFAC,
+ INFAC,
+ THR1TERM,
+ THR2TERM,
+ START_ALIGN_TIME,
+ START_ALIGN_VOLTAGE,
+ START_STEPS_NUM,
+ START_STEPS_PERIOD,
+ START_STEPS_ACCEL,
+ PWM_LOWPASS,
+ RPM_MEAS_LP,
+ SERVO_DUTY,
+ SERVO_P,
+ SERVO_D,
+ SERVO_MAX_RATE,
+ SERVO_SCALE,
+ ESC_ID,
+ DIRECTION,
+ CONFIG_NUM_PARAMS
+};
+
+#endif
+
diff --git a/sw/ext/pprzlink b/sw/ext/pprzlink
index c915a014e0..721de3af45 160000
--- a/sw/ext/pprzlink
+++ b/sw/ext/pprzlink
@@ -1 +1 @@
-Subproject commit c915a014e03f9bd717cbc3a2606c351563f53feb
+Subproject commit 721de3af45bef20aeceeae0118524ccfd370140e