Home | History | Annotate | Download | only in grovescam
      1 /*
      2  * Author: Jon Trulson <jtrulson (at) ics.com>
      3  * Copyright (c) 2015 Intel Corporation.
      4  *
      5  * Thanks to Seeed Studio for a working arduino sketch
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining
      8  * a copy of this software and associated documentation files (the
      9  * "Software"), to deal in the Software without restriction, including
     10  * without limitation the rights to use, copy, modify, merge, publish,
     11  * distribute, sublicense, and/or sell copies of the Software, and to
     12  * permit persons to whom the Software is furnished to do so, subject to
     13  * the following conditions:
     14  *
     15  * The above copyright notice and this permission notice shall be
     16  * included in all copies or substantial portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  */
     26 
     27 #include <iostream>
     28 #include <string>
     29 #include <stdexcept>
     30 #include <errno.h>
     31 
     32 #include "grovescam.h"
     33 
     34 using namespace upm;
     35 using namespace std;
     36 
     37 static const int maxRetries = 100;
     38 
     39 GROVESCAM::GROVESCAM(int uart, uint8_t camAddr)
     40 {
     41   m_ttyFd = -1;
     42 
     43   // save our shifted camera address, we'll need it a lot
     44   m_camAddr = (camAddr << 5);
     45 
     46   m_picTotalLen = 0;
     47 
     48   if ( !(m_uart = mraa_uart_init(uart)) )
     49     {
     50       throw std::invalid_argument(std::string(__FUNCTION__) +
     51                                   ": mraa_uart_init() failed");
     52       return;
     53     }
     54 
     55   // This requires a recent MRAA (1/2015)
     56   const char *devPath = mraa_uart_get_dev_path(m_uart);
     57 
     58   if (!devPath)
     59     {
     60       throw std::runtime_error(std::string(__FUNCTION__) +
     61                                ": mraa_uart_get_dev_path() failed");
     62       return;
     63     }
     64 
     65   // now open the tty
     66   if ( (m_ttyFd = open(devPath, O_RDWR)) == -1)
     67     {
     68       throw std::runtime_error(std::string(__FUNCTION__) +
     69                                ": open of " +
     70                                string(devPath) + " failed:" +
     71                                string(strerror(errno)));
     72       return;
     73     }
     74 }
     75 
     76 GROVESCAM::~GROVESCAM()
     77 {
     78   if (m_ttyFd != -1)
     79     close(m_ttyFd);
     80 }
     81 
     82 bool GROVESCAM::dataAvailable(unsigned int millis)
     83 {
     84   if (m_ttyFd == -1)
     85     return false;
     86 
     87   struct timeval timeout;
     88 
     89   if (millis == 0)
     90     {
     91       // no waiting
     92       timeout.tv_sec = 0;
     93       timeout.tv_usec = 0;
     94     }
     95   else
     96     {
     97       timeout.tv_sec = millis / 1000;
     98       timeout.tv_usec = (millis % 1000) * 1000;
     99     }
    100 
    101   int nfds;
    102   fd_set readfds;
    103 
    104   FD_ZERO(&readfds);
    105 
    106   FD_SET(m_ttyFd, &readfds);
    107 
    108   if (select(m_ttyFd + 1, &readfds, NULL, NULL, &timeout) > 0)
    109     return true;                // data is ready
    110   else
    111     return false;
    112 }
    113 
    114 int GROVESCAM::readData(uint8_t *buffer, int len)
    115 {
    116   if (m_ttyFd == -1)
    117     return(-1);
    118 
    119   int rv = read(m_ttyFd, (char *)buffer, len);
    120 
    121   if (rv < 0)
    122     {
    123       throw std::runtime_error(std::string(__FUNCTION__) +
    124                                ": read() failed: " +
    125                                string(strerror(errno)));
    126       return rv;
    127     }
    128 
    129   return rv;
    130 }
    131 
    132 int GROVESCAM::writeData(uint8_t *buffer, int len)
    133 {
    134   if (m_ttyFd == -1)
    135     return(-1);
    136 
    137   // first, flush any pending but unread input
    138 
    139   tcflush(m_ttyFd, TCIFLUSH);
    140 
    141   int rv = write(m_ttyFd, (char *)buffer, len);
    142 
    143   if (rv < 0)
    144     {
    145       throw std::runtime_error(std::string(__FUNCTION__) +
    146                                ": write() failed: " +
    147                                string(strerror(errno)));
    148       return rv;
    149     }
    150 
    151   tcdrain(m_ttyFd);
    152 
    153   return rv;
    154 }
    155 
    156 bool GROVESCAM::setupTty(speed_t baud)
    157 {
    158   if (m_ttyFd == -1)
    159     return(false);
    160 
    161   struct termios termio;
    162 
    163   // get current modes
    164   tcgetattr(m_ttyFd, &termio);
    165 
    166   // setup for a 'raw' mode.  81N, no echo or special character
    167   // handling, such as flow control.
    168   cfmakeraw(&termio);
    169 
    170   // set our baud rates
    171   cfsetispeed(&termio, baud);
    172   cfsetospeed(&termio, baud);
    173 
    174   // make it so
    175   if (tcsetattr(m_ttyFd, TCSAFLUSH, &termio) < 0)
    176     {
    177       throw std::runtime_error(std::string(__FUNCTION__) +
    178                                ": tcsetattr() failed: " +
    179                                string(strerror(errno)));
    180       return false;
    181     }
    182 
    183   return true;
    184 }
    185 
    186 void GROVESCAM::drainInput()
    187 {
    188   uint8_t ch;
    189 
    190   while (dataAvailable(0))
    191     readData(&ch, 1);
    192 }
    193 
    194 bool GROVESCAM::init()
    195 {
    196   const unsigned int pktLen = 6;
    197   uint8_t cmd[pktLen] = {0xaa, static_cast<uint8_t>(0x0d|m_camAddr), 0x00,
    198                          0x00, 0x00, 0x00};
    199   uint8_t resp[pktLen];
    200   int retries = 0;
    201 
    202   while (true)
    203     {
    204       if (retries++ > maxRetries)
    205         {
    206           throw std::runtime_error(std::string(__FUNCTION__) +
    207                                    ": maximum retries exceeded");
    208           return false;
    209         }
    210 
    211       writeData(cmd, pktLen);
    212 
    213       if (!dataAvailable(500))
    214         continue;
    215 
    216       if (readData(resp, pktLen) != pktLen)
    217         continue;
    218 
    219       if (resp[0] == 0xaa
    220           && resp[1] == (0x0e | m_camAddr)
    221           && resp[2] == 0x0d
    222           && resp[4] == 0
    223           && resp[5] == 0)
    224         {
    225           if (readData(resp, pktLen) != pktLen)
    226             continue;
    227           else
    228             {
    229               if (resp[0] == 0xaa
    230                   && resp[1] == (0x0d | m_camAddr)
    231                   && resp[2] == 0
    232                   && resp[3] == 0
    233                   && resp[4] == 0
    234                   && resp[5] == 0)
    235                 break;
    236             }
    237         }
    238     }
    239 
    240   cmd[1] = 0x0e | m_camAddr;
    241   cmd[2] = 0x0d;
    242   writeData(cmd, pktLen);
    243 
    244   return true;
    245 }
    246 
    247 bool GROVESCAM::preCapture(PIC_FORMATS_T fmt)
    248 {
    249   const unsigned int pktLen = 6;
    250   uint8_t cmd[pktLen] = { 0xaa, static_cast<uint8_t>(0x01 | m_camAddr), 0x00,
    251                           0x07, 0x00, static_cast<uint8_t>(fmt) };
    252   uint8_t resp[pktLen];
    253   int retries = 0;
    254 
    255   while (true)
    256     {
    257       if (retries++ > maxRetries)
    258         {
    259           throw std::runtime_error(std::string(__FUNCTION__) +
    260                                    ": maximum retries exceeded");
    261           return false;
    262         }
    263 
    264       drainInput();
    265 
    266       writeData(cmd, pktLen);
    267 
    268       if (!dataAvailable(100))
    269         continue;
    270 
    271       if (readData(resp, pktLen) != pktLen)
    272         continue;
    273 
    274       if (resp[0] == 0xaa
    275           && resp[1] == (0x0e | m_camAddr)
    276           && resp[2] == 0x01
    277           && resp[4] == 0
    278           && resp[5] == 0) break;
    279     }
    280 
    281   return true;
    282 }
    283 
    284 bool GROVESCAM::doCapture()
    285 {
    286   const unsigned int pktLen = 6;
    287   uint8_t cmd[pktLen] = { 0xaa, static_cast<uint8_t>(0x06 | m_camAddr), 0x08,
    288                           MAX_PKT_LEN & 0xff, (MAX_PKT_LEN >> 8) & 0xff, 0};
    289   uint8_t resp[pktLen];
    290   int retries = 0;
    291 
    292   m_picTotalLen = 0;
    293 
    294   while (true)
    295     {
    296       if (retries++ > maxRetries)
    297         {
    298           throw std::runtime_error(std::string(__FUNCTION__) +
    299                                    ": maximum retries exceeded");
    300           return false;
    301         }
    302 
    303       drainInput();
    304       writeData(cmd, pktLen);
    305       usleep(100000);
    306 
    307       if (!dataAvailable(100))
    308         continue;
    309 
    310       if (readData(resp, pktLen) != pktLen)
    311         continue;
    312 
    313       if (resp[0] == 0xaa
    314           && resp[1] == (0x0e | m_camAddr)
    315           && resp[2] == 0x06
    316           && resp[4] == 0
    317           && resp[5] == 0)
    318         break;
    319     }
    320 
    321   cmd[1] = 0x05 | m_camAddr;
    322   cmd[2] = 0;
    323   cmd[3] = 0;
    324   cmd[4] = 0;
    325   cmd[5] = 0;
    326 
    327   retries = 0;
    328   while (true)
    329     {
    330       if (retries++ > maxRetries)
    331         {
    332           throw std::runtime_error(std::string(__FUNCTION__) +
    333                                    ": maximum retries exceeded");
    334           return false;
    335         }
    336 
    337       drainInput();
    338       writeData(cmd, pktLen);
    339       if (readData(resp, pktLen) != pktLen)
    340         continue;
    341 
    342       if (resp[0] == 0xaa
    343           && resp[1] == (0x0e | m_camAddr)
    344           && resp[2] == 0x05
    345           && resp[4] == 0
    346           && resp[5] == 0)
    347         break;
    348     }
    349 
    350   cmd[1] = 0x04 | m_camAddr;
    351   cmd[2] = 0x01;
    352 
    353   retries = 0;
    354   while (true)
    355     {
    356       if (retries++ > maxRetries)
    357         {
    358           throw std::runtime_error(std::string(__FUNCTION__) +
    359                                    ": maximum retries exceeded");
    360           return false;
    361         }
    362 
    363       drainInput();
    364       writeData(cmd, 6);
    365 
    366       if (readData(resp, pktLen) != pktLen)
    367         continue;
    368 
    369       if (resp[0] == 0xaa
    370           && resp[1] == (0x0e | m_camAddr)
    371           && resp[2] == 0x04
    372           && resp[4] == 0
    373           && resp[5] == 0)
    374         {
    375           if (!dataAvailable(1000))
    376             continue;
    377 
    378           if (readData(resp, pktLen) != pktLen)
    379             continue;
    380 
    381           if (resp[0] == 0xaa
    382               && resp[1] == (0x0a | m_camAddr)
    383               && resp[2] == 0x01)
    384             {
    385               m_picTotalLen = (resp[3]) | (resp[4] << 8) | (resp[5] << 16);
    386               break;
    387             }
    388         }
    389     }
    390 
    391   return true;
    392 }
    393 
    394 bool GROVESCAM::storeImage(const char *fname)
    395 {
    396   if (!fname)
    397     {
    398       throw std::invalid_argument(std::string(__FUNCTION__) +
    399                                   ": filename is NULL");
    400       return false;
    401     }
    402 
    403   if (!m_picTotalLen)
    404     {
    405       throw std::runtime_error(std::string(__FUNCTION__) +
    406                     ": Picture length is zero, you need to capture first.");
    407 
    408       return false;
    409     }
    410 
    411   FILE *file = fopen(fname, "wb");
    412 
    413   if (!file)
    414     {
    415       throw std::runtime_error(std::string(__FUNCTION__) +
    416                                ": fopen() failed: " +
    417                                string(strerror(errno)));
    418       return false;
    419     }
    420 
    421   /// let the games begin...
    422   const unsigned int pktLen = 6;
    423   unsigned int pktCnt = (m_picTotalLen) / (MAX_PKT_LEN - 6);
    424   if ((m_picTotalLen % (MAX_PKT_LEN-6)) != 0)
    425     pktCnt += 1;
    426 
    427   uint8_t cmd[pktLen] = { 0xaa, static_cast<uint8_t>(0x0e | m_camAddr), 0x00,
    428                           0x00, 0x00, 0x00 };
    429   uint8_t pkt[MAX_PKT_LEN];
    430   int retries = 0;
    431 
    432   for (unsigned int i = 0; i < pktCnt; i++)
    433     {
    434       cmd[4] = i & 0xff;
    435       cmd[5] = (i >> 8) & 0xff;
    436 
    437       retries = 0;
    438 
    439     retry:
    440 
    441       usleep(10000);
    442 
    443       drainInput();
    444       writeData(cmd, pktLen);
    445 
    446       if (!dataAvailable(1000))
    447         {
    448           if (retries++ > maxRetries)
    449             {
    450               throw std::runtime_error(std::string(__FUNCTION__) +
    451                                        ": timeout, maximum retries exceeded");
    452               return false;
    453             }
    454           goto retry;
    455         }
    456 
    457       uint16_t cnt = readData(pkt, MAX_PKT_LEN);
    458 
    459       unsigned char sum = 0;
    460       for (int y = 0; y < cnt - 2; y++)
    461         {
    462           sum += pkt[y];
    463         }
    464       if (sum != pkt[cnt-2])
    465         {
    466           if (retries++ <= maxRetries)
    467             goto retry;
    468           else
    469             {
    470               throw std::runtime_error(std::string(__FUNCTION__) +
    471                                        ": cksum error, maximum retries exceeded");
    472               return false;
    473             }
    474         }
    475 
    476       fwrite((const uint8_t *)&pkt[4], cnt - 6, 1, file);
    477     }
    478 
    479   cmd[4] = 0xf0;
    480   cmd[5] = 0xf0;
    481   writeData(cmd, pktLen);
    482 
    483   fclose(file);
    484 
    485   // reset the pic length to 0 for another run.
    486   m_picTotalLen = 0;
    487 
    488   return true;
    489 }
    490