PX4 Firmware
PX4 Autopilot Software http://px4.io
df_bebop_bus_wrapper.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2016 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 /**
36  * @file df_bebop_bus_wrapper.cpp
37  *
38  * This is a wrapper around the Parrot Bebop bus driver of the DriverFramework. It sends the
39  * motor and contol commands to the Bebop and reads its status and informations.
40  */
41 
42 #include <stdlib.h>
43 #include <stdint.h>
44 
45 #include <px4_platform_common/tasks.h>
46 #include <px4_platform_common/getopt.h>
47 #include <px4_platform_common/posix.h>
48 
49 #include <errno.h>
50 #include <string.h>
51 #include <math.h>
52 
57 #include <uORB/topics/esc_status.h>
58 
59 #include <lib/mixer/MixerGroup.hpp>
60 #include <lib/mixer/mixer_load.h>
61 #include <battery/battery.h>
62 
63 #include <bebop_bus/BebopBus.hpp>
64 #include <DevMgr.hpp>
65 
66 extern "C" { __EXPORT int df_bebop_bus_wrapper_main(int argc, char *argv[]); }
67 
68 using namespace DriverFramework;
69 
70 class DfBebopBusWrapper : public BebopBus
71 {
72 public:
74  ~DfBebopBusWrapper() = default;
75 
76  /// Start and initialize the driver
77  int start();
78 
79  /// Stop the driver
80  int stop();
81 
82  /// Print various infos (version, type, flights, errors
83  int print_info();
84 
85  /// Start the motors
86  int start_motors();
87 
88  /// Stop the motors
89  int stop_motors();
90 
91  /// Reset pending errors on the Bebop hardware
92  int clear_errors();
93 
94  /// Set the ESC speeds [front left, front right, back right, back left]
95  int set_esc_speeds(const float speed_scaled[4]);
96 
97  /// Capture the last throttle value for the battey computation
98  void set_last_throttle(float throttle) {_last_throttle = throttle;};
99 
100 private:
101  orb_advert_t _battery_topic;
103 
105  bool _armed;
107 
109 
110  // map for bebop motor index to PX4 motor index
111  const uint8_t _esc_map[4] = {0, 2, 3, 1};
112 
113  int _publish(struct bebop_state_data &data);
114 };
115 
117  BebopBus(BEBOP_BUS_DEVICE_PATH), _battery_topic(nullptr), _esc_topic(nullptr), _battery(), _armed(false),
118  _last_throttle(0.0f),
119  _battery_orb_class_instance(-1)
120 {}
121 
123 {
124  /* Init device and start sensor. */
125  int ret = init();
126 
127  if (ret != 0) {
128  PX4_ERR("BebopBus init fail: %d", ret);
129  return ret;
130  }
131 
132  ret = BebopBus::start();
133 
134  if (ret < 0) {
135  PX4_ERR("BebopBus start fail: %d", ret);
136  return ret;
137  }
138 
139  // Signal bus start on Bebop
140  BebopBus::_play_sound(BebopBus::BOOT);
141 
142  return 0;
143 }
144 
146 {
147 
148  int ret = BebopBus::stop();
149 
150  if (ret < 0) {
151  PX4_ERR("BebopBus stop fail: %d", ret);
152  return ret;
153  }
154 
155  return 0;
156 }
157 
159 {
160  bebop_bus_info info;
161  int ret = _get_info(&info);
162 
163  if (ret < 0) {
164  return -1;
165  }
166 
167  PX4_INFO("Bebop Controller Info");
168  PX4_INFO(" Software Version: %d.%d", info.version_major, info.version_minor);
169  PX4_INFO(" Software Type: %d", info.type);
170  PX4_INFO(" Number of controlled motors: %d", info.n_motors_controlled);
171  PX4_INFO(" Number of flights: %d", info.n_flights);
172  PX4_INFO(" Last flight time: %d", info.last_flight_time);
173  PX4_INFO(" Total flight time: %d", info.total_flight_time);
174  PX4_INFO(" Last Error: %d\n", info.last_error);
175 
176  return 0;
177 }
178 
180 {
181  _armed = true;
182  return BebopBus::_start_motors();
183 }
184 
186 {
187  _armed = false;
188  return BebopBus::_stop_motors();
189 }
190 
192 {
193  return BebopBus::_clear_errors();
194 }
195 
196 int DfBebopBusWrapper::set_esc_speeds(const float speed_scaled[4])
197 {
198  return BebopBus::_set_esc_speed(speed_scaled);
199 }
200 
201 int DfBebopBusWrapper::_publish(struct bebop_state_data &data)
202 {
203 
204  battery_status_s battery_report;
205  const hrt_abstime timestamp = hrt_absolute_time();
206 
207  // TODO Check if this is the right way for the Bebop
208  // We don't have current measurements
209  _battery.updateBatteryStatus(timestamp, data.battery_voltage_v, 0.0, true, true, 0, _last_throttle, _armed,
210  &battery_report);
211 
212  esc_status_s esc_status = {};
213 
214  uint16_t esc_speed_setpoint_rpm[4] = {};
215  BebopBus::_get_esc_speed_setpoint(esc_speed_setpoint_rpm);
216  esc_status.timestamp = hrt_absolute_time();
217  esc_status.esc_count = 4;
218 
219  for (int i = 0; i < 4; i++) {
220  esc_status.esc[_esc_map[i]].timestamp = esc_status.timestamp;
221  esc_status.esc[_esc_map[i]].esc_rpm = data.rpm[i];
222  }
223 
224  // TODO: when is this ever blocked?
225  if (!(m_pub_blocked)) {
226 
227  if (_battery_topic == nullptr) {
230 
231  } else {
232  orb_publish(ORB_ID(battery_status), _battery_topic, &battery_report);
233  }
234 
235  if (_esc_topic == nullptr) {
236  _esc_topic = orb_advertise(ORB_ID(esc_status), &esc_status);
237 
238  } else {
239  orb_publish(ORB_ID(esc_status), _esc_topic, &esc_status);
240  }
241 
242  }
243 
244  return 0;
245 }
246 
248 {
249 
250 DfBebopBusWrapper *g_dev = nullptr; // interface to the Bebop's I2C device
251 volatile bool _task_should_exit = false; // flag indicating if bebop esc control task should exit
252 static bool _is_running = false; // flag indicating if bebop esc app is running
253 static bool _motors_running = false; // flag indicating if the motors are running
254 static px4_task_t _task_handle = -1; // handle to the task main thread
255 
256 static const char *MIXER_FILENAME = "/home/root/bebop.main.mix";
257 
258 // subscriptions
261 
262 // publications
265 
266 // topic structures
270 
271 MixerGroup *_mixers = nullptr;
272 
273 int start();
274 int stop();
275 int info();
276 int clear_errors();
277 void usage();
278 void task_main(int argc, char *argv[]);
279 
280 /* mixers initialization */
281 int initialize_mixers(const char *mixers_filename);
282 int mixers_control_callback(uintptr_t handle, uint8_t control_group,
283  uint8_t control_index, float &input);
284 
285 int mixers_control_callback(uintptr_t handle,
286  uint8_t control_group,
287  uint8_t control_index,
288  float &input)
289 {
290  const actuator_controls_s *controls = (actuator_controls_s *)handle;
291 
292  input = controls[control_group].control[control_index];
293 
294  return 0;
295 }
296 
297 int initialize_mixers(const char *mixers_filename)
298 {
299  char buf[2048] = {0};
300  size_t buflen = sizeof(buf);
301 
302  PX4_INFO("Trying to initialize mixers from config file %s", mixers_filename);
303 
304  if (load_mixer_file(mixers_filename, &buf[0], sizeof(buf)) < 0) {
305  PX4_ERR("can't load mixer: %s", mixers_filename);
306  return -1;
307  }
308 
309  if (_mixers == nullptr) {
310  _mixers = new MixerGroup();
311  }
312 
313  if (_mixers == nullptr) {
314  PX4_ERR("No mixers available");
315  return -1;
316 
317  } else {
318  int ret = _mixers->load_from_buf(mixers_control_callback, (uintptr_t)_controls, buf, buflen);
319 
320  if (ret != 0) {
321  PX4_ERR("Unable to parse mixers file");
322  delete _mixers;
323  _mixers = nullptr;
324  ret = -1;
325  }
326 
327  return 0;
328  }
329 }
330 
331 void task_main(int argc, char *argv[])
332 {
333  _is_running = true;
334 
335  // Subscribe for orb topics
336  _controls_sub = orb_subscribe(ORB_ID(actuator_controls_0));
337  _armed_sub = orb_subscribe(ORB_ID(actuator_armed));
338 
339  // Start disarmed
340  _armed.armed = false;
341  _armed.prearmed = false;
342  _motors_running = false;
343 
344  // Set up poll topic
345  px4_pollfd_struct_t fds[1];
346  fds[0].fd = _controls_sub;
347  fds[0].events = POLLIN;
348  /* Don't limit poll intervall for now, 250 Hz should be fine. */
349  //orb_set_interval(_controls_sub, 10);
350 
351  // Set up mixers
352  if (initialize_mixers(MIXER_FILENAME) < 0) {
353  PX4_ERR("Mixer initialization failed.");
354  return;
355  }
356 
357  // Main loop
358  while (!_task_should_exit) {
359 
360  int pret = px4_poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 10);
361 
362  /* Timed out, do a periodic check for _task_should_exit. */
363  if (pret == 0) {
364  continue;
365  }
366 
367  /* This is undesirable but not much we can do. */
368  if (pret < 0) {
369  PX4_WARN("poll error %d, %d", pret, errno);
370  /* sleep a bit before next try */
371  usleep(100000);
372  continue;
373  }
374 
375  if (fds[0].revents & POLLIN) {
376  orb_copy(ORB_ID(actuator_controls_0), _controls_sub, &_controls[0]);
377 
378  _outputs.timestamp = _controls[0].timestamp;
379 
380 
381  if (_mixers != nullptr) {
382  /* do mixing */
383  _outputs.noutputs = _mixers->mix(_outputs.output, 4);
384  }
385 
386  // Set last throttle for battery calculations
387  g_dev->set_last_throttle(_controls[0].control[3]);
388 
389  /* disable unused ports by setting their output to NaN */
390  for (size_t i = _outputs.noutputs; i < sizeof(_outputs.output) / sizeof(_outputs.output[0]);
391  i++) {
392  _outputs.output[i] = NAN;
393  }
394 
395  // Check if the outputs are in range and rescale
396  for (size_t i = 0; i < _outputs.noutputs; ++i) {
397  if (i < _outputs.noutputs &&
398  PX4_ISFINITE(_outputs.output[i]) &&
399  _outputs.output[i] >= -1.0f &&
400  _outputs.output[i] <= 1.0f) {
401  /* scale for Bebop output 0.0 - 1.0 */
402  _outputs.output[i] = (_outputs.output[i] + 1.0f) / 2.0f;
403 
404  } else {
405  /*
406  * Value is NaN, INF or out of band - set to the minimum value.
407  * This will be clearly visible on the servo status and will limit the risk of accidentally
408  * spinning motors. It would be deadly in flight.
409  */
410  _outputs.output[i] = 0.0;
411  }
412  }
413 
414  // Adjust order of BLDCs from PX4 to Bebop
415  float motor_out[4];
416  motor_out[0] = _outputs.output[2];
417  motor_out[1] = _outputs.output[0];
418  motor_out[2] = _outputs.output[3];
419  motor_out[3] = _outputs.output[1];
420 
421  g_dev->set_esc_speeds(motor_out);
422 
423  if (_outputs_pub != nullptr) {
424  orb_publish(ORB_ID(actuator_outputs), _outputs_pub, &_outputs);
425 
426  } else {
427  _outputs_pub = orb_advertise(ORB_ID(actuator_outputs), &_outputs);
428  }
429  }
430 
431  bool updated;
432  orb_check(_armed_sub, &updated);
433 
434  if (updated) {
435  orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
436  }
437 
438  const bool lockdown = _armed.manual_lockdown || _armed.lockdown || _armed.force_failsafe;
439 
440  // Start the motors if armed but not alreay running
441  if (_armed.armed && !lockdown && !_motors_running) {
442  g_dev->start_motors();
443  _motors_running = true;
444  }
445 
446  // Stop motors if not armed or killed, but running
447  if ((!_armed.armed || lockdown) && _motors_running) {
448  g_dev->stop_motors();
449  _motors_running = false;
450  }
451  }
452 
453  orb_unsubscribe(_controls_sub);
454  orb_unsubscribe(_armed_sub);
455 
456  _is_running = false;
457 
458 }
459 
460 int start()
461 {
462  g_dev = new DfBebopBusWrapper();
463 
464  if (g_dev == nullptr) {
465  PX4_ERR("failed instantiating DfBebopBusWrapper object");
466  return -1;
467  }
468 
469  int ret = g_dev->start();
470 
471  if (ret != 0) {
472  PX4_ERR("DfBebopBusWrapper start failed");
473  return ret;
474  }
475 
476  // Open the Bebop dirver
477  DevHandle h;
478  DevMgr::getHandle(BEBOP_BUS_DEVICE_PATH, h);
479 
480  if (!h.isValid()) {
481  DF_LOG_INFO("Error: unable to obtain a valid handle for the receiver at: %s (%d)",
482  BEBOP_BUS_DEVICE_PATH, h.getError());
483  return -1;
484  }
485 
486  DevMgr::releaseHandle(h);
487 
488  // Start the task to forward the motor control commands
489  _task_handle = px4_task_spawn_cmd("bebop_bus_esc_main",
490  SCHED_DEFAULT,
491  SCHED_PRIORITY_MAX,
492  2000,
493  (px4_main_t)&task_main,
494  nullptr);
495 
496  if (_task_handle < 0) {
497  PX4_ERR("task start failed");
498  return -1;
499  }
500 
501  _is_running = true;
502  return 0;
503 }
504 
505 int stop()
506 {
507  // Stop bebop motor control task
508  _task_should_exit = true;
509 
510  while (_is_running) {
511  usleep(200000);
512  PX4_INFO(".");
513  }
514 
515  _task_handle = -1;
516 
517  if (g_dev == nullptr) {
518  PX4_ERR("driver not running");
519  return 1;
520  }
521 
522  // Stop DF device
523  int ret = g_dev->stop();
524 
525  if (ret != 0) {
526  PX4_ERR("driver could not be stopped");
527  return ret;
528  }
529 
530  delete g_dev;
531  g_dev = nullptr;
532  return 0;
533 }
534 
535 /**
536  * Print a little info about the driver.
537  */
538 int
540 {
541  if (g_dev == nullptr) {
542  PX4_ERR("driver not running");
543  return 1;
544  }
545 
546  PX4_DEBUG("state @ %p", g_dev);
547 
548  int ret = g_dev->print_info();
549 
550  if (ret != 0) {
551  PX4_ERR("Unable to print info");
552  return ret;
553  }
554 
555  return 0;
556 }
557 
558 /**
559  * Clear errors present on the Bebop hardware
560  */
561 int
563 {
564  if (g_dev == nullptr) {
565  PX4_ERR("driver not running");
566  return 1;
567  }
568 
569  PX4_DEBUG("state @ %p", g_dev);
570 
571  int ret = g_dev->clear_errors();
572 
573  if (ret != 0) {
574  PX4_ERR("Unable to clear errors");
575  return ret;
576  }
577 
578  return 0;
579 }
580 
581 void
583 {
584  PX4_INFO("Usage: df_bebop_bus_wrapper 'start', 'info', 'clear_errors', 'stop'");
585 }
586 
587 } /* df_bebop_bus_wrapper */
588 
589 
590 int
591 df_bebop_bus_wrapper_main(int argc, char *argv[])
592 {
593  int ret = 0;
594  int myoptind = 1;
595 
596  if (argc <= 1) {
598  return 1;
599  }
600 
601  const char *verb = argv[myoptind];
602 
603 
604  if (!strcmp(verb, "start")) {
606  }
607 
608  else if (!strcmp(verb, "stop")) {
610  }
611 
612  else if (!strcmp(verb, "info")) {
614  }
615 
616  else if (!strcmp(verb, "clear_errors")) {
618  }
619 
620  else {
622  return 1;
623  }
624 
625  return ret;
626 }
int load_from_buf(Mixer::ControlCallback control_cb, uintptr_t cb_handle, const char *buf, unsigned &buflen)
Adds mixers to the group based on a text description in a buffer.
Definition: MixerGroup.cpp:173
int start()
Attempt to start driver on all available I2C busses.
int set_esc_speeds(const float speed_scaled[4])
Set the ESC speeds [front left, front right, back right, back left].
struct esc_report_s esc[8]
Definition: esc_status.h:67
int initialize_mixers(const char *mixers_filename)
static const char * MIXER_FILENAME
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
Definition: uORB.cpp:90
DfBebopBusWrapper * g_dev
void usage()
Prints info about the driver argument usage.
int info()
Print a little info about the driver.
actuator_controls_s _controls[1]
int stop()
Stop the driver.
int clear_errors()
Clear errors present on the Bebop hardware.
void usage(const char *reason)
Print the correct usage.
Definition: Commander.cpp:4238
int clear_errors()
Reset pending errors on the Bebop hardware.
Definition: I2C.hpp:51
int start()
Start and initialize the driver.
static px4_task_t _task_handle
int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
Definition: uORB.cpp:43
uint64_t timestamp
Definition: esc_status.h:61
__EXPORT int df_bebop_bus_wrapper_main(int argc, char *argv[])
static void stop()
Definition: dataman.cpp:1491
int32_t esc_rpm
Definition: esc_report.h:55
Library calls for battery functionality.
int start_motors()
Start the motors.
#define BOOT
Definition: LPS22HB.hpp:57
int _publish(struct bebop_state_data &data)
int orb_subscribe(const struct orb_metadata *meta)
Definition: uORB.cpp:75
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
uint64_t timestamp
Definition: esc_report.h:53
void init()
Activates/configures the hardware registers.
uint8_t * data
Definition: dataman.cpp:149
int orb_unsubscribe(int handle)
Definition: uORB.cpp:85
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
void set_last_throttle(float throttle)
Capture the last throttle value for the battey computation.
int print_info()
Print various infos (version, type, flights, errors.
int stop_motors()
Stop the motors.
void task_main(int argc, char *argv[])
int load_mixer_file(const char *fname, char *buf, unsigned maxlen)
int stop()
Stop the driver.
__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
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
static int start()
Definition: dataman.cpp:1452
uint8_t esc_count
Definition: esc_status.h:63
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
const uint8_t _esc_map[4]
int orb_check(int handle, bool *updated)
Definition: uORB.cpp:95
actuator_outputs_s _outputs
unsigned mix(float *outputs, unsigned space)
Definition: MixerGroup.cpp:53
int mixers_control_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input)
Group of mixers, built up from single mixers and processed in order when mixing.
Definition: MixerGroup.hpp:42
orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority)
Definition: uORB.cpp:53
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).