PX4 Firmware
PX4 Autopilot Software http://px4.io
irlock.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2015 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 irlock.cpp
36  * @author Michael Landes
37  *
38  * Driver for an IR-Lock and Pixy vision sensor connected via I2C.
39  *
40  * Created on: Nov 12, 2014
41  **/
42 
43 #include <string.h>
44 
45 #include <drivers/device/i2c.h>
47 
48 #include <px4_platform_common/getopt.h>
49 #include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
50 #include <systemlib/err.h>
51 
52 #include <uORB/uORB.h>
54 
55 /** Configuration Constants **/
56 #define IRLOCK_I2C_BUS PX4_I2C_BUS_EXPANSION
57 #define IRLOCK_I2C_ADDRESS 0x54 /** 7-bit address (non shifted) **/
58 #define IRLOCK_CONVERSION_INTERVAL_US 20000U /** us = 20ms = 50Hz **/
59 
60 #define IRLOCK_SYNC 0xAA55
61 #define IRLOCK_RESYNC 0x5500
62 #define IRLOCK_ADJUST 0xAA
63 
64 #define IRLOCK_RES_X 320
65 #define IRLOCK_RES_Y 200
66 
67 #define IRLOCK_CENTER_X (IRLOCK_RES_X/2) // the x-axis center pixel position
68 #define IRLOCK_CENTER_Y (IRLOCK_RES_Y/2) // the y-axis center pixel position
69 
70 #define IRLOCK_FOV_X (60.0f*M_PI_F/180.0f)
71 #define IRLOCK_FOV_Y (35.0f*M_PI_F/180.0f)
72 
73 #define IRLOCK_TAN_HALF_FOV_X 0.57735026919f // tan(0.5 * 60 * pi/180)
74 #define IRLOCK_TAN_HALF_FOV_Y 0.31529878887f // tan(0.5 * 35 * pi/180)
75 
76 #define IRLOCK_TAN_ANG_PER_PIXEL_X (2*IRLOCK_TAN_HALF_FOV_X/IRLOCK_RES_X)
77 #define IRLOCK_TAN_ANG_PER_PIXEL_Y (2*IRLOCK_TAN_HALF_FOV_Y/IRLOCK_RES_Y)
78 
79 #define IRLOCK_BASE_DEVICE_PATH "/dev/irlock"
80 #define IRLOCK0_DEVICE_PATH "/dev/irlock0"
81 
82 #define IRLOCK_OBJECTS_MAX 5 /** up to 5 objects can be detected/reported **/
83 
85  uint16_t signature; /** target signature **/
86  float pos_x; /** x-axis distance from center of image to center of target in units of tan(theta) **/
87  float pos_y; /** y-axis distance from center of image to center of target in units of tan(theta) **/
88  float size_x; /** size of target along x-axis in units of tan(theta) **/
89  float size_y; /** size of target along y-axis in units of tan(theta) **/
90 };
91 
92 /** irlock_s structure returned from read calls **/
93 struct irlock_s {
94  uint64_t timestamp; /** microseconds since system start **/
95  uint8_t num_targets;
97 };
98 
99 class IRLOCK : public device::I2C, public px4::ScheduledWorkItem
100 {
101 public:
102  IRLOCK(int bus = IRLOCK_I2C_BUS, int address = IRLOCK_I2C_ADDRESS);
103  virtual ~IRLOCK();
104 
105  virtual int init();
106  virtual int probe();
107  virtual int info();
108  virtual int test();
109 
110  virtual ssize_t read(struct file *filp, char *buffer, size_t buflen);
111 
112 private:
113 
114  /** start periodic reads from sensor **/
115  void start();
116 
117  /** stop periodic reads from sensor **/
118  void stop();
119 
120  /** read from device and schedule next read **/
121  void Run() override;
122 
123  /** low level communication with sensor **/
124  int read_device();
125  bool sync_device();
126  int read_device_word(uint16_t *word);
127  int read_device_block(struct irlock_target_s *block);
128 
129  /** internal variables **/
130  ringbuffer::RingBuffer *_reports;
132  uint32_t _read_failures;
133 
136 };
137 
138 /** global pointer for single IRLOCK sensor **/
139 namespace
140 {
141 IRLOCK *g_irlock = nullptr;
142 }
143 
144 void irlock_usage();
145 
146 extern "C" __EXPORT int irlock_main(int argc, char *argv[]);
147 
148 /** constructor **/
149 IRLOCK::IRLOCK(int bus, int address) :
150  I2C("irlock", IRLOCK0_DEVICE_PATH, bus, address, 400000),
151  ScheduledWorkItem(MODULE_NAME, px4::device_bus_to_wq(get_device_id())),
152  _reports(nullptr),
153  _sensor_ok(false),
154  _read_failures(0),
155  _orb_class_instance(-1),
156  _irlock_report_topic(nullptr)
157 {
158 }
159 
160 /** destructor **/
162 {
163  stop();
164 
165  /** clear reports queue **/
166  if (_reports != nullptr) {
167  delete _reports;
168  }
169 }
170 
171 /** initialise driver to communicate with sensor **/
173 {
174  /** initialise I2C bus **/
175  int ret = I2C::init();
176 
177  if (ret != OK) {
178  return ret;
179  }
180 
181  /** allocate buffer storing values read from sensor **/
182  _reports = new ringbuffer::RingBuffer(2, sizeof(struct irlock_s));
183 
184  if (_reports == nullptr) {
185  return ENOTTY;
186 
187  } else {
188  _sensor_ok = true;
189  /** start work queue **/
190  start();
191  return OK;
192  }
193 }
194 
195 /** probe the device is on the I2C bus **/
197 {
198  /*
199  * IRLock defaults to sending 0x00 when there is no block
200  * data to return, so really all we can do is check to make
201  * sure a transfer completes successfully.
202  **/
203  uint8_t byte;
204 
205  if (transfer(nullptr, 0, &byte, 1) != OK) {
206  return -EIO;
207  }
208 
209  return OK;
210 }
211 
212 /** display driver info **/
214 {
215  if (g_irlock == nullptr) {
216  errx(1, "irlock device driver is not running");
217  }
218 
219  /** display reports in queue **/
220  if (_sensor_ok) {
221  _reports->print_info("report queue: ");
222  warnx("read errors:%lu", (unsigned long)_read_failures);
223 
224  } else {
225  warnx("sensor is not healthy");
226  }
227 
228  return OK;
229 }
230 
231 /** test driver **/
233 {
234  /** exit immediately if driver not running **/
235  if (g_irlock == nullptr) {
236  errx(1, "irlock device driver is not running");
237  }
238 
239  /** exit immediately if sensor is not healty **/
240  if (!_sensor_ok) {
241  errx(1, "sensor is not healthy");
242  }
243 
244  /** instructions to user **/
245  warnx("searching for object for 10 seconds");
246 
247  /** read from sensor for 10 seconds **/
248  struct irlock_s report;
249  uint64_t start_time = hrt_absolute_time();
250 
251  while ((hrt_absolute_time() - start_time) < 10000000) {
252  if (_reports->get(&report)) {
253  /** output all objects found **/
254  for (uint8_t i = 0; i < report.num_targets; i++) {
255  warnx("sig:%d x:%4.3f y:%4.3f width:%4.3f height:%4.3f",
256  (int)report.targets[i].signature,
257  (double)report.targets[i].pos_x,
258  (double)report.targets[i].pos_y,
259  (double)report.targets[i].size_x,
260  (double)report.targets[i].size_y);
261  }
262  }
263 
264  /** sleep for 0.05 seconds **/
265  usleep(50000);
266  }
267 
268  return OK;
269 }
270 
271 /** start periodic reads from sensor **/
273 {
274  /** flush ring and reset state machine **/
275  _reports->flush();
276 
277  /** start work queue cycle **/
278  ScheduleNow();
279 }
280 
281 /** stop periodic reads from sensor **/
283 {
284  ScheduleClear();
285 }
286 
288 {
289  /** ignoring failure, if we do, we will be back again right away... **/
290  read_device();
291 
292  /** schedule the next cycle **/
293  ScheduleDelayed(IRLOCK_CONVERSION_INTERVAL_US);
294 }
295 
296 ssize_t IRLOCK::read(struct file *filp, char *buffer, size_t buflen)
297 {
298  unsigned count = buflen / sizeof(struct irlock_s);
299  struct irlock_s *rbuf = reinterpret_cast<struct irlock_s *>(buffer);
300  int ret = 0;
301 
302  if (count < 1) {
303  return -ENOSPC;
304  }
305 
306  /** try to read **/
307  while (count--) {
308  if (_reports->get(rbuf)) {
309  ret += sizeof(*rbuf);
310  ++rbuf;
311  }
312  }
313 
314  return ret ? ret : -EAGAIN;
315 
316  return ret;
317 }
318 
319 /** sync device to ensure reading starts at new frame*/
321 {
322  uint8_t sync_byte;
323  uint16_t sync_word;
324 
325  if (read_device_word(&sync_word) != OK) {
326  return false;
327  }
328 
329  if (sync_word == IRLOCK_RESYNC) {
330  transfer(nullptr, 0, &sync_byte, 1);
331 
332  if (sync_byte == IRLOCK_ADJUST) {
333  return true;
334  }
335 
336  } else if (sync_word == IRLOCK_SYNC) {
337  return true;
338  }
339 
340  return false;
341 }
342 
343 /** read all available frames from sensor **/
345 {
346  /** if we sync, then we are starting a new frame, else fail **/
347  if (!sync_device()) {
348  return -ENOTTY;
349  }
350 
351  struct irlock_s report;
352 
353  report.timestamp = hrt_absolute_time();
354 
355  report.num_targets = 0;
356 
357  while (report.num_targets < IRLOCK_OBJECTS_MAX) {
358  if (!sync_device() || read_device_block(&report.targets[report.num_targets]) != OK) {
359  break;
360  }
361 
362  report.num_targets++;
363  }
364 
365  _reports->force(&report);
366 
367  // publish over uORB
368  if (report.num_targets > 0) {
369  struct irlock_report_s orb_report;
370 
371  orb_report.timestamp = report.timestamp;
372  orb_report.signature = report.targets[0].signature;
373  orb_report.pos_x = report.targets[0].pos_x;
374  orb_report.pos_y = report.targets[0].pos_y;
375  orb_report.size_x = report.targets[0].size_x;
376  orb_report.size_y = report.targets[0].size_y;
377 
378  if (_irlock_report_topic != nullptr) {
379  orb_publish(ORB_ID(irlock_report), _irlock_report_topic, &orb_report);
380 
381  } else {
383 
384  if (_irlock_report_topic == nullptr) {
385  DEVICE_LOG("failed to create irlock_report object. Did you start uOrb?");
386  }
387  }
388  }
389 
390  return OK;
391 }
392 
393 /** read a word (two bytes) from sensor **/
394 int IRLOCK::read_device_word(uint16_t *word)
395 {
396  uint8_t bytes[2];
397  memset(bytes, 0, sizeof bytes);
398 
399  int status = transfer(nullptr, 0, &bytes[0], 2);
400  *word = bytes[1] << 8 | bytes[0];
401 
402  return status;
403 }
404 
405 /** read a single block (a full frame) from sensor **/
407 {
408  uint8_t bytes[12];
409  memset(bytes, 0, sizeof bytes);
410 
411  int status = transfer(nullptr, 0, &bytes[0], 12);
412  uint16_t checksum = bytes[1] << 8 | bytes[0];
413  uint16_t signature = bytes[3] << 8 | bytes[2];
414  uint16_t pixel_x = bytes[5] << 8 | bytes[4];
415  uint16_t pixel_y = bytes[7] << 8 | bytes[6];
416  uint16_t pixel_size_x = bytes[9] << 8 | bytes[8];
417  uint16_t pixel_size_y = bytes[11] << 8 | bytes[10];
418 
419  /** crc check **/
420  if (signature + pixel_x + pixel_y + pixel_size_x + pixel_size_y != checksum) {
421  _read_failures++;
422  return -EIO;
423  }
424 
425  /** convert to angles **/
426  block->signature = signature;
427  block->pos_x = (pixel_x - IRLOCK_CENTER_X) * IRLOCK_TAN_ANG_PER_PIXEL_X;
428  block->pos_y = (pixel_y - IRLOCK_CENTER_Y) * IRLOCK_TAN_ANG_PER_PIXEL_Y;
429  block->size_x = pixel_size_x * IRLOCK_TAN_ANG_PER_PIXEL_X;
430  block->size_y = pixel_size_y * IRLOCK_TAN_ANG_PER_PIXEL_Y;
431  return status;
432 }
433 
435 {
436  warnx("missing command: try 'start', 'stop', 'info', 'test'");
437  warnx("options:");
438  warnx(" -b i2cbus (%d)", IRLOCK_I2C_BUS);
439 }
440 
441 int irlock_main(int argc, char *argv[])
442 {
443  int i2cdevice = IRLOCK_I2C_BUS;
444 
445  int ch;
446  int myoptind = 1;
447  const char *myoptarg = nullptr;
448 
449  while ((ch = px4_getopt(argc, argv, "b:", &myoptind, &myoptarg)) != EOF) {
450  switch (ch) {
451  case 'b':
452  i2cdevice = (uint8_t)atoi(myoptarg);
453  break;
454 
455  default:
456  PX4_WARN("Unknown option!");
457  return -1;
458  }
459  }
460 
461  if (myoptind >= argc) {
462  irlock_usage();
463  exit(1);
464  }
465 
466  const char *command = argv[myoptind];
467 
468  /** start driver **/
469  if (!strcmp(command, "start")) {
470  if (g_irlock != nullptr) {
471  errx(1, "driver has already been started");
472  }
473 
474  /** instantiate global instance **/
475  g_irlock = new IRLOCK(i2cdevice, IRLOCK_I2C_ADDRESS);
476 
477  if (g_irlock == nullptr) {
478  errx(1, "failed to allocated memory for driver");
479  }
480 
481  /** initialise global instance **/
482  if (g_irlock->init() != OK) {
483  IRLOCK *tmp_irlock = g_irlock;
484  g_irlock = nullptr;
485  delete tmp_irlock;
486  errx(1, "failed to initialize device, stopping driver");
487  }
488 
489  exit(0);
490  }
491 
492  /** need the driver past this point **/
493  if (g_irlock == nullptr) {
494  warnx("not started");
495  irlock_usage();
496  exit(1);
497  }
498 
499  /** stop the driver **/
500  if (!strcmp(command, "stop")) {
501  IRLOCK *tmp_irlock = g_irlock;
502  g_irlock = nullptr;
503  delete tmp_irlock;
504  warnx("irlock stopped");
505  exit(OK);
506  }
507 
508  /** Print driver information **/
509  if (!strcmp(command, "info")) {
510  g_irlock->info();
511  exit(OK);
512  }
513 
514  /** test driver **/
515  if (!strcmp(command, "test")) {
516  g_irlock->test();
517  exit(OK);
518  }
519 
520  /** display usage info **/
521  irlock_usage();
522  exit(0);
523 }
orb_advert_t _irlock_report_topic
Definition: irlock.cpp:135
float pos_y
x-axis distance from center of image to center of target in units of tan(theta)
Definition: irlock.cpp:87
void start()
start periodic reads from sensor
Definition: irlock.cpp:272
int read_device_word(uint16_t *word)
read a word (two bytes) from sensor
Definition: irlock.cpp:394
static struct vehicle_status_s status
Definition: Commander.cpp:138
#define IRLOCK_CENTER_Y
Definition: irlock.cpp:68
virtual int init()
initialise driver to communicate with sensor
Definition: irlock.cpp:172
int read_device()
low level communication with sensor
Definition: irlock.cpp:344
uint16_t signature
Definition: irlock.cpp:85
API for the uORB lightweight object broker.
float pos_x
target signature
Definition: irlock.cpp:86
bool sync_device()
sync device to ensure reading starts at new frame
Definition: irlock.cpp:320
#define IRLOCK_RESYNC
Definition: irlock.cpp:61
bool _sensor_ok
Definition: irlock.cpp:131
#define IRLOCK_CENTER_X
Definition: irlock.cpp:67
Definition: I2C.hpp:51
int info()
Print a little info about the driver.
#define IRLOCK_OBJECTS_MAX
Definition: irlock.cpp:82
void irlock_usage()
Definition: irlock.cpp:434
#define IRLOCK_TAN_ANG_PER_PIXEL_X
Definition: irlock.cpp:76
virtual ssize_t read(struct file *filp, char *buffer, size_t buflen)
Definition: irlock.cpp:296
static void stop()
Definition: dataman.cpp:1491
#define IRLOCK_I2C_BUS
Configuration Constants.
Definition: irlock.cpp:56
virtual ~IRLOCK()
destructor
Definition: irlock.cpp:161
virtual int test()
test driver
Definition: irlock.cpp:232
static void read(bootloader_app_shared_t *pshared)
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
int _orb_class_instance
Definition: irlock.cpp:134
uint16_t signature
Definition: irlock_report.h:58
#define IRLOCK_CONVERSION_INTERVAL_US
Definition: irlock.cpp:58
void init()
Activates/configures the hardware registers.
#define DEVICE_LOG(FMT,...)
Definition: Device.hpp:51
__EXPORT int irlock_main(int argc, char *argv[])
Definition: irlock.cpp:441
virtual int info()
display driver info
Definition: irlock.cpp:213
int read_device_block(struct irlock_target_s *block)
read a single block (a full frame) from sensor
Definition: irlock.cpp:406
#define warnx(...)
Definition: err.h:95
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
void test(enum LPS25H_BUS busid)
Perform some basic functional tests on the driver; make sure we can collect data from the sensor in p...
Definition: lps25h.cpp:792
uint32_t _read_failures
Definition: irlock.cpp:132
Definition: reflect.c:56
__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
uint64_t timestamp
Definition: irlock_report.h:53
ringbuffer::RingBuffer * _reports
internal variables
Definition: irlock.cpp:130
static int start()
Definition: dataman.cpp:1452
float size_y
size of target along x-axis in units of tan(theta)
Definition: irlock.cpp:89
#define IRLOCK_TAN_ANG_PER_PIXEL_Y
Definition: irlock.cpp:77
struct irlock_target_s targets[IRLOCK_OBJECTS_MAX]
Definition: irlock.cpp:96
void Run() override
read from device and schedule next read
Definition: irlock.cpp:287
irlock_s structure returned from read calls
Definition: irlock.cpp:93
struct @83::@85::@87 file
#define errx(eval,...)
Definition: err.h:89
Definition: bst.cpp:62
uint64_t timestamp
Definition: irlock.cpp:94
#define OK
Definition: uavcan_main.cpp:71
#define IRLOCK0_DEVICE_PATH
Definition: irlock.cpp:80
#define IRLOCK_ADJUST
Definition: irlock.cpp:62
IRLOCK(int bus=IRLOCK_I2C_BUS, int address=IRLOCK_I2C_ADDRESS)
constructor
Definition: irlock.cpp:149
#define IRLOCK_I2C_ADDRESS
Definition: irlock.cpp:57
void stop()
stop periodic reads from sensor
Definition: irlock.cpp:282
orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority)
Definition: uORB.cpp:53
#define IRLOCK_SYNC
Definition: irlock.cpp:60
uint8_t num_targets
microseconds since system start
Definition: irlock.cpp:95
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
float size_x
y-axis distance from center of image to center of target in units of tan(theta)
Definition: irlock.cpp:88
virtual int probe()
probe the device is on the I2C bus
Definition: irlock.cpp:196
Base class for devices connected via I2C.