PX4 Firmware
PX4 Autopilot Software http://px4.io
battery_status.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2019 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file sensors.cpp
36  *
37  * @author Lorenz Meier <lorenz@px4.io>
38  * @author Julian Oes <julian@oes.ch>
39  * @author Thomas Gubler <thomas@px4.io>
40  * @author Anton Babushkin <anton@px4.io>
41  * @author Beat Küng <beat-kueng@gmx.net>
42  */
43 
44 #include <px4_platform_common/px4_config.h>
45 #include <px4_platform_common/module.h>
46 #include <px4_platform_common/module_params.h>
47 #include <px4_platform_common/getopt.h>
48 #include <px4_platform_common/posix.h>
49 #include <px4_platform_common/tasks.h>
50 #include <px4_platform_common/time.h>
51 #include <px4_platform_common/log.h>
52 #include <lib/mathlib/mathlib.h>
53 #include <drivers/drv_hrt.h>
54 #include <drivers/drv_adc.h>
55 #include <lib/parameters/param.h>
56 #include <lib/perf/perf_counter.h>
57 #include <lib/battery/battery.h>
59 #include <uORB/Subscription.hpp>
60 #include <uORB/Publication.hpp>
65 #include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
66 
67 #include <DevMgr.hpp>
68 
69 #include "parameters.h"
70 
71 using namespace DriverFramework;
72 using namespace battery_status;
73 using namespace time_literals;
74 
75 /**
76  * Analog layout:
77  * FMU:
78  * IN2 - battery voltage
79  * IN3 - battery current
80  * IN4 - 5V sense
81  * IN10 - spare (we could actually trim these from the set)
82  * IN11 - spare on FMUv2 & v3, RC RSSI on FMUv4
83  * IN12 - spare (we could actually trim these from the set)
84  * IN13 - aux1 on FMUv2, unavaible on v3 & v4
85  * IN14 - aux2 on FMUv2, unavaible on v3 & v4
86  * IN15 - pressure sensor on FMUv2, unavaible on v3 & v4
87  *
88  * IO:
89  * IN4 - servo supply rail
90  * IN5 - analog RSSI on FMUv2 & v3
91  *
92  * The channel definitions (e.g., ADC_BATTERY_VOLTAGE_CHANNEL, ADC_BATTERY_CURRENT_CHANNEL, and ADC_AIRSPEED_VOLTAGE_CHANNEL) are defined in board_config.h
93  */
94 
95 /**
96  * Battery status app start / stop handling function
97  *
98  * @ingroup apps
99  */
100 extern "C" __EXPORT int battery_status_main(int argc, char *argv[]);
101 
102 #ifndef BOARD_NUMBER_BRICKS
103 #error "battery_status module requires power bricks"
104 #endif
105 
106 #if BOARD_NUMBER_BRICKS == 0
107 #error "battery_status module requires power bricks"
108 #endif
109 
110 class BatteryStatus : public ModuleBase<BatteryStatus>, public ModuleParams, public px4::ScheduledWorkItem
111 {
112 public:
113  BatteryStatus();
114  ~BatteryStatus() override;
115 
116  /** @see ModuleBase */
117  static int task_spawn(int argc, char *argv[]);
118 
119  /** @see ModuleBase */
120  static int custom_command(int argc, char *argv[]);
121 
122  /** @see ModuleBase */
123  static int print_usage(const char *reason = nullptr);
124 
125  void Run() override;
126  bool init();
127 
128  /** @see ModuleBase::print_status() */
129  int print_status() override;
130 
131 private:
132  DevHandle _h_adc; /**< ADC driver handle */
133 
134  bool _armed{false}; /**< arming status of the vehicle */
135 
136  uORB::Subscription _actuator_ctrl_0_sub{ORB_ID(actuator_controls_0)}; /**< attitude controls sub */
137  uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)}; /**< notification of parameter updates */
138  uORB::Subscription _vcontrol_mode_sub{ORB_ID(vehicle_control_mode)}; /**< vehicle control mode subscription */
139 
140  orb_advert_t _battery_pub[BOARD_NUMBER_BRICKS] {}; /**< battery status */
141  Battery _battery[BOARD_NUMBER_BRICKS]; /**< Helper lib to publish battery_status topic. */
142 
143 #if BOARD_NUMBER_BRICKS > 1
144  int _battery_pub_intance0ndx {0}; /**< track the index of instance 0 */
145 #endif /* BOARD_NUMBER_BRICKS > 1 */
146 
147  perf_counter_t _loop_perf; /**< loop performance counter */
148 
149  hrt_abstime _last_config_update{0};
150 
151  Parameters _parameters{}; /**< local copies of interesting parameters */
152  ParameterHandles _parameter_handles{}; /**< handles for interesting parameters */
153 
154  /**
155  * Update our local parameter cache.
156  */
157  int parameters_update();
158 
159  /**
160  * Do adc-related initialisation.
161  */
162  int adc_init();
163 
164  /**
165  * Check for changes in parameters.
166  */
167  void parameter_update_poll(bool forced = false);
168 
169  /**
170  * Poll the ADC and update readings to suit.
171  *
172  * @param raw Combined sensor data structure into which
173  * data should be returned.
174  */
175  void adc_poll();
176 };
177 
179  ModuleParams(nullptr),
180  ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::hp_default),
181  _loop_perf(perf_alloc(PC_ELAPSED, MODULE_NAME))
182 {
184 
185  for (int b = 0; b < BOARD_NUMBER_BRICKS; b++) {
186  _battery[b].setParent(this);
187  }
188 }
189 
191 {
192  ScheduleClear();
193 }
194 
195 int
197 {
198  /* read the parameter values into _parameters */
200 
201  if (ret) {
202  return ret;
203  }
204 
205  return ret;
206 }
207 
208 int
210 {
211  DevMgr::getHandle(ADC0_DEVICE_PATH, _h_adc);
212 
213  if (!_h_adc.isValid()) {
214  PX4_ERR("no ADC found: %s (%d)", ADC0_DEVICE_PATH, _h_adc.getError());
215  return PX4_ERROR;
216  }
217 
218  return OK;
219 }
220 
221 void
223 {
224  // check for parameter updates
225  if (_parameter_update_sub.updated() || forced) {
226  // clear update
227  parameter_update_s pupdate;
228  _parameter_update_sub.copy(&pupdate);
229 
230  // update parameters from storage
232  updateParams();
233  }
234 }
235 
236 void
238 {
239  /* make space for a maximum of twelve channels (to ensure reading all channels at once) */
241 
242  /* read all channels available */
243  int ret = _h_adc.read(&buf_adc, sizeof(buf_adc));
244 
245  //todo:abosorb into new class Power
246 
247  /* For legacy support we publish the battery_status for the Battery that is
248  * associated with the Brick that is the selected source for VDD_5V_IN
249  * Selection is done in HW ala a LTC4417 or similar, or may be hard coded
250  * Like in the FMUv4
251  */
252 
253  /* The ADC channels that are associated with each brick, in power controller
254  * priority order highest to lowest, as defined by the board config.
255  */
256  int bat_voltage_v_chan[BOARD_NUMBER_BRICKS] = BOARD_BATT_V_LIST;
257  int bat_voltage_i_chan[BOARD_NUMBER_BRICKS] = BOARD_BATT_I_LIST;
258 
259  if (_parameters.battery_adc_channel >= 0) { // overwrite default
260  bat_voltage_v_chan[0] = _parameters.battery_adc_channel;
261  }
262 
263  /* The valid signals (HW dependent) are associated with each brick */
264  bool valid_chan[BOARD_NUMBER_BRICKS] = BOARD_BRICK_VALID_LIST;
265 
266  /* Per Brick readings with default unread channels at 0 */
267  float bat_current_a[BOARD_NUMBER_BRICKS] = {0.0f};
268  float bat_voltage_v[BOARD_NUMBER_BRICKS] = {0.0f};
269 
270  /* Based on the valid_chan, used to indicate the selected the lowest index
271  * (highest priority) supply that is the source for the VDD_5V_IN
272  * When < 0 none selected
273  */
274 
275  int selected_source = -1;
276 
277  if (ret >= (int)sizeof(buf_adc[0])) {
278 
279  /* Read add channels we got */
280  for (unsigned i = 0; i < ret / sizeof(buf_adc[0]); i++) {
281  {
282  for (int b = 0; b < BOARD_NUMBER_BRICKS; b++) {
283 
284  /* Once we have subscriptions, Do this once for the lowest (highest priority
285  * supply on power controller) that is valid.
286  */
287  if (_battery_pub[b] != nullptr && selected_source < 0 && valid_chan[b]) {
288  /* Indicate the lowest brick (highest priority supply on power controller)
289  * that is valid as the one that is the selected source for the
290  * VDD_5V_IN
291  */
292  selected_source = b;
293 # if BOARD_NUMBER_BRICKS > 1
294 
295  /* Move the selected_source to instance 0 */
296  if (_battery_pub_intance0ndx != selected_source) {
297 
298  orb_advert_t tmp_h = _battery_pub[_battery_pub_intance0ndx];
299  _battery_pub[_battery_pub_intance0ndx] = _battery_pub[selected_source];
300  _battery_pub[selected_source] = tmp_h;
301  _battery_pub_intance0ndx = selected_source;
302  }
303 
304 # endif /* BOARD_NUMBER_BRICKS > 1 */
305  }
306 
307  // todo:per brick scaling
308  /* look for specific channels and process the raw voltage to measurement data */
309  if (bat_voltage_v_chan[b] == buf_adc[i].am_channel) {
310  /* Voltage in volts */
311  bat_voltage_v[b] = (buf_adc[i].am_data * _parameters.battery_voltage_scaling) * _parameters.battery_v_div;
312 
313  } else if (bat_voltage_i_chan[b] == buf_adc[i].am_channel) {
314  bat_current_a[b] = ((buf_adc[i].am_data * _parameters.battery_current_scaling)
316  }
317  }
318  }
319  }
320 
321  if (_parameters.battery_source == 0) {
322  for (int b = 0; b < BOARD_NUMBER_BRICKS; b++) {
323 
324  /* Consider the brick connected if there is a voltage */
325  bool connected = bat_voltage_v[b] > BOARD_ADC_OPEN_CIRCUIT_V;
326 
327  /* In the case where the BOARD_ADC_OPEN_CIRCUIT_V is
328  * greater than the BOARD_VALID_UV let the HW qualify that it
329  * is connected.
330  */
331  if (BOARD_ADC_OPEN_CIRCUIT_V > BOARD_VALID_UV) {
332  connected &= valid_chan[b];
333  }
334 
335  actuator_controls_s ctrl{};
336  _actuator_ctrl_0_sub.copy(&ctrl);
337 
339  _battery[b].updateBatteryStatus(hrt_absolute_time(), bat_voltage_v[b], bat_current_a[b],
340  connected, selected_source == b, b,
341  ctrl.control[actuator_controls_s::INDEX_THROTTLE],
343  int instance;
345  }
346  }
347  }
348 }
349 
350 void
352 {
353  if (should_exit()) {
354  exit_and_cleanup();
355  return;
356  }
357 
358  if (!_h_adc.isValid()) {
359  adc_init();
360  }
361 
363 
364  /* check vehicle status for changes to publication state */
365  if (_vcontrol_mode_sub.updated()) {
366  vehicle_control_mode_s vcontrol_mode{};
367  _vcontrol_mode_sub.copy(&vcontrol_mode);
368  _armed = vcontrol_mode.flag_armed;
369  }
370 
371  /* check parameters for updates */
373 
374  /* check battery voltage */
375  adc_poll();
376 
378 }
379 
380 int
381 BatteryStatus::task_spawn(int argc, char *argv[])
382 {
384 
385  if (instance) {
386  _object.store(instance);
387  _task_id = task_id_is_work_queue;
388 
389  if (instance->init()) {
390  return PX4_OK;
391  }
392 
393  } else {
394  PX4_ERR("alloc failed");
395  }
396 
397  delete instance;
398  _object.store(nullptr);
399  _task_id = -1;
400 
401  return PX4_ERROR;
402 }
403 
404 bool
406 {
407  ScheduleOnInterval(10_ms); // 100 Hz
408 
409  return true;
410 }
411 
413 {
414  return 0;
415 }
416 
417 int BatteryStatus::custom_command(int argc, char *argv[])
418 {
419  return print_usage("unknown command");
420 }
421 
422 int BatteryStatus::print_usage(const char *reason)
423 {
424  if (reason) {
425  PX4_WARN("%s\n", reason);
426  }
427 
428  PRINT_MODULE_DESCRIPTION(
429  R"DESCR_STR(
430 ### Description
431 
432 The provided functionality includes:
433 - Read the output from the ADC driver (via ioctl interface) and publish `battery_status`.
434 
435 
436 ### Implementation
437 It runs in its own thread and polls on the currently selected gyro topic.
438 
439 )DESCR_STR");
440 
441  PRINT_MODULE_USAGE_NAME("battery_status", "system");
442  PRINT_MODULE_USAGE_COMMAND("start");
443  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
444 
445  return 0;
446 }
447 
448 int battery_status_main(int argc, char *argv[])
449 {
450  return BatteryStatus::main(argc, argv);
451 }
px4_adc_msg_t
Definition: drv_adc.h:56
static int custom_command(int argc, char *argv[])
Battery _battery[BOARD_NUMBER_BRICKS]
Helper lib to publish battery_status topic.
uORB::Subscription _actuator_ctrl_0_sub
attitude controls sub
int print_status() override
__EXPORT int battery_status_main(int argc, char *argv[])
Analog layout: FMU: IN2 - battery voltage IN3 - battery current IN4 - 5V sense IN10 - spare (we could...
measure the time elapsed performing an event
Definition: perf_counter.h:56
struct uart_esc::@19 _parameter_handles
static void parameter_update_poll()
poll parameter update
#define PX4_MAX_ADC_CHANNELS
Definition: drv_adc.h:52
void Run() override
int main(int argc, char **argv)
Definition: main.cpp:3
int adc_init()
Do adc-related initialisation.
Definition: I2C.hpp:51
void print_status()
Definition: Commander.cpp:517
ADC driver interface.
static void print_usage()
#define ADC0_DEVICE_PATH
Definition: drv_adc.h:59
LidarLite * instance
Definition: ll40ls.cpp:65
Vector rotation library.
High-resolution timer with callouts and timekeeping.
Library calls for battery functionality.
Global flash based parameter store.
DevHandle _h_adc
ADC driver handle.
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
Header common to all counters.
static void parameters_update()
update all parameters
void parameter_update_poll(bool forced=false)
Check for changes in parameters.
static int task_spawn(int argc, char *argv[])
void init()
Activates/configures the hardware registers.
uORB::Subscription _parameter_update_sub
notification of parameter updates
#define perf_alloc(a, b)
Definition: px4io.h:59
void perf_end(perf_counter_t handle)
End a performance event.
bool updated()
Check if there is a new update.
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
Definition: uORB.h:134
Parameters _parameters
local copies of interesting parameters
void initialize_parameter_handles(ParameterHandles &parameter_handles)
initialize ParameterHandles struct
Definition: parameters.cpp:49
void updateBatteryStatus(hrt_abstime timestamp, float voltage_v, float current_a, bool connected, bool selected_source, int priority, float throttle_normalized, bool armed, battery_status_s *status)
Update current battery status message.
Definition: battery.cpp:68
perf_counter_t _loop_perf
loop performance counter
static int print_usage(const char *reason=nullptr)
struct uart_esc::@18 _parameters
int update_parameters(const ParameterHandles &parameter_handles, Parameters &parameters)
Read out the parameters using the handles into the parameters struct.
Definition: parameters.cpp:60
~BatteryStatus() override
Definition: bst.cpp:62
int parameters_update()
Update our local parameter cache.
void adc_poll()
Poll the ADC and update readings to suit.
#define OK
Definition: uavcan_main.cpp:71
bool _armed
arming status of the vehicle
orb_advert_t _battery_pub[BOARD_NUMBER_BRICKS]
battery status
bool copy(void *dst)
Copy the struct.
int adc_init(void)
Sensors/misc inputs.
Definition: adc.c:82
ParameterHandles _parameter_handles
handles for interesting parameters
void perf_begin(perf_counter_t handle)
Begin a performance event.
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
static int orb_publish_auto(const struct orb_metadata *meta, orb_advert_t *handle, const void *data, int *instance, int priority)
Advertise as the publisher of a topic.
Definition: uORB.h:177
uORB::Subscription _vcontrol_mode_sub
vehicle control mode subscription
Performance measuring tools.