11#ifndef THRUSTER_INTERFACE_AUV__THRUSTER_INTERFACE_AUV_DRIVER_HPP_
22#define THRUSTER_INTERFACE_AUV__THRUSTER_INTERFACE_AUV_DRIVER_HPP_
33
4- #include < fcntl.h>
5- #include < linux/i2c-dev.h>
6- #include < sys/ioctl.h>
7- #include < unistd.h>
8- #include < algorithm>
94#include < array>
10- #include < cmath>
5+ // Need to include utility before asio
6+ // clang-format off
7+ #include < utility>
8+ #include < asio.hpp>
9+ // clang-format on
1110#include < cstdint>
12- #include < cstring>
13- #include < iostream>
14- #include < map>
11+ #include < optional>
1512#include < string>
13+ #include < thread>
1614#include < vector>
1715
1816/* *
1917 * @brief struct to hold the parameters for a single thruster
2018 */
2119struct ThrusterParameters {
22- uint8_t mapping;
23- int8_t direction;
24- uint16_t pwm_min;
25- uint16_t pwm_max;
20+ std:: uint8_t mapping;
21+ std:: int8_t direction;
22+ std:: uint16_t pwm_min;
23+ std:: uint16_t pwm_max;
2624};
2725
2826enum PolySide {
2927 LEFT = 0 ,
3028 RIGHT = 1
3129}; // vector index for the position of the coefficients in the coeff vector
3230
31+ using FaultEventCallback =
32+ std::function<void (std::uint8_t channel, std::uint8_t code)>;
33+
34+ using PGoodEventCallback =
35+ std::function<void (std::uint8_t channel, std::uint8_t code)>;
36+
37+ using KillswitchEventCallback = std::function<void ()>;
38+
39+ using CurrentMeasurementsCallback =
40+ std::function<void (const std::array<float , 8 >& currents)>;
41+
3342/* *
3443 * @brief class instantiated by ThrusterInterfaceAUVNode to control the
3544 * thrusters, takes the thruster forces and converts them to PWM signals to be
36- * sent via I2C to the ESCs (PCA9685 Adafruit 16-Channel 12-bit PWM/Servo
37- * Driver)
45+ * sent via UART to the ESC controller.
3846 *
3947 * @details Based on the datasheets found in /resources, approximate the map
4048 * with a piecewise (>0 and <0) third order polynomial.
@@ -44,6 +52,9 @@ enum PolySide {
4452 * all the handling of the other voltages to save resources. Could be
4553 * re-implemented in the future for more flexibility if we ever need it to
4654 * operate at different voltages in different situations.
55+ *
56+ * @note Over UART, the PWM values are packed into a framed packet:
57+ * [magic][id][length][payload][checksum], where the payload is 8 uint16_t.
4758 */
4859class ThrusterInterfaceAUVDriver {
4960 public:
@@ -53,41 +64,57 @@ class ThrusterInterfaceAUVDriver {
5364 * @brief called from ThrusterInterfaceAUVNode .cpp when instantiating the
5465 * object, initializes all the params.
5566 *
56- * @param i2c_bus bus number used to communicate
57- * @param pico_i2c_address i2c address of the ESC that drive the
67+ * @param serial_device serial device used to communicate
68+ * (for example /dev/ttyUSB0)
69+ * @param baud_rate UART baud rate
70+ * @param packet_id packet ID sent in the UART frame
5871 * @param thruster_parameters describe mapping, direction, min and max pwm
59- * value for each thruster
60- * @param poly_coeffs LEFT(<0) and RIGHT(>0) third order
61- * polynomial coefficients
72+ * value for each thruster
73+ * @param right_coeffs RIGHT(>0) third order polynomial
74+ * coefficients
75+ * @param left_coeffs LEFT(<0) third order polynomial coefficients
6276 */
6377 ThrusterInterfaceAUVDriver (
64- std::int16_t i2c_bus ,
65- int pico_i2c_address ,
78+ const std::string& serial_device ,
79+ unsigned int baud_rate ,
6680 const std::vector<ThrusterParameters>& thruster_parameters,
67- const std::vector<std::vector<double >>& poly_coeffs);
81+ const std::vector<double >& right_coeffs,
82+ const std::vector<double >& left_coeffs);
83+
84+ /* *
85+ * @brief initializes UART
86+ * @return 0 on success, negative number on failure
87+ */
88+ int init_uart ();
89+
6890 /* *
6991 * @brief calls both 1) interpolate_forces_to_pwm() to
7092 * convert the thruster forces to PWM values and 2) send_data_to_escs() to
71- * send them to the ESCs via I2C
93+ * send them over UART
7294 *
7395 * @param thruster_forces_array vector of forces for each thruster
7496 *
75- * @return std::vector<uint16_t> vector of pwm values sent to each thruster
97+ * @return std::optional<std::vector<uint16_t>> vector of pwm values sent to
98+ * each thruster, or std::nullopt on failure
7699 */
77- std::vector<uint16_t > drive_thrusters (
100+ std::optional<std:: vector<std:: uint16_t > > drive_thrusters (
78101 const std::vector<double >& thruster_forces_array);
79102
80- private:
81- int bus_fd_; // /< file descriptor for the I2C bus (integer >0 that uniquely
82- // /< identifies the device. -1 if it fails)
83-
84- int i2c_bus_;
85- int pico_i2c_address_;
86- std::vector<ThrusterParameters> thruster_parameters_;
87- std::vector<std::vector<double >> poly_coeffs_;
103+ /* *
104+ * @brief Sets the camera light intensity
105+ *
106+ * @param[in] percentage float in the range 0-1
107+ * @return 0 on success -1 on failure
108+ */
109+ int set_camera_light (float percentage);
88110
89- uint16_t idle_pwm_value_; // /< pwm value when force = 0.00
111+ void set_fault_event_callback (FaultEventCallback callback);
112+ void set_pgood_event_callback (PGoodEventCallback callback);
113+ void set_killswitch_event_callback (KillswitchEventCallback callback);
114+ void set_current_measurements_callback (
115+ CurrentMeasurementsCallback callback);
90116
117+ private:
91118 /* *
92119 * @brief only take the thruster forces and return PWM values
93120 *
@@ -96,24 +123,22 @@ class ThrusterInterfaceAUVDriver {
96123 * @return std::vector<uint16_t> vector of pwm values sent to each thruster
97124 * if we want to publish them for debug purposes
98125 */
99- std::vector<uint16_t > interpolate_forces_to_pwm (
126+ std::vector<std:: uint16_t > interpolate_forces_to_pwm (
100127 const std::vector<double >& thruster_forces_array);
101128
102129 /* *
103130 * @brief scalar map from force to pwm x->y. Choose coefficients [LEFT] or
104131 * [RIGHT] based on sign(force)
105132 *
106- * @param force scalar force value
107- * @param coeffs std::vector<std::vector<double>> coeffs contains the pair
108- * of coefficients
133+ * @param force scalar force value
109134 *
110135 * @return std::uint16_t scalar pwm value
111136 */
112- std::uint16_t force_to_pwm (double force,
113- const std::vector<std::vector<double >>& coeffs);
137+ std::uint16_t force_to_pwm (double force);
114138
115139 /* *
116140 * @brief compute y = a*x^3 + b*x^2 + c*x + d
141+ *
117142 * @param force x
118143 * @param coeffs a,b,c,d
119144 *
@@ -123,11 +148,25 @@ class ThrusterInterfaceAUVDriver {
123148
124149 /* *
125150 * @brief only takes the pwm values computed and sends them
126- * to the ESCs via I2C
151+ * over UART as a framed packet
127152 *
128153 * @param thruster_pwm_array vector of pwm values to send
154+ * @return 0 on success, -1 on failure
155+ */
156+ int send_data_to_escs (const std::vector<std::uint16_t >& thruster_pwm_array);
157+
158+ /* *
159+ * @brief create UART packet with format:
160+ * [magic][id][length][payload][checksum]
161+ *
162+ * @param id packet ID
163+ * @param thruster_pwm_array vector of 8 pwm values to send as payload
164+ *
165+ * @return std::vector<uint8_t> serialized packet bytes
129166 */
130- void send_data_to_escs (const std::vector<uint16_t >& thruster_pwm_array);
167+ std::vector<std::uint8_t > create_packet (
168+ std::uint8_t id,
169+ const std::vector<std::uint16_t >& thruster_pwm_array) const ;
131170
132171 /* *
133172 * @brief convert Newtons to Kg
@@ -139,17 +178,76 @@ class ThrusterInterfaceAUVDriver {
139178 static constexpr double to_kg (double force) { return force / 9.80665 ; }
140179
141180 /* *
142- * @brief convert pwm values to i2c bytes
181+ * @brief start the asynchronous UART receive loop
182+ */
183+ void start_receive ();
184+
185+ /* *
186+ * @brief issue one asynchronous read on the UART port
187+ */
188+ void do_receive ();
189+
190+ /* *
191+ * @brief process the accumulated receive buffer and extract valid frames
192+ */
193+ void process_receive_buffer ();
194+
195+ /* *
196+ * @brief handle one decoded UART frame
197+ *
198+ * @param frame_bytes complete frame bytes including header and checksum
199+ */
200+ void handle_received_frame (const std::vector<std::uint8_t >& frame_bytes);
201+
202+ /* *
203+ * @brief compute checksum for framed UART packets
143204 *
144- * @param pwm pwm value
205+ * @param msg_id message id
206+ * @param length payload length
207+ * @param payload pointer to payload bytes
145208 *
146- * @return std::array<std::uint8_t, 2> i2c data
209+ * @return checksum byte
147210 */
148- static constexpr std::array<std::uint8_t , 2 > pwm_to_i2c_data (
149- std::uint16_t pwm) {
150- return {static_cast <std::uint8_t >((pwm >> 8 ) & 0xFF ),
151- static_cast <std::uint8_t >(pwm & 0xFF )};
152- }
211+ static std::uint8_t compute_checksum (std::uint8_t msg_id,
212+ std::uint8_t length,
213+ const std::uint8_t * payload);
214+
215+ private:
216+ static constexpr std::uint8_t UART_START_BYTE = 0xAA ;
217+ static constexpr std::size_t MAX_PAYLOAD_SIZE = 64 ;
218+ static constexpr std::size_t READ_CHUNK_SIZE = 256 ;
219+
220+ // Outgoing
221+ static constexpr std::uint8_t MSG_TURN_THRUSTERS_OFF = 0x01U ;
222+ static constexpr std::uint8_t MSG_TURN_LIGHTS_OFF = 0x02U ;
223+ static constexpr std::uint8_t MSG_RESET = 0x03 ;
224+ static constexpr std::uint8_t MSG_SET_THRUSTER_PWM = 0x04 ;
225+ static constexpr std::uint8_t MSG_SET_LIGHT_PWM = 0x05 ;
226+ // Incoming
227+ static constexpr std::uint8_t MSG_FLT_EVENT = 0x10 ;
228+ static constexpr std::uint8_t MSG_PGOOD_EVENT = 0x11 ;
229+ static constexpr std::uint8_t MSG_KILLSWITCH_EVENT = 0x12 ;
230+ static constexpr std::uint8_t MSG_CURRENT_MEASUREMENTS = 0x13 ;
231+
232+ std::string serial_device_;
233+ unsigned int baud_rate_;
234+
235+ asio::io_context io_;
236+ asio::serial_port serial_{io_};
237+ std::thread io_thread_;
238+
239+ std::vector<ThrusterParameters> thruster_parameters_;
240+ std::vector<double > right_coeffs_;
241+ std::vector<double > left_coeffs_;
242+ std::uint16_t idle_pwm_value_{1500 };
243+
244+ std::array<std::uint8_t , READ_CHUNK_SIZE > read_buf_{};
245+ std::vector<std::uint8_t > receive_buffer_;
246+
247+ FaultEventCallback fault_event_callback_;
248+ PGoodEventCallback pgood_event_callback_;
249+ KillswitchEventCallback killswitch_event_callback_;
250+ CurrentMeasurementsCallback current_measurements_callback_;
153251};
154252
155253#endif // THRUSTER_INTERFACE_AUV__THRUSTER_INTERFACE_AUV_DRIVER_HPP_
0 commit comments