Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
power_uavcan.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Freek van Tienen <freek.v.tienen@gmail.com>
3  *
4  * This file is part of paparazzi
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, write to
18  * the Free Software Foundation, 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
26 #include "power_uavcan.h"
27 #include "uavcan/uavcan.h"
29 #include "math/pprz_random.h"
30 
31 /* uavcan EQUIPMENT_ESC_STATUS message definition */
32 #define UAVCAN_EQUIPMENT_POWER_BATTERYINFO_ID 1092
33 #define UAVCAN_EQUIPMENT_POWER_BATTERYINFO_SIGNATURE (0x249C26548A711966ULL)
34 #define UAVCAN_EQUIPMENT_POWER_BATTERYINFO_MAX_SIZE 55
35 
36 /* uavcan EQUIPMENT_POWER_CIRCUITSTATUS message definition */
37 #define UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_ID 1091
38 #define UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_SIGNATURE (0x8313D33D0DDDA115ULL)
39 #define UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_MAX_SIZE 7
40 
41 /* Default maximum amount of batteries */
42 #ifndef POWER_UAVCAN_BATTERIES_MAX
43 #define POWER_UAVCAN_BATTERIES_MAX 3
44 #endif
45 
46 /* Default maximum amount of circuits */
47 #ifndef POWER_UAVCAN_CIRCUITS_MAX
48 #define POWER_UAVCAN_CIRCUITS_MAX 15
49 #endif
50 
51 /* Default Battery circuits */
52 #ifndef POWER_UAVCAN_BATTERY_CIRCUITS
53 #define POWER_UAVCAN_BATTERY_CIRCUITS {}
54 #endif
55 
56 /* Local variables */
59 
60 /* Batteries */
62  bool set;
64 
65  float temperature;
66  float voltage;
67  float current;
78 // struct { uint8_t len; uint8_t data[31]; }model_name;
79 };
81 
82 /* Circuits */
84  bool set;
86  bool is_battery;
87 
89  float voltage;
90  float current;
92 };
94 
95 /* Battery circuits */
99 };
101 
102 #if PERIODIC_TELEMETRY
104 
105 static void power_uavcan_send_power_device(struct transport_tx *trans, struct link_device *dev)
106 {
107  static uint8_t idx = 0;
108  // Send the circuit status
109  if(circuits[idx].set) {
111  pprz_msg_send_POWER_DEVICE(trans, dev, AC_ID, &circuits[idx].node_id, &cid, &circuits[idx].current, &circuits[idx].voltage);
112  }
113 
114  // Go to the next
115  if (rand_uniform() > 0.02) {
116  idx++;
117  }
118  if(idx >= POWER_UAVCAN_CIRCUITS_MAX || !circuits[idx].set) {
119  idx = 0;
120  }
121 }
122 #endif /* PERIODIC_TELEMETRY */
123 
124 
125 static void power_uavcan_battery_cb(struct uavcan_iface_t *iface __attribute__((unused)), CanardRxTransfer *transfer)
126 {
127  uint16_t tmp_float = 0;
128 
129  /* Decode the message */
130  canardDecodeScalar(transfer, (uint32_t)0, 16, true, (void *)&tmp_float);
131  float temperature = canardConvertFloat16ToNativeFloat(tmp_float);
132  canardDecodeScalar(transfer, (uint32_t)16, 16, true, (void *)&tmp_float);
133  float voltage = canardConvertFloat16ToNativeFloat(tmp_float);
134  canardDecodeScalar(transfer, (uint32_t)32, 16, true, (void *)&tmp_float);
135  float current = canardConvertFloat16ToNativeFloat(tmp_float);
136  canardDecodeScalar(transfer, (uint32_t)48, 16, true, (void *)&tmp_float);
137  float average_power_10sec = canardConvertFloat16ToNativeFloat(tmp_float);
138  canardDecodeScalar(transfer, (uint32_t)64, 16, true, (void *)&tmp_float);
139  float remaining_capacity_wh = canardConvertFloat16ToNativeFloat(tmp_float);
140  canardDecodeScalar(transfer, (uint32_t)80, 16, true, (void *)&tmp_float);
141  float full_charge_capacity_wh = canardConvertFloat16ToNativeFloat(tmp_float);
142  canardDecodeScalar(transfer, (uint32_t)96, 16, true, (void *)&tmp_float);
143  float hours_to_full_charge = canardConvertFloat16ToNativeFloat(tmp_float);
144  uint16_t status_flags = 0;
145  canardDecodeScalar(transfer, (uint32_t)112, 11, false, (void *)&status_flags);
146  uint8_t state_of_health_pct = 0;
147  canardDecodeScalar(transfer, (uint32_t)123, 7, false, (void *)&state_of_health_pct);
148  uint8_t state_of_charge_pct = 0;
149  canardDecodeScalar(transfer, (uint32_t)130, 7, false, (void *)&state_of_charge_pct);
150  uint8_t state_of_charge_pct_stdev = 0;
151  canardDecodeScalar(transfer, (uint32_t)137, 7, false, (void *)&state_of_charge_pct_stdev);
152  uint8_t battery_id = 0;
153  canardDecodeScalar(transfer, (uint32_t)144, 8, false, (void *)&battery_id);
154  uint32_t model_instance_id = 0;
155  canardDecodeScalar(transfer, (uint32_t)152, 32, false, (void *)&model_instance_id);
156 
157  // Search for the battery or free spot
158  uint8_t battery_idx = POWER_UAVCAN_BATTERIES_MAX;
159  for (uint8_t i = 0; i < POWER_UAVCAN_BATTERIES_MAX; i++) {
160  if (batteries[i].set && batteries[i].node_id == transfer->source_node_id &&
161  batteries[i].battery_id == battery_id && batteries[i].model_instance_id == model_instance_id) {
162  battery_idx = i;
163  break;
164  }
165  else if(!batteries[i].set) {
166  battery_idx = i;
167  break;
168  }
169  }
170 
171  // No free spot found
172  if (battery_idx >= POWER_UAVCAN_BATTERIES_MAX) {
173  return;
174  }
175 
176  // Set the battery info
177  batteries[battery_idx].set = true;
178  batteries[battery_idx].node_id = transfer->source_node_id;
179  batteries[battery_idx].temperature = temperature;
180  batteries[battery_idx].voltage = voltage;
181  batteries[battery_idx].current = current;
182  batteries[battery_idx].average_power_10sec = average_power_10sec;
183  batteries[battery_idx].remaining_capacity_wh = remaining_capacity_wh;
184  batteries[battery_idx].full_charge_capacity_wh = full_charge_capacity_wh;
185  batteries[battery_idx].hours_to_full_charge = hours_to_full_charge;
186  batteries[battery_idx].status_flags = status_flags;
187  batteries[battery_idx].state_of_health_pct = state_of_health_pct;
188  batteries[battery_idx].state_of_charge_pct = state_of_charge_pct;
189  batteries[battery_idx].state_of_charge_pct_stdev = state_of_charge_pct_stdev;
190  batteries[battery_idx].battery_id = battery_id;
191  batteries[battery_idx].model_instance_id = model_instance_id;
192 
193  // Sum the battery currents
194  float current_sum = 0;
195  for (uint8_t i = 0; i < POWER_UAVCAN_BATTERIES_MAX; i++) {
196  if (batteries[i].set) {
197  current_sum += batteries[i].current;
198  }
199  }
200  electrical.current = current_sum;
201  if (voltage > 0) {
202  electrical.vsupply = voltage;
203  }
204 }
205 
206 static void power_uavcan_circuit_cb(struct uavcan_iface_t *iface __attribute__((unused)), CanardRxTransfer *transfer)
207 {
208  uint16_t tmp_float = 0;
209 
210  /* Decode the message */
211  uint16_t circuit_id = 0;
212  canardDecodeScalar(transfer, (uint32_t)0, 16, false, (void *)&circuit_id);
213  canardDecodeScalar(transfer, (uint32_t)16, 16, true, (void *)&tmp_float);
214  float voltage = canardConvertFloat16ToNativeFloat(tmp_float);
215  canardDecodeScalar(transfer, (uint32_t)32, 16, true, (void *)&tmp_float);
216  float current = canardConvertFloat16ToNativeFloat(tmp_float);
217  uint8_t error_flags = 0;
218  canardDecodeScalar(transfer, (uint32_t)48, 8, false, (void *)&error_flags);
219 
220  // Search for the circuit or free spot
221  uint8_t circuit_idx = POWER_UAVCAN_CIRCUITS_MAX;
222  for (uint8_t i = 0; i < POWER_UAVCAN_CIRCUITS_MAX; i++) {
223  if (circuits[i].set && circuits[i].node_id == transfer->source_node_id && circuits[i].circuit_id == circuit_id) {
224  circuit_idx = i;
225  break;
226  }
227  else if(!circuits[i].set) {
228  circuit_idx = i;
229  break;
230  }
231  }
232 
233  // No free spot found
234  if (circuit_idx >= POWER_UAVCAN_CIRCUITS_MAX) {
235  return;
236  }
237 
238  // Set the circuit info
239  circuits[circuit_idx].set = true;
240  circuits[circuit_idx].node_id = transfer->source_node_id;
241  circuits[circuit_idx].circuit_id = circuit_id;
242  circuits[circuit_idx].voltage = voltage;
243  circuits[circuit_idx].current = current;
244  circuits[circuit_idx].error_flags = error_flags;
245 
246  // Sum the 'battery' circuit currents
247  float current_sum = 0;
248  for (uint8_t i = 0; i < POWER_UAVCAN_CIRCUITS_MAX; i++) {
249  if (circuits[i].set && circuits[i].is_battery) {
250  current_sum += circuits[i].current;
251  }
252  }
253  electrical.current = current_sum;
254  if (voltage > 0 && circuits[circuit_idx].is_battery) {
255  electrical.vsupply = voltage;
256  }
257 }
258 
260 {
261  // Init the battery circuits
262  for (uint8_t i = 0; i < sizeof(battery_circuits) / sizeof(struct uavcan_circuit_battery_t); i++) {
263  circuits[i].set = true;
266  circuits[i].is_battery = true;
267  }
268 
269  // Bind uavcan BATTERYINFO message from EQUIPMENT.POWER
272 
273  // Bind uavcan CIRCUIT_STATUS message from EQUIPMENT.POWER
276 
277  // Configure telemetry
278 #if PERIODIC_TELEMETRY
280 #endif
281 
282  // Initialize Random (for telemetry)
283  init_random();
284 }
Main uavcan event structure for registering/calling callbacks.
Definition: uavcan.h:60
struct Electrical electrical
Definition: electrical.c:92
Interface for electrical status: supply voltage, current, battery status, etc.
float current
current in A
Definition: electrical.h:47
float vsupply
supply voltage in V
Definition: electrical.h:45
static uint32_t idx
static struct uavcan_circuit_battery_t battery_circuits[]
Definition: power_uavcan.c:100
static void power_uavcan_battery_cb(struct uavcan_iface_t *iface, CanardRxTransfer *transfer)
Definition: power_uavcan.c:125
#define POWER_UAVCAN_BATTERY_CIRCUITS
Definition: power_uavcan.c:53
static void power_uavcan_circuit_cb(struct uavcan_iface_t *iface, CanardRxTransfer *transfer)
Definition: power_uavcan.c:206
#define UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_SIGNATURE
Definition: power_uavcan.c:38
static void power_uavcan_send_power_device(struct transport_tx *trans, struct link_device *dev)
Definition: power_uavcan.c:105
#define UAVCAN_EQUIPMENT_POWER_BATTERYINFO_SIGNATURE
Definition: power_uavcan.c:33
static uavcan_event circuit_uavcan_ev
Definition: power_uavcan.c:58
#define POWER_UAVCAN_CIRCUITS_MAX
Definition: power_uavcan.c:48
static struct uavcan_equipment_power_BatteryInfo batteries[POWER_UAVCAN_BATTERIES_MAX]
Definition: power_uavcan.c:80
void power_uavcan_init(void)
Definition: power_uavcan.c:259
#define UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_ID
Definition: power_uavcan.c:37
static uavcan_event power_uavcan_ev
Definition: power_uavcan.c:57
#define POWER_UAVCAN_BATTERIES_MAX
Definition: power_uavcan.c:43
static struct uavcan_equipment_power_CircuitStatus circuits[POWER_UAVCAN_CIRCUITS_MAX]
Definition: power_uavcan.c:93
#define UAVCAN_EQUIPMENT_POWER_BATTERYINFO_ID
Definition: power_uavcan.c:32
Power sensors on the uavcan bus.
void init_random(void)
Definition: pprz_random.c:35
double rand_uniform(void)
Definition: pprz_random.c:45
uavcan interface structure
Definition: uavcan.h:34
static const struct usb_device_descriptor dev
Definition: usb_ser_hw.c:74
int8_t register_periodic_telemetry(struct periodic_telemetry *_pt, uint8_t _id, telemetry_cb _cb)
Register a telemetry callback function.
Definition: telemetry.c:51
Periodic telemetry system header (includes downlink utility and generated code).
#define DefaultPeriodic
Set default periodic telemetry.
Definition: telemetry.h:66
void uavcan_bind(uint16_t data_type_id, uint64_t data_type_signature, uavcan_event *ev, uavcan_callback cb)
Bind to a receiving message from uavcan.
Definition: uavcan.c:412
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
Definition: vl53l1_types.h:88
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
Definition: vl53l1_types.h:78
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
Definition: vl53l1_types.h:98
int transfer(const Mat *from, const image_t *to)