diff --git a/conf/airframes/tudelft/rot_wing_25kg.xml b/conf/airframes/tudelft/rot_wing_25kg.xml
index 768acd8208..418e079743 100644
--- a/conf/airframes/tudelft/rot_wing_25kg.xml
+++ b/conf/airframes/tudelft/rot_wing_25kg.xml
@@ -23,6 +23,7 @@
+
@@ -41,8 +42,6 @@
-
-
@@ -106,6 +105,8 @@
+
+
@@ -179,6 +180,10 @@
+
+
+
+
@@ -221,6 +226,9 @@
+
+
+
diff --git a/conf/modules/actuators_faulhaber.xml b/conf/modules/actuators_faulhaber.xml
new file mode 100644
index 0000000000..c3698b129a
--- /dev/null
+++ b/conf/modules/actuators_faulhaber.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ Actuators Driver for the Faulhaber controller
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ uart
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sw/airborne/modules/actuators/actuators_faulhaber.c b/sw/airborne/modules/actuators/actuators_faulhaber.c
new file mode 100644
index 0000000000..507f18aa12
--- /dev/null
+++ b/sw/airborne/modules/actuators/actuators_faulhaber.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) Freek van Tienen
+ *
+ * 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/actuators/actuators_faulhaber.c"
+ * @author Freek van Tienen
+ * Serial Connection module between ap and a faulhaber motor controller
+ */
+
+#include "modules/actuators/actuators_faulhaber.h"
+#include "mcu_periph/uart.h"
+#include "mcu_periph/sys_time.h"
+#include "modules/datalink/downlink.h"
+#include "modules/core/abi.h"
+#include
+#include
+
+static struct uart_periph *faulhaber_dev = &(FAULHABER_DEV);
+
+struct faulhaber_parser_t {
+ uint8_t state;
+ uint8_t node_nb;
+ uint8_t cmd_code;
+ uint8_t data_length;
+ uint8_t data_idx;
+ uint8_t data[64];
+
+ uint8_t calc_crc8;
+};
+static struct faulhaber_parser_t faulhaber_p;
+struct faulhaber_t faulhaber;
+
+#define Polynom 0xD5
+uint8_t faulhaber_crc8(uint8_t u8Byte, uint8_t u8CRC)
+{
+ uint8_t i;
+ u8CRC = u8CRC ^ u8Byte;
+ for (i = 0; i < 8; i++) {
+ if (u8CRC & 0x01) {
+ u8CRC = (u8CRC >> 1) ^ Polynom;
+ } else {
+ u8CRC >>= 1;
+ }
+ }
+ return u8CRC;
+}
+
+static void faulhaber_send_command(struct uart_periph *dev, uint8_t cmd_code, uint8_t *data, uint8_t data_length)
+{
+ uart_put_byte(dev, 0, 'S'); // Character (S) as Start of Frame
+ uart_put_byte(dev, 0, data_length + 4); // Telegram length without SOF/EOF (packet length)
+ uint8_t crc8 = faulhaber_crc8(data_length + 4, 0xFF); // Start CRC with 0xFF
+ uart_put_byte(dev, 0, 0x01); // Node number of the slave (0 = Broadcast)
+ crc8 = faulhaber_crc8(0x01, crc8);
+ uart_put_byte(dev, 0, cmd_code); // See Tab. 2
+ crc8 = faulhaber_crc8(cmd_code, crc8);
+ for (uint8_t i = 0; i < data_length; i++) { // Data area (length = packet length – 4)
+ uart_put_byte(dev, 0, data[i]);
+ crc8 = faulhaber_crc8(data[i], crc8);
+ }
+ uart_put_byte(dev, 0, crc8); // CRC8 with polynomial 0xD5 over byte 2–N
+ uart_put_byte(dev, 0, 'E'); // Character (E) as End of Frame
+ uart_send_message(dev, 0);
+}
+
+#define UINIT 0
+#define GOT_SOF 1
+#define GOT_LENGTH 2
+#define GOT_NODE_NB 3
+#define GET_DATA 4
+#define GET_CRC 5
+#define GET_EOF 6
+#define GOT_FULL_PACKET 7
+
+static void faulhaber_parser(struct faulhaber_parser_t *p, uint8_t c)
+{
+ switch (p->state) {
+ case UINIT:
+ if (c == 'S') {
+ p->state = GOT_SOF;
+ p->calc_crc8 = 0xFF;
+ }
+ break;
+ case GOT_SOF:
+ if (c - 4 < 0) {
+ p->state = UINIT;
+ } else {
+ p->data_length = c - 4;
+ p->calc_crc8 = faulhaber_crc8(c, p->calc_crc8);
+ p->state = GOT_LENGTH;
+ }
+ break;
+ case GOT_LENGTH:
+ p->node_nb = c;
+ p->calc_crc8 = faulhaber_crc8(c, p->calc_crc8);
+ p->state = GOT_NODE_NB;
+ break;
+ case GOT_NODE_NB:
+ p->cmd_code = c;
+ p->data_idx = 0;
+ p->calc_crc8 = faulhaber_crc8(c, p->calc_crc8);
+ p->state = GET_DATA;
+ break;
+ case GET_DATA:
+ p->data[p->data_idx++] = c;
+ p->calc_crc8 = faulhaber_crc8(c, p->calc_crc8);
+ if (p->data_idx >= p->data_length) {
+ p->state = GET_CRC;
+ }
+ break;
+ case GET_CRC:
+ if (p->calc_crc8 == c) {
+ p->state = GET_EOF;
+ } else {
+ p->state = UINIT;
+ }
+ break;
+ case GET_EOF:
+ if (c == 'E') {
+ p->state = GOT_FULL_PACKET;
+ } else {
+ p->state = UINIT;
+ }
+ break;
+ default:
+ p->state = UINIT;
+ break;
+ }
+}
+
+// Controlword 0x6040.00
+// > 8) & 0xFF), ((faulhaber.target_position >> 16) & 0xFF), ((faulhaber.target_position >> 24) & 0xFF) }; // Set 0x607A.00 to 0x00000000: Target position 0
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 7);
+ faulhaber.state++;
+ break;
+ }
+ case 2: {
+ // Enable operation
+ uint8_t data[] = { 0x40, 0x60, 0x00, 0x0F, 0x00}; // Set 0x6040.00 to 0x000F: Enable operation
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 5);
+ faulhaber.state++;
+ break;
+ }
+ case 3: {
+ // Start moving
+ uint8_t data[] = { 0x40, 0x60, 0x00, 0x3F, 0x00}; // Set 0x6040.00 to 0x003F: Start moving
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 5);
+ faulhaber.state++;
+ break;
+ }
+ default:
+ faulhaber.mode = FH_MODE_IDLE;
+ faulhaber.state = 0;
+ break;
+
+ }
+ break;
+
+ /* FH_MODE_IDLE */
+ case FH_MODE_IDLE: {
+ // Move to the new position
+ if (faulhaber.target_position != faulhaber.setpoint_position) {
+ faulhaber.mode = FH_MODE_POSITION;
+ faulhaber.state = 0;
+ break;
+ }
+
+ // Request the position
+ uint8_t data[] = { 0x64, 0x60, 0x00}; // Get 0x6064.00: Get the actual position
+ faulhaber_send_command(faulhaber_dev, 0x01, data, 3);
+ faulhaber.state = 0;
+ break;
+ }
+
+ case FH_MODE_ENABLE:
+ switch (faulhaber.state) {
+ case 0: {
+ // Disable drive
+ uint8_t data[] = { 0x40, 0x60, 0x00, 0x80, 0x00}; // Set 0x6040.00 to 0x0007: Disable drive
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 5);
+ faulhaber.state++;
+ //break;
+ }
+ case 1: {
+ // Disable drive
+ uint8_t data[] = { 0x40, 0x60, 0x00, 0x06, 0x00}; // Set 0x6040.00 to 0x0006: Disable drive
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 5);
+ faulhaber.state++;
+ //break;
+ }
+ case 2: {
+ // Enable drive
+ uint8_t data[] = { 0x40, 0x60, 0x00, 0x07, 0x00}; // Set 0x6040.00 to 0x0007: Enable drive
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 5);
+ faulhaber.state++;
+ //break;
+ }
+ case 3: {
+ // Execute
+ uint8_t data[] = { 0x40, 0x60, 0x00, 0x0F, 0x00}; // Set 0x6040.00 to 0x000F: Execute
+ faulhaber_send_command(faulhaber_dev, 0x02, data, 5);
+ faulhaber.state++;
+ //break;
+ }
+ default:
+ faulhaber.mode = FH_MODE_INIT;
+ faulhaber.state = 0;
+ break;
+ }
+ break;
+
+ /* Do nothing */
+ default:
+ break;
+ }
+
+}
+
+static void faulhaber_parse_msg(struct faulhaber_parser_t *p)
+{
+ // Process position message
+ if (p->cmd_code == 0x01 && p->data[0] == 0x64 && p->data[1] == 0x60 && p->data[2] == 0x00) {
+ faulhaber.real_position = p->data[3] | (p->data[4] << 8) | (p->data[5] << 16) | (p->data[6] << 24);
+
+ struct act_feedback_t feedback = {0};
+ feedback.position = (faulhaber.real_position - get_servo_min_FAULHABER(0)) / (double)(get_servo_max_FAULHABER(
+ 0) - get_servo_min_FAULHABER(0)) * M_PI_2; // In radians
+ feedback.set.position = true;
+ feedback.idx = get_servo_idx_FAULHABER(0);
+
+ // Send ABI message
+ AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_FAULHABER_ID, &feedback, 1);
+ }
+}
+
+
+void actuators_faulhaber_event(void)
+{
+ while (uart_char_available(faulhaber_dev)) {
+ faulhaber_parser(&faulhaber_p, uart_getch(faulhaber_dev));
+ if (faulhaber_p.state == GOT_FULL_PACKET) {
+ faulhaber_parse_msg(&faulhaber_p);
+ faulhaber_p.state = UINIT;
+ }
+ }
+}
+
+void actuators_faulhaber_SetMode(uint8_t mode)
+{
+ faulhaber.mode = mode;
+ faulhaber.state = 0;
+}
diff --git a/sw/airborne/modules/actuators/actuators_faulhaber.h b/sw/airborne/modules/actuators/actuators_faulhaber.h
new file mode 100644
index 0000000000..28c253a952
--- /dev/null
+++ b/sw/airborne/modules/actuators/actuators_faulhaber.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) Freek van Tienen
+ *
+ * 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/actuators/actuators_faulhaber.h"
+ * @author Freek van Tienen
+ * Serial Connection module between ap and a faulhaber motor controller
+ */
+
+#ifndef ACTUATORS_FAULHABER_H
+#define ACTUATORS_FAULHABER_H
+
+#include "std.h"
+
+enum faulhaber_modes_t {
+ FH_MODE_INIT,
+ FH_MODE_IDLE,
+ FH_MODE_HOME,
+ FH_MODE_POSITION,
+ FH_MODE_ENABLE
+};
+
+struct faulhaber_t {
+ enum faulhaber_modes_t mode;
+ uint8_t state;
+
+ int32_t setpoint_position;
+ int32_t target_position;
+ int32_t real_position;
+};
+extern struct faulhaber_t faulhaber;
+
+extern void actuators_faulhaber_init(void);
+extern void actuators_faulhaber_periodic(void);
+extern void actuators_faulhaber_event(void);
+extern void actuators_faulhaber_SetMode(uint8_t mode);
+
+
+#if USE_NPS
+#define ActuatorsFaulhaberInit() {}
+#define ActuatorFaulhaberSet(_i, _v) {}
+#define ActuatorsFaulhaberCommit() {}
+#else
+#define ActuatorsFaulhaberInit() actuators_faulhaber_init()
+#define ActuatorFaulhaberSet(_i, _v) { faulhaber.setpoint_position = ((get_servo_max_FAULHABER(0)-_v) + get_servo_min_FAULHABER(0))*1000.0f; }
+#define ActuatorsFaulhaberCommit() {}
+#endif
+
+#endif /* ACTUATORS_FAULHABER_H */
diff --git a/sw/airborne/modules/core/abi_sender_ids.h b/sw/airborne/modules/core/abi_sender_ids.h
index 5f191aceed..940af28c98 100644
--- a/sw/airborne/modules/core/abi_sender_ids.h
+++ b/sw/airborne/modules/core/abi_sender_ids.h
@@ -503,6 +503,10 @@
#define ACT_FEEDBACK_RPM_SENSOR_ID 4
#endif
+#ifndef ACT_FEEDBACK_FAULHABER_ID
+#define ACT_FEEDBACK_FAULHABER_ID 5
+#endif
+
/*
* IDs of THRUST increment calculation (message 16)
*/