From fff14189ad400f1fa2c35c35bc5a2c38dc154d43 Mon Sep 17 00:00:00 2001 From: Tropicananass Date: Thu, 30 Jan 2025 17:43:56 +0100 Subject: [PATCH] attiny841 support & various improvments --- README.md | 5 +- platformio.ini | 20 +- .../attiny841-2025-01-30_173132.zip | Bin 0 -> 9880 bytes .../attiny841-2025-01-30_173922.zip | Bin 0 -> 22370 bytes schematics/attiny841/attiny841.kicad_pcb | 2422 + schematics/attiny841/attiny841.kicad_prl | 83 + schematics/attiny841/attiny841.kicad_pro | 584 + schematics/attiny841/attiny841.kicad_sch | 3180 + schematics/attiny841/fp-info-cache | 99352 ++++++++++++++++ schematics/attiny_841.pdf | Bin 0 -> 13819 bytes src/button.cpp | 26 +- src/button.hpp | 2 +- src/global.hpp | 2 + src/led_strip.cpp | 391 +- src/led_strip.hpp | 57 +- src/main.cpp | 317 +- src/pin_map.hpp | 36 +- 17 files changed, 106008 insertions(+), 469 deletions(-) create mode 100644 schematics/attiny841/attiny841-backups/attiny841-2025-01-30_173132.zip create mode 100644 schematics/attiny841/attiny841-backups/attiny841-2025-01-30_173922.zip create mode 100644 schematics/attiny841/attiny841.kicad_pcb create mode 100644 schematics/attiny841/attiny841.kicad_prl create mode 100644 schematics/attiny841/attiny841.kicad_pro create mode 100644 schematics/attiny841/attiny841.kicad_sch create mode 100644 schematics/attiny841/fp-info-cache create mode 100644 schematics/attiny_841.pdf diff --git a/README.md b/README.md index afdbea7..4c032ed 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ -# LedRing +# LedStrip + +Manage one wire RGB led (ws2812b, ws2815, etc) strip with attiny (attiny85, attiny841) and a rotary encoder with a push button -Manage ws2812b ring (neopixel 24 led) with attiny85 and a rotary encoder \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 99c15c8..8b4c0b8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,7 @@ default_envs = attiny841_debug [env] build_flags = -Wall lib_deps = - Encoder + paulstoffregen/Encoder@^1.4.4 [env:attiny85] build_type = release @@ -24,11 +24,10 @@ framework = arduino upload_protocol = usbtiny [env:attiny85_debug] -build_type = debug extends = env:attiny85 +build_flags = -D __BUILD_DEBUG__ [env:attiny85_init_debug] -build_type = debug extends = env:attiny85_debug build_flags = -D DEBUG_INIT @@ -38,8 +37,6 @@ platform = atmelavr board = attiny841 framework = arduino upload_protocol = usbtiny -; default is 42 which divides by 8 internal clock to wich gives a system clock of 1MHz -; whith C2 we do not divide and system runs at 8MHz board_build.f_cpu = 8000000L board_fuses.lfuse = 0xC2 board_fuses.hfuse = 0xDF @@ -47,13 +44,18 @@ board_fuses.efuse = 0xFF [env:attiny841_debug] extends = env:attiny841 -build_flags = -D DEBUG_INIT -D __BUILD_DEBUG__ +build_flags = -D __BUILD_DEBUG__ [env:uno] build_type = debug -lib_deps = - Adafruit NeoPixel +lib_deps = + ${env.lib_deps} + Adafruit NeoPixel platform = atmelavr board = uno framework = arduino -build_flags = -D DEBUG \ No newline at end of file +build_flags = -D __BUILD_DEBUG__ + +[env:uno_analog] +extends = env:uno +build_flags = ${env:uno.build_flags} -D ANALOG_BUTTON \ No newline at end of file diff --git a/schematics/attiny841/attiny841-backups/attiny841-2025-01-30_173132.zip b/schematics/attiny841/attiny841-backups/attiny841-2025-01-30_173132.zip new file mode 100644 index 0000000000000000000000000000000000000000..a89ce59de5800a21f7fe54ac23d0a8a4aa0ea528 GIT binary patch literal 9880 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 ZK~7~k=)Wu=FfgcpZ-0RQc0bhej+ag41xjx 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 op8ziZ4)~|m=-+^2(fQ4 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¥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 bYl@4jDaiFd8p6rV$H9sA{{2T)S+xHL7IL;s literal 0 HcmV?d00001 diff --git a/src/button.cpp b/src/button.cpp index 17e3e27..cf477bd 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -2,9 +2,11 @@ #include +#include "global.hpp" + void Button::setup(void) { pinMode(_pin, INPUT_PULLUP); } -bool Button::is_button_pressed(void) { return digitalRead(_pin) == 0; } +bool Button::is_pressed(void) { return digitalRead(_pin) == 0; } void AnalogButton::setup(void) { if (_vRef < 0) { @@ -17,8 +19,9 @@ bool AnalogButton::is_button_pressed(void) { return analogRead(_pin) < _vRef; } void AnalogButton::calibrate(void) { int highRef, lowRef; -#if defined(__BUILD_DEBUG__) && false - debugSerial.print(__func__); +#if defined(__BUILD_DEBUG__) + debugSerial.println(__func__); + debugSerial.println("Getting high ref > 1000"); #endif /* defined(__BUILD_DEBUG__) */ /* Get the button released (High) state voltage, should be close to 1024 (analogRead max) */ @@ -27,23 +30,28 @@ void AnalogButton::calibrate(void) { delay(300); } while (highRef < 1000); -#if defined(__BUILD_DEBUG__) && false - debugSerial.println("highRef="); +#if defined(__BUILD_DEBUG__) + debugSerial.print("highRef="); debugSerial.println(highRef); + debugSerial.print("Getting low ref < "); + debugSerial.println(highRef - 50); #endif /* defined(__BUILD_DEBUG__) */ /* Get the button pushed (Low) state voltage (min of ~5% difference needed to avoid false detection) */ - lowRef = analogRead(_pin); - while (highRef - lowRef < 50) { + do { lowRef = analogRead(_pin); delay(300); - } + } while (lowRef > highRef - 50); -#if defined(__BUILD_DEBUG__) && false +#if defined(__BUILD_DEBUG__) debugSerial.println("lowRef="); debugSerial.println(lowRef); #endif /* defined(__BUILD_DEBUG__) */ /* Add ~1% tolerance */ _vRef = lowRef + 10; +#if defined(__BUILD_DEBUG__) + debugSerial.println("vRef="); + debugSerial.println(_vRef); +#endif /* defined(__BUILD_DEBUG__) */ } diff --git a/src/button.hpp b/src/button.hpp index 3f9b049..db30859 100644 --- a/src/button.hpp +++ b/src/button.hpp @@ -10,7 +10,7 @@ public: ~Button() {}; void setup(void); - bool is_button_pressed(void); + bool is_pressed(void); protected: uint8_t _pin; diff --git a/src/global.hpp b/src/global.hpp index a641695..a1daceb 100644 --- a/src/global.hpp +++ b/src/global.hpp @@ -2,6 +2,8 @@ #define __GLOBAL_HPP__ #include +#define LED_STRIP_LEGNTH 36 + /* debug Serial definition */ #if defined(__BUILD_DEBUG__) #if defined(ARDUINO_AVR_ATTINYX5) diff --git a/src/led_strip.cpp b/src/led_strip.cpp index 277da79..1b931dc 100644 --- a/src/led_strip.cpp +++ b/src/led_strip.cpp @@ -1,5 +1,4 @@ #include "led_strip.hpp" -#include "global.hpp" // LedStrip::LedStrip(/* args */) {} // LedStrip::~LedStrip() {} @@ -25,170 +24,288 @@ void LedStrip::fill(uint32_t c, uint16_t first, uint16_t count) { } for (i = first; i < end; i++) { - setPixelColor(i, c); + this->setPixelColor(i, c); } } + +uint32_t tinyNeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { + + uint8_t r, g, b; + + /* Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover; + * 0 is not the start of pure red, but the midpoint...a few values above + * zero and a few below 65536 all yield pure red (similarly, 32768 is the + * midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values + * each for red, green, blue) really only allows for 1530 distinct hues + * (not 1536, more on that below), but the full unsigned 16-bit type was + * chosen for hue so that one's code can easily handle a contiguous color + * wheel by allowing hue to roll over in either direction. + */ + hue = (hue * 1530L + 32768) / 65536; + /* Because red is centered on the rollover point (the +32768 above, + * essentially a fixed-point +0.5), the above actually yields 0 to 1530, + * where 0 and 1530 would yield the same thing. Rather than apply a + * costly modulo operator, 1530 is handled as a special case below. + * + * So you'd think that the color "hexcone" (the thing that ramps from + * pure red, to pure yellow, to pure green and so forth back to red, + * yielding six slices), and with each color component having 256 + * possible values (0-255), might have 1536 possible items (6*256), + * but in reality there's 1530. This is because the last element in + * each 256-element slice is equal to the first element of the next + * slice, and keeping those in there this would create small + * discontinuities in the color wheel. So the last element of each + * slice is dropped...we regard only elements 0-254, with item 255 + * being picked up as element 0 of the next slice. Like this: + * Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0 + * Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0 + * Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254 + * and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why + * the constants below are not the multiples of 256 you might expect. + */ + // Convert hue to R,G,B (nested ifs faster than divide+mod+switch): + if (hue < 510) { // Red to Green-1 + b = 0; + if (hue < 255) { // Red to Yellow-1 + r = 255; + g = hue; // g = 0 to 254 + } else { // Yellow to Green-1 + r = 510 - hue; // r = 255 to 1 + g = 255; + } + } else if (hue < 1020) { // Green to Blue-1 + r = 0; + if (hue < 765) { // Green to Cyan-1 + g = 255; + b = hue - 510; // b = 0 to 254 + } else { // Cyan to Blue-1 + g = 1020 - hue; // g = 255 to 1 + b = 255; + } + } else if (hue < 1530) { // Blue to Red-1 + g = 0; + if (hue < 1275) { // Blue to Magenta-1 + r = hue - 1020; // r = 0 to 254 + b = 255; + } else { // Magenta to Red-1 + r = 255; + b = 1530 - hue; // b = 255 to 1 + } + } else { // Last 0.5 Red (quicker than % operator) + r = 255; + g = b = 0; + } + + // Apply saturation and value to R,G,B, pack into 32-bit result: + uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 + uint16_t s1 = 1 + sat; // 1 to 256; same reason + uint8_t s2 = 255 - sat; // 255 to 0 + return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | (((((g * s1) >> 8) + s2) * v1) & 0xff00) | + (((((b * s1) >> 8) + s2) * v1) >> 8); +} #endif /* defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) */ -void LedStrip::setup() { +void LedStrip::setup(bool is_diag_mode) { pinMode(LedPin, OUTPUT); - setPixelColor(0, 255, 0, 0); - fill(Color(45, 0, 0)); - show(); - delay(300); - fill(Color(0, 45, 0)); - show(); - delay(300); - fill(Color(0, 0, 45)); - show(); - delay(300); + if (is_diag_mode) { + setPixelColor(0, 255, 0, 0); + fill(Color(45, 0, 0)); + show(); + delay(300); + fill(Color(0, 45, 0)); + show(); + delay(300); + fill(Color(0, 0, 45)); + show(); + delay(300); + } + this->refresh(); +} + +void LedStrip::shiftHue(int shift) { + hue += (shift << 11); + /* | encoder | hue | sat + * values | 20 | 65536 | 256 + * step | 1 | 3277 | + * shift | 0 | 11 | 4 + * rounded | | 2048 | 16 + * total steps | | 32 | 16 + */ + +#if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) + if (shift != 0) { + debugSerial.print(__func__); + debugSerial.print(" : shift="); + debugSerial.print(shift, DEC); + debugSerial.print(", hue="); + debugSerial.println(hue, DEC); + } +#endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ +} + +void LedStrip::shiftSaturation(int shift) { + saturation += (shift << 4); + +#if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) + if (shift != 0) { + debugSerial.print(__func__); + debugSerial.print(" : shift="); + debugSerial.print(shift, DEC); + debugSerial.print(", saturation="); + debugSerial.println(saturation, DEC); + } +#endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ +} + +void LedStrip::shiftBrightness(int shift) { + brightness += (shift << 4); + +#if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) + if (shift != 0) { + debugSerial.print(__func__); + debugSerial.print(" : shift="); + debugSerial.print(shift, DEC); + debugSerial.print(", brightness="); + debugSerial.println(brightness, DEC); + } +#endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ +} + +void LedStrip::shiftFirstLedOn(int shift) { + if (shift > 0) { + if (firstLedOn > LedCount) { + firstLedOn = 0; + } else { + ++firstLedOn; + } + } else { + if (firstLedOn < 1) { + firstLedOn = LedCount - 1; + } else { + --firstLedOn; + } + } + +#if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) + if (shift != 0) { + debugSerial.print(__func__); + debugSerial.print(" : shift="); + debugSerial.print(shift, DEC); + debugSerial.print(", firstLedOn="); + debugSerial.println(firstLedOn, DEC); + } +#endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ +} + +void LedStrip::shiftLedOnLenght(int shift) { + if (shift > 0) { + if (ledOnCount < LedCount) { + ++ledOnCount; + } else { + ledOnCount = 1; + } + } else { + if (ledOnCount > 1) { + --ledOnCount; + } else { + ledOnCount = LedCount; + } + } + +#if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) + if (shift != 0) { + debugSerial.print(__func__); + debugSerial.print(" : shift="); + debugSerial.print(shift, DEC); + debugSerial.print(", ledOnCount="); + debugSerial.println(ledOnCount, DEC); + } +#endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ } void LedStrip::refresh() { - uint16_t const &ledOnMin = ledRingColorState.ledOnMin; - uint16_t const &ledOnMax = ledRingColorState.ledOnMax; - uint16_t LedOnCount = 0; + unsigned int lastLedOn = {firstLedOn + ledOnCount}; + /* 0 <= Chunk 1 < x <= Chunk 2 < y <= Chunk 3 < ledcount*/ + unsigned int chunk_index[4] = {0, 0, 0, LedCount}; + bool isChunkOn; #if defined(__BUILD_DEBUG__) debugSerial.print("Color hue,sat,bri="); - debugSerial.print(ledRingColorState.hue); + debugSerial.print(this->hue); debugSerial.print(","); - debugSerial.print(ledRingColorState.saturation); + debugSerial.print(this->saturation); debugSerial.print(","); - debugSerial.println(ledRingColorState.brightness); + debugSerial.println(this->brightness); #endif /* defined(__BUILD_DEBUG__) */ /* reset ring */ - fill(); + // this->fill(0); - /* compute number of led on */ - if (ledOnMin <= ledOnMax) { - LedOnCount = ledOnMax - ledOnMin; + if (lastLedOn < LedCount) { + isChunkOn = false; + /* firstPhysicalLedOn */ + chunk_index[1] = firstLedOn; + /* lastPhysicalLedOn */ + chunk_index[2] = chunk_index[1] + ledOnCount; } else { - LedOnCount = LedCount - (ledOnMax - ledOnMax); + isChunkOn = true; + /* firstPhysicalLedOff */ + chunk_index[1] = firstLedOn + ledOnCount - LedCount; + /* lastPhysicalLedOff */ + chunk_index[2] = chunk_index[1] + LedCount - ledOnCount; } #if defined(__BUILD_DEBUG__) - debugSerial.print("Led min,max,count="); - debugSerial.print(ledOnMin); - debugSerial.print(","); - debugSerial.print(ledOnMax); - debugSerial.print(","); - debugSerial.println(LedOnCount); + debugSerial.print("LedStrip=>"); + debugSerial.print(chunk_index[0]); + debugSerial.print("<"); + debugSerial.print(isChunkOn ? "ON" : "OFF"); + debugSerial.print(">"); + debugSerial.print(chunk_index[1]); + debugSerial.print("<"); + debugSerial.print(isChunkOn ? "OFF" : "ON"); + debugSerial.print(">"); + debugSerial.print(chunk_index[2]); + debugSerial.print("<"); + debugSerial.print(isChunkOn ? "ON" : "OFF"); + debugSerial.print(">"); + debugSerial.print(chunk_index[3]); + debugSerial.print("<"); + debugSerial.print(isChunkOn ? "OFF" : "ON"); + debugSerial.println(">"); #endif /* defined(__BUILD_DEBUG__) */ - for (size_t i = ledRingColorState.ledOnMin; i < ledRingColorState.ledOnMin + LedOnCount; ++i) { - unsigned int ledId = i; - if (ledId >= LedCount) { - ledId -= LedCount; + for (size_t i = 0; i < 3; ++i) { + unsigned int firstLedOnChunk = chunk_index[i]; + unsigned int lastLedOnChunk = chunk_index[i + 1]; + uint32_t color; + + if (isChunkOn) { + color = ColorHSV(this->hue, this->saturation, this->brightness); + } else { + color = 0; } - setPixelColor(ledId, ColorHSV(ledRingColorState.hue, ledRingColorState.saturation, ledRingColorState.brightness)); - } - show(); -} -void LedStrip::next_preset(void) { - ++presetState.index; - if (presetState.index >= PresetMax) { - presetState.index = 0; - ++presetState.level; - if (presetState.level >= PresetLevelMax) { - presetState.level = 0; + for (size_t j = firstLedOnChunk; j < lastLedOnChunk; ++j) { + this->setPixelColor(j, color); } + + isChunkOn = !isChunkOn; } + + this->show(); } -void LedStrip::previous_preset(void) { - --presetState.index; - if (presetState.index == UINT8_MAX) { - presetState.index = PresetMax - 1; - --presetState.level; - if (presetState.level == UINT8_MAX) { - presetState.level = PresetLevelMax - 1; +void LedStrip::display_mode(unsigned int mode_index) { + for (size_t i = 0; i < 3; ++i) { + this->fill(0); + this->show(); + delay(150); + for (size_t i = 0; i < mode_index; ++i) { + this->setPixelColor(i, ColorHSV(0, 255, 16)); } + this->show(); + delay(150); } -} - -void LedStrip::display_led_ring(bool is_preset_enabled) { - LedRingColorState_t colorState; - - if (is_preset_enabled) { -#if defined(__BUILD_DEBUG__) && false - debugSerial.print("Preset index,level="); - debugSerial.print(presetState.index); - debugSerial.print(","); - debugSerial.print(presetState.level); - debugSerial.println(); -#endif /* defined(__BUILD_DEBUG__) */ - colorState = {.ledOnMin = this->ledRingColorState.ledOnMin, - .ledOnMax = this->ledRingColorState.ledOnMax, - .hue = PresetHue[presetState.index], - .saturation = (presetState.index == (PresetMax - 1U)) ? 0U : UINT8_MAX, - .brightness = PresetBrightness[presetState.level]}; - refresh_led_ring(&colorState); - } else { - refresh_led_ring(); - } -} - -void LedStrip::refresh_led_ring(LedRingColorState_t const *ledRingColorState) { - if (ledRingColorState == nullptr) { - ledRingColorState = &this->ledRingColorState - } - uint16_t const &ledOnMin = ledRingColorState.ledOnMin; - uint16_t const &ledOnMax = ledRingColorState.ledOnMax; - uint16_t LedOnCount = 0; - -#if defined(__BUILD_DEBUG__) - debugSerial.print("Color hue,sat,bri="); - debugSerial.print(ledRingColorState.hue); - debugSerial.print(","); - debugSerial.print(ledRingColorState.saturation); - debugSerial.print(","); - debugSerial.println(ledRingColorState.brightness); -#endif /* defined(__BUILD_DEBUG__) */ - - /* reset ring */ - this->fill(0); - - /* compute number of led on */ - if (ledOnMin <= ledOnMax) { - LedOnCount = ledOnMax - ledOnMin; - } else { - LedOnCount = ledCount - (ledOnMax - ledOnMax); - } - -#if defined(__BUILD_DEBUG__) - debugSerial.print("Led min,max,count="); - debugSerial.print(ledOnMin); - debugSerial.print(","); - debugSerial.print(ledOnMax); - debugSerial.print(","); - debugSerial.println(LedOnCount); -#endif /* defined(__BUILD_DEBUG__) */ - - for (size_t i = ledRingColorState.ledOnMin; i < ledRingColorState.ledOnMin + LedOnCount; ++i) { - unsigned int ledId = i; - if (ledId >= LedCount) { - ledId -= LedCount; - } - ledRing.setPixelColor(ledId, Adafruit_NeoPixel::ColorHSV(ledRingColorState.hue, ledRingColorState.saturation, - ledRingColorState.brightness)); - } - ledRing.show(); -} - -// void display_mode(Mode_e mode) { -// if (currentMode != Mode_e::ControlOff) { -// for (size_t i = 0; i < 3; ++i) { -// fill(0); -// show(); -// delay(300); -// for (Mode_e i = Mode_e::Init; i < currentMode; ++i) { -// setPixelColor(int(i), Adafruit_NeoPixel::ColorHSV(0, 255, 16)); -// } -// show(); -// delay(300); -// } -// } -// } \ No newline at end of file + this->refresh(); +} \ No newline at end of file diff --git a/src/led_strip.hpp b/src/led_strip.hpp index 48ae393..2f987f3 100644 --- a/src/led_strip.hpp +++ b/src/led_strip.hpp @@ -9,32 +9,8 @@ #endif /* defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) */ #include "pin_map.hpp" +#include "global.hpp" -/* Leds */ - -/* Led ring color and led count state */ -typedef struct { - uint16_t ledOnMin; - uint16_t ledOnMax; - uint16_t hue; - uint8_t saturation; - uint8_t brightness; -} LedRingColorState_t; - -uint8_t const PresetMax{8}; - -// https://hslpicker.com/ = {0, 40, 50, 110, 240, 280, 310, 360 for white} -uint16_t const PresetHue[PresetMax] = {0, 7282, 9102, 20025, 43691, 50972, 56434, 65535}; - -uint8_t const PresetLevelMax{3}; - -uint8_t const PresetBrightness[PresetLevelMax] = {16, 64, 127}; - -/* Preset state */ -typedef struct { - uint8_t index; - uint8_t level; -} PresetState_t; #if defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) class LedStrip : tinyNeoPixel { @@ -44,32 +20,39 @@ class LedStrip : Adafruit_NeoPixel { public: #if defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) - LedStrip(void) : tinyNeoPixel{LedCount, LedPin, NEO_GRB + NEO_KHZ800, pixels} {} + LedStrip(void) + : tinyNeoPixel{LedCount, LedPin, NEO_GRB + NEO_KHZ800, pixels}, firstLedOn{0}, + ledOnCount{LedCount}, hue{2048}, saturation{207}, brightness{239} {} void fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0); #else - LedStrip(void) : Adafruit_NeoPixel{LedCount, LedPin, NEO_GRB + NEO_KHZ800} {} + LedStrip(void) + : Adafruit_NeoPixel{LedCount, LedPin, NEO_GRB + NEO_KHZ800}, firstLedOn{0}, ledOnCount{LedCount}, + hue{2048}, saturation{207}, brightness{239} {} #endif /* defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) */ virtual ~LedStrip() {} - void setup(); + void setup(bool is_diag_mode); + + void display_mode(unsigned int mode_index); void refresh(); - void next_preset(); - void previous_preset(); - void display_led_ring(bool is_preset_enabled); + void shiftHue(int shift); + void shiftSaturation(int shift); + void shiftBrightness(int shift); + void shiftFirstLedOn(int shift); + void shiftLedOnLenght(int shift); private: - uint16_t static const LedCount{24}; + uint16_t static const LedCount{LED_STRIP_LEGNTH}; -/* IO Objects */ #if defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) byte pixels[LedCount * 3]; #endif /* defined(ARDUINO_AVR_ATTINYX5) || defined(ARDUINO_AVR_ATTINYX41) */ - LedRingColorState_t ledRingColorState = { - .ledOnMin = 0, .ledOnMax = LedCount / 2, .hue = 0, .saturation = 255, .brightness = 16}; - PresetState_t presetState = {.index = 0, .level = 0}; - void refresh_led_ring(LedRingColorState_t const *ledRingColorState = nullptr); + unsigned int firstLedOn, ledOnCount; + uint16_t hue; + uint8_t saturation; + uint8_t brightness; }; #endif /* __LED_STRIP_HPP__ */ diff --git a/src/main.cpp b/src/main.cpp index c4a23dc..f6bf01a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,8 @@ #include +#if defined(__BUILD_DEBUG__) && !defined(ARDUINO_AVR_UNO) +/* Using interrupts with serial seems to cause instability */ +#define ENCODER_DO_NOT_USE_INTERRUPTS +#endif #include #include "global.hpp" @@ -12,14 +16,13 @@ /* State machine */ #define FOREACH_MODE(MODE) \ - MODE(Init) \ - MODE(LightOnMin) \ - MODE(LightOnMax) \ - MODE(Preset) \ + MODE(FirstLedOn) \ + MODE(LedOnLength) \ MODE(SetColor) \ MODE(SetSaturation) \ MODE(SetBrightness) \ - MODE(ControlOff) + MODE(ControlOff) \ + #define GENERATE_MODE_ENUM(ENUM) ENUM, @@ -29,7 +32,7 @@ enum class Mode_e { FOREACH_MODE(GENERATE_MODE_ENUM) }; /* Cyclic increment operator for Mode_e */ Mode_e &operator++(Mode_e &mode) { if (mode == Mode_e::ControlOff) { - return mode = Mode_e::Init; + return mode = Mode_e::FirstLedOn; } return mode = static_cast(static_cast(mode) + 1); } @@ -41,7 +44,7 @@ char static const *mode_str[] = {FOREACH_MODE(GENERATE_MODE_STRING)}; #endif /* defined(__BUILD_DEBUG__) */ /* Led ring system state */ -Mode_e currentMode{Mode_e::Init}; +Mode_e currentMode{Mode_e::ControlOff}; /* Global variables */ /*--------------------------------------------------------------------------------------------------------------------*/ @@ -49,12 +52,16 @@ Mode_e currentMode{Mode_e::Init}; /* IO Objects */ Encoder encoder{EncoderPinA, EncoderPinB}; #if defined(ARDUINO_AVR_ATTINYX5) -AnalogButton button{AnalogButtonPin, AnalogButtonVRef}; +AnalogButton button{ButtonPin, AnalogButtonVRef}; #elif defined(ARDUINO_AVR_ATTINYX41) Button button{ButtonPin}; #else +#if defined(ANALOG_BUTTON) /* No vRef defined force to calibrate for test purpose */ -AnalogButton button{AnalogButtonPin}; +AnalogButton button{ButtonPin}; +#else +Button button{ButtonPin}; +#endif /* defined(ANALOG_BUTTON) */ #endif /* defined(ARDUINO_AVR_ATTINYX41) */ LedStrip ledStrip{}; @@ -65,7 +72,7 @@ LedStrip ledStrip{}; SoftwareSerial debugSerial{RxPin, TxPin}; #elif defined(ARDUINO_AVR_ATTINYX41) HardwareSerial &debugSerial = Serial1; -#elif +#else HardwareSerial &debugSerial = Serial; #endif #endif /* defined(__BUILD_DEBUG__) */ @@ -77,23 +84,11 @@ HardwareSerial &debugSerial = Serial; #define GENERATE_MODE_EXEC_DECLARTION(MODE) void MODE##_execute(int shift); FOREACH_MODE(GENERATE_MODE_EXEC_DECLARTION) -/* Encoder */ -void analog_button_calibration(void); - int get_encoder_shift(void); -// void display_mode(Mode_e mode); - -// void display_led_ring(void); - -// void refresh_led_ring(LedRingColorState_t const &ledRingColorState = ledRingState.ledRingColorState); - /* Function definitions */ /*--------------------------------------------------------------------------------------------------------------------*/ void setup() { - ledStrip.setup(); - button.setup(); - #if defined(__BUILD_DEBUG__) debugSerial.begin(9600); #if defined(DEBUG_INIT) @@ -103,6 +98,9 @@ void setup() { debugSerial.println("Init ... "); #endif /* defined(__BUILD_DEBUG__) */ + button.setup(); + ledStrip.setup(button.is_pressed()); + get_encoder_shift(); #if defined(__BUILD_DEBUG__) @@ -111,12 +109,11 @@ void setup() { } void loop() { - bool hasModeChanged{false}; int shift{get_encoder_shift()}; - if (button.is_button_pressed()) { - while (button.is_button_pressed()) { - delay(150); + if (button.is_pressed()) { + while (button.is_pressed()) { + delay(10); } ++currentMode; @@ -124,10 +121,10 @@ void loop() { debugSerial.println(mode_str[static_cast(currentMode)]); #endif /* defined(__BUILD_DEBUG__) */ - // display_mode(currentMode); - hasModeChanged = true; + if (currentMode != Mode_e::ControlOff) { + ledStrip.display_mode(static_cast(currentMode) + 1); + } } - ledStrip.setup(); if (shift != 0) { switch (currentMode) { @@ -135,38 +132,34 @@ void loop() { ControlOff_execute(shift); break; - case Mode_e::Preset: - Preset_execute(shift); + case Mode_e::SetColor: + SetColor_execute(shift); break; - // case Mode_e::SetColor: - // SetColor_execute(shift); - // break; + case Mode_e::SetBrightness: + SetBrightness_execute(shift); + break; - // case Mode_e::SetBrightness: - // SetBrightness_execute(shift); - // break; + case Mode_e::SetSaturation: + SetSaturation_execute(shift); + break; - // case Mode_e::SetSaturation: - // SetSaturation_execute(shift); - // break; + case Mode_e::FirstLedOn: + FirstLedOn_execute(shift); + break; - // case Mode_e::LightOnMin: - // LightOnMin_execute(shift); - // break; - - // case Mode_e::LightOnMax: - // LightOnMax_execute(shift); - // break; + case Mode_e::LedOnLength: + LedOnLength_execute(shift); + break; default: break; } } - // if (currentMode != Mode_e::ControlOff && (shift != 0 || hasModeChanged)) { - // display_led_ring(); - // } + if (currentMode != Mode_e::ControlOff && shift != 0) { + ledStrip.refresh(); + } } /* Private function definitions */ @@ -176,135 +169,25 @@ void loop() { void ControlOff_execute(int shift) {} -void Preset_execute(int shift) { - if (shift > 0) { - ledStrip.next_preset(); - } else { - ledStrip.previous_preset(); - } -} /* Preset_execute */ +void SetColor_execute(int shift) { + ledStrip.shiftHue(shift); +} /* SetColor_execute */ -// void SetColor_execute(int shift) { -// uint16_t &hue = ledRingState.ledRingColorState.hue; +void SetBrightness_execute(int shift) { + ledStrip.shiftBrightness(shift); +} /* SetBrightness_execute */ -// hue += (shift << 8); +void SetSaturation_execute(int shift) { + ledStrip.shiftSaturation(shift); +} /* SetSaturation_execute */ -// #if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) -// if (shift != 0) { -// debugSerial.print(__func__); -// debugSerial.print(" : shift="); -// debugSerial.print(shift, DEC); -// debugSerial.print(", hue="); -// debugSerial.println(hue, DEC); -// } -// #endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ -// } /* SetColor_execute */ +void FirstLedOn_execute(int shift) { + ledStrip.shiftFirstLedOn(shift); +} /* FirstLedOn_execute */ -// void SetBrightness_execute(int shift) { -// uint8_t &brightness = ledRingState.ledRingColorState.brightness; - -// brightness += (shift << 2); - -// #if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) -// if (shift != 0) { -// debugSerial.print(__func__); -// debugSerial.print(" : shift="); -// debugSerial.print(shift, DEC); -// debugSerial.print(", brightness="); -// debugSerial.println(brightness, DEC); -// } -// #endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ -// } /* SetBrightness_execute */ - -// void SetSaturation_execute(int shift) { -// uint8_t &saturation = ledRingState.ledRingColorState.saturation; - -// saturation += (shift << 2); - -// #if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) -// if (shift != 0) { -// debugSerial.print(__func__); -// debugSerial.print(" : shift="); -// debugSerial.print(shift, DEC); -// debugSerial.print(", saturation="); -// debugSerial.println(saturation, DEC); -// } -// #endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ -// } /* SetSaturation_execute */ - -// void LightOnMin_execute(int shift) { -// uint16_t &ledOnMin = ledRingState.ledRingColorState.ledOnMin; -// uint16_t &ledOnMax = ledRingState.ledRingColorState.ledOnMax; - -// if (shift > 0) { -// if (ledOnMin == ledOnMax) { -// currentMode = Mode_e::LightOnMax; -// ++ledOnMax; -// } else { -// ++ledOnMin; -// if (ledOnMin == LedCount) { -// ledOnMin = 0; -// } -// } -// } else { -// if (ledOnMin == ledOnMax) { -// currentMode = Mode_e::LightOnMax; -// --ledOnMax; -// } else { -// --ledOnMin; -// if (ledOnMin == UINT16_MAX) { -// ledOnMin = LedCount - 1; -// } -// } -// } - -// #if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) -// if (shift != 0) { -// debugSerial.print(__func__); -// debugSerial.print(" : shift="); -// debugSerial.print(shift, DEC); -// debugSerial.print(", ledOnMin="); -// debugSerial.println(ledOnMin, DEC); -// } -// #endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ -// } /* LightOnMin_execute */ - -// void LightOnMax_execute(int shift) { -// uint16_t &ledOnMin = ledRingState.ledRingColorState.ledOnMin; -// uint16_t &ledOnMax = ledRingState.ledRingColorState.ledOnMax; - -// if (shift > 0) { -// if (ledOnMax == ledOnMin) { -// currentMode = Mode_e::LightOnMin; -// ++ledOnMin; -// } else { -// ++ledOnMax; -// if (ledOnMax == LedCount) { -// ledOnMax = 0; -// } -// } -// } else { -// if (ledOnMax == ledOnMin) { -// currentMode = Mode_e::LightOnMin; -// --ledOnMin; -// } else { -// --ledOnMax; -// if (ledOnMax == UINT16_MAX) { -// ledOnMax = LedCount - 1; -// } -// } -// } - -// #if defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO) -// if (shift != 0) { -// debugSerial.print(__func__); -// debugSerial.print(" : shift="); -// debugSerial.print(shift, DEC); -// debugSerial.print(", ledOnMax="); -// debugSerial.println(ledOnMax, DEC); -// } -// #endif /*defined(__BUILD_DEBUG__) && defined(ARDUINO_AVR_UNO)*/ -// } /* LightOnMax_execute */ +void LedOnLength_execute(int shift) { + ledStrip.shiftLedOnLenght(shift); +} /* LedOnLength_execute */ /* Others functions */ int get_encoder_shift(void) { @@ -324,86 +207,4 @@ int get_encoder_shift(void) { } else { return 0; } -} - -// void display_mode(Mode_e mode) { -// if (currentMode != Mode_e::ControlOff) { -// for (size_t i = 0; i < 3; ++i) { -// ledRing.fill(0); -// ledRing.show(); -// delay(300); -// for (Mode_e i = Mode_e::Init; i < currentMode; ++i) { -// ledRing.setPixelColor(int(i), Adafruit_NeoPixel::ColorHSV(0, 255, 16)); -// } -// ledRing.show(); -// delay(300); -// } -// } -// } - -// void display_led_ring(void) { -// PresetState_t &preset = ledRingState.presetState; -// LedRingColorState_t ledRingColorState; - -// if (currentMode == Mode_e::Preset) { -// #if defined(__BUILD_DEBUG__) && false -// debugSerial.print("Preset index,level="); -// debugSerial.print(preset.index); -// debugSerial.print(","); -// debugSerial.print(preset.level); -// debugSerial.println(); -// #endif /* defined(__BUILD_DEBUG__) */ -// ledRingColorState = {.ledOnMin = ledRingState.ledRingColorState.ledOnMin, -// .ledOnMax = ledRingState.ledRingColorState.ledOnMax, -// .hue = PresetHue[preset.index], -// .saturation = (preset.index == (PresetMax - 1)) ? 0 : UINT8_MAX, -// .brightness = PresetBrightness[preset.level]}; -// refresh_led_ring(ledRingColorState); -// } else { -// refresh_led_ring(); -// } -// } - -// void refresh_led_ring(LedRingColorState_t const &ledRingColorState) { -// uint16_t const &ledOnMin = ledRingColorState.ledOnMin; -// uint16_t const &ledOnMax = ledRingColorState.ledOnMax; -// uint16_t LedOnCount = 0; - -// #if defined(__BUILD_DEBUG__) -// debugSerial.print("Color hue,sat,bri="); -// debugSerial.print(ledRingColorState.hue); -// debugSerial.print(","); -// debugSerial.print(ledRingColorState.saturation); -// debugSerial.print(","); -// debugSerial.println(ledRingColorState.brightness); -// #endif /* defined(__BUILD_DEBUG__) */ - -// /* reset ring */ -// ledRing.fill(0); - -// /* compute number of led on */ -// if (ledOnMin <= ledOnMax) { -// LedOnCount = ledOnMax - ledOnMin; -// } else { -// LedOnCount = LedCount - (ledOnMax - ledOnMax); -// } - -// #if defined(__BUILD_DEBUG__) -// debugSerial.print("Led min,max,count="); -// debugSerial.print(ledOnMin); -// debugSerial.print(","); -// debugSerial.print(ledOnMax); -// debugSerial.print(","); -// debugSerial.println(LedOnCount); -// #endif /* defined(__BUILD_DEBUG__) */ - -// for (size_t i = ledRingColorState.ledOnMin; i < ledRingColorState.ledOnMin + LedOnCount; ++i) { -// unsigned int ledId = i; -// if (ledId >= LedCount) { -// ledId -= LedCount; -// } -// ledRing.setPixelColor(ledId, Adafruit_NeoPixel::ColorHSV(ledRingColorState.hue, ledRingColorState.saturation, -// ledRingColorState.brightness)); -// } -// ledRing.show(); -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/pin_map.hpp b/src/pin_map.hpp index d6a17e1..f380aae 100644 --- a/src/pin_map.hpp +++ b/src/pin_map.hpp @@ -3,47 +3,51 @@ #if defined(ARDUINO_AVR_ATTINYX5) /* LedStrip output pin */ -uint8_t const LedPin{PB0}; +uint8_t const LedPin{PIN_PB0}; /* Button analog input pin */ /* Note: we use PB5 as button input which is also the reset pin, to avoid reseting we read an analogic value with * lower voltage than the triger value of reset */ int const AnalogButtonVRef{970}; -uint8_t const AnalogButtonPin{0}; +uint8_t const ButtonPin{PINA0}; /* Rotary encoder input pins */ -uint8_t const EncoderPinA{PB3}; -uint8_t const EncoderPinB{PB4}; +/* TODO: change to PB2 for using int0 */ +uint8_t const EncoderPinA{PIN_PB3}; +uint8_t const EncoderPinB{PIN_PB4}; /* Uart for serial debug pins */ #if defined(__BUILD_DEBUG__) -uint8_t const TxPin{PB1}; -uint8_t const RxPin{PB2}; +uint8_t const TxPin{PIN_PB1}; +uint8_t const RxPin{PIN_PB2}; #endif /* defined(__BUILD_DEBUG__) */ #elif defined(ARDUINO_AVR_ATTINYX41) /* LedStrip output pin */ -uint8_t const LedPin{PA1}; +uint8_t const LedPin{PIN_PA1}; /* Button input pin (internally pulled up) */ -uint8_t const ButtonPin{ADC11D}; +uint8_t const ButtonPin{PIN_PA0}; /* Rotary encoder input pins */ -uint8_t const EncoderPinA{PA2}; -uint8_t const EncoderPinB{PA3}; +/* PB1 is interrupt pin (int0)*/ +uint8_t const EncoderPinA{PIN_PB1}; +uint8_t const EncoderPinB{PIN_PB0}; /* Uart for serial debug pins */ #if defined(__BUILD_DEBUG__) -uint8_t const TxPin{PA5}; -uint8_t const RxPin{PA4}; +uint8_t const TxPin{PIN_PA5}; +uint8_t const RxPin{PIN_PA4}; #endif /* defined(__BUILD_DEBUG__) */ #elif defined(ARDUINO_AVR_UNO) /* LedStrip output pin */ -uint8_t const LedPin{6}; +uint8_t const LedPin{11}; /* Button analog input pin */ -uint8_t const AnalogButtonPin{A0}; +uint8_t const ButtonPin{A0}; + /* Rotary encoder input pins */ -uint8_t const EncoderPinA{8}; -uint8_t const EncoderPinB{9}; +/* INT0 and INT1 */ +uint8_t const EncoderPinA{2}; +uint8_t const EncoderPinB{3}; #endif /* Hardware selection */ #endif /* __PIN_MAP_HPP__ */ \ No newline at end of file