Initial commit

This commit is contained in:
2024-02-09 21:33:45 -05:00
commit 87d17bc0d1
25 changed files with 1146 additions and 0 deletions

38
lib/Board/BoardLayout.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "BoardLayout.h"
uint8_t BoardLayout::GetNumberStacks(){
return this->boardWidth * this->boardHeight;
}
void BoardLayout::SetStackColors(uint8_t stackNum, Color * colors){
CubeStack * stack = this->stacks[stackNum];
stack->SetLEDColors(colors, this->boardHeight);
}
bool BoardLayout::BoardStateHasChanged(){
uint16_t boardState[this->boardWidth * this->boardLength];
this->GetBoardState(boardState);
// compare the board state to the last board state
for(int i = 0; i < (this->boardWidth * this->boardLength); i++){
uint16_t stackState = boardState[i];
uint16_t lastStackState = (this->lastBoardState)[i];
if(stackState != lastStackState){
// copy the board state into the last board state
for(int k = 0; k < (this->boardWidth * this->boardLength); k++){
this->lastBoardState[k] = boardState[k];
}
return true;
}
}
return false;
}
void BoardLayout::GetBoardState(uint16_t * boardStateBuffer){
for(int i = 0; i < (this->boardLength * this->boardWidth); i++){
CubeStack * stack = this->stacks[i];
stack->SendLEDData(); // Enable this if you want to constantly stream LED data
boardStateBuffer[i] = stack->GetNumberCubes();
}
}

69
lib/Board/BoardLayout.h Normal file
View File

@@ -0,0 +1,69 @@
/**
* @brief This is the full board manager which handles the state of every stack on the board
*/
#pragma once
#include "CubeStack.h"
#include "Color.h"
class BoardLayout{
public:
/**
* @brief BoardLayout COnstructor
*/
BoardLayout(uint8_t boardWidth, uint8_t boardLength, uint8_t boardHeight, CubeStack ** stacks) :
boardWidth(boardWidth),
boardLength(boardLength),
boardHeight(boardHeight),
stacks(stacks)
{
this->lastBoardState = new uint16_t[boardWidth * boardLength];
}
/**
* @brief Check if our board state has changed
* @return true if the board state has changed, false otherwise
*/
bool BoardStateHasChanged();
/**
* @brief Get the Number of Stacks
* @return the number of stacks
*/
uint8_t GetNumberStacks();
/**
* @brief Set the LED Colors
* @param stackNum the stack index you would like to address.
* From top left to bottom right, the stack numbers are as follows:
* | 0 1 2 |
* | 3 4 5 |
* | 6 7 8 |
* @param Colors the array of colors to set the LEDs in a stack to
*/
void SetStackColors(uint8_t stackNum, Color * colors);
/**
* @brief Get the board population state
* @param boardStateBuffer the buffer to write the board state to. It must be at least boardWidth * boardLength in length
*/
void GetBoardState(uint16_t * boardStateBuffer);
private:
uint8_t boardWidth;
uint8_t boardLength;
uint8_t boardHeight;
/*
An array of arrays of stacks
[ [stack1, stack2, stack3],
[stack4, stack5, stack6],
[stack7, stack8, stack9] ]
etc
*/
CubeStack ** stacks;
// records the last known board state
uint16_t * lastBoardState;
};

21
lib/Board/Color.h Normal file
View File

@@ -0,0 +1,21 @@
/**
* @file Color.h
* @brief This file contains the color struct
*/
#pragma once
#include <Arduino.h>
// store a color
struct Color{
// create a constructor for this struct
Color(uint8_t red, uint8_t green, uint8_t blue) :
red(red),
green(green),
blue(blue)
{}
Color() = default;
uint8_t red{0};
uint8_t green{0};
uint8_t blue{0};
};

