|
|
|
@@ -3,7 +3,7 @@
|
|
|
|
#include <math.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <FastLED.h>
|
|
|
|
#include <FastLED.h>
|
|
|
|
|
|
|
|
|
|
|
|
static const uint8_t GAUGE_COUNT = 3;
|
|
|
|
static const uint8_t GAUGE_COUNT = 4;
|
|
|
|
|
|
|
|
|
|
|
|
// One shared WS2812B strip, split into per-gauge segments.
|
|
|
|
// One shared WS2812B strip, split into per-gauge segments.
|
|
|
|
static const uint8_t LED_DATA_PIN = 22;
|
|
|
|
static const uint8_t LED_DATA_PIN = 22;
|
|
|
|
@@ -226,18 +226,23 @@ struct GaugePins {
|
|
|
|
bool dirInverted;
|
|
|
|
bool dirInverted;
|
|
|
|
bool stepActiveHigh;
|
|
|
|
bool stepActiveHigh;
|
|
|
|
bool enableActiveLow;
|
|
|
|
bool enableActiveLow;
|
|
|
|
uint8_t ledCount; // LEDs assigned to this gauge
|
|
|
|
const char* ledOrder; // one char per LED: 'G' = GRB, 'R' = RGB; length defines ledCount
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
constexpr GaugePins gaugePins[GAUGE_COUNT] = {
|
|
|
|
constexpr GaugePins gaugePins[GAUGE_COUNT] = {
|
|
|
|
// dir, step, en, dirInv, stepHigh, enActiveLow, leds
|
|
|
|
// dir, step, en, dirInv, stepHigh, enActiveLow, ledOrder
|
|
|
|
{50, 51, -1, false, true, true, 7}, // Gauge 0
|
|
|
|
{50, 51, -1, false, true, true, "RRRGGRR"}, // Gauge 0
|
|
|
|
{8, 9, -1, true, true, true, 7}, // Gauge 1
|
|
|
|
{8, 9, -1, true, true, true, "GGGRRRR"}, // Gauge 1
|
|
|
|
{52, 53, -1, false, true, true, 7}, // Gauge 2
|
|
|
|
{52, 53, -1, false, true, true, "GGGRRRR"}, // Gauge 2
|
|
|
|
|
|
|
|
{48, 49, -1, false, true, true, "GGGRRRR"}, // Gauge 3
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constexpr uint8_t cstrLen(const char* s) {
|
|
|
|
|
|
|
|
return *s ? uint8_t(1 + cstrLen(s + 1)) : uint8_t(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
constexpr uint8_t sumLedCounts(uint8_t i = 0) {
|
|
|
|
constexpr uint8_t sumLedCounts(uint8_t i = 0) {
|
|
|
|
return i >= GAUGE_COUNT ? 0 : gaugePins[i].ledCount + sumLedCounts(i + 1);
|
|
|
|
return i >= GAUGE_COUNT ? 0 : cstrLen(gaugePins[i].ledOrder) + sumLedCounts(i + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static const uint8_t TOTAL_LEDS = sumLedCounts();
|
|
|
|
static const uint8_t TOTAL_LEDS = sumLedCounts();
|
|
|
|
|
|
|
|
|
|
|
|
@@ -297,9 +302,40 @@ String rxLine;
|
|
|
|
|
|
|
|
|
|
|
|
CRGB leds[TOTAL_LEDS];
|
|
|
|
CRGB leds[TOTAL_LEDS];
|
|
|
|
uint8_t gaugeLedOffset[GAUGE_COUNT];
|
|
|
|
uint8_t gaugeLedOffset[GAUGE_COUNT];
|
|
|
|
|
|
|
|
uint8_t gaugeLedCount[GAUGE_COUNT];
|
|
|
|
BlinkState blinkState[TOTAL_LEDS];
|
|
|
|
BlinkState blinkState[TOTAL_LEDS];
|
|
|
|
bool ledsDirty = false;
|
|
|
|
bool ledsDirty = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// FastLED drives the shared strip as RGB. Each gauge's ledOrder string marks per-LED
|
|
|
|
|
|
|
|
// type ('R' = RGB, 'G' = GRB); writes to GRB-ordered LEDs pre-swap R and G to compensate.
|
|
|
|
|
|
|
|
inline bool ledNeedsRgSwap(uint8_t globalIdx) {
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
|
|
|
|
uint8_t off = gaugeLedOffset[i];
|
|
|
|
|
|
|
|
if (globalIdx >= off && globalIdx < off + gaugeLedCount[i]) {
|
|
|
|
|
|
|
|
char c = gaugePins[i].ledOrder[globalIdx - off];
|
|
|
|
|
|
|
|
return c == 'G' || c == 'g';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline CRGB encodeForStrip(uint8_t globalIdx, CRGB color) {
|
|
|
|
|
|
|
|
if (ledNeedsRgSwap(globalIdx)) {
|
|
|
|
|
|
|
|
uint8_t tmp = color.r;
|
|
|
|
|
|
|
|
color.r = color.g;
|
|
|
|
|
|
|
|
color.g = tmp;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return color;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void writeLed(uint8_t globalIdx, CRGB color) {
|
|
|
|
|
|
|
|
leds[globalIdx] = encodeForStrip(globalIdx, color);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline CRGB readLed(uint8_t globalIdx) {
|
|
|
|
|
|
|
|
return encodeForStrip(globalIdx, leds[globalIdx]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sends one-line command replies back over the control port.
|
|
|
|
// Sends one-line command replies back over the control port.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Serial protocol summary.
|
|
|
|
// Serial protocol summary.
|
|
|
|
@@ -853,8 +889,8 @@ bool parseVfd(const String& line) {
|
|
|
|
bool parseLedQuery(const String& line) {
|
|
|
|
bool parseLedQuery(const String& line) {
|
|
|
|
if (line == "LED?") {
|
|
|
|
if (line == "LED?") {
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
for (uint8_t j = 0; j < gaugePins[i].ledCount; j++) {
|
|
|
|
for (uint8_t j = 0; j < gaugeLedCount[i]; j++) {
|
|
|
|
const CRGB& c = leds[gaugeLedOffset[i] + j];
|
|
|
|
CRGB c = readLed(gaugeLedOffset[i] + j);
|
|
|
|
CMD_PORT.print("LED ");
|
|
|
|
CMD_PORT.print("LED ");
|
|
|
|
CMD_PORT.print(i);
|
|
|
|
CMD_PORT.print(i);
|
|
|
|
CMD_PORT.print(' ');
|
|
|
|
CMD_PORT.print(' ');
|
|
|
|
@@ -883,13 +919,13 @@ bool parseLed(const String& line) {
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugePins[id].ledCount || idxFirst > idxLast) {
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) {
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255));
|
|
|
|
CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255));
|
|
|
|
for (int i = idxFirst; i <= idxLast; i++) {
|
|
|
|
for (int i = idxFirst; i <= idxLast; i++) {
|
|
|
|
blinkState[gaugeLedOffset[id] + i].active = false;
|
|
|
|
blinkState[gaugeLedOffset[id] + i].active = false;
|
|
|
|
leds[gaugeLedOffset[id] + i] = color;
|
|
|
|
writeLed(gaugeLedOffset[id] + i, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ledsDirty = true;
|
|
|
|
ledsDirty = true;
|
|
|
|
sendReply("OK");
|
|
|
|
sendReply("OK");
|
|
|
|
@@ -912,7 +948,7 @@ bool parseBlink(const String& line) {
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugePins[id].ledCount || idxFirst > idxLast) {
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) {
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -933,13 +969,13 @@ bool parseBlink(const String& line) {
|
|
|
|
uint8_t globalIdx = gaugeLedOffset[id] + i;
|
|
|
|
uint8_t globalIdx = gaugeLedOffset[id] + i;
|
|
|
|
BlinkState& bs = blinkState[globalIdx];
|
|
|
|
BlinkState& bs = blinkState[globalIdx];
|
|
|
|
bs.fx = FX_BLINK;
|
|
|
|
bs.fx = FX_BLINK;
|
|
|
|
bs.onColor = (count == 7) ? color : leds[globalIdx];
|
|
|
|
bs.onColor = (count == 7) ? color : readLed(globalIdx);
|
|
|
|
bs.onMs = (uint16_t)onMs;
|
|
|
|
bs.onMs = (uint16_t)onMs;
|
|
|
|
bs.offMs = (uint16_t)offMs;
|
|
|
|
bs.offMs = (uint16_t)offMs;
|
|
|
|
bs.currentlyOn = true;
|
|
|
|
bs.currentlyOn = true;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.active = true;
|
|
|
|
bs.active = true;
|
|
|
|
leds[globalIdx] = bs.onColor;
|
|
|
|
writeLed(globalIdx, bs.onColor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ledsDirty = true;
|
|
|
|
ledsDirty = true;
|
|
|
|
sendReply("OK");
|
|
|
|
sendReply("OK");
|
|
|
|
@@ -957,7 +993,7 @@ bool parseBreathe(const String& line) {
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugePins[id].ledCount || idxFirst > idxLast) {
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) {
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (periodMs <= 0) { sendReply("ERR BAD_TIME"); return true; }
|
|
|
|
if (periodMs <= 0) { sendReply("ERR BAD_TIME"); return true; }
|
|
|
|
@@ -972,7 +1008,7 @@ bool parseBreathe(const String& line) {
|
|
|
|
bs.cyclePos = 0;
|
|
|
|
bs.cyclePos = 0;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.active = true;
|
|
|
|
bs.active = true;
|
|
|
|
leds[gi] = CRGB::Black;
|
|
|
|
writeLed(gi, CRGB::Black);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ledsDirty = true;
|
|
|
|
ledsDirty = true;
|
|
|
|
sendReply("OK");
|
|
|
|
sendReply("OK");
|
|
|
|
@@ -990,7 +1026,7 @@ bool parseDflash(const String& line) {
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
char* dash = strchr(idxToken, '-');
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxFirst = atoi(idxToken);
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
int idxLast = dash ? atoi(dash + 1) : idxFirst;
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugePins[id].ledCount || idxFirst > idxLast) {
|
|
|
|
if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) {
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
sendReply("ERR BAD_IDX"); return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255));
|
|
|
|
CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255));
|
|
|
|
@@ -1003,7 +1039,7 @@ bool parseDflash(const String& line) {
|
|
|
|
bs.dphase = 0;
|
|
|
|
bs.dphase = 0;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.active = true;
|
|
|
|
bs.active = true;
|
|
|
|
leds[gi] = color; // phase 0 = on
|
|
|
|
writeLed(gi, color); // phase 0 = on
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ledsDirty = true;
|
|
|
|
ledsDirty = true;
|
|
|
|
sendReply("OK");
|
|
|
|
sendReply("OK");
|
|
|
|
@@ -1016,7 +1052,7 @@ void updateBlink() {
|
|
|
|
bool changed = false;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
for (uint8_t j = 0; j < gaugePins[i].ledCount; j++) {
|
|
|
|
for (uint8_t j = 0; j < gaugeLedCount[i]; j++) {
|
|
|
|
uint8_t gi = gaugeLedOffset[i] + j;
|
|
|
|
uint8_t gi = gaugeLedOffset[i] + j;
|
|
|
|
BlinkState& bs = blinkState[gi];
|
|
|
|
BlinkState& bs = blinkState[gi];
|
|
|
|
if (!bs.active) continue;
|
|
|
|
if (!bs.active) continue;
|
|
|
|
@@ -1027,7 +1063,7 @@ void updateBlink() {
|
|
|
|
if ((nowMs - bs.lastMs) >= period) {
|
|
|
|
if ((nowMs - bs.lastMs) >= period) {
|
|
|
|
bs.currentlyOn = !bs.currentlyOn;
|
|
|
|
bs.currentlyOn = !bs.currentlyOn;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
leds[gi] = bs.currentlyOn ? bs.onColor : CRGB::Black;
|
|
|
|
writeLed(gi, bs.currentlyOn ? bs.onColor : CRGB::Black);
|
|
|
|
changed = true;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -1043,8 +1079,9 @@ void updateBlink() {
|
|
|
|
uint8_t bri = (bs.cyclePos < half)
|
|
|
|
uint8_t bri = (bs.cyclePos < half)
|
|
|
|
? (uint8_t)((uint32_t)bs.cyclePos * 255 / half)
|
|
|
|
? (uint8_t)((uint32_t)bs.cyclePos * 255 / half)
|
|
|
|
: (uint8_t)((uint32_t)(bs.periodMs - bs.cyclePos) * 255 / half);
|
|
|
|
: (uint8_t)((uint32_t)(bs.periodMs - bs.cyclePos) * 255 / half);
|
|
|
|
leds[gi] = bs.onColor;
|
|
|
|
CRGB scaled = bs.onColor;
|
|
|
|
leds[gi].nscale8(bri ? bri : 1);
|
|
|
|
scaled.nscale8(bri ? bri : 1);
|
|
|
|
|
|
|
|
writeLed(gi, scaled);
|
|
|
|
changed = true;
|
|
|
|
changed = true;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1053,7 +1090,7 @@ void updateBlink() {
|
|
|
|
if ((nowMs - bs.lastMs) >= dur[bs.dphase]) {
|
|
|
|
if ((nowMs - bs.lastMs) >= dur[bs.dphase]) {
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.lastMs = nowMs;
|
|
|
|
bs.dphase = (bs.dphase + 1) & 3;
|
|
|
|
bs.dphase = (bs.dphase + 1) & 3;
|
|
|
|
leds[gi] = (bs.dphase == 0 || bs.dphase == 2) ? bs.onColor : CRGB::Black;
|
|
|
|
writeLed(gi, (bs.dphase == 0 || bs.dphase == 2) ? bs.onColor : CRGB::Black);
|
|
|
|
changed = true;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -1134,10 +1171,11 @@ void setup() {
|
|
|
|
// Flatten the per-gauge LED counts into offsets on the shared strip.
|
|
|
|
// Flatten the per-gauge LED counts into offsets on the shared strip.
|
|
|
|
uint8_t ledOff = 0;
|
|
|
|
uint8_t ledOff = 0;
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
|
|
|
|
|
|
|
|
gaugeLedCount[i] = cstrLen(gaugePins[i].ledOrder);
|
|
|
|
gaugeLedOffset[i] = ledOff;
|
|
|
|
gaugeLedOffset[i] = ledOff;
|
|
|
|
ledOff += gaugePins[i].ledCount;
|
|
|
|
ledOff += gaugeLedCount[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FastLED.addLeds<WS2812B, LED_DATA_PIN, GRB>(leds, TOTAL_LEDS);
|
|
|
|
FastLED.addLeds<WS2812B, LED_DATA_PIN, RGB>(leds, TOTAL_LEDS);
|
|
|
|
FastLED.setBrightness(255);
|
|
|
|
FastLED.setBrightness(255);
|
|
|
|
FastLED.show();
|
|
|
|
FastLED.show();
|
|
|
|
|
|
|
|
|
|
|
|
|