PX4 Firmware
PX4 Autopilot Software http://px4.io
uart_esc_main.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 *
3 * Copyright (c) 2015 Mark Charlebois. 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/tasks.h>
37 #include <px4_platform_common/getopt.h>
38 #include <px4_platform_common/posix.h>
39 #include <errno.h>
40 
41 #include <uORB/uORB.h>
46 
47 #include <drivers/drv_hrt.h>
48 #include <drivers/drv_mixer.h>
49 #include <lib/mixer/MixerGroup.hpp>
50 #include <lib/mixer/mixer_multirotor_normalized.generated.h>
51 #include <parameters/param.h>
52 
53 #ifdef __cplusplus
54 extern "C" {
55 #endif
56 #include <uart_esc.h>
57 #ifdef __cplusplus
58 }
59 #endif
60 
61 /** driver 'main' command */
62 extern "C" { __EXPORT int uart_esc_main(int argc, char *argv[]); }
63 
64 #define MAX_LEN_DEV_PATH 32
65 
66 namespace uart_esc
67 {
68 #define UART_ESC_MAX_MOTORS 4
69 
70 volatile bool _task_should_exit = false; // flag indicating if uart_esc task should exit
72 static bool _is_running = false; // flag indicating if uart_esc app is running
73 static px4_task_t _task_handle = -1; // handle to the task main thread
74 UartEsc *esc; // esc instance
75 void uart_esc_rotate_motors(int16_t *motor_rpm, int num_rotors); // motor re-mapping
76 
77 // subscriptions
81 
82 // filenames
83 // /dev/fs/ is mapped to /usr/share/data/adsp/
84 static const char *MIXER_FILENAME = "/dev/fs/mixer_config.mix";
85 
86 
87 // publications
89 
90 // topic structures
95 
96 /** Print out the usage information */
97 static void usage();
98 
99 /** uart_esc start */
100 static void start(const char *device) __attribute__((unused));
101 
102 /** uart_esc stop */
103 static void stop();
104 
105 /** task main trampoline function */
106 static void task_main_trampoline(int argc, char *argv[]);
107 
108 /** uart_esc thread primary entry point */
109 static void task_main(int argc, char *argv[]);
110 
111 /** mixer initialization */
113 static int initialize_mixer(const char *mixer_filename);
114 static int mixer_control_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input);
115 
116 static void parameters_init();
117 static void parameters_update();
118 
119 struct {
120  int model;
121  int baudrate;
123 } _parameters;
124 
125 struct {
126  param_t model;
130 
132 {
133  _parameter_handles.model = param_find("UART_ESC_MODEL");
134  _parameter_handles.baudrate = param_find("UART_ESC_BAUD");
135 
136  /* PX4 motor mapping parameters */
137  for (unsigned int i = 0; i < UART_ESC_MAX_MOTORS; i++) {
138  char nbuf[20];
139 
140  /* min values */
141  sprintf(nbuf, "UART_ESC_MOTOR%d", i + 1);
142  _parameter_handles.px4_motor_mapping[i] = param_find(nbuf);
143  }
144 
146 }
147 
149 {
150  PX4_WARN("uart_esc_main parameters_update");
151  int32_t v_int;
152 
153  if (param_get(_parameter_handles.model, &v_int) == 0) {
154  _parameters.model = v_int;
155  PX4_WARN("UART_ESC_MODEL %d", _parameters.model);
156  }
157 
158  if (param_get(_parameter_handles.baudrate, &v_int) == 0) {
159  _parameters.baudrate = v_int;
160  PX4_WARN("UART_ESC_BAUD %d", _parameters.baudrate);
161  }
162 
163  for (unsigned int i = 0; i < UART_ESC_MAX_MOTORS; i++) {
164  if (param_get(_parameter_handles.px4_motor_mapping[i], &v_int) == 0) {
165  _parameters.px4_motor_mapping[i] = v_int;
166  PX4_WARN("UART_ESC_MOTOR%d %d", i + 1, _parameters.px4_motor_mapping[i]);
167  }
168  }
169 }
170 
171 int mixer_control_callback(uintptr_t handle,
172  uint8_t control_group,
173  uint8_t control_index,
174  float &input)
175 {
176  const actuator_controls_s *controls = (actuator_controls_s *)handle;
177 
178  input = controls[control_group].control[control_index];
179 
180  /* motor spinup phase - lock throttle to zero *
181  if (_pwm_limit.state == PWM_LIMIT_STATE_RAMP) {
182  if (control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE &&
183  control_index == actuator_controls_s::INDEX_THROTTLE) {
184  * limit the throttle output to zero during motor spinup,
185  * as the motors cannot follow any demand yet
186  *
187  input = 0.0f;
188  }
189  }
190  */
191  return 0;
192 }
193 
194 
195 int initialize_mixer(const char *mixer_filename)
196 {
197  mixer = nullptr;
198 
199  int mixer_initialized = -1;
200 
201  char buf[2048];
202  unsigned int buflen = sizeof(buf);
203 
204  PX4_INFO("Initializing mixer from config file in %s", mixer_filename);
205 
206  int fd_load = open(mixer_filename, O_RDONLY);
207 
208  if (fd_load != -1) {
209  int nRead = read(fd_load, buf, buflen);
210  close(fd_load);
211 
212  if (nRead > 0) {
213  mixer = MultirotorMixer::from_text(mixer_control_callback, (uintptr_t)&_controls, buf, buflen);
214 
215  if (mixer != nullptr) {
216  PX4_INFO("Successfully initialized mixer from config file");
217  mixer_initialized = 0;
218 
219  } else {
220  PX4_WARN("Unable to parse from mixer config file");
221  }
222 
223  } else {
224  PX4_WARN("Unable to read from mixer config file");
225  }
226 
227  } else {
228  PX4_WARN("Unable to open mixer config file");
229  }
230 
231  // mixer file loading failed, fall back to default mixer configuration for
232  // QUAD_X airframe
233  if (mixer_initialized < 0) {
234  float roll_scale = 1;
235  float pitch_scale = 1;
236  float yaw_scale = 1;
237  float deadband = 0;
238 
239  mixer = new MultirotorMixer(mixer_control_callback, (uintptr_t)&_controls,
240  MultirotorGeometry::QUAD_X,
241  roll_scale, pitch_scale, yaw_scale, deadband);
242 
243  if (mixer == nullptr) {
244  PX4_ERR("mixer initialization failed");
245  mixer_initialized = -1;
246  return mixer_initialized;
247  }
248 
249  PX4_WARN("mixer config file not found, successfully initialized default quad x mixer");
250  mixer_initialized = 0;
251  }
252 
253  return mixer_initialized;
254 }
255 
256 /**
257 * Rotate the motor rpm values based on the motor mappings configuration stored
258 * in motor_mapping
259 */
260 void uart_esc_rotate_motors(int16_t *motor_rpm, int num_rotors)
261 {
262  ASSERT(num_rotors <= UART_ESC_MAX_MOTORS);
263  int i;
264  int16_t motor_rpm_copy[UART_ESC_MAX_MOTORS];
265 
266  for (i = 0; i < num_rotors; i++) {
267  motor_rpm_copy[i] = motor_rpm[i];
268  }
269 
270  for (i = 0; i < num_rotors; i++) {
271  motor_rpm[_parameters.px4_motor_mapping[i] - 1] = motor_rpm_copy[i];
272  }
273 }
274 
275 void task_main(int argc, char *argv[])
276 {
277  PX4_INFO("enter uart_esc task_main");
278 
279  _outputs_pub = nullptr;
280 
281  parameters_init();
282 
283  esc = UartEsc::get_instance();
284 
285  if (esc == NULL) {
286  PX4_ERR("failed to new UartEsc instance");
287 
288  } else if (esc->initialize((enum esc_model_t)_parameters.model,
289  _device, _parameters.baudrate) < 0) {
290  PX4_ERR("failed to initialize UartEsc");
291 
292  } else {
293  // Subscribe for orb topics
294  _controls_sub = orb_subscribe(ORB_ID(actuator_controls_0)); // single group for now
295  _armed_sub = orb_subscribe(ORB_ID(actuator_armed));
296  _param_sub = orb_subscribe(ORB_ID(parameter_update));
297 
298  // initialize publication structures
299  memset(&_outputs, 0, sizeof(_outputs));
300 
301  // set up poll topic and limit poll interval
302  px4_pollfd_struct_t fds[1];
303  fds[0].fd = _controls_sub;
304  fds[0].events = POLLIN;
305  //orb_set_interval(_controls_sub, 10); // max actuator update period, ms
306 
307  // set up mixer
308  if (initialize_mixer(MIXER_FILENAME) < 0) {
309  PX4_ERR("Mixer initialization failed.");
310  _task_should_exit = true;
311  }
312 
313  // Main loop
314  while (!_task_should_exit) {
315  int pret = px4_poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 100);
316 
317  /* timed out - periodic check for _task_should_exit */
318  if (pret == 0) {
319  continue;
320  }
321 
322  /* this is undesirable but not much we can do - might want to flag unhappy status */
323  if (pret < 0) {
324  PX4_WARN("poll error %d, %d", pret, errno);
325  /* sleep a bit before next try */
326  usleep(100000);
327  continue;
328  }
329 
330  // Handle new actuator controls data
331  if (fds[0].revents & POLLIN) {
332 
333  // Grab new controls data
334  orb_copy(ORB_ID(actuator_controls_0), _controls_sub, &_controls);
335  // Mix to the outputs
336  _outputs.timestamp = hrt_absolute_time();
337  int16_t motor_rpms[UART_ESC_MAX_MOTORS];
338 
339  if (_armed.armed && !_armed.lockdown && !_armed.manual_lockdown) {
340  _outputs.noutputs = mixer->mix(&_outputs.output[0], actuator_controls_s::NUM_ACTUATOR_CONTROLS);
341 
342  // Make sure we support only up to UART_ESC_MAX_MOTORS motors
343  if (_outputs.noutputs > UART_ESC_MAX_MOTORS) {
344  PX4_ERR("Unsupported motors %d, up to %d motors supported",
345  _outputs.noutputs, UART_ESC_MAX_MOTORS);
346  continue;
347  }
348 
349  // Send outputs to the ESCs
350  for (unsigned outIdx = 0; outIdx < _outputs.noutputs; outIdx++) {
351  // map -1.0 - 1.0 outputs to RPMs
352  motor_rpms[outIdx] = (int16_t)(((_outputs.output[outIdx] + 1.0) / 2.0) *
353  (esc->max_rpm() - esc->min_rpm()) + esc->min_rpm());
354  }
355 
356  uart_esc_rotate_motors(motor_rpms, _outputs.noutputs);
357 
358  } else {
359  _outputs.noutputs = UART_ESC_MAX_MOTORS;
360 
361  for (unsigned outIdx = 0; outIdx < _outputs.noutputs; outIdx++) {
362  motor_rpms[outIdx] = 0;
363  _outputs.output[outIdx] = -1.0;
364  }
365  }
366 
367  esc->send_rpms(&motor_rpms[0], _outputs.noutputs);
368 
369  // TODO-JYW: TESTING-TESTING
370  // MAINTAIN FOR REFERENCE, COMMENT OUT FOR RELEASE BUILDS
371 // static int count=0;
372 // count++;
373 // if (!(count % 100)) {
374 // PX4_DEBUG(" ");
375 // PX4_DEBUG("Time t: %13lu, Armed: %d ",(unsigned long)_outputs.timestamp,_armed.armed);
376 // PX4_DEBUG("Act Controls: 0: %+8.4f, 1: %+8.4f, 2: %+8.4f, 3: %+8.4f ",_controls.control[0],_controls.control[1],_controls.control[2],_controls.control[3]);
377 // PX4_DEBUG("Act Outputs : 0: %+8.4f, 1: %+8.4f, 2: %+8.4f, 3: %+8.4f ",_outputs.output[0],_outputs.output[1],_outputs.output[2],_outputs.output[3]);
378 // }
379  // TODO-JYW: TESTING-TESTING
380 
381 
382  /* Publish mixed control outputs */
383  if (_outputs_pub != nullptr) {
384  orb_publish(ORB_ID(actuator_outputs), _outputs_pub, &_outputs);
385 
386  } else {
387  _outputs_pub = orb_advertise(ORB_ID(actuator_outputs), &_outputs);
388  }
389  }
390 
391  // Check for updates in other subscripions
392  bool updated = false;
393  orb_check(_armed_sub, &updated);
394 
395  if (updated) {
396  orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
397  PX4_DEBUG("arming status updated, _armed.armed: %d", _armed.armed);
398  }
399 
400  orb_check(_param_sub, &updated);
401 
402  if (updated) {
403  // Even though we are only interested in the update status of the parameters, copy
404  // the subscription to clear the update status.
405  orb_copy(ORB_ID(parameter_update), _param_sub, &_param_update);
407  }
408  }
409  }
410 
411  PX4_WARN("closing uart_esc");
412 
413  delete esc;
414 }
415 
416 /** uart_esc main entrance */
417 void task_main_trampoline(int argc, char *argv[])
418 {
419  PX4_WARN("task_main_trampoline");
420  task_main(argc, argv);
421 }
422 
423 void start()
424 {
425  ASSERT(_task_handle == -1);
426 
427  /* start the task */
428  _task_handle = px4_task_spawn_cmd("uart_esc_main",
429  SCHED_DEFAULT,
430  SCHED_PRIORITY_MAX,
431  1500,
432  (px4_main_t)&task_main_trampoline,
433  nullptr);
434 
435  if (_task_handle < 0) {
436  warn("task start failed");
437  return;
438  }
439 
440  _is_running = true;
441 }
442 
443 void stop()
444 {
445  // TODO - set thread exit signal to terminate the task main thread
446 
447  _is_running = false;
448  _task_handle = -1;
449 }
450 
451 void usage()
452 {
453  PX4_WARN("missing command: try 'start', 'stop', 'status'");
454  PX4_WARN("options:");
455  PX4_WARN(" -D device");
456 }
457 
458 }; // namespace uart_esc
459 
460 int uart_esc_main(int argc, char *argv[])
461 {
462  const char *device = NULL;
463  int ch;
464  int myoptind = 1;
465  const char *myoptarg = NULL;
466 
467  while ((ch = px4_getopt(argc, argv, "D:", &myoptind, &myoptarg)) != EOF) {
468  switch (ch) {
469  case 'D':
470  device = myoptarg;
471  break;
472 
473  default:
474  uart_esc::usage();
475  return 1;
476  }
477  }
478 
479  // Check on required arguments
480  if (device == NULL || strlen(device) == 0) {
481  uart_esc::usage();
482  return 1;
483  }
484 
486  strncpy(uart_esc::_device, device, strlen(device));
487 
488  const char *verb = argv[myoptind];
489 
490  /*
491  * Start/load the driver.
492  */
493  if (!strcmp(verb, "start")) {
494  if (uart_esc::_is_running) {
495  PX4_WARN("uart_esc already running");
496  return 1;
497  }
498 
499  uart_esc::start();
500  }
501 
502  else if (!strcmp(verb, "stop")) {
503  if (uart_esc::_is_running) {
504  PX4_WARN("uart_esc is not running");
505  return 1;
506  }
507 
508  uart_esc::stop();
509  }
510 
511  else if (!strcmp(verb, "status")) {
512  PX4_WARN("uart_esc is %s", uart_esc::_is_running ? "running" : "stopped");
513  return 0;
514 
515  } else {
516  uart_esc::usage();
517  return 1;
518  }
519 
520  return 0;
521 }
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
Definition: uORB.cpp:90
struct uart_esc::@19 _parameter_handles
int _controls_sub
#define MAX_LEN_DEV_PATH
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
API for the uORB lightweight object broker.
static int initialize_mixer(const char *mixer_filename)
static MultirotorMixer * from_text(Mixer::ControlCallback control_cb, uintptr_t cb_handle, const char *buf, unsigned &buflen)
Factory method.
static void stop()
uart_esc stop
Definition: I2C.hpp:51
int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
static char _device[MAX_LEN_DEV_PATH]
orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
Definition: uORB.cpp:43
Namespace encapsulating all device framework classes, functions and data.
Definition: CDev.cpp:47
High-resolution timer with callouts and timekeeping.
Global flash based parameter store.
static MultirotorMixer * mixer
mixer initialization
Mixer ioctl interfaces.
static void read(bootloader_app_shared_t *pshared)
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
static void start(const char *device) __attribute__((unused))
uart_esc start
static void task_main_trampoline(int argc, char *argv[])
task main trampoline function
actuator_outputs_s _outputs
UartEsc * esc
static const char * MIXER_FILENAME
void uart_esc_rotate_motors(int16_t *motor_rpm, int num_rotors)
Rotate the motor rpm values based on the motor mappings configuration stored in motor_mapping.
static px4_task_t _task_handle
static void usage()
Print out the usage information.
struct __attribute__((__packed__)) reading_msg
Definition: leddar_one.cpp:80
__EXPORT int uart_esc_main(int argc, char *argv[])
driver &#39;main&#39; command
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
Definition: uORB.h:134
orb_advert_t _outputs_pub
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
int px4_motor_mapping[UART_ESC_MAX_MOTORS]
actuator_controls_s _controls
#define UART_ESC_MAX_MOTORS
struct uart_esc::@18 _parameters
unsigned mix(float *outputs, unsigned space) override
Perform the mixing function.
static void parameters_init()
int orb_check(int handle, bool *updated)
Definition: uORB.cpp:95
volatile bool _task_should_exit
actuator_armed_s _armed
Multi-rotor mixer for pre-defined vehicle geometries.
static int mixer_control_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input)
static void parameters_update()
static bool _is_running
#define warn(...)
Definition: err.h:94
static void task_main(int argc, char *argv[])
uart_esc thread primary entry point
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uint32_t param_t
Parameter handle.
Definition: param.h:98
parameter_update_s _param_update