77
lib/Board/CubeStack.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "CubeStack.h"
CubeStack::CubeStack(uint16_t ADCPin, uint16_t ledPin, uint8_t numLEDs){
this->ADCPin = ADCPin;
this->blockLights = *(new Adafruit_NeoPixel(numLEDs*2, ledPin, NEO_GRB + NEO_KHZ800));
this->ledColors = new Color[numLEDs];
this->numLEDs = numLEDs;
// initialize the LED colors to off
for(int i = 0; i < numLEDs; i++){
this->ledColors[i] = *(new Color(0, 0, 0));
}
};
uint8_t CubeStack::GetNumberCubes(){
// read the ADC and return the number of cubes
/*
0 cubes: 1 : 4095-3071
1 cube: 1/2 3070-1706
2 cubes: 1/3 1705-1195
3 cubes: 1/4 1195-0
*/
uint16_t value = analogRead(this->ADCPin);
this->lowPassADCRead = static_cast<uint16_t>((static_cast<float>(this->lowPassADCRead) * 0.9) + (static_cast<float>(value) * 0.1));
// temporary definitions to define value ranges:
uint16_t zeroCubesHigh = 4095;
uint16_t zeroCubesLow = 3071;
uint16_t oneCubeLow = 1706;
uint16_t twoCubesLow = 1000;
uint16_t threeCubesLow = 0;
uint8_t stackHeight = 0;
if(this->lowPassADCRead >= zeroCubesLow && this->lowPassADCRead <= zeroCubesHigh){
stackHeight = 0;
}
else if(this->lowPassADCRead >= oneCubeLow){
stackHeight = 1;
}
else if(this->lowPassADCRead >= twoCubesLow){
stackHeight = 2;
}
else if(this->lowPassADCRead >= threeCubesLow){
stackHeight = 3;
}
if(this->lastStackHeight != stackHeight){
this->lastStackHeight = stackHeight;
this->SendLEDData();
}
return stackHeight;
}
void CubeStack::SetLEDColors(Color * colors, uint8_t numColors){
// copy the colors into the ledColors array
for(int i = 0; i < numColors; i++){
this->ledColors[i].red = colors[i].red;
this->ledColors[i].green = colors[i].green;
this->ledColors[i].blue = colors[i].blue;
}
this->SendLEDData();
}
void CubeStack::SendLEDData(){
// we always initialize before we do anything because other CubeStacks could be hogging the hardware
// between our writes
this->blockLights.begin();
// set the LED colors
for(int i = 0; i < this->numLEDs; i++){
this->blockLights.setPixelColor(i*2, this->blockLights.Color(this->ledColors[i].red, this->ledColors[i].green, this->ledColors[i].blue));
this->blockLights.setPixelColor((i*2 + 1), this->blockLights.Color(this->ledColors[i].red, this->ledColors[i].green, this->ledColors[i].blue));
}
this->blockLights.show();
}

52
lib/Board/CubeStack.h Normal file
View File

@@ -0,0 +1,52 @@
/**
* @brief this manages a single cube stack and the lighting / detecting of how many cubes
*/
#pragma once
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include "Color.h"
class CubeStack{
public:
/**
* @brief Construct a new Cube Stack object
* @param ADCPin the pin that the ADC is connected to
* @param ledPin the pin that the LED is connected to
*/
CubeStack(uint16_t ADCPin, uint16_t ledPin, uint8_t numLEDs);
/**
* @brief Returns the number of cubes in the stack
* @return the number of cubes in the stack
*/
uint8_t GetNumberCubes();
/**
* @brief Set the led color array and then send the data to the LED strip
* @param colors the array of colors to set the LEDs to
* @param numColors the number of colors in the array
*/
void SetLEDColors(Color * colors, uint8_t numColors);
/**
* @brief sends the LED data to the LED strip
*/
void SendLEDData();
private:
uint8_t ADCPin;
// we will probably need a pointer to a fastled object here
Adafruit_NeoPixel blockLights;
uint16_t lowPassADCRead{0};
// store the Color of each LED
Color * ledColors;
uint8_t numLEDs;
uint8_t lastStackHeight{0};
};

View File

