text: /*
   * Author: Jon Trulson <jtrulson@ics.com>
   * Copyright (c) 2016 Intel Corporation.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #include <string.h>
  #include <assert.h>
  
  #include "upm_utilities.h"
  #include "hka5.h"
  
  #define HKA5_BAUDRATE 9600 // Only baud supported
  #define HKA5_PKT_SIZE 32
  #define HKA5_PKT_START1 0x42
  #define HKA5_PKT_START2 0x4d
  
  static bool verify_cksum(uint8_t *pkt)
  {
    uint16_t pkt_cksum = (pkt[30] << 8) | pkt[31];
  
    int i;
    uint16_t cksum = 0;
    for (i=0; i<(HKA5_PKT_SIZE - 2); i++)
      cksum += pkt[i];
  
    if (pkt_cksum == cksum)
      return true; // all good
    else
      return false; // :(
  }
  
  hka5_context hka5_init(unsigned int uart, int set_pin, int reset_pin)
  {
    hka5_context dev =
      (hka5_context)malloc(sizeof(struct _hka5_context));
  
    if (!dev)
      return NULL;
  
    // zero out context
    memset((void *)dev, 0, sizeof(struct _hka5_context));
  
    dev->uart = NULL;
    dev->gpio_reset = NULL;
    dev->gpio_set = NULL;
  
    // initialize the MRAA contexts
  
    // uart, default should be 8N1
    if (!(dev->uart = mraa_uart_init(uart)))
      {
        printf("%s: mraa_uart_init() failed.\n", __FUNCTION__);
        hka5_close(dev);
        return NULL;
      }
  
    if (mraa_uart_set_baudrate(dev->uart, HKA5_BAUDRATE))
      {
        printf("%s: mraa_uart_set_baudrate(%d) failed.\n", __FUNCTION__,
               HKA5_BAUDRATE);
        hka5_close(dev);
        return NULL;
      }
  
    mraa_uart_set_flowcontrol(dev->uart, false, false);
  
    // now the set_pin, if enabled
    if (set_pin >= 0)
      {
        if (!(dev->gpio_set = mraa_gpio_init(set_pin)))
          {
            printf("%s: mraa_gpio_init(set) failed.\n", __FUNCTION__);
            hka5_close(dev);
            return NULL;
          }
  
        mraa_gpio_dir(dev->gpio_set, MRAA_GPIO_OUT);
  
        // wake up
        hka5_enable(dev, true);
      }
  
    // now the reset_pin, if enabled
    if (set_pin >= 0)
      {
        if (!(dev->gpio_reset = mraa_gpio_init(reset_pin)))
          {
            printf("%s: mraa_gpio_init(reset) failed.\n", __FUNCTION__);
            hka5_close(dev);
            return NULL;
          }
  
        mraa_gpio_dir(dev->gpio_reset, MRAA_GPIO_OUT);
  
        // reset
        hka5_reset(dev);
      }
    return dev;
  }
  
  void hka5_close(hka5_context dev)
  {
    assert(dev != NULL);
  
    // sleep
    hka5_enable(dev, false);
  
    if (dev->uart)
      mraa_uart_stop(dev->uart);
    if (dev->gpio_set)
      mraa_gpio_close(dev->gpio_set);
    if (dev->gpio_reset)
      mraa_gpio_close(dev->gpio_reset);
  
    free(dev);
  }
  
  upm_result_t hka5_enable(const hka5_context dev, bool enable)
  {
    assert(dev != NULL);
  
    if (!dev->gpio_set)
      return UPM_ERROR_NO_RESOURCES;
  
    if (enable)
      mraa_gpio_write(dev->gpio_set, 1);
    else
      mraa_gpio_write(dev->gpio_set, 0);
  
    return UPM_SUCCESS;
  }
  
  upm_result_t hka5_reset(const hka5_context dev)
  {
    assert(dev != NULL);
  
    if (!dev->gpio_reset)
      return UPM_ERROR_NO_RESOURCES;
  
    mraa_gpio_write(dev->gpio_set, 0);
    upm_delay(1);
    mraa_gpio_write(dev->gpio_set, 1);
    upm_delay(1);
  
  return UPM_SUCCESS;
  }
  
  upm_result_t hka5_update(const hka5_context dev)
  {
    assert(dev != NULL);
  
    uint8_t pkt[HKA5_PKT_SIZE];
    int idx = 0;
    bool done = false;
    char byte;
  
    memset((void *)pkt, 0, HKA5_PKT_SIZE);
  
    while (!done)
      {
        // wait up to 2 seconds for data and start
        if (mraa_uart_data_available(dev->uart, 2000))
          {
            while ((mraa_uart_read(dev->uart, &byte, 1) == 1) &&
                   idx < HKA5_PKT_SIZE)
              {
                // first look for a byte starting with HKA5_PKT_START1
                if (idx == 0 && byte != HKA5_PKT_START1)
                  continue;
  
                // look for second byte, make sure it matches
                // HKA5_PKT_START1.  Start over if it's not found.
                if (idx == 1 && byte != HKA5_PKT_START2)
                  {
                    // start over
                    idx = 0;
                    continue;
                  }
  
                // else we found the byte, store it and start reading
                // the rest
                pkt[idx++] = (uint8_t)byte;
              }
  
            if (idx == HKA5_PKT_SIZE)
              {
                done = true;
              }
            else
              {
                // error
                printf("%s: read failed.\n", __FUNCTION__);
                return UPM_ERROR_OPERATION_FAILED;
              }
          }
        else
          {
            printf("%s: read timed out.\n", __FUNCTION__);
            return UPM_ERROR_TIMED_OUT;
          }
      }
  
    // we have our data
    if (verify_cksum(pkt))
      {
        dev->pm1   = (pkt[4] << 8) | pkt[5];
        dev->pm2_5 = (pkt[6] << 8) | pkt[7];
        dev->pm10  = (pkt[8] << 8) | pkt[9];
      }
    else
      {
        printf("%s: checksum failure.\n", __FUNCTION__);
        // could fail here, but occasional cksum failures are not unusual...
      }
  
    return UPM_SUCCESS;
  }
  
  unsigned int hka5_get_pm1(const hka5_context dev)
  {
    assert(dev != NULL);
  
    return (unsigned int)dev->pm1;
  }
  
  unsigned int hka5_get_pm2_5(const hka5_context dev)
  {
    assert(dev != NULL);
  
    return (unsigned int)dev->pm2_5;
  }
  
  unsigned int hka5_get_pm10(const hka5_context dev)
  {
    assert(dev != NULL);
  
    return (unsigned int)dev->pm10;
  }
  