Hi all,
First of all please forgive the not so explanatory topic name and the very newbie questions that will follow, what I'm trying to do far exceeds my coding knowledge.
I have a LED controller(Pixelblaze) that connects to an external sensor board that adds some extra functionality(more analog pins, accelerometer, sound, light sensor) via an RX pin. For a project I'm currently making(a LED controller for my kids), I need more analog pins than the board can provide. As I don't need the sensor board itself, I want to use an Arduino in its stead and take advantage of the extra analog pins. Pixelblaze expects 5 analog pins coming from the sensor board, but I can send the data of the rest in the bytes used for the light sensor or accelerometer.
The sensor board is open source so there is enough documentation available, but given my limited understanding I don't know how to replicate the stream.
From the board's GitHub page:
The protocol is fairly simple:
- Each frame starts with "SB1.0" including a null character (6 bytes).
- The frequency information follows, as 32 x 16-bit unsigned integers.
- Then is the audio energy average, max frequency magnitiude, max frequency Hz, all 3 as 16-bit unsigned ints.
- Next the accelerometer information as 3 x 16-bit signed integers.
- The data from the Light sensor is next, as a single 16-bit unsigned integer.
- Followed by the 5 x 16-bit analog inputs (12-bit resolution, shifted up to 16 bits)
- Finally "END" including a null character (4 bytes).
If I understand this correctly, the total byte count for the above is 98. I tried using code from similar topics to read the stream, like Robin2's example from this thread, but I don't understand how to read(or recreate for that matter) data that has a string in the header(SB1.0) or how to place that in the startMarker bit of the above example, so all I get is endless rows that always start with different values.
I converted SB1.0 to bytes and did this:
const byte startMarker = (0x53, 0x42, 0x31, 0x2E, 0x30);
but I get nothing back in Serial Monitor.
The sensor board has an Arduino library that I guess makes it even easier for someone who understands, but not me...
//
// Created by Ben Hencke on 2019-07-28.
//
#ifndef SB_ARDUINO_PIXELBLAZESENSORBOARD_H
#define SB_ARDUINO_PIXELBLAZESENSORBOARD_H
#include <Arduino.h>
#ifndef ICACHE_RAM_ATTR
#define ICACHE_RAM_ATTR
#endif
typedef struct {
// char header[6]; // "SB1.0\0"
uint16_t frequencyData[32];
uint16_t energyAverage;
uint16_t maxFrequencyMagnitude;
uint16_t maxFrequency; //in hz
int16_t accelerometer[3];
uint16_t light;
uint16_t analogInputs[5];
// char end[4]; // "END\0"
} __attribute__((__packed__)) SB10Frame;
class PixelblazeSensorBoard {
public:
Stream &source;
enum {HEADER, DATA, END} mode = HEADER;
uint8_t pos = 0;
SB10Frame sb10Frame;
uint8_t frameBuffer[sizeof(SB10Frame)];
unsigned long lastFrameTime = 0;
PixelblazeSensorBoard(Stream &source) : source(source) {
}
uint16_t frequencyData(int index) {
if (index < 0 || index > 31)
return 0;
return sb10Frame.frequencyData[index];
}
uint16_t energyAverage() {
return sb10Frame.energyAverage;
}
uint16_t maxFrequencyMagnitude() {
return sb10Frame.maxFrequencyMagnitude;
}
uint16_t maxFrequency() {
return sb10Frame.maxFrequency;
}
int16_t accelerometerX() {
return sb10Frame.accelerometer[1];
}
int16_t accelerometerY() {
return sb10Frame.accelerometer[0];
}
int16_t accelerometerZ() {
return sb10Frame.accelerometer[2];
}
uint16_t light() {
return sb10Frame.light;
}
uint16_t analogInput(int index) {
if (index < 0 || index > 4)
return 0;
return sb10Frame.analogInputs[index];
}
unsigned long dataAgeMillis() {
return millis() - lastFrameTime;
}
bool readNextFrame() {
if (source.available() < 1)
return false;
unsigned long timeout = 15;
unsigned long startMs = millis();
do {
int c = source.read();
if (c < 0)
continue;
if (processFrameByte(c))
return true;
} while (millis() - startMs < timeout);
//in event of timeout reset state to sync back to header
mode = HEADER;
pos = 0;
return false;
}
void ICACHE_RAM_ATTR readAvailable() {
int c;
while ((c = source.read()) >= 0) {
processFrameByte(c);
}
}
bool ICACHE_RAM_ATTR processFrameByte(uint8_t c) {
bool result = false;
switch (mode) {
case HEADER:
if (c != "SB1.0"[pos++]) //sync to header
pos = 0;
if (pos == 6) { //full header found, including null byte
mode = DATA;
pos = 0;
}
break;
case DATA:
frameBuffer[pos++] = c;
if (pos == sizeof(SB10Frame)) {
mode = END;
pos = 0;
}
break;
case END:
if (c != "END"[pos++]) {//sync to header
//lost sync somewhere in the middle, throw away the frame
mode = HEADER;
pos = 0;
}
if (pos == 4) { //full footer found, including null byte
mode = HEADER;
pos = 0;
memcpy(&sb10Frame, frameBuffer, sizeof(SB10Frame));
lastFrameTime = millis();
result = true;
}
}
return result;
}
};
#endif //SB_ARDUINO_PIXELBLAZESENSORBOARD_H
I already know how to read values from the analog inputs and setup my sketch for that. What I don't know is how to send the values in the above way.
With my limited understanding, I'd say that I have to do the following:
- Setup an array with the different bytes. This, I'm guessing will be in HEX, correct?
- Assign the input values to some of the bytes, say the light sensor and accelerometer, plus the 5 analog inputs already there.
- Send it via Serial at the interval Pixelblaze expects(40ms).
If someone could help me understand a bit, I would appreciate it.