@@ -0,0 +1,48 @@
#include "ColorManager.h"
void ColorManager::Update(){
if(!(this->enabled)){
return;
}
// go through our colors and have them fade from r->g->b->r
for(uint8_t i = 0; i < 9; i++){
for(uint8_t j = 0; j < 3; j++){
Color * color = this->colors[i][j];
// fade from red to green
if(color->red > 0 && color->green >= 0 && color->blue == 0){
color->red--;
color->green++;
}
// fade from green to blue
else if(color->green > 0 && color->blue >= 0 && color->red == 0){
color->green--;
color->blue++;
}
// fade from blue to red
else if(color->blue > 0 && color->red >= 0 && color->green == 0){
color->blue--;
color->red++;
}
}
}
// set the colors
for(uint8_t i = 0; i < 9; i++){
Color temp_colors[3] = {*(this->colors[i][0]), *(this->colors[i][1]), *(this->colors[i][2])};
this->board->SetStackColors(i, temp_colors);
}
}
void ColorManager::Enable(bool enable){
this->enabled = enable;
if(this->enabled == false){
// set all the colors to black
Color black(0, 0, 0);
Color temp_colors[3] = {black, black, black};
// set the colors
for(uint8_t i = 0; i < 9; i++){
this->board->SetStackColors(i, temp_colors);
}
}
}

View File

@@ -0,0 +1,44 @@
/**
* @file ColorManager.h
* @brief Generate pretty colors for the board and make it do something when unity isn't controlling it
*/
#pragma once
#include "BoardLayout.h"
#include "Color.h"
class ColorManager{
public:
ColorManager(BoardLayout * board) :
board(board)
{}
/**
* @brief Allows the color manager to update the board colors
*/
void Update();
/**
* @brief Enables or disables the color manager
* @param enable true to enable, false to disable
*/
void Enable(bool enable);
private:
BoardLayout * board;
bool enabled{true};
Color * colors[9][3] = {
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)}
};
};

