Skip to content

Commit 31862fd

Browse files
committed
uavcan battery: add support for cuav::equipment::power::CBAT message
emitted by e.g. uavcan Tattu batteries.
1 parent 8df6d3c commit 31862fd

File tree

4 files changed

+109
-8
lines changed

4 files changed

+109
-8
lines changed

src/drivers/uavcan/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ if DRIVERS_UAVCAN
4747
default y
4848

4949
config UAVCAN_SENSOR_BATTERY
50-
bool "Subscribe to Battery: uavcan::equipment::power::BatteryInfo | ardupilot::equipment::power::BatteryInfoAux"
50+
bool "Subscribe to Battery: uavcan::equipment::power::BatteryInfo | ardupilot::equipment::power::BatteryInfoAux | cuav::equipment::power::CBAT"
5151
default y
5252

5353
config UAVCAN_SENSOR_DIFFERENTIAL_PRESSURE

src/drivers/uavcan/sensors/battery.cpp

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ UavcanBatteryBridge::UavcanBatteryBridge(uavcan::INode &node) :
4444
ModuleParams(nullptr),
4545
_sub_battery(node),
4646
_sub_battery_aux(node),
47+
_sub_cbat(node),
4748
_warning(battery_status_s::WARNING_NONE),
4849
_last_timestamp(0)
4950
{
@@ -78,6 +79,13 @@ int UavcanBatteryBridge::init()
7879
return res;
7980
}
8081

82+
res = _sub_cbat.start(CBATCbBinder(this, &UavcanBatteryBridge::cbat_sub_cb));
83+
84+
if (res < 0) {
85+
PX4_ERR("failed to start uavcan sub: %d", res);
86+
return res;
87+
}
88+
8189
return 0;
8290
}
8391

@@ -92,7 +100,8 @@ UavcanBatteryBridge::battery_sub_cb(const uavcan::ReceivedDataStructure<uavcan::
92100
}
93101
}
94102

95-
if (instance >= battery_status_s::MAX_INSTANCES) {
103+
if (instance >= battery_status_s::MAX_INSTANCES
104+
|| _batt_update_mod[instance] == BatteryDataType::CBAT) {
96105
return;
97106
}
98107

@@ -165,11 +174,9 @@ UavcanBatteryBridge::battery_aux_sub_cb(const uavcan::ReceivedDataStructure<ardu
165174
}
166175
}
167176

168-
if (instance >= battery_status_s::MAX_INSTANCES) {
169-
return;
170-
}
171-
172-
if (_batt_update_mod[instance] == BatteryDataType::Filter) {
177+
if (instance >= battery_status_s::MAX_INSTANCES
178+
|| _batt_update_mod[instance] == BatteryDataType::Filter
179+
|| _batt_update_mod[instance] == BatteryDataType::CBAT) {
173180
return;
174181
}
175182

@@ -197,6 +204,90 @@ UavcanBatteryBridge::battery_aux_sub_cb(const uavcan::ReceivedDataStructure<ardu
197204
}
198205
}
199206

207+
void UavcanBatteryBridge::cbat_sub_cb(const uavcan::ReceivedDataStructure<cuav::equipment::power::CBAT> &msg)
208+
{
209+
uint8_t instance = 0;
210+
211+
for (instance = 0; instance < battery_status_s::MAX_INSTANCES; instance++) {
212+
if (_battery_status[instance].id == msg.getSrcNodeID().get()) {
213+
break;
214+
}
215+
}
216+
217+
if (instance >= battery_status_s::MAX_INSTANCES
218+
|| _batt_update_mod[instance] == BatteryDataType::Filter) {
219+
return;
220+
}
221+
222+
// If CBAT message with superset of data was received, skip BatteryInfo messages
223+
_batt_update_mod[instance] = BatteryDataType::CBAT;
224+
225+
_battery_status[instance].timestamp = hrt_absolute_time();
226+
_battery_status[instance].voltage_v = msg.voltage;
227+
_battery_status[instance].current_a = msg.current;
228+
_battery_status[instance].current_average_a = msg.average_current;
229+
_battery_status[instance].discharged_mah =
230+
(msg.full_charge_capacity - msg.remaining_capacity) / msg.nominal_voltage * 1000.f;
231+
_battery_status[instance].remaining = msg.state_of_charge / 100.f;
232+
_battery_status[instance].scale = -1;
233+
_battery_status[instance].temperature = msg.temperature + atmosphere::kAbsoluteNullCelsius; // Kelvin to Celsius
234+
_battery_status[instance].full_charge_capacity_wh =
235+
msg.nominal_voltage * msg.full_charge_capacity / 1000.f; // mAh -> Wh
236+
_battery_status[instance].remaining_capacity_wh = msg.nominal_voltage * msg.remaining_capacity / 1000.f; // mAh -> Wh
237+
_battery_status[instance].nominal_voltage = msg.nominal_voltage;
238+
_battery_status[instance].capacity = msg.design_capacity; // mAh
239+
_battery_status[instance].cycle_count = msg.cycle_count;
240+
_battery_status[instance].average_time_to_empty = msg.average_time_to_empty;
241+
_battery_status[instance].manufacture_date = msg.manufacture_date;
242+
_battery_status[instance].state_of_health = msg.state_of_health;
243+
_battery_status[instance].max_error = msg.max_error;
244+
_battery_status[instance].over_discharge_count = msg.over_discharge_count;
245+
_battery_status[instance].connected = true;
246+
_battery_status[instance].cell_count = msg.cell_count;
247+
_battery_status[instance].source = battery_status_s::SOURCE_EXTERNAL;
248+
_battery_status[instance].id = msg.getSrcNodeID().get();
249+
_battery_status[instance].is_powering_off = msg.is_powering_off;
250+
251+
const float remaining_ah = msg.remaining_capacity / 1000.f;
252+
const float current_a = math::isZero(msg.average_current) ? msg.current : msg.average_current;
253+
_battery_status[instance].time_remaining_s = math::isZero(current_a) ? NAN : (remaining_ah / -current_a * 3600.f);
254+
255+
for (uint8_t i = 0; i < _battery_status[instance].cell_count; i++) {
256+
_battery_status[instance].voltage_cell_v[i] = msg.voltage_cell[i];
257+
}
258+
259+
determineWarning(_battery_status[instance].remaining);
260+
_battery_status[instance].warning = _warning;
261+
262+
uint16_t faults = 0;
263+
264+
if (msg.status_flags & cuav::equipment::power::CBAT::STATUS_FLAG_OVERLOAD) {
265+
faults |= (1 << battery_status_s::FAULT_OVER_CURRENT);
266+
}
267+
268+
if (msg.status_flags & cuav::equipment::power::CBAT::STATUS_FLAG_BAD_BATTERY) {
269+
faults |= (1 << battery_status_s::FAULT_HARDWARE_FAILURE);
270+
}
271+
272+
if (msg.status_flags & cuav::equipment::power::CBAT::STATUS_FLAG_TEMP_HOT) {
273+
faults |= (1 << battery_status_s::FAULT_OVER_TEMPERATURE);
274+
}
275+
276+
if (msg.status_flags & cuav::equipment::power::CBAT::STATUS_FLAG_TEMP_COLD) {
277+
faults |= (1 << battery_status_s::FAULT_UNDER_TEMPERATURE);
278+
}
279+
280+
_battery_status[instance].faults = faults;
281+
282+
publish(msg.getSrcNodeID().get(), &_battery_status[instance]);
283+
284+
_battery_info[instance].timestamp = _battery_status[instance].timestamp;
285+
_battery_info[instance].id = _battery_status[instance].id;
286+
snprintf(_battery_info[instance].serial_number, sizeof(_battery_info[instance].serial_number), "%" PRIu16,
287+
msg.serial_number);
288+
_battery_info_pub[instance].publish(_battery_info[instance]);
289+
}
290+
200291
void
201292
UavcanBatteryBridge::sumDischarged(hrt_abstime timestamp, float current_a)
202293
{

src/drivers/uavcan/sensors/battery.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <uORB/topics/battery_status.h>
4343
#include <uavcan/equipment/power/BatteryInfo.hpp>
4444
#include <ardupilot/equipment/power/BatteryInfoAux.hpp>
45+
#include <cuav/equipment/power/CBAT.hpp>
4546
#include <battery/battery.h>
4647
#include <drivers/drv_hrt.h>
4748
#include <px4_platform_common/module_params.h>
@@ -66,10 +67,12 @@ class UavcanBatteryBridge : public UavcanSensorBridgeBase, public ModuleParams
6667
Raw, // data from BatteryInfo message only
6768
RawAux, // data combination from BatteryInfo and BatteryInfoAux messages
6869
Filter, // filter data from BatteryInfo message with Battery library
70+
CBAT, // CBAT messages
6971
};
7072

7173
void battery_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::power::BatteryInfo> &msg);
7274
void battery_aux_sub_cb(const uavcan::ReceivedDataStructure<ardupilot::equipment::power::BatteryInfoAux> &msg);
75+
void cbat_sub_cb(const uavcan::ReceivedDataStructure<cuav::equipment::power::CBAT> &msg);
7376
void sumDischarged(hrt_abstime timestamp, float current_a);
7477
void determineWarning(float remaining);
7578
void filterData(const uavcan::ReceivedDataStructure<uavcan::equipment::power::BatteryInfo> &msg, uint8_t instance);
@@ -83,8 +86,14 @@ class UavcanBatteryBridge : public UavcanSensorBridgeBase, public ModuleParams
8386
(const uavcan::ReceivedDataStructure<ardupilot::equipment::power::BatteryInfoAux> &) >
8487
BatteryInfoAuxCbBinder;
8588

89+
typedef uavcan::MethodBinder < UavcanBatteryBridge *,
90+
void (UavcanBatteryBridge::*)
91+
(const uavcan::ReceivedDataStructure<cuav::equipment::power::CBAT> &) >
92+
CBATCbBinder;
93+
8694
uavcan::Subscriber<uavcan::equipment::power::BatteryInfo, BatteryInfoCbBinder> _sub_battery;
8795
uavcan::Subscriber<ardupilot::equipment::power::BatteryInfoAux, BatteryInfoAuxCbBinder> _sub_battery_aux;
96+
uavcan::Subscriber<cuav::equipment::power::CBAT, CBATCbBinder> _sub_cbat;
8897

8998
DEFINE_PARAMETERS(
9099
(ParamFloat<px4::params::BAT_LOW_THR>) _param_bat_low_thr,

src/drivers/uavcan/uavcan_params.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,11 @@ PARAM_DEFINE_INT32(UAVCAN_SUB_BARO, 0);
288288
* Enable UAVCAN battery subscription.
289289
* uavcan::equipment::power::BatteryInfo
290290
* ardupilot::equipment::power::BatteryInfoAux
291+
* cuav::equipment::power::CBAT
291292
*
292293
* 0 - Disable
293294
* 1 - Use raw data. Recommended for Smart battery
294-
* 2 - Filter the data with internal battery library
295+
* 2 - Filter the data with internal battery library (unsupported with CBAT)
295296
*
296297
* @min 0
297298
* @max 2

0 commit comments

Comments
 (0)