The beginnings of a rework

This commit is contained in:
2026-08-09 21:47:17 -04:00
parent 414a5bb749
commit 02a0bfee2b
13 changed files with 251 additions and 267 deletions

83
diagram.json Normal file
View File

@@ -0,0 +1,83 @@
{
"version": 1,
"author": "Uri Shaked",
"editor": "wokwi",
"parts": [
{
"type": "wokwi-arduino-uno",
"id": "uno",
"top": 100,
"left": 100.78,
"attrs": {
}
},
{
"id": "neopixels",
"type": "wokwi-led-strip",
"top": 0,
"left": 0,
"attrs": {
"pixels": "6"
}
},
{
"id": "matrix",
"type": "wokwi-led-matrix",
"top": -200,
"left": 500,
"attrs": {
"rows": "6",
"cols": "6",
"layout": "serpentine",
"pixelShape": "square"
}
},
{
"id": "matrix2",
"type": "wokwi-led-matrix",
"top": -350,
"left": 500,
"attrs": {
"rows": "6",
"cols": "6",
"layout": "serpentine",
"pixelShape": "square"
}
},
{
"id": "matrix3",
"type": "wokwi-led-matrix",
"top": -500,
"left": 500,
"attrs": {
"rows": "6",
"cols": "6",
"layout": "serpentine",
"pixelShape": "square"
}
}
],
"connections": [
["uno:0", "$serialMonitor:TX", ""],
["uno:1", "$serialMonitor:RX", ""],
["uno:GND.2", "neopixels:VSS", "black"],
["uno:6", "neopixels:DIN", "green"],
["uno:5V", "neopixels:VDD", "red"],
["uno:7", "matrix:DIN", "yellow"],
["uno:5V", "matrix:VDD", "red"],
["uno:GND.3", "matrix:VSS", "black"],
["matrix:DOUT", "matrix2:DIN", "blue"],
["uno:5V", "matrix2:VDD", "red"],
["uno:GND.3", "matrix2:VSS", "black"],
["matrix2:DOUT", "matrix3:DIN", "blue"],
["uno:5V", "matrix3:VDD", "red"],
["uno:GND.3", "matrix3:VSS", "black"]
],
"dependencies": {},
"serialMonitor": {
"collapse": false,
"convertEol": false,
"display": "always",
"newline": "lf"
}
}

View File

@@ -1,33 +0,0 @@
#ifndef ACTION_H
#define ACTION_H
#include "Arduino.h"
#include "functional"
#include "StreamEvent.h"
template <typename T>
class Action {
public:
Action(function<bool(StreamEvent*)> _funcEventMatchesAction, function<T*(StreamEvent*)> _funcInvokeAction,
function<void(Print*)> _funcDescribeAction) : funcEventMatchesAction(_funcEventMatchesAction),
funcInvokeAction(_funcInvokeAction), funcDescribeAction(_funcDescribeAction) {}
bool eventMatchesAction(StreamEvent* event) {
return funcEventMatchesAction(event);
}
void describeAction(Print* printStream) {
funcDescribeAction(printStream);
}
T* invokeAction(StreamEvent* event) {
return funcInvokeAction(event);
}
private:
function<bool(StreamEvent*)> funcEventMatchesAction;
function<T*(StreamEvent*)> funcInvokeAction;
function<void(Print*)> funcDescribeAction;
};
#endif

View File

@@ -2,17 +2,11 @@
#define CLEDCONTROLLERPHYSICALMATRIX_H #define CLEDCONTROLLERPHYSICALMATRIX_H
#include "LEDHAL2D.h" #include "LEDHAL2D.h"
#include "ILEDArrangement.h"
enum ArrangementType {
HORIZONTALSCAN,
HORIZONTALSERPENTINE,
COLUMNSCAN,
COLUMNSERPENTINE
};
class CLEDControllerPhysicalMatrix : public LEDHAL2D { class CLEDControllerPhysicalMatrix : public LEDHAL2D {
public: public:
CLEDControllerPhysicalMatrix(CLEDController* _controller, char* ledName, ArrangementType _arrangement, CLEDControllerPhysicalMatrix(CLEDController* _controller, char* ledName, ILEDArrangement& _arrangement,
int16_t width, int16_t height): LEDHAL2D(width, height, ledName, true), controller(_controller), arrangement(_arrangement) {} int16_t width, int16_t height): LEDHAL2D(width, height, ledName, true), controller(_controller), arrangement(_arrangement) {}
virtual ~CLEDControllerPhysicalMatrix() {} virtual ~CLEDControllerPhysicalMatrix() {}
@@ -33,25 +27,7 @@ class CLEDControllerPhysicalMatrix : public LEDHAL2D {
} }
int16_t XY(int16_t x, int16_t y) { int16_t XY(int16_t x, int16_t y) {
if(arrangement == ArrangementType::HORIZONTALSCAN) { return arrangement.XY(x, y, getWidth(), getHeight());
return getWidth() * y + x;
} else if(arrangement == ArrangementType::COLUMNSCAN) {
return getHeight() * x + y;
} else if(arrangement == ArrangementType::HORIZONTALSERPENTINE) {
if(y & 0x1) {
return y * getWidth() + (getWidth() - 1 - x);
} else {
return y * getWidth() + x;
}
} else if(arrangement == ArrangementType::COLUMNSERPENTINE) {
if(x & 0x1) {
return x * getHeight() + (getHeight() - 1 - y);
} else {
return x * getHeight() + y;
}
} else {
return 0; //How did you get here?
}
} }
uint16_t getNumLEDs() { uint16_t getNumLEDs() {
@@ -60,13 +36,13 @@ class CLEDControllerPhysicalMatrix : public LEDHAL2D {
protected: protected:
void updateLEDs() { void updateLEDs() {
controller->showLeds(); controller->showLeds(255);
} }
private: private:
CLEDController* CLEDController*
controller; controller;
ArrangementType ILEDArrangement&
arrangement; arrangement;
}; };

View File

@@ -13,7 +13,7 @@ class CLEDControllerPhysicalStrip : public LEDHAL {
void setColor(int16_t pixel, CRGB color) { controller->leds()[pixel] = color; } void setColor(int16_t pixel, CRGB color) { controller->leds()[pixel] = color; }
protected: protected:
void updateLEDs() { controller->showLeds(); } void updateLEDs() { controller->showLeds(255); }
private: private:

View File

@@ -1,41 +0,0 @@
#ifndef GULLSMANAGER_H
#define GULLSMANAGER_H
#include "Arduino.h"
#include "Action.h"
#include "LEDHAL.h"
#include "AnimationBase.h"
class GULLSManager {
public:
GULLSManager(Stream* _stream, uint16_t _maxHALs, uint16_t _maxAnimations,
uint16_t _maxResponseActions, uint16_t _maxAnimationBuilderActions);
bool addLEDHAL(LEDHAL* hal);
bool addAnimation(AnimationBase* animation);
bool addResponseAction(Action<char>* action);
bool addAnimationBuilderAction(Action<AnimationBase>* action);
void update();
private:
uint16_t
maxHALs,
maxAnimations,
maxResponseActions,
maxAnimationBuilderActions;
Stream*
stream;
LEDHAL**
hals;
AnimationBase**
animations;
Action<char>**
responseActions;
Action<AnimationBase>**
animationBuilderActions;
};
#endif

15
include/ILEDArrangement.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef ILEDARRANGEMENT_H
#define ILEDARRANGEMENT_H
#include <Arduino.h>
// Interface is not pure virtual
class ILEDArrangement {
public:
ILEDArrangement() {}
virtual ~ILEDArrangement() {}
virtual int16_t XY(int16_t x, int16_t y, int16_t width, int16_t height) = 0;
};
#endif

24
include/ScanArrangement.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef SCANARRANGEMENT_H
#define SCANARRANGEMENT_H
#include "ILEDArrangement.h"
class ScanArrangement : public ILEDArrangement {
public:
ScanArrangement(bool _isColumnScan) : isColumnScan(_isColumnScan) {}
virtual ~ScanArrangement() {}
int16_t XY(int16_t x, int16_t y, int16_t width, int16_t height) {
if(isColumnScan) {
return height * x + y;
} else {
return width * y + x;
}
}
private:
bool
isColumnScan;
};
#endif

View File

@@ -0,0 +1,31 @@
#ifndef SERPENTINEARRANGEMENT_H
#define SERPENTINEARRANGEMENT_H
#include "ILEDArrangement.h"
class SerpentineArrangement : public ILEDArrangement {
public:
SerpentineArrangement(bool _isColumnSerpentine) : isColumnSerpentine(_isColumnSerpentine) {}
virtual ~SerpentineArrangement() {}
int16_t XY(int16_t x, int16_t y, int16_t width, int16_t height) {
if(isColumnSerpentine) {
if(x & 0x1) {
return x * height + (height - 1 - y);
} else {
return x * height + y;
}
} else {
if(y & 0x1) {
return y * width + (width - 1 - x);
} else {
return y * width + x;
}
}
}
private:
bool
isColumnSerpentine;
};
#endif

View File

@@ -0,0 +1,46 @@
#ifndef TILEDSERPENTINEARRANGEMENT_H
#define TILEDSERPENTINEARRANGEMENT_H
#include "ILEDArrangement.h"
class TiledSerpentineArrangement : public ILEDArrangement {
public:
TiledSerpentineArrangement(bool _isColumnSerpentine, bool _isVerticallyTiled,
int16_t _tileWidth, int16_t _tileHeight) : isColumnSerpentine(_isColumnSerpentine),
isVerticallyTiled(_isVerticallyTiled), tileWidth(_tileWidth), tileHeight(_tileHeight) {}
virtual ~TiledSerpentineArrangement() {}
int16_t XY(int16_t x, int16_t y, int16_t width, int16_t height) {
int16_t tileNumber = isVerticallyTiled ? y / tileHeight : x / tileHeight;
int16_t modX = x % tileWidth;
int16_t modY = y % tileHeight;
int16_t tilePosition = 0;
if(isColumnSerpentine) {
if (x & 0x1) {
tilePosition = modX * tileHeight + (tileHeight - 1 - modY);
} else {
tilePosition = modX * tileHeight + modY;
}
} else {
if (y & 0x1) {
tilePosition = modY * tileWidth + (tileWidth - 1 - modX);
} else {
tilePosition = modY * tileWidth + modX;
}
}
return tileWidth * tileHeight * tileNumber + tilePosition;
}
private:
bool
isColumnSerpentine,
isVerticallyTiled;
int16_t
tileWidth,
tileHeight;
};
#endif

View File

@@ -9,7 +9,17 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[platformio] [platformio]
default_envs = teensy36 default_envs = uno
[env:uno]
platform = atmelavr
board = uno
framework = arduino
lib_deps =
fastled/FastLED@3.10.1
pfeerick/elapsedMillis@^1.0.6
adafruit/Adafruit GFX Library@^1.11.9
arduino-libraries/SD@^1.3.0
[common] [common]

View File

@@ -1,127 +0,0 @@
#include "GULLSManager.h"
GULLSManager::GULLSManager(Stream* _stream, uint16_t _maxHALs, uint16_t _maxAnimations,
uint16_t _maxResponseActions, uint16_t _maxAnimationBuilderActions) {
stream = _stream;
maxHALs = _maxHALs;
maxAnimations = _maxAnimations;
maxResponseActions = _maxResponseActions;
maxAnimationBuilderActions = _maxAnimationBuilderActions;
hals = new LEDHAL*[maxHALs];
for(uint16_t i = 0; i < maxHALs; i++) {
hals[i] = nullptr;
}
animations = new AnimationBase*[maxAnimations];
for(uint16_t i = 0; i < maxAnimations; i++) {
animations[i] = nullptr;
}
responseActions = new Action<char>*[maxResponseActions];
for(uint16_t i = 0; i < maxResponseActions; i++) {
responseActions[i] = nullptr;
}
animationBuilderActions = new Action<AnimationBase>*[maxAnimationBuilderActions];
for(uint16_t i = 0; i < maxAnimationBuilderActions; i++) {
animationBuilderActions[i] = nullptr;
}
}
bool GULLSManager::addLEDHAL(LEDHAL* hal) {
for(uint16_t i = 0; i < maxHALs; i++) {
if(hals[i] == nullptr) {
hals[i] = hal;
return true;
}
}
return false;
}
bool GULLSManager::addAnimation(AnimationBase* animation) {
for(uint16_t i = 0; i < maxAnimations; i++) {
if(animations[i] == nullptr) {
animations[i] = animation;
return true;
}
}
return false;
}
bool GULLSManager::addResponseAction(Action<char>* action) {
for(uint16_t i = 0; i < maxResponseActions; i++) {
if(responseActions[i] == nullptr) {
responseActions[i] = action;
return true;
}
}
return false;
}
bool GULLSManager::addAnimationBuilderAction(Action<AnimationBase>* action) {
for(uint16_t i = 0; i < maxAnimationBuilderActions; i++) {
if(animationBuilderActions[i] == nullptr) {
animationBuilderActions[i] = action;
return true;
}
}
return false;
}
void GULLSManager::update() {
if(stream->available()) {
StreamEvent event(stream);
for(uint16_t i = 0; i < maxResponseActions; i++) {
if(responseActions[i] != nullptr) {
if(responseActions[i]->eventMatchesAction(&event)) {
stream->write(responseActions[i]->invokeAction(&event));
event.markHandled();
break;
}
}
}
if(!event.isHandled()) {
for(uint16_t i = 0; i < maxAnimationBuilderActions; i++) {
if(animationBuilderActions[i] != nullptr) {
if(animationBuilderActions[i]->eventMatchesAction(&event)) {
this->addAnimation(animationBuilderActions[i]->invokeAction(&event));
event.markHandled();
break;
}
}
}
}
if(!event.isHandled()) {
stream->print(F("ERROR: An event with code "));
stream->print(event.getCode());
stream->println(F(" was registered but went unprocessed by any actions!"));
}
}
for(uint16_t i = 0; i < maxAnimations; i++) {
if(animations[i] != nullptr) {
animations[i]->update();
}
}
for(uint16_t i = 0; i < maxHALs; i++) {
if(hals[i] != nullptr) {
hals[i]->show();
}
}
}

View File

@@ -1,59 +1,54 @@
#include <Arduino.h> #include <Arduino.h>
#include "MatrixHardware_Teensy3_ShieldV1toV3.h"
#include "SmartMatrix.h"
#include "FastLED.h" #include "FastLED.h"
#include "LEDHAL.h" #include "LEDHAL.h"
#include "LEDHAL2D.h" #include "LEDHAL2D.h"
#include "CLEDControllerPhysicalStrip.h" #include "CLEDControllerPhysicalStrip.h"
#include "CLEDControllerPhysicalMatrix.h" #include "CLEDControllerPhysicalMatrix.h"
#include "SmartMatrixPhysicalMatrix.h" #include "SerpentineArrangement.h"
#include "TiledSerpentineArrangement.h"
#define NUMLEDS 24
#define COLOR_DEPTH 24 // known working: 24, 48 - If the sketch uses type `rgb24` directly, COLOR_DEPTH must be 24
const uint8_t kMatrixWidth = 96; // known working: 32, 64, 96, 128
const uint8_t kMatrixHeight = 64; // known working: 16, 32, 48, 64
const uint8_t kRefreshDepth = 36; // known working: 24, 36, 48
const uint8_t kDmaBufferRows = 4; // known working: 2-4, use 2 to save memory, more to keep from dropping frames and automatically lowering refresh rate
const uint8_t kPanelType = SMARTMATRIX_HUB75_32ROW_MOD16SCAN; // use SMARTMATRIX_HUB75_16ROW_MOD8SCAN for common 16x32 panels
const uint8_t kMatrixOptions = SMARTMATRIX_OPTIONS_BOTTOM_TO_TOP_STACKING; // see http://docs.pixelmatix.com/SmartMatrix for options
const uint8_t kBackgroundLayerOptions = (SM_BACKGROUND_OPTIONS_NONE);
const uint8_t kScrollingLayerOptions = (SM_SCROLLING_OPTIONS_NONE);
SMARTMATRIX_ALLOCATE_BUFFERS(matrix, kMatrixWidth, kMatrixHeight, kRefreshDepth, kDmaBufferRows, kPanelType, kMatrixOptions);
SMARTMATRIX_ALLOCATE_BACKGROUND_LAYER(backgroundLayer, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kBackgroundLayerOptions);
#define NUMLEDS 6
#define WIDTH 6
#define HEIGHT 18
#define TILE_WIDTH 6
#define TILE_HEIGHT 6
CRGB leds[NUMLEDS]; CRGB leds[NUMLEDS];
CRGB leds2[NUMLEDS]; CRGB leds2D[WIDTH * HEIGHT];
CRGB leds3[NUMLEDS];
CRGB someColor(255, 255, 255); CRGB rainbow[] = {CRGB::Red, CRGB::Orange, CRGB::Yellow, CRGB::Green, CRGB::Blue, CRGB::Purple};
LEDHAL* hal; LEDHAL* hal;
LEDHAL2D* hal2D; LEDHAL2D* hal2D;
LEDHAL2D* smHAL;
rgb24 canRGB24Translate(rgb24 color) { uint16_t shiftNumber = 0;
color.red = 0;
return color; SerpentineArrangement arrangement(false);
} TiledSerpentineArrangement tileArrangement(false, true, TILE_WIDTH, TILE_HEIGHT);
void setup() { void setup() {
Serial.begin(115200);
Serial.println("Hello World!");
CLEDController* controller = &FastLED.addLeds<NEOPIXEL, 6>(leds, NUMLEDS); CLEDController* controller = &FastLED.addLeds<NEOPIXEL, 6>(leds, NUMLEDS);
CLEDController* controller2d = &FastLED.addLeds<NEOPIXEL, 7>(leds2, NUMLEDS); CLEDController* controller2D = &FastLED.addLeds<NEOPIXEL, 7>(leds2D, WIDTH * HEIGHT);
hal = new CLEDControllerPhysicalStrip(controller, "Test Strip"); hal = new CLEDControllerPhysicalStrip(controller, "Test Strip");
hal2D = new CLEDControllerPhysicalMatrix(controller2d, "Test Matrix", ArrangementType::COLUMNSERPENTINE, 6, 4); hal2D = new CLEDControllerPhysicalMatrix(controller2D, "Test Matrix", arrangement, WIDTH, HEIGHT);
smHAL = new SmartMatrixPhysicalMatrix(&backgroundLayer, "Test SM Matrix", kMatrixWidth, kMatrixHeight);
canRGB24Translate(someColor);
} }
void loop() { void loop() {
// put your main code here, to run repeatedly: for(uint8_t i = 0; i < 6; i++) {
hal->setColor(i, rainbow[(i + shiftNumber) % 6]);
}
for(uint8_t i = 0; i < HEIGHT; i++) {
hal2D->drawFastHLine(0, i, WIDTH, rainbow[(i + shiftNumber) % 6]);
}
hal->requestShow();
hal->show();
hal2D->requestShow();
hal2D->show();
shiftNumber++;
delay(300);
} }

5
wokwi.toml Normal file
View File

@@ -0,0 +1,5 @@
[wokwi]
version = 1
firmware = '.pio/build/uno/firmware.hex'
elf = '.pio/build/uno/firmware.elf'
gdbServerPort=3333