46
lib/README Normal file
View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,58 @@
#include "BluetoothSerialMessage.h"
BluetoothSerialMessage::BluetoothSerialMessage(BluetoothSerial *serial){
this->serial = serial;
}
void BluetoothSerialMessage::Init(unsigned int baud_rate){
// Don't need to do anything here, just let the user init bluetooth serial
}
void BluetoothSerialMessage::PrintArgs(){
serial->print("Current number of args: ");
serial->println(populated_args);
for (int i = 0; i < populated_args; i++) {
serial->print(args[i]);
serial->print(" ");
}
serial->println();
}
void BluetoothSerialMessage::readSerial(){
boolean recvInProgress = false;
byte ndx = 0;
char c;
// read the incoming serial data:
while (serial->available() > 0 && data_recieved == false) {
// get the neext character in the serial buffer
c = serial->read();
Serial.print(c);
// only execute this if the startMarker has been received
if (recvInProgress == true) {
// if the incoming character is not the endMarker...
if (c != endMarker) {
// add it to the data array
data[ndx] = c;
ndx++; // increment the data array index
// if the index is greater than the maximum data array size,
// keep overwriting the last element until the endMarker is received.
if (ndx >= num_chars) {
ndx = num_chars - 1;
}
}
// if the incoming character is the endMarker clean up and set the flags
else {
data[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
data_recieved = true;
}
}
// if the incoming character is the startMarker, set the recvInProgress flag
else if (c == startMarker) {
recvInProgress = true;
}
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include "SerialMessage.h"
#include <BluetoothSerial.h>
class BluetoothSerialMessage : public SerialMessage{
public:
/**
* @brief Construct a new Bluetooth Serial Message object
*/
BluetoothSerialMessage(BluetoothSerial *serial);
/**
* @brief Initialize the BluetoothSerialMessage object
*/
void Init(unsigned int baud_rate = 115200) override;
/**
* @brief prints the args array to the serial monitor
*/
void PrintArgs() override;
private:
/**
* @brief reads the serial data and stores it in the data array
*/
void readSerial() override;
BluetoothSerial *serial;
};

View File

@@ -0,0 +1,111 @@
/**
* @file SerialMessage.cpp
* @brief This file contains the SerialMessage class
* @details This file contains the SerialMessage class which is used to parse serial messages
* @version 1.0.0
* @author Quinn Henthorne. Contact: quinn.henthorne@gmail.com
*/
#include "SerialMessage.h"
SerialMessage::SerialMessage(HardwareSerial *serial) :
serial(serial){}
void SerialMessage::Init(unsigned int baud_rate){
serial->begin(baud_rate);
}
void SerialMessage::readSerial(){
char c;
// read the incoming serial data:
while (serial->available() > 0 && data_recieved == false) {
// get the neext character in the serial buffer
c = serial->read();
// only execute this if the startMarker has been received
// if the incoming character is the endMarker clean up and set the flags
if (recvInProgress == true) {
if (c == endMarker) {
data[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
data_recieved = true;
}
// if the incoming character is not the endMarker
else {
// add it to the data array
data[ndx] = c;
ndx++; // increment the data array index
// if the index is greater than the maximum data array size,
// keep overwriting the last element until the endMarker is received.
if (ndx >= num_chars) {
ndx = num_chars - 1;
}
}
}
// if the incoming character is the startMarker, set the recvInProgress flag
else if (c == startMarker) {
recvInProgress = true;
}
}
}
void SerialMessage::parseData() { // split the data into its parts
this->populated_args = 0; // reset the populated args counter
char * indx; // this is used by strtok() as an index
int i = 0;
indx = strtok(temp_data, ","); // get the first part - the string
while(indx != NULL){
this->args[i] = atoi(indx);
populated_args++;
i++;
indx = strtok(NULL, ","); // this continues where the previous call left off
}
}
void SerialMessage::Update(){
readSerial();
if (data_recieved == true) {
// for debug only:
// Serial.print("Received:");
// Serial.print(data);
// Serial.println(":End");
strcpy(temp_data, data);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
//PrintArgs();
data_recieved = false;
new_data = true;
}
}
bool SerialMessage::IsNewData(){
return new_data;
}
void SerialMessage::ClearNewData(){
new_data = false;
}
int * SerialMessage::GetArgs(){
return args;
}
int SerialMessage::GetArgsLength(){
return args_length;
}
int SerialMessage::GetPopulatedArgs(){
return populated_args;
}
void SerialMessage::PrintArgs(){
serial->print("Current number of args: ");
serial->println(populated_args);
for (int i = 0; i < populated_args; i++) {
serial->print(args[i]);
serial->print(" ");
}
serial->println();
}

View File

@@ -0,0 +1,87 @@
/**
* @file SerialMessage.h
* @brief This file contains the SerialMessage class
* @details This file contains the SerialMessage class which is used to parse serial messages
* @version 1.0.0
* @author Quinn Henthorne. Contact: quinn.henthorne@gmail.com
*/
#ifndef SERIALMESSAGE_H
#define SERIALMESSAGE_H
#include "Arduino.h"
#define num_chars 500
class SerialMessage{
public:
/**
* @brief Construct a new Serial Message object
*/
SerialMessage(HardwareSerial *serial = &Serial);
/**
* @brief Initialize the SerialMessage object
*/
virtual void Init(unsigned int baud_rate = 115200);
/**
* @brief Update the SerialMessage object and parse any data that's available
*/
void Update();
/**
* @brief Returns true if there is new data available
* @return true if there is new data available
*/
bool IsNewData();
/**
* @brief Clears the new data flag
*/
virtual void ClearNewData();
/**
* @brief Return a pointer to the args array
* @return a pointer to the args array
*/
int * GetArgs();
/**
* @brief Returns the number of args that have been populated for the current message
* @return the number of args that have been populated for the current message
*/
int GetArgsLength();
/**
* @brief Returns the number of args that have been populated for the current message
* @return the number of args that have been populated for the current message
*/
int GetPopulatedArgs();
/**
* @brief Prints the args array to the serial monitor
*/
virtual void PrintArgs();
protected:
virtual void readSerial();
virtual void parseData();
bool new_data = false;
bool data_recieved = false;
bool recvInProgress = false;
char data[num_chars]; // an array to store the received data
char temp_data[num_chars]; // an array that will be used with strtok()
uint16_t ndx = 0;
const static int args_length = 30;
int populated_args = 0; // the number of args that have been populated for the current message
int args[args_length];
const char startMarker = '!';
const char endMarker = ';';
private:
HardwareSerial *serial;
};
#endif