PX4 Firmware
PX4 Autopilot Software http://px4.io
tap_esc.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 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 #include <stdint.h>
35 
36 #include <px4_platform_common/defines.h>
37 #include <px4_platform_common/module.h>
38 #include <px4_platform_common/tasks.h>
39 #include <px4_platform_common/getopt.h>
40 #include <px4_platform_common/posix.h>
41 #include <errno.h>
42 
43 #include <math.h> // NAN
44 #include <cstring>
45 
46 #include <lib/mathlib/mathlib.h>
47 #include <lib/cdev/CDev.hpp>
48 #include <perf/perf_counter.h>
49 #include <px4_platform_common/module_params.h>
50 #include <uORB/Subscription.hpp>
54 #include <uORB/topics/test_motor.h>
55 #include <uORB/topics/input_rc.h>
56 #include <uORB/topics/esc_status.h>
59 
60 #include <drivers/drv_hrt.h>
61 #include <drivers/drv_mixer.h>
62 #include <lib/mixer/MixerGroup.hpp>
63 
64 #include "tap_esc_common.h"
65 
66 #include "drv_tap_esc.h"
67 
68 #if !defined(BOARD_TAP_ESC_MODE)
69 # define BOARD_TAP_ESC_MODE 0
70 #endif
71 
72 #if !defined(DEVICE_ARGUMENT_MAX_LENGTH)
73 # define DEVICE_ARGUMENT_MAX_LENGTH 32
74 #endif
75 
76 // uorb update rate for control groups in miliseconds
77 #if !defined(TAP_ESC_CTRL_UORB_UPDATE_INTERVAL)
78 # define TAP_ESC_CTRL_UORB_UPDATE_INTERVAL 2 // [ms] min: 2, max: 100
79 #endif
80 
81 /*
82  * This driver connects to TAP ESCs via serial.
83  */
84 class TAP_ESC : public cdev::CDev, public ModuleBase<TAP_ESC>, public ModuleParams
85 {
86 public:
87  TAP_ESC(char const *const device, uint8_t channels_count);
88  virtual ~TAP_ESC();
89 
90  /** @see ModuleBase */
91  static int task_spawn(int argc, char *argv[]);
92 
93  /** @see ModuleBase */
94  static TAP_ESC *instantiate(int argc, char *argv[]);
95 
96  /** @see ModuleBase */
97  static int custom_command(int argc, char *argv[]);
98 
99  /** @see ModuleBase */
100  static int print_usage(const char *reason = nullptr);
101 
102  /** @see ModuleBase::run() */
103  void run() override;
104 
105  virtual int init();
106  virtual int ioctl(cdev::file_t *filp, int cmd, unsigned long arg);
107  void cycle();
108 
109 private:
111  int _uart_fd = -1;
112  static const uint8_t _device_mux_map[TAP_ESC_MAX_MOTOR_NUM];
113  static const uint8_t _device_dir_map[TAP_ESC_MAX_MOTOR_NUM];
114  bool _is_armed = false;
115  int _armed_sub = -1;
116  int _test_motor_sub = -1;
117 
119 
123 
125 
126  int _control_subs[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS];
127  actuator_controls_s _controls[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS];
128  orb_id_t _control_topics[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS];
129  px4_pollfd_struct_t _poll_fds[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS];
130  unsigned _poll_fds_num = 0;
131 
133  orb_advert_t _to_mixer_status = nullptr; ///< mixer status flags
135  uint8_t _channels_count = 0; ///< nnumber of ESC channels
136  uint8_t _responding_esc = 0;
137 
138  MixerGroup *_mixers = nullptr;
139  uint32_t _groups_required = 0;
140  uint32_t _groups_subscribed = 0;
143 
145  (ParamInt<px4::params::MC_AIRMODE>) _param_mc_airmode ///< multicopter air-mode
146  )
147 
148  void subscribe();
149  void send_esc_outputs(const uint16_t *pwm, const uint8_t motor_cnt);
150  static int control_callback_trampoline(uintptr_t handle,
151  uint8_t control_group, uint8_t control_index, float &input);
152  inline int control_callback(uint8_t control_group, uint8_t control_index, float &input);
153 };
154 
157 
158 TAP_ESC::TAP_ESC(char const *const device, uint8_t channels_count):
160  ModuleParams(nullptr),
161  _perf_control_latency(perf_alloc(PC_ELAPSED, "tap_esc control latency")),
162  _channels_count(channels_count)
163 {
164  strncpy(_device, device, sizeof(_device));
165  _device[sizeof(_device) - 1] = '\0'; // Fix in case of overflow
166 
167  _control_topics[0] = ORB_ID(actuator_controls_0);
168  _control_topics[1] = ORB_ID(actuator_controls_1);
169  _control_topics[2] = ORB_ID(actuator_controls_2);
170  _control_topics[3] = ORB_ID(actuator_controls_3);
171  memset(_controls, 0, sizeof(_controls));
172  memset(_poll_fds, 0, sizeof(_poll_fds));
173 
174  for (int i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; ++i) {
175  _control_subs[i] = -1;
176  }
177 
178  for (size_t i = 0; i < sizeof(_outputs.output) / sizeof(_outputs.output[0]); i++) {
179  _outputs.output[i] = NAN;
180  }
181 
182  _outputs.noutputs = 0;
183 }
184 
186 {
187  for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
188  if (_control_subs[i] >= 0) {
190  _control_subs[i] = -1;
191  }
192  }
193 
196 
200 
202 
203  PX4_INFO("stopping");
204 
206 }
207 
208 /** @see ModuleBase */
209 TAP_ESC *TAP_ESC::instantiate(int argc, char *argv[])
210 {
211  /* Parse arguments */
212  const char *device = nullptr;
213  uint8_t channels_count = 0;
214 
215  int ch;
216  int myoptind = 1;
217  const char *myoptarg = nullptr;
218 
219  if (argc < 2) {
220  print_usage("not enough arguments");
221  return nullptr;
222  }
223 
224  while ((ch = px4_getopt(argc, argv, "d:n:", &myoptind, &myoptarg)) != EOF) {
225  switch (ch) {
226  case 'd':
227  device = myoptarg;
228  break;
229 
230  case 'n':
231  channels_count = atoi(myoptarg);
232  break;
233  }
234  }
235 
236  /* Sanity check on arguments */
237  if (channels_count == 0) {
238  print_usage("Channel count is invalid (0)");
239  return nullptr;
240  }
241 
242  if (device == nullptr || strlen(device) == 0) {
243  print_usage("no device specified");
244  return nullptr;
245  }
246 
247  TAP_ESC *tap_esc = new TAP_ESC(device, channels_count);
248 
249  if (tap_esc == nullptr) {
250  PX4_ERR("failed to instantiate module");
251  return nullptr;
252  }
253 
254  if (tap_esc->init() != 0) {
255  PX4_ERR("failed to initialize module");
256  delete tap_esc;
257  return nullptr;
258  }
259 
260  return tap_esc;
261 }
262 
263 /** @see ModuleBase */
264 int TAP_ESC::custom_command(int argc, char *argv[])
265 {
266  return print_usage("unknown command");
267 }
268 
270 {
271  int ret;
272 
274 
275  if (ret != 0) {
276  PX4_ERR("failed to initialise UART.");
277  return ret;
278  }
279 
280  /* Respect boot time required by the ESC FW */
281  hrt_abstime uptime_us = hrt_absolute_time();
282 
283  if (uptime_us < MAX_BOOT_TIME_MS * 1000) {
284  usleep((MAX_BOOT_TIME_MS * 1000) - uptime_us);
285  }
286 
287  /* Issue Basic Config */
290  memset(&config, 0, sizeof(ConfigInfoBasicRequest));
291  config.maxChannelInUse = _channels_count;
292  /* Enable closed-loop control if supported by the board */
293  config.controlMode = BOARD_TAP_ESC_MODE;
294 
295  /* Asign the id's to the ESCs to match the mux */
296  for (uint8_t phy_chan_index = 0; phy_chan_index < _channels_count; phy_chan_index++) {
297  config.channelMapTable[phy_chan_index] = _device_mux_map[phy_chan_index] &
299  config.channelMapTable[phy_chan_index] |= (_device_dir_map[phy_chan_index] << 4) &
301  }
302 
303  config.maxChannelValue = RPMMAX;
304  config.minChannelValue = RPMMIN;
305 
306  ret = tap_esc_common::send_packet(_uart_fd, packet, 0);
307 
308  if (ret < 0) {
309  return ret;
310  }
311 
312  /* To Unlock the ESC from the Power up state we need to issue 10
313  * ESCBUS_MSG_ID_RUN request with all the values 0;
314  */
316  unlock_packet.len *= sizeof(unlock_packet.d.reqRun.rpm_flags[0]);
317  memset(unlock_packet.d.bytes, 0, sizeof(unlock_packet.d.bytes));
318 
319  int unlock_times = 10;
320 
321  while (unlock_times--) {
322 
323  tap_esc_common::send_packet(_uart_fd, unlock_packet, -1);
324 
325  /* Min Packet to Packet time is 1 Ms so use 2 */
326  usleep(2000);
327  }
328 
329  /* do regular cdev init */
330  ret = CDev::init();
331 
332  /* advertise the mixed control outputs, insist on the first group output */
333  _outputs_pub = orb_advertise(ORB_ID(actuator_outputs), &_outputs);
335  multirotor_motor_limits_s multirotor_motor_limits = {};
336  _to_mixer_status = orb_advertise(ORB_ID(multirotor_motor_limits), &multirotor_motor_limits);
337 
338  _armed_sub = orb_subscribe(ORB_ID(actuator_armed));
339  _test_motor_sub = orb_subscribe(ORB_ID(test_motor));
340 
341  return ret;
342 }
343 
344 void TAP_ESC::subscribe()
345 {
346  /* subscribe/unsubscribe to required actuator control groups */
347  uint32_t sub_groups = _groups_required & ~_groups_subscribed;
348  uint32_t unsub_groups = _groups_subscribed & ~_groups_required;
349  _poll_fds_num = 0;
350 
351  for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
352  if (sub_groups & (1 << i)) {
353  PX4_DEBUG("subscribe to actuator_controls_%d", i);
355  }
356 
357  if (unsub_groups & (1 << i)) {
358  PX4_DEBUG("unsubscribe from actuator_controls_%d", i);
360  _control_subs[i] = -1;
361  }
362 
363  if (_control_subs[i] >= 0) {
365  _poll_fds[_poll_fds_num].events = POLLIN;
366  _poll_fds_num++;
367  }
368  }
369 }
370 
371 void TAP_ESC::send_esc_outputs(const uint16_t *pwm, const uint8_t motor_cnt)
372 {
373  uint16_t rpm[TAP_ESC_MAX_MOTOR_NUM] = {};
374 
375  for (uint8_t i = 0; i < motor_cnt; i++) {
376  rpm[i] = pwm[i];
377 
378  if (rpm[i] > RPMMAX) {
379  rpm[i] = RPMMAX;
380 
381  } else if (rpm[i] < RPMSTOPPED) {
382  rpm[i] = RPMSTOPPED;
383  }
384  }
385 
387 
389  packet.len *= sizeof(packet.d.reqRun.rpm_flags[0]);
390 
391  for (uint8_t i = 0; i < _channels_count; i++) {
392  packet.d.reqRun.rpm_flags[i] = rpm[i];
393  }
394 
396 
397  if (++_responding_esc >= _channels_count) {
398  _responding_esc = 0;
399  }
400 
401  if (ret < 1) {
402  PX4_WARN("TX ERROR: ret: %d, errno: %d", ret, errno);
403  }
404 }
405 
407 {
409  subscribe();
411 
412  /* Set uorb update rate */
413  for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
414  if (_control_subs[i] >= 0) {
416  PX4_DEBUG("New actuator update interval: %ums", TAP_ESC_CTRL_UORB_UPDATE_INTERVAL);
417  }
418  }
419  }
420 
421  if (_mixers) {
422  _mixers->set_airmode((Mixer::Airmode)_param_mc_airmode.get());
423  }
424 
425  /* check if anything updated */
426  int ret = px4_poll(_poll_fds, _poll_fds_num, 5);
427 
428  /* this would be bad... */
429  if (ret < 0) {
430  PX4_ERR("poll error %d", errno);
431 
432  } else { /* update even in the case of a timeout, to check for test_motor commands */
433 
434  /* get controls for required topics */
435  unsigned poll_id = 0;
436 
437  for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
438  if (_control_subs[i] >= 0) {
439  if (_poll_fds[poll_id].revents & POLLIN) {
441 
442  }
443 
444  poll_id++;
445  }
446  }
447 
448  uint8_t num_outputs = _channels_count;
449 
450  /* can we mix? */
451  if (_is_armed && _mixers != nullptr) {
452 
453  /* do mixing */
454  num_outputs = _mixers->mix(&_outputs.output[0], num_outputs);
455  _outputs.noutputs = num_outputs;
456 
457  /* publish mixer status */
458  multirotor_motor_limits_s multirotor_motor_limits = {};
459  multirotor_motor_limits.saturation_status = _mixers->get_saturation_status();
460 
461  orb_publish(ORB_ID(multirotor_motor_limits), _to_mixer_status, &multirotor_motor_limits);
462 
463  /* disable unused ports by setting their output to NaN */
464  for (size_t i = num_outputs; i < sizeof(_outputs.output) / sizeof(_outputs.output[0]); i++) {
465  _outputs.output[i] = NAN;
466  }
467 
468  /* iterate actuators */
469  for (unsigned i = 0; i < num_outputs; i++) {
470  /* last resort: catch NaN, INF and out-of-band errors */
471  if (i < _outputs.noutputs && PX4_ISFINITE(_outputs.output[i])
473  /* scale for PWM output 1200 - 1900us */
474  _outputs.output[i] = (RPMMAX + RPMMIN) / 2 + ((RPMMAX - RPMMIN) / 2) * _outputs.output[i];
475  math::constrain(_outputs.output[i], (float)RPMMIN, (float)RPMMAX);
476 
477  } else {
478  /*
479  * Value is NaN, INF, or we are in lockdown - stop the motor.
480  * This will be clearly visible on the servo status and will limit the risk of accidentally
481  * spinning motors. It would be deadly in flight.
482  */
484  }
485  }
486 
487  } else {
488 
489  _outputs.noutputs = num_outputs;
491 
492  /* check for motor test commands */
493  bool test_motor_updated;
494  orb_check(_test_motor_sub, &test_motor_updated);
495 
496  if (test_motor_updated) {
497  struct test_motor_s test_motor;
498  orb_copy(ORB_ID(test_motor), _test_motor_sub, &test_motor);
499 
500  if (test_motor.action == test_motor_s::ACTION_STOP) {
501  _outputs.output[test_motor.motor_number] = RPMSTOPPED;
502 
503  } else {
504  _outputs.output[test_motor.motor_number] = RPMSTOPPED + ((RPMMAX - RPMSTOPPED) * test_motor.value);
505  }
506 
507  PX4_INFO("setting motor %i to %.1lf", test_motor.motor_number,
508  (double)_outputs.output[test_motor.motor_number]);
509  }
510 
511  /* set the invalid values to the minimum */
512  for (unsigned i = 0; i < num_outputs; i++) {
513  if (!PX4_ISFINITE(_outputs.output[i])) {
515  }
516  }
517 
518  /* disable unused ports by setting their output to NaN */
519  for (size_t i = num_outputs; i < sizeof(_outputs.output) / sizeof(_outputs.output[0]); i++) {
520  _outputs.output[i] = NAN;
521  }
522 
523  }
524 
525  uint16_t motor_out[TAP_ESC_MAX_MOTOR_NUM];
526 
527  // We need to remap from the system default to what PX4's normal
528  // scheme is
529  switch (num_outputs) {
530  case 4:
531  motor_out[0] = (uint16_t)_outputs.output[2];
532  motor_out[1] = (uint16_t)_outputs.output[1];
533  motor_out[2] = (uint16_t)_outputs.output[0];
534  motor_out[3] = (uint16_t)_outputs.output[3];
535  break;
536 
537  case 6:
538  motor_out[0] = (uint16_t)_outputs.output[3];
539  motor_out[1] = (uint16_t)_outputs.output[0];
540  motor_out[2] = (uint16_t)_outputs.output[4];
541  motor_out[3] = (uint16_t)_outputs.output[2];
542  motor_out[4] = (uint16_t)_outputs.output[1];
543  motor_out[5] = (uint16_t)_outputs.output[5];
544  break;
545 
546  default:
547 
548  // Use the system defaults
549  for (uint8_t i = 0; i < num_outputs; ++i) {
550  motor_out[i] = (uint16_t)_outputs.output[i];
551  }
552 
553  break;
554  }
555 
556  // Set remaining motors to RPMSTOPPED to be on the safe side
557  for (uint8_t i = num_outputs; i < TAP_ESC_MAX_MOTOR_NUM; ++i) {
558  motor_out[i] = RPMSTOPPED;
559  }
560 
562 
563  send_esc_outputs(motor_out, num_outputs);
565 
568  RunInfoRepsonse &feed_back_data = _packet.d.rspRunInfo;
569 
570  if (feed_back_data.channelID < esc_status_s::CONNECTED_ESC_MAX) {
571  _esc_feedback.esc[feed_back_data.channelID].esc_rpm = feed_back_data.speed;
572  _esc_feedback.esc[feed_back_data.channelID].esc_state = feed_back_data.ESCStatus;
573  _esc_feedback.esc_connectiontype = esc_status_s::ESC_CONNECTION_TYPE_SERIAL;
575  _esc_feedback.esc_count = num_outputs;
576 
578 
580  }
581  }
582  }
583 
584  /* and publish for anyone that cares to see */
585  orb_publish(ORB_ID(actuator_outputs), _outputs_pub, &_outputs);
586 
587  // use first valid timestamp_sample for latency tracking
588  for (int i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
589  const bool required = _groups_required & (1 << i);
590  const hrt_abstime &timestamp_sample = _controls[i].timestamp_sample;
591 
592  if (required && (timestamp_sample > 0)) {
594  break;
595  }
596  }
597 
598  }
599 
600  bool updated;
601 
602  orb_check(_armed_sub, &updated);
603 
604  if (updated) {
605  orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
606 
607  if (_is_armed != _armed.armed) {
608  /* reset all outputs */
609  for (size_t i = 0; i < sizeof(_outputs.output) / sizeof(_outputs.output[0]); i++) {
610  _outputs.output[i] = NAN;
611  }
612  }
613 
615 
616  }
617 
618  // check for parameter updates
620  // clear update
621  parameter_update_s pupdate;
622  _parameter_update_sub.copy(&pupdate);
623 
624  // update parameters from storage
625  updateParams();
626  }
627 }
628 
629 int TAP_ESC::control_callback_trampoline(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input)
630 {
631  TAP_ESC *obj = (TAP_ESC *)handle;
632  return obj->control_callback(control_group, control_index, input);
633 }
634 
635 int TAP_ESC::control_callback(uint8_t control_group, uint8_t control_index, float &input)
636 {
637  input = _controls[control_group].control[control_index];
638 
639  /* limit control input */
640  if (input > 1.0f) {
641  input = 1.0f;
642 
643  } else if (input < -1.0f) {
644  input = -1.0f;
645  }
646 
647  /* throttle not arming - mark throttle input as invalid */
648  if (_armed.prearmed && !_armed.armed) {
649  if ((control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE ||
650  control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE_ALTERNATE) &&
651  control_index == actuator_controls_s::INDEX_THROTTLE) {
652  /* set the throttle to an invalid value */
653  input = NAN;
654  }
655  }
656 
657  return 0;
658 }
659 
660 int TAP_ESC::ioctl(cdev::file_t *filp, int cmd, unsigned long arg)
661 {
662  int ret = OK;
663 
664  switch (cmd) {
665 
666  case MIXERIOCRESET:
667  if (_mixers != nullptr) {
668  delete _mixers;
669  _mixers = nullptr;
670  _groups_required = 0;
671  }
672 
673  break;
674 
675  case MIXERIOCLOADBUF: {
676  const char *buf = (const char *)arg;
677  unsigned buflen = strlen(buf);
678 
679  if (_mixers == nullptr) {
680  _mixers = new MixerGroup();
681  }
682 
683  if (_mixers == nullptr) {
684  _groups_required = 0;
685  ret = -ENOMEM;
686 
687  } else {
688  ret = _mixers->load_from_buf(control_callback_trampoline, (uintptr_t)this, buf, buflen);
689 
690  if (ret != 0) {
691  PX4_DEBUG("mixer load failed with %d", ret);
692  delete _mixers;
693  _mixers = nullptr;
694  _groups_required = 0;
695  ret = -EINVAL;
696 
697  } else {
698 
700  }
701  }
702 
703  break;
704  }
705 
706  default:
707  ret = -ENOTTY;
708  break;
709  }
710 
711  return ret;
712 }
713 
714 /** @see ModuleBase */
716 {
717  // Main loop
718  while (!should_exit()) {
719  cycle();
720  }
721 }
722 
723 /** @see ModuleBase */
724 int TAP_ESC::task_spawn(int argc, char *argv[])
725 {
726  /* start the task */
727  _task_id = px4_task_spawn_cmd("tap_esc",
728  SCHED_DEFAULT,
729  SCHED_PRIORITY_ACTUATOR_OUTPUTS,
730  1180,
731  (px4_main_t)&run_trampoline,
732  argv);
733 
734  if (_task_id < 0) {
735  PX4_ERR("task start failed");
736  _task_id = -1;
737  return PX4_ERROR;
738  }
739 
740  // wait until task is up & running
741  if (wait_until_running() < 0) {
742  _task_id = -1;
743  return -1;
744  }
745 
746  return PX4_OK;
747 }
748 
749 /** @see ModuleBase */
750 int TAP_ESC::print_usage(const char *reason)
751 {
752  if (reason) {
753  PX4_WARN("%s\n", reason);
754  }
755 
756  PRINT_MODULE_DESCRIPTION(
757  R"DESCR_STR(
758 ### Description
759 This module controls the TAP_ESC hardware via UART. It listens on the
760 actuator_controls topics, does the mixing and writes the PWM outputs.
761 
762 ### Implementation
763 Currently the module is implementd as a threaded version only, meaning that it
764 runs in its own thread instead of on the work queue.
765 
766 ### Example
767 The module is typically started with:
768 tap_esc start -d /dev/ttyS2 -n <1-8>
769 
770 )DESCR_STR");
771 
772  PRINT_MODULE_USAGE_NAME("tap_esc", "driver");
773 
774  PRINT_MODULE_USAGE_COMMAND_DESCR("start", "Start the task");
775  PRINT_MODULE_USAGE_PARAM_STRING('d', nullptr, "<device>", "Device used to talk to ESCs", true);
776  PRINT_MODULE_USAGE_PARAM_INT('n', 4, 0, 8, "Number of ESCs", true);
777  return PX4_OK;
778 }
779 
780 extern "C" __EXPORT int tap_esc_main(int argc, char *argv[])
781 {
782  return TAP_ESC::main(argc, argv);
783 }
EscPacket _packet
Definition: tap_esc.cpp:142
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
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
uint8_t action
Definition: test_motor.h:61
uint32_t _groups_required
Definition: tap_esc.cpp:139
virtual ~TAP_ESC()
Definition: tap_esc.cpp:185
struct esc_report_s esc[8]
Definition: esc_status.h:67
void send_esc_outputs(const uint16_t *pwm, const uint8_t motor_cnt)
Definition: tap_esc.cpp:371
uint32_t _groups_subscribed
Definition: tap_esc.cpp:140
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
Definition: uORB.cpp:90
#define MIXERIOCLOADBUF
Add mixer(s) from the buffer in (const char *)arg.
Definition: drv_mixer.h:79
#define TAP_ESC_CTRL_UORB_UPDATE_INTERVAL
Definition: tap_esc.cpp:78
uint16_t get_saturation_status()
Definition: MixerGroup.cpp:153
#define RPMMAX
Definition: drv_tap_esc.h:71
measure the time elapsed performing an event
Definition: perf_counter.h:56
perf_counter_t _perf_control_latency
Definition: tap_esc.cpp:124
#define PACKET_HEAD
Definition: drv_tap_esc.h:48
union EscPacket::@22 d
void run() override
Definition: tap_esc.cpp:715
int _test_motor_sub
Definition: tap_esc.cpp:116
int send_packet(int uart_fd, EscPacket &packet, int responder)
Sends a packet to all ESCs and requests a specific ESC to respond.
int main(int argc, char **argv)
Definition: main.cpp:3
RunInfoRepsonse rspRunInfo
Definition: drv_tap_esc.h:156
int read_data_from_uart(int uart_fd, ESC_UART_BUF *const uart_buf)
Read data from the UART into a buffer.
DEFINE_PARAMETERS((ParamInt< px4::params::MC_AIRMODE >) _param_mc_airmode) void subscribe()
int orb_set_interval(int handle, unsigned interval)
Definition: uORB.cpp:126
uint8_t esc_connectiontype
Definition: esc_status.h:64
Definition: I2C.hpp:51
int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
uint8_t ESCStatus
Definition: drv_tap_esc.h:97
static int print_usage(const char *reason=nullptr)
Definition: tap_esc.cpp:750
void perf_set_elapsed(perf_counter_t handle, int64_t elapsed)
Register a measurement.
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
int deinitialise_uart(int &uart_fd)
Closes a device previously opened with initialise_uart().
int32_t esc_rpm
Definition: esc_report.h:55
virtual int ioctl(cdev::file_t *filp, int cmd, unsigned long arg)
Perform an ioctl operation on the device.
Definition: tap_esc.cpp:660
Namespace encapsulating all device framework classes, functions and data.
Definition: CDev.cpp:47
uint32_t motor_number
Definition: test_motor.h:58
High-resolution timer with callouts and timekeeping.
int control_callback(uint8_t control_group, uint8_t control_index, float &input)
Definition: tap_esc.cpp:635
#define ESC_DIR
Definition: drv_tap_esc.h:69
Mixer ioctl interfaces.
#define RPMMIN
Definition: drv_tap_esc.h:72
int orb_subscribe(const struct orb_metadata *meta)
Definition: uORB.cpp:75
int _uart_fd
Definition: tap_esc.cpp:111
static int control_callback_trampoline(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input)
Definition: tap_esc.cpp:629
#define DEVICE_ARGUMENT_MAX_LENGTH
Definition: tap_esc.cpp:73
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
char _device[DEVICE_ARGUMENT_MAX_LENGTH]
Definition: tap_esc.cpp:110
Abstract class for any character device.
Definition: CDev.hpp:58
bool _is_armed
Definition: tap_esc.cpp:114
Header common to all counters.
void perf_free(perf_counter_t handle)
Free a counter.
#define ESC_MASK_MAP_CHANNEL
Definition: drv_tap_esc.h:126
orb_advert_t _esc_feedback_pub
Definition: tap_esc.cpp:132
void init()
Activates/configures the hardware registers.
static TAP_ESC * instantiate(int argc, char *argv[])
Definition: tap_esc.cpp:209
MixerGroup * _mixers
Definition: tap_esc.cpp:138
uint8_t esc_state
Definition: esc_report.h:60
#define perf_alloc(a, b)
Definition: px4io.h:59
RunReq reqRun
Definition: drv_tap_esc.h:154
actuator_controls_s _controls[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS]
Definition: tap_esc.cpp:127
actuator_outputs_s _outputs
Definition: tap_esc.cpp:121
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
orb_advert_t _to_mixer_status
mixer status flags
Definition: tap_esc.cpp:133
int parse_tap_esc_feedback(ESC_UART_BUF *const serial_buf, EscPacket *const packetdata)
Parse feedback from an ESC.
__EXPORT int tap_esc_main(int argc, char *argv[])
Definition: tap_esc.cpp:780
uint8_t msg_id
Definition: drv_tap_esc.h:150
void cycle()
Definition: tap_esc.cpp:406
px4_pollfd_struct_t _poll_fds[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS]
Definition: tap_esc.cpp:129
uint8_t len
Definition: drv_tap_esc.h:149
static const uint8_t _device_dir_map[TAP_ESC_MAX_MOTOR_NUM]
Definition: tap_esc.cpp:113
uORB::Subscription _parameter_update_sub
Definition: tap_esc.cpp:118
unsigned _poll_fds_num
Definition: tap_esc.cpp:130
Airmode
Definition: Mixer.hpp:139
bool updated()
Check if there is a new update.
uint16_t counter
Definition: esc_status.h:62
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
static int task_spawn(int argc, char *argv[])
Definition: tap_esc.cpp:724
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
Definition: uORB.h:134
ESC_UART_BUF _uartbuf
Definition: tap_esc.cpp:141
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
uint8_t _channels_count
nnumber of ESC channels
Definition: tap_esc.cpp:135
static void subscribe()
#define RUN_FEEDBACK_ENABLE_MASK
Definition: drv_tap_esc.h:87
#define MIXERIOCRESET
reset (clear) the mixer configuration
Definition: drv_mixer.h:71
uint8_t esc_count
Definition: esc_status.h:63
uint8_t bytes[100]
Definition: drv_tap_esc.h:157
void groups_required(uint32_t &groups)
Definition: MixerGroup.cpp:165
ConfigInfoBasicRequest reqConfigInfoBasic
Definition: drv_tap_esc.h:153
#define TAP_ESC_DEVICE_PATH
Definition: drv_tap_esc.h:40
actuator_armed_s _armed
Definition: tap_esc.cpp:122
uint8_t channelID
Definition: drv_tap_esc.h:96
int _control_subs[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS]
Definition: tap_esc.cpp:126
Object metadata.
Definition: uORB.h:50
void set_airmode(Mixer::Airmode airmode)
Definition: MixerGroup.cpp:131
int _armed_sub
Definition: tap_esc.cpp:115
static const uint8_t _device_mux_map[TAP_ESC_MAX_MOTOR_NUM]
Definition: tap_esc.cpp:112
int initialise_uart(const char *const device, int &uart_fd)
Opens a device for use as UART.
#define RPMSTOPPED
Definition: drv_tap_esc.h:73
#define TAP_ESC_MAX_MOTOR_NUM
Definition: drv_tap_esc.h:46
int orb_check(int handle, bool *updated)
Definition: uORB.cpp:95
#define RUN_BLUE_LED_ON_MASK
Definition: drv_tap_esc.h:85
float value
Definition: test_motor.h:59
int orb_unadvertise(orb_advert_t handle)
Definition: uORB.cpp:65
CDev(const char *devname)
Constructor.
Definition: CDev.cpp:50
unsigned mix(float *outputs, unsigned space)
Definition: MixerGroup.cpp:53
#define ESC_MASK_MAP_RUNNING_DIRECTION
Definition: drv_tap_esc.h:127
orb_advert_t _outputs_pub
Definition: tap_esc.cpp:120
TAP_ESC(char const *const device, uint8_t channels_count)
Definition: tap_esc.cpp:158
static int custom_command(int argc, char *argv[])
Definition: tap_esc.cpp:264
uint8_t _responding_esc
Definition: tap_esc.cpp:136
#define OK
Definition: uavcan_main.cpp:71
Group of mixers, built up from single mixers and processed in order when mixing.
Definition: MixerGroup.hpp:42
uint16_t rpm_flags[TAP_ESC_MAX_MOTOR_NUM]
Definition: drv_tap_esc.h:92
bool copy(void *dst)
Copy the struct.
#define ESC_POS
Definition: drv_tap_esc.h:67
esc_status_s _esc_feedback
Definition: tap_esc.cpp:134
orb_id_t _control_topics[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS]
Definition: tap_esc.cpp:128
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
#define BOARD_TAP_ESC_MODE
Definition: tap_esc.cpp:69
virtual int init()
Definition: tap_esc.cpp:269
#define MAX_BOOT_TIME_MS
Definition: drv_tap_esc.h:76
Performance measuring tools.