PX4 Firmware
PX4 Autopilot Software http://px4.io
uavcannode_main.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in
12  * the documentation and/or other materials provided with the
13  * distribution.
14  * 3. Neither the name PX4 nor the names of its contributors may be
15  * used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  ****************************************************************************/
32 
33 #include <px4_platform_common/px4_config.h>
34 #include <px4_platform_common/log.h>
35 #include <px4_platform_common/tasks.h>
36 
37 #ifdef __PX4_NUTTX
38 #include <nuttx/clock.h>
39 #
40 #else
41 #include <px4_platform_common/workqueue.h>
42 #endif
43 
44 #include <cstdlib>
45 #include <cstring>
46 #include <fcntl.h>
47 #include <systemlib/err.h>
48 #include <parameters/param.h>
49 #include <lib/mixer/MixerGroup.hpp>
50 #include <version/version.h>
52 #include <nuttx/board.h>
53 #include <arch/chip/chip.h>
55 
56 #include <drivers/drv_hrt.h>
57 #include <drivers/drv_pwm_output.h>
58 
59 #include "uavcannode_main.hpp"
61 #include "sim_controller.hpp"
62 #include "resources.hpp"
63 #include "led.hpp"
64 
65 #include "boot_app_shared.h"
66 
67 using namespace time_literals;
68 
69 /**
70  * @file uavcan_main.cpp
71  *
72  * Implements basic functionality of UAVCAN node.
73  *
74  * @author Pavel Kirienko <pavel.kirienko@gmail.com>
75  * David Sidrane <david_s5@nscdg.com>
76  */
77 
78 #define RESOURCE_DEBUG
79 #if defined(RESOURCE_DEBUG)
80 #define resources(s) ::syslog(LOG_INFO," %s\n",(s)); \
81  if (UavcanNode::instance()) { \
82  syslog(LOG_INFO,"UAVCAN getPeakNumUsedBlocks() in bytes %d\n", \
83  UAVCAN_MEM_POOL_BLOCK_SIZE * UavcanNode::instance()->get_node().getAllocator().getPeakNumUsedBlocks()); \
84  } \
85  free_check(); \
86  stack_check();
87 #else
88 #define resources(s)
89 #endif
90 
91 /*
92  * This is the AppImageDescriptor used
93  * by the make_can_boot_descriptor.py tool to set
94  * the application image's descriptor so that the
95  * uavcan bootloader has the ability to validate the
96  * image crc, size etc of this application
97 */
98 
101  .image_crc = 0,
102  .image_size = 0,
103  .vcs_commit = 0,
104  .major_version = APP_VERSION_MAJOR,
105  .minor_version = APP_VERSION_MINOR,
106  .reserved = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
107 };
108 
109 /*
110  * UavcanNode
111  */
113 
114 UavcanNode::UavcanNode(uavcan::ICanDriver &can_driver, uavcan::ISystemClock &system_clock) :
115  CDev(UAVCAN_DEVICE_PATH),
116  active_bitrate(0),
117  _node(can_driver, system_clock),
118  _node_mutex(),
119  _time_sync_slave(_node),
120  _fw_update_listner(_node),
121  _reset_timer(_node)
122 {
123  const int res = pthread_mutex_init(&_node_mutex, nullptr);
124 
125  if (res < 0) {
126  std::abort();
127  }
128 
129  px4_sem_init(&_sem, 0, 0);
130  /* semaphore use case is a signal */
131  px4_sem_setprotocol(&_sem, SEM_PRIO_NONE);
132 }
133 
135 {
136  if (_task != -1) {
137  /* tell the task we want it to go away */
138  _task_should_exit = true;
139 
140  unsigned i = 10;
141 
142  do {
143  /* wait 5ms - it should wake every 10ms or so worst-case */
144  ::usleep(5000);
145 
146  /* if we have given up, kill it */
147  if (--i == 0) {
148  task_delete(_task);
149  break;
150  }
151 
152  } while (_task != -1);
153  }
154 
155  _instance = nullptr;
156  px4_sem_destroy(&_sem);
157 }
158 
159 int UavcanNode::start(uavcan::NodeID node_id, uint32_t bitrate)
160 {
161 
162 
163  if (_instance != nullptr) {
164  warnx("Already started");
165  return -1;
166  }
167 
168  /*
169  * CAN driver init
170  */
171  static CanInitHelper can;
172  static bool can_initialized = false;
173 
174  if (!can_initialized) {
175  const int can_init_res = can.init(bitrate);
176 
177  if (can_init_res < 0) {
178  warnx("CAN driver init failed %i", can_init_res);
179  return can_init_res;
180  }
181 
182  can_initialized = true;
183  }
184 
185  /*
186  * Node init
187  */
188  _instance = new UavcanNode(can.driver, uavcan_stm32::SystemClock::instance());
189 
190  if (_instance == nullptr) {
191  warnx("Out of memory");
192  return -1;
193  }
194 
195 
196  resources("Before _instance->init:");
197  const int node_init_res = _instance->init(node_id, can.driver.updateEvent());
198  resources("After _instance->init:");
199 
200  if (node_init_res < 0) {
201  delete _instance;
202  _instance = nullptr;
203  warnx("Node init failed %i", node_init_res);
204  return node_init_res;
205  }
206 
207 
208  /* Keep the bit rate for reboots on BenginFirmware updates */
209 
210  _instance->active_bitrate = bitrate;
211 
212  /*
213  * Start the task. Normally it should never exit.
214  */
215  static auto run_trampoline = [](int, char *[]) {return UavcanNode::_instance->run();};
216  _instance->_task = px4_task_spawn_cmd("uavcan", SCHED_DEFAULT, SCHED_PRIORITY_ACTUATOR_OUTPUTS, StackSize,
217  static_cast<main_t>(run_trampoline), nullptr);
218 
219  if (_instance->_task < 0) {
220  warnx("start failed: %d", errno);
221  return -errno;
222  }
223 
224  return OK;
225 }
226 
228 {
229  /* software version */
230  uavcan::protocol::SoftwareVersion swver;
231 
232  // Extracting the first 8 hex digits of the git hash and converting them to int
233  char fw_git_short[9] = {};
234  std::memmove(fw_git_short, px4_firmware_version_string(), 8);
235  char *end = nullptr;
236  swver.vcs_commit = std::strtol(fw_git_short, &end, 16);
237  swver.optional_field_flags |= swver.OPTIONAL_FIELD_FLAG_VCS_COMMIT;
238  swver.major = AppDescriptor.major_version;
239  swver.minor = AppDescriptor.minor_version;
240  swver.image_crc = AppDescriptor.image_crc;
241 
242  warnx("SW version vcs_commit: 0x%08x", unsigned(swver.vcs_commit));
243 
244  _node.setSoftwareVersion(swver);
245 
246  /* hardware version */
247  uavcan::protocol::HardwareVersion hwver;
248 
249  hwver.major = HW_VERSION_MAJOR;
250  hwver.minor = HW_VERSION_MINOR;
251 
252  mfguid_t mfgid = {};
253  board_get_mfguid(mfgid);
254  uavcan::copy(mfgid, mfgid + sizeof(mfgid), hwver.unique_id.begin());
255 
256  _node.setHardwareVersion(hwver);
257 }
258 
259 static void cb_reboot(const uavcan::TimerEvent &)
260 {
261  px4_systemreset(false);
262 
263 }
264 
265 void UavcanNode::cb_beginfirmware_update(const uavcan::ReceivedDataStructure<UavcanNode::BeginFirmwareUpdate::Request>
266  &req,
267  uavcan::ServiceResponseDataStructure<UavcanNode::BeginFirmwareUpdate::Response> &rsp)
268 {
269  static bool inprogress = false;
270 
271  rsp.error = rsp.ERROR_UNKNOWN;
272 
273  if (req.image_file_remote_path.path.size()) {
274  rsp.error = rsp.ERROR_IN_PROGRESS;
275 
276  if (!inprogress) {
277  inprogress = true;
279  shared.bus_speed = active_bitrate;
280  shared.node_id = _node.getNodeID().get();
282  rgb_led(255, 128, 0, 5);
283  _reset_timer.setCallback(cb_reboot);
284  _reset_timer.startOneShotWithDelay(uavcan::MonotonicDuration::fromMSec(1000));
285  rsp.error = rsp.ERROR_OK;
286  }
287  }
288 }
289 
290 int UavcanNode::init(uavcan::NodeID node_id, uavcan_stm32::BusEvent &bus_events)
291 {
292  int ret = -1;
293 
294  // Do regular cdev init
295  ret = CDev::init();
296 
297  if (ret != OK) {
298  return ret;
299  }
300 
301  _node.setName(HW_UAVCAN_NAME);
302 
303  _node.setNodeID(node_id);
304 
305  fill_node_info();
306 
307  const int srv_start_res = _fw_update_listner.start(BeginFirmwareUpdateCallBack(this,
309 
310  if (srv_start_res < 0) {
311  return ret;
312  }
313 
314  bus_events.registerSignalCallback(UavcanNode::busevent_signal_trampoline);
315 
316  return _node.start();
317 }
318 
319 
320 /*
321  * Restart handler
322  */
323 class RestartRequestHandler: public uavcan::IRestartRequestHandler
324 {
325  bool handleRestartRequest(uavcan::NodeID request_source) override
326  {
327  ::syslog(LOG_INFO, "UAVCAN: Restarting by request from %i\n", int(request_source.get()));
328  ::usleep(20 * 1000 * 1000);
329  px4_systemreset(false);
330  return true; // Will never be executed BTW
331  }
333 
335 {
336  const int spin_res = _node.spin(uavcan::MonotonicTime());
337 
338  if (spin_res < 0) {
339  warnx("node spin error %i", spin_res);
340  }
341 }
342 
343 static void signal_callback(void *arg)
344 {
345  /* Note: we are in IRQ context here */
346  px4_sem_t *sem = (px4_sem_t *)arg;
347  int semaphore_value;
348 
349  if (px4_sem_getvalue(sem, &semaphore_value) == 0 && semaphore_value > 1) {
350  return;
351  }
352 
353  px4_sem_post(sem);
354 }
355 
356 
357 void
359 {
360  if (_instance) {
361  signal_callback(&_instance->_sem);
362  }
363 }
364 
365 
366 
368 {
369 
370  get_node().setRestartRequestHandler(&restart_request_handler);
371 
372  while (init_indication_controller(get_node()) < 0) {
373  ::syslog(LOG_INFO, "UAVCAN: Indication controller init failed\n");
374  ::sleep(1);
375  }
376 
377  while (init_sim_controller(get_node()) < 0) {
378  ::syslog(LOG_INFO, "UAVCAN: sim controller init failed\n");
379  ::sleep(1);
380  }
381 
382  (void)pthread_mutex_lock(&_node_mutex);
383 
384  /*
385  * Set up the time synchronization
386  */
387 
388  const int slave_init_res = _time_sync_slave.start();
389 
390  if (slave_init_res < 0) {
391  warnx("Failed to start time_sync_slave");
392  _task_should_exit = true;
393  }
394 
395  _node.setModeOperational();
396 
397  uint32_t start_tick = clock_systimer();
398 
399  hrt_call timer_call{};
400  hrt_call_every(&timer_call, 50_ms, 50_ms, signal_callback, &_sem);
401 
402  while (!_task_should_exit) {
403  // Mutex is unlocked while the thread is blocked on IO multiplexing
404  (void)pthread_mutex_unlock(&_node_mutex);
405 
406  while (px4_sem_wait(&_sem) != 0);
407 
408  (void)pthread_mutex_lock(&_node_mutex);
409 
410  node_spin_once(); // Non-blocking
411 
412 
413  // Do Something
414 
415 
416  if (clock_systimer() - start_tick > TICK_PER_SEC) {
417  start_tick = clock_systimer();
418  resources("Udate:");
419  /*
420  * Printing the slave status information once a second
421  */
422  const bool active = _time_sync_slave.isActive(); // Whether it can sync with a remote master
423 
424  const int master_node_id = _time_sync_slave.getMasterNodeID().get(); // Returns an invalid Node ID if (active == false)
425 
426  const long msec_since_last_adjustment = (_node.getMonotonicTime() - _time_sync_slave.getLastAdjustmentTime()).toMSec();
427  const uavcan::UtcTime utc = uavcan_stm32::clock::getUtc();
428  syslog(LOG_INFO, "Time:%lld\n"
429  " Time sync slave status:\n"
430  " Active: %d\n"
431  " Master Node ID: %d\n"
432  " Last clock adjustment was %ld ms ago\n",
433  utc .toUSec(), int(active), master_node_id, msec_since_last_adjustment);
434  syslog(LOG_INFO, "UTC %lu sec Rate corr: %fPPM Jumps: %lu Locked: %i\n\n",
435  static_cast<unsigned long>(utc.toMSec() / 1000),
436  static_cast<double>(uavcan_stm32::clock::getUtcRateCorrectionPPM()),
439  }
440 
441  }
442 
443  hrt_cancel(&timer_call);
444  teardown();
445  (void)pthread_mutex_unlock(&_node_mutex);
446 
447  exit(0);
448 }
449 
450 int
452 {
453  return 0;
454 }
455 
456 
457 
458 int
459 UavcanNode::ioctl(file *filp, int cmd, unsigned long arg)
460 {
461  int ret = OK;
462 
463  lock();
464 
465  switch (cmd) {
466 
467 
468 
469  default:
470  ret = -ENOTTY;
471  break;
472  }
473 
474  unlock();
475 
476  if (ret == -ENOTTY) {
477  ret = CDev::ioctl(filp, cmd, arg);
478  }
479 
480  return ret;
481 }
482 
483 void
485 {
486  if (!_instance) {
487  warnx("not running, start first");
488  }
489 
490  (void)pthread_mutex_lock(&_node_mutex);
491 
492 
493  (void)pthread_mutex_unlock(&_node_mutex);
494 }
495 
496 /*
497  * App entry point
498  */
499 static void print_usage()
500 {
501  warnx("usage: \n"
502  "\tuavcannode {start|status|stop|arm|disarm}");
503 }
504 
505 extern "C" __EXPORT int uavcannode_start(int argc, char *argv[]);
506 
507 int uavcannode_start(int argc, char *argv[])
508 {
509  resources("Before board_app_initialize");
510 
511  board_app_initialize(NULL);
512 
513  resources("After board_app_initialize");
514 
515  // CAN bitrate
516  int32_t bitrate = 0;
517  // Node ID
518  int32_t node_id = 0;
519 
520  // Did the bootloader auto baud and get a node ID Allocated
521 
523  int valid = bootloader_app_shared_read(&shared, BootLoader);
524 
525  if (valid == 0) {
526 
527  bitrate = shared.bus_speed;
528  node_id = shared.node_id;
529 
530  // Invalidate to prevent deja vu
531 
533 
534  } else {
535 
536  // Node ID
537  (void)param_get(param_find("CANNODE_NODE_ID"), &node_id);
538  (void)param_get(param_find("CANNODE_BITRATE"), &bitrate);
539  }
540 
541  if (node_id < 0 || node_id > uavcan::NodeID::Max || !uavcan::NodeID(node_id).isUnicast()) {
542  warnx("Invalid Node ID %i", node_id);
543  ::exit(1);
544  }
545 
546  // Start
547  warnx("Node ID %u, bitrate %u", node_id, bitrate);
548  int rv = UavcanNode::start(node_id, bitrate);
549  resources("After UavcanNode::start");
550  ::sleep(1);
551  return rv;
552 }
553 
554 extern "C" __EXPORT int uavcannode_main(int argc, char *argv[]);
555 int uavcannode_main(int argc, char *argv[])
556 {
557  if (argc < 2) {
558  print_usage();
559  ::exit(1);
560  }
561 
562  if (!std::strcmp(argv[1], "start")) {
563 
564  if (UavcanNode::instance()) {
565  errx(1, "already started");
566  }
567 
568  return uavcannode_start(argc, argv);
569  }
570 
571  /* commands below require the app to be started */
572  UavcanNode *const inst = UavcanNode::instance();
573 
574  if (!inst) {
575  errx(1, "application not running");
576  }
577 
578  if (!std::strcmp(argv[1], "status") || !std::strcmp(argv[1], "info")) {
579  inst->print_info();
580  ::exit(0);
581  }
582 
583  if (!std::strcmp(argv[1], "stop")) {
584  delete inst;
585  ::exit(0);
586  }
587 
588  print_usage();
589  ::exit(1);
590 }
virtual ~UavcanNode()
float getUtcRateCorrectionPPM()
Clock rate error.
static int start(uavcan::NodeID node_id, uint32_t bitrate)
__EXPORT void bootloader_app_shared_invalidate(void)
static void signal_callback(void *arg)
#define UAVCAN_DEVICE_PATH
#define __END_DECLS
Definition: visibility.h:59
static void cb_reboot(const uavcan::TimerEvent &)
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
uavcan::uint32_t getUtcJumpCount()
Number of non-gradual adjustments performed so far.
static void busevent_signal_trampoline()
static SystemClock & instance()
Calls clock::init() as needed.
Definition: I2C.hpp:51
virtual int init()
Definition: CDev.cpp:119
static void print_usage()
void print_info()
__EXPORT int bootloader_app_shared_read(bootloader_app_shared_t *shared, eRole_t role)
High-resolution timer with callouts and timekeeping.
int init_sim_controller(uavcan::INode &node)
Global flash based parameter store.
UavcanNode(uavcan::ICanDriver &can_driver, uavcan::ISystemClock &system_clock)
Definition: uavcan_main.cpp:78
A UAVCAN node.
void init()
Activates/configures the hardware registers.
bool handleRestartRequest(uavcan::NodeID request_source) override
__EXPORT void bootloader_app_shared_write(bootloader_app_shared_t *shared, eRole_t role)
#define __BEGIN_DECLS
Definition: visibility.h:58
void cb_beginfirmware_update(const uavcan::ReceivedDataStructure< UavcanNode::BeginFirmwareUpdate::Request > &req, uavcan::ServiceResponseDataStructure< UavcanNode::BeginFirmwareUpdate::Response > &rsp)
__EXPORT int uavcannode_start(int argc, char *argv[])
uavcan::UtcTime getUtc()
Returns UTC time if it has been set, otherwise returns zero time.
#define warnx(...)
Definition: err.h:95
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
__EXPORT void hrt_call_every(struct hrt_call *entry, hrt_abstime delay, hrt_abstime interval, hrt_callout callout, void *arg)
Call callout(arg) after delay, and then after every interval.
#define APP_DESCRIPTOR_SIGNATURE
const char * px4_firmware_version_string(void)
Firmware version as human readable string (git tag)
Definition: version.c:338
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
Tools for system version detection.
static UavcanNode * _instance
singleton pointer
int init_indication_controller(uavcan::INode &node)
uavcan::MethodBinder< UavcanNode *, void(UavcanNode::*)(const uavcan::ReceivedDataStructure< UavcanNode::BeginFirmwareUpdate::Request > &, uavcan::ServiceResponseDataStructure< UavcanNode::BeginFirmwareUpdate::Response > &)> BeginFirmwareUpdateCallBack
static UavcanNode * instance()
int teardown()
struct @83::@85::@87 file
boot_app_shared_section app_descriptor_t AppDescriptor
bool isUtcLocked()
Whether UTC is synchronized and locked.
#define boot_app_shared_section
#define errx(eval,...)
Definition: err.h:89
Callout record.
Definition: drv_hrt.h:72
#define OK
Definition: uavcan_main.cpp:71
void rgb_led(int r, int g, int b, int freqs)
Definition: led.cpp:44
virtual int ioctl(file *filp, int cmd, unsigned long arg)
uint8_t signature[sizeof(uint64_t)]
void node_spin_once()
void fill_node_info()
__EXPORT int uavcannode_main(int argc, char *argv[])
RestartRequestHandler restart_request_handler
__EXPORT void hrt_cancel(struct hrt_call *entry)
Remove the entry from the callout list.
#define resources(s)