/ EyeLights_Bluetooth_Scroller / packetParser.cpp
packetParser.cpp
  1  // SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  #include <bluefruit.h>
  6  
  7  // packetbuffer holds inbound data
  8  #define READ_BUFSIZE 20
  9  uint8_t packetbuffer[READ_BUFSIZE + 1]; // +1 is for NUL string terminator
 10  
 11  /**************************************************************************/
 12  /*!
 13      @brief    Casts the four bytes at the specified address to a float.
 14      @param    ptr  Pointer into packet buffer.
 15      @returns  Floating-point value.
 16  */
 17  /**************************************************************************/
 18  float parsefloat(uint8_t *ptr) {
 19    float f;            // Make a suitably-aligned float variable,
 20    memcpy(&f, ptr, 4); // because in-buffer instance might be misaligned!
 21    return f;           // (You can't always safely parse in-place)
 22  }
 23  
 24  /**************************************************************************/
 25  /*!
 26      @brief  Prints a series of bytes in 0xNN hexadecimal notation.
 27      @param  buf  Pointer to array of byte data.
 28      @param  len  Data length in bytes.
 29  */
 30  /**************************************************************************/
 31  void printHex(const uint8_t *buf, const uint32_t len) {
 32    for (uint32_t i=0; i < len; i++) {
 33      Serial.print(F("0x"));
 34      if (buf[i] <= 0xF) Serial.write('0'); // Zero-pad small values
 35      Serial.print(buf[i], HEX);
 36      if (i < (len - 1)) Serial.write(' '); // Space between bytes
 37    }
 38    Serial.println();
 39  }
 40  
 41  static const struct { // Special payloads from Bluefruit Connect app...
 42    char    id;         // Packet type identifier
 43    uint8_t len;        // Size of complete, well-formed packet of this type
 44  } _app_packet[] = {
 45    {'A', 15}, // Accelerometer
 46    {'G', 15}, // Gyro
 47    {'M', 15}, // Magnetometer
 48    {'Q', 19}, // Quaterion
 49    {'B',  5}, // Button
 50    {'C',  6}, // Color
 51    {'L', 15}, // Location
 52  };
 53  #define NUM_PACKET_TYPES (sizeof _app_packet / sizeof _app_packet[0])
 54  #define SHORTEST_PACKET_LEN 5 // Button, for now
 55  
 56  /**************************************************************************/
 57  /*!
 58      @brief    Given packet data, identify if it's one of the known
 59                Bluefruit Connect app packet types.
 60      @param    buf  Pointer to packet data.
 61      @param    len  Size of packet in bytes.
 62      @returns  Packet type index (0 to NUM_PACKET_TYPES-1) if recognized,
 63                -1 if unrecognized.
 64  */
 65  /**************************************************************************/
 66  int8_t packetType(uint8_t *buf, uint8_t len) {
 67    if ((len >= SHORTEST_PACKET_LEN) && (buf[0] == '!')) {
 68      for (int8_t type=0; type<NUM_PACKET_TYPES; type++) {
 69        if ((buf[1] == _app_packet[type].id) &&
 70            (len == _app_packet[type].len)) {
 71          return type;
 72        }
 73      }
 74    }
 75    return -1; // Length too short for a packet, or not a recognized type
 76  }
 77  
 78  /**************************************************************************/
 79  /*!
 80      @brief    Wait for incoming data and determine if it's one of the
 81                special Bluefruit Connect app packet types.
 82      @param    ble      Pointer to BLE UART object.
 83                timeout  Character read timeout in milliseconds.
 84      @returns  Length of data, or 0 if checksum is invalid for the type of
 85                packet detected.
 86      @note     Packet buffer is not cleared. Calling function is expected
 87                to check return value before deciding whether to act on the
 88                data.
 89  */
 90  /**************************************************************************/
 91  uint8_t readPacket(BLEUart *ble, uint16_t timeout) {
 92    int8_t   type = -1; // App packet type, -1 if unknown or freeform string
 93    uint8_t  len = 0, xsum = 255; // Packet length and ~checksum so far
 94    uint32_t now, start_time = millis();
 95    do {
 96      now = millis();
 97      if (ble->available()) {
 98        char c = ble->read();
 99        if (c == '!') { // '!' resets buffer to start
100          len = 0;
101          xsum = 255;
102        }
103        packetbuffer[len++] = c;
104        // Stop when buffer's full or packet type recognized
105        if ((len >= READ_BUFSIZE) ||
106            ((type = packetType(packetbuffer, len)) >= 0)) break;
107        start_time = now; // Reset timeout on char received
108        xsum -= c;        // Not last char, do checksum math
109        type = -1;        // Reset packet type finder
110      }
111    } while((now - start_time) < timeout);
112  
113    // If packet type recognized, verify checksum (else freeform string)
114    if ((type >= 0) && (xsum != packetbuffer[len-1])) { // Last byte = checksum
115      Serial.print("Packet checksum mismatch: ");
116      printHex(packetbuffer, len);
117      return 0;
118    }
119  
120    packetbuffer[len] = 0; // Terminate packet string
121  
122    return len; // Checksum is valid for packet, or it's a freeform string
123  }