/************************************************************************************
Author : MsH
Version : 0.5 (WIP)
This project was made in purpose of exploring Adafruit libraries
Feel free to cherrypick my logics if you find them useful
Made in Wokwi.com
************************************************************************************/
//#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/************************************************************************************
* Neopixel
***********************************************************************************/
const uint8_t LED_STRIP = 13;
const uint16_t LED_COUNT = 45;
Adafruit_NeoPixel NeoPixel(LED_COUNT, LED_STRIP, NEO_GRB + NEO_KHZ800);
// Pixels close to oled screen are meant to preview colors
Adafruit_NeoPixel ColorPreview(2, 14, NEO_GRB + NEO_KHZ800);
// analogs
const uint8_t
rgb = 27,
brightness = 26,
white = 25;
const uint8_t analogCount = 3;
uint8_t analogs[analogCount] = {rgb, brightness, white};
const uint16_t colorValsTotal = 256; // color has 256 different values (0-255)
enum values {
R = 0,
G = 1,
B = 2,
W = 3,
RGB = 4,
Brightness = 5,
lastW = 6,
lastRGB = 7,
lastBrightness = 8
};
// Color arrays
const uint8_t valCount = 9;
uint16_t vals[valCount]; // for looping purposes
// Color save arrays
uint16_t color1[valCount], color2[valCount],
color3[valCount], color4[valCount];
// Array of saves
uint16_t colors[4][valCount] = {
color1[valCount], color2[valCount],
color3[valCount], color4[valCount]};
// Led settings
enum options {
Speed = 0,
Intensity = 1,
WaveAmount = 2,
WaveLenght = 3,
WaveDirection = 4
};
const uint8_t settingsCount = 5;
int16_t settings[settingsCount] = {1, 40, 3, 3, true};
// Rainbows / RGB
bool readAnalogRGB = false;
uint16_t RGBval = 255; // rgb starting value
uint16_t rainbowHue;
uint16_t rainbowValues[3];
// Pulse
uint8_t pulseMinBrightness;
uint8_t pulseVal;
bool addPulseVal;
// Wave
int32_t selectedPixel;
// Fade
uint8_t targetColor;
uint16_t fadeCurrent[6] = {0,0,0,0,256,0};
bool directionRight;
/************************************************************************************
* Buttons // 4, 5, 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 35
***********************************************************************************/
// Button pins
const uint8_t
btn0 = 18,
btn1 = 19,
btn2 = 23;
// Button variables
const uint8_t buttonCount = 7;
const uint8_t buttons[buttonCount] = {btn0, btn1, btn2};
byte buttonBytes[buttonCount];
byte pressedButtons;
// Arrays for togglebutton logic
bool buttonLastStates[buttonCount];
bool buttonStates[buttonCount];
/************************************************************************************
* OLED
***********************************************************************************/
// pins
const uint8_t OLED_RESET = 22;
Adafruit_SSD1306 display(128,64, &Wire, OLED_RESET);
// OLED refresh
bool userDetected; // used to detect user input
bool valueChanged; // used to detect current value changed (user or esp)
bool notRefreshed; // used to detect if refreshed by userinput or onValueChange
// OLED sleep mode
bool sleeping = false;
unsigned long lastActivity;
uint16_t timeOut = 30000; // milliseconds before going sleep
/************************************************************************************
* UI / navigation
***********************************************************************************/
uint16_t spacer, offset, marginT, marginL;
// menuitems
uint8_t menu;
const uint8_t colorItemCount = 4;
String colorItems[colorItemCount] = {"Color 1", "Color 2", "Color 3", "Color 4"};
const uint8_t modeItemCount = 8;
String modeItems[modeItemCount] = {
"Rainbow","RGB Fade", "RGB Wave", "Static",
"Fade", "Wave", "Pulse", "Sparkle"};
int8_t n, ledMode;
// mode options
const uint8_t optionsCount = 6;
String options[optionsCount] = {"Return", "Speed", "Wavelenght", "Wave amount", "Direction", "Intensity"};
int16_t opt, setting, minSetting, maxSetting;
uint8_t maxOpt = optionsCount-1;
enum menus {
Main = 0,
Colors = 1,
Modes = 2,
Settings = 3,
AdjustSetting = 4
};
/************************************************************************************
* Icons
***********************************************************************************/
// light_9x9, 9x9 px
const unsigned char light_9x9 [] PROGMEM = { 0x08, 0x00, 0x49, 0x00, 0x22, 0x00, 0x1c, 0x00, 0xdd, 0x80, 0x1c, 0x00, 0x22, 0x00, 0x49, 0x00, 0x08, 0x00};
// arrow_5x7, 5x7 px
const unsigned char arrow_5x7 [] PROGMEM = {0x18, 0x38, 0x78, 0xf8, 0x78, 0x38, 0x18};
// Pigeon design Copyright (c) MsH. All rights reserved.
// pigeon_128x64, 128x64px
const unsigned char pigeon_128x64 [] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x07, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x07, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x07, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x0f, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xec, 0x0f, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xee, 0x0f, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7, 0x0f, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf3, 0x0f, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf9, 0xcf, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xef, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x77, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x3b, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x1c, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x8f, 0x71, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc7, 0xb9, 0x8f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xe3, 0xdf, 0x9f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xe1, 0xef, 0xdf, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0xf7, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf8, 0xf7, 0xbf, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x7f, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x78, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0x79, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf9, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x03, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xdf, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/************************************************************************************
* Setup & mainloop
***********************************************************************************/
void setup() {
Serial.begin(9600);
// OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.setTextColor(WHITE);
display.display(); // Draw adafruit logo
delay(1000); // Wait a bit
drawLogo(); // Draw my logo
// Buttons
for ( uint8_t i = 0; i < buttonCount; i++ ) {
pinMode(buttons[i], INPUT_PULLUP);
buttonLastStates[i] = HIGH;
buttonStates[i] = HIGH;
buttonBytes[i] = 0b1 << i;
}
// Analogs
analogSetWidth(11);
analogReadResolution(11); // Use 0 - 2047 (11 bits) values
// to get all RGB values from one analog
for ( uint8_t i = 0; i < analogCount; i++ ) {
pinMode(analogs[i], INPUT);
}
// Ledstrip
NeoPixel.begin();
ColorPreview.begin();
}
void loop() {
// Display is not refreshed yet during this loop
notRefreshed = true;
// Check for user inputs
userDetected = false;
valueChanged = false;
readButtons();
readAnalogs();
// Button functions
for ( uint16_t i = 0; i < buttonCount; i++ ) {
buttonLastStates[i] = buttonStates[i]; // Save current to last state
buttonStates[i] = !digitalRead(buttons[i]); // Read new state to current
// Do once
if (buttonLastStates[i] == LOW && buttonStates[i] == HIGH) {
userDetected = true;
if (sleeping == false) {
valueChanged = true;
if (i == 0) buttonFunction1();
if (i == 1) buttonFunction2();
if (i == 2) buttonFunction3();
}
}
}
// If user touched inputs
if (userDetected) {
if (sleeping) {
Serial.println("Activity detected -> Display ON");
lastActivity = millis();
sleeping = false;
display.ssd1306_command(SSD1306_DISPLAYON);
}
else {
Serial.println("Activity detected");
lastActivity = millis();
// Refresh display on user input
updateDisplay();
notRefreshed = false;
}
}
else {
// Turn off display on inactivity
checkActivity();
}
if (valueChanged) getColorValues(vals[RGB], vals, true);
if (menu == Colors) {
if (valueChanged && sleeping == false) {
ColorPreview.setPixelColor(0, ColorPreview.Color(vals[R], vals[G], vals[B]));
ColorPreview.setPixelColor(1, ColorPreview.Color(colors[n][R],
colors[n][G],
colors[n][B]));
ColorPreview.show();
}
}
// Do things to led strip
switch(ledMode)
{
case 1:
ledRGBfade();
break;
case 2:
ledRGBwave();
break;
case 3:
ledStatic();
break;
case 4:
ledFade();
break;
case 5:
ledWave();
break;
case 6:
ledPulse();
break;
case 7:
ledSparkle();
break;
default:
ledRainbow();
break;
}
NeoPixel.show();
// Refresh display on valueChange when in colors menu
if (valueChanged && menu == Colors && notRefreshed && sleeping == false)
updateDisplay();
delay(10);
}
/************************************************************************************
* Analog Functions
***********************************************************************************/
// Reads all analog values
void readAnalogs() {
if (menu == Colors || ledMode == 3 /*ledStatic*/) {
vals[RGB] = analogRead(rgb);
}
vals[W] = map(analogRead(white), 0, 2047, 0, 255); // map to 0 - 255
vals[Brightness] = map(analogRead(brightness), 0, 2047, 0, 100); // 0 - 100 %
// Looping lastValues (Check enums) (3 loops)
for ( uint8_t i = lastW; i < lastW + 3; i++ ) {
uint8_t current = i - 3;
// Compare current to last value
if ( vals[current] != vals[i] ) {
vals[i] = vals[current];
valueChanged = true;
if (sleeping == false) userDetected = true;
}
}
}
// Converts analog range of 0 - 2023 to hue and saves them into red green and blue
// (actual range is 255-1791)
void getColorValues(uint16_t value, uint16_t color[], bool refreshScreen) {
// If wanted, refresh screen
//if (refreshScreen) valueChanged = true;
// Deadzones both ends -> red color
if (value < 256 || value >= 1792) {
color[R] = 255;
color[G] = 0;
color[B] = 0;
}
// Shift to orange to yellow
else if (value < 512) {
color[R] = 255;
if (color[G] != value - (512 + colorValsTotal)) { // Rising
color[G] = value - (512 - colorValsTotal);
}
color[B] = 0;
}
// Shift to green
else if (value < 768) {
if (color[R] != 768 - value) { // Falling
color[R] = 768 - value;
}
color[G] = 255;
color[B] = 0;
}
// Shift to turqoise to aqua
else if (value < 1024) {
color[R] = 0;
color[G] = 255;
if (color[B] != value - (1024 + colorValsTotal)) { // Rising
color[B] = value - (1024 - colorValsTotal);
}
}
// Shift to blue
else if (value < 1280) {
color[R] = 0;
if (color[G] != 1280 - value) { // Falling
color[G] = 1280 - value;
}
color[B] = 255;
}
// Shift to purple to pink
else if (value < 1536) {
if (color[R] != value - (1536 + colorValsTotal)) { // Rising
color[R] = value - (1536 - colorValsTotal);
}
color[G] = 0;
color[B] = 255;
}
// Shift to red
else if (value < 1792) {
color[R] = 255;
color[G] = 0;
if (color[B] != 1792 - value) { // Falling
color[B] = 1792 - value;
}
}
// Converts white and brightness values to hue
for (uint8_t i = 0; i < 3; i++) {
// Set new value with white level
if (color[i] < vals[W]) {
color[i] = constrain(vals[W], 0, 255);
}
// Set new value with brightness level
color[i] = constrain((color[i] * vals[Brightness]) / 100, 0, 255);
}
}
/************************************************************************************
* Button Functions
***********************************************************************************/
// Read all buttons and save pressedButton
void readButtons() {
pressedButtons = 0;
for ( uint8_t i = 0; i < buttonCount; i++ ) {
if ( !digitalRead(buttons[i]) == HIGH ){
pressedButtons = pressedButtons | buttonBytes[i];
}
}
}
// Change item up / down depending on menu
void buttonFunction1() {
switch(menu)
{
case Colors:
n--; if ( n < 0 ) n = colorItemCount;
break;
case Modes:
ledMode++; if ( ledMode > modeItemCount-1 ) ledMode = 0;
break;
case Settings:
opt++; if ( opt > maxOpt ) opt = 0;
break;
case AdjustSetting:
if (settings[setting] != minSetting) settings[setting]--;
break;
default:
menu = Colors;
break;
}
}
// Confirm/select button
void buttonFunction2() {
switch(menu)
{
case Colors:
if (n > colorItemCount-1) { // return
n = 0;
menu = Main;
ColorPreview.clear();
ColorPreview.show();
}
else saveColor(colors[n]); // colors[n] exists -> save color
break;
case Modes: // Change LED mode
menu = Main;
break;
case Settings:
switch(opt)
{
case 1:
setting = Speed;
minSetting = 1;
maxSetting = 10;
menu = AdjustSetting;
break;
case 2:
setting = WaveLenght;
minSetting = 1;
maxSetting = LED_COUNT / settings[WaveAmount];;
menu = AdjustSetting;
break;
case 3:
setting = WaveAmount;
minSetting = 1;
maxSetting = LED_COUNT / settings[WaveLenght];
menu = AdjustSetting;
break;
case 4:
setting = WaveDirection;
minSetting = 0;
maxSetting = 1;
menu = AdjustSetting;
break;
case 5:
setting = Intensity;
minSetting = 20;
maxSetting = 60;
menu = AdjustSetting;
break;
default:
menu = Main;
break;
}
break;
case AdjustSetting:
menu = Settings;
break;
default:
menu = Modes;
break;
}
}
// Change item up / down depending on menu
void buttonFunction3() {
switch(menu)
{
case Colors:
n++; if ( n > colorItemCount ) n = 0;
break;
case Modes:
ledMode--; if ( ledMode < 0 ) ledMode = modeItemCount-1;
break;
case Settings:
opt--; if ( opt < 0 ) opt = maxOpt;
break;
case AdjustSetting:
if (settings[setting] != maxSetting) settings[setting]++;
break;
default:
menu = Settings;
break;
}
}
/************************************************************************************
* OLED / Navigation
***********************************************************************************/
// OLED updating
void updateDisplay() {
display.clearDisplay();
switch (menu)
{
case Colors:
drawRGBValues(10, 0, 44, 55, "Current", vals);
if (n > colorItemCount-1) {
display.setCursor(73, 64/2-8);
display.println("Return");
}
else {
drawRGBValues(73, 0, 44, 55, String(colorItems[n]), colors[n]);
}
drawRGBfooter(0, 59, 128, 5);
break;
case Modes:
drawModeMenu(0,0);
break;
case Settings:
drawModeOptions(0,0);
break;
case AdjustSetting:
drawSettingAdjustment(0,0,settings[setting]);
break;
default:
drawMainMenu(0,0);
break;
}
display.display();
Serial.println("Display refreshed");
}
// Sleepmode: read buttoninputs only with long delays
void checkActivity() {
// Go sleep if time exeeds timeOut limit
if ( sleeping == false && millis() - lastActivity > timeOut ) {
sleeping = true;
display.ssd1306_command(SSD1306_DISPLAYOFF);
ColorPreview.clear();
ColorPreview.show();
Serial.println("Inactivity detected -> Display OFF");
}
}
/************************************************************************************
* Draw UI Elements
***********************************************************************************/
// mainmenu to start navigating settings
void drawMainMenu(int16_t x, int16_t y) {
// Header
offset = 5;
display.setCursor(x+offset, y);
display.println("RGB strip controller");
y += 10;
display.drawFastHLine(x, y, 128, WHITE);
// Menu items
spacer = 14;
y -= 5;
offset = 27;
display.setCursor(x+offset, y+spacer*1);
display.println("Mode settings");
offset = 33;
display.setCursor(x+offset, y+spacer*2);
display.println("Switch mode");
offset = 23;
display.setCursor(x+offset, y+spacer*3);
display.println("Color settings");
}
// Draw modes items (origin = x,y)
void drawModeMenu(int16_t x, int16_t y) {
ledMode;
int8_t prev = ledMode-1; if (prev < 0) prev = modeItemCount-1;
int8_t next = ledMode+1; if (next > modeItemCount-1) next = 0;
int8_t prev2nd = ledMode-2; if (prev2nd < 0) prev2nd = modeItemCount-1;
int8_t next2nd = ledMode+2; if (next2nd > modeItemCount-1) next2nd = 0;
int8_t l;
// Selected
display.setTextSize(2);
l = modeItems[ledMode].length()*12;
offset = (128 - l) / 2; // Center x
display.setCursor(x+offset, y+64/2-10); // Center menuitem to 128x64 screen
display.println(modeItems[ledMode]);
// prev & next
display.setTextSize(1);
l = modeItems[prev].length()*6;
offset = (128 - l) / 2; // Center x
display.setCursor(x+offset, y+8); // Top of screen
display.println(modeItems[prev]);
l = modeItems[next].length()*6;
offset = (128 - l) / 2; // Center x
display.setCursor(x+offset, y+64-20); // Bottom of screen
display.println(modeItems[next]);
// draw 2nd prev and 2nd next (unreadable, so it's just a line)
l = modeItems[prev2nd].length()*2;
offset = (128 - l) / 2; // Center x
display.drawFastHLine(x+offset, y, l, WHITE);
l = modeItems[next2nd].length()*2;
offset = (128 - l) / 2; // Center x
display.drawFastHLine(x+offset, y+62, l, WHITE);
}
// Draw options to select
void drawModeOptions(int16_t x, int16_t y) {
int8_t prev = opt-1; if (prev < 0) prev = maxOpt;
int8_t next = opt+1; if (next > maxOpt) next = 0;
offset = 5;
spacer = 14;
display.setTextSize(2);
display.setCursor(x+offset, y);
display.println("Settings");
y += 18;
display.drawFastHLine(x, y, 128, WHITE);
display.setTextSize(1);
offset = 15;
y -= 4;
display.setCursor(x+offset, y+spacer*(1));
display.println(String(options[prev]));
display.setCursor(x+offset, y+spacer*(2));
display.println(String(options[opt]));
display.setCursor(x+offset, y+spacer*(3));
display.println(String(options[next]));
// Set Arrow cursor position
display.drawBitmap(128-30, y+2*spacer, arrow_5x7, 5, 7, WHITE);
}
// Draw table of current values (origin = x,y)
void drawRGBValues(int16_t x, int16_t y, int16_t w, int16_t h,
String header, uint16_t color[])
{
// Header
marginL = 1;
marginT = 10;
display.setCursor(x+marginL,y);
display.println(header);
display.drawFastHLine(x, y+marginT, w, WHITE);
// RGB values
marginL = 0;
marginT = 4;
spacer = 11;
display.setTextSize(1);
display.setCursor( x+marginL, marginT+spacer*1 );
display.println("R:" + String(color[R]));
display.setCursor( x+marginL, marginT+spacer*2 );
display.println("G:" + String(color[G]));
display.setCursor( x+marginL, marginT+spacer*3 );
display.println("B:" + String(color[B]));
display.setCursor( x+marginL, marginT+spacer*4 );
display.println(" " + String(color[Brightness]) + "%");
display.drawBitmap(x+marginL, marginT+spacer*4-1, light_9x9, 9, 9, WHITE);
// White level
drawVerticalSlider(x+w-5, y+15, 5, 40, color[W], 0, 255, 2);
// Divider line
display.drawFastVLine(x+w+3, y, h, WHITE);
display.drawFastVLine(x-4, y, h, WHITE);
}
// Draw vertical slot (origin = x,y)
void drawVerticalSlider(int16_t x, int16_t y, int16_t w, int16_t h,
int16_t value, int16_t minVal, int16_t maxVal, uint8_t style)
{
offset = 1;
int16_t xKnob = x + offset;
int16_t yKnob = map(value, minVal, maxVal, y+offset, h+12-offset);
int16_t wKnob = w-2;
int16_t hKnob = 1;
// Draw slot and knob depending on style
if (style == 1) {
display.drawFastVLine(x, y, h, WHITE);
display.drawFastVLine(x+w-1, y, h, WHITE);
display.fillRect( xKnob, yKnob, wKnob, hKnob, WHITE );
}
else if (style == 2) {
display.drawFastVLine(x+w/2-1, y, h, WHITE);
display.fillCircle(xKnob, yKnob, wKnob, WHITE);
}
else {
Serial.println("drawVeticalSlider(): Style not found");
}
}
// Draw arrows and selected option's value in mode settings
void drawSettingAdjustment(int16_t x, int16_t y, uint8_t value) {
// Draw arrow up
display.fillTriangle(x+63, y, x+59, y+4, x+67, y+4, WHITE);
display.fillTriangle(x+62, y, x+58, y+4, x+66, y+4, WHITE);
display.drawFastHLine(x+58,y+5,10,WHITE);
// Center and draw value
uint8_t l = String(value).length()*12;
offset = (128 - l) / 2; // Center x
display.setCursor(x+offset, y+32-7);
display.setTextSize(2);
if (opt == 4) {
if (settings[WaveDirection]) {
display.setCursor(x+35, y+32-7);
display.println("Right");
}
else {
display.setCursor(x+40, y+32-7);
display.println("Left");
}
}
else display.println(String(value));
display.setTextSize(1);
// Draw arrow down
display.fillTriangle(x+63, y+63, x+59, y+59, x+67, y+59, WHITE);
display.fillTriangle(x+62, y+63, x+58, y+59, x+66, y+59, WHITE);
display.drawFastHLine(x+58,y+58,10,WHITE);
}
// Draw RGB slider footer
void drawRGBfooter(int16_t x, int16_t y, int16_t w, int16_t h) {
// Draw line
display.setCursor(x,y);
display.drawFastHLine(x, y, w, WHITE);
// Remove deadzones from both ends
int16_t rgbNoDeadZone = constrain(vals[RGB], 255, 1792);
// Draw slider knob
int16_t xKnob = map(rgbNoDeadZone, 255, 1792, 1, 123);
int16_t yKnob = y;
int16_t wKnob = 3;
int16_t hKnob = 5;
display.fillRect( xKnob, yKnob, wKnob, hKnob, WHITE );
}
/************************************************************************************
* LED modes
***********************************************************************************/
// Basic wavy rainbow shifts
void ledRainbow() {
if (valueChanged) { // Rainbow (Static RGB)
uint8_t b = map(vals[Brightness], 0, 100, 0, 255);
NeoPixel.rainbow(0, settings[WaveAmount], 255-vals[W], b, true);
}
}
// RGB colors shifting smoothly
void ledRGBfade() {
RGBval++; if (RGBval > 1792) {RGBval = 255;}
getColorValues(RGBval, rainbowValues, true);
NeoPixel.fill(NeoPixel.Color(rainbowValues[R],
rainbowValues[G],
rainbowValues[B]), 0, LED_COUNT);
}
// RGB colors waving
void ledRGBwave() {
rainbowHue -= 250;
if ( rainbowHue >= 65535) rainbowHue = 0; // Looped trough -> set next pixel as 0
uint8_t b = map(vals[Brightness], 0, 100, 0, 255);
NeoPixel.rainbow(rainbowHue, settings[WaveAmount], 255-vals[W], b, true);
}
// Use current values for static
void ledStatic() {
if (valueChanged) {
NeoPixel.fill(NeoPixel.Color(vals[R], vals[G], vals[B]), 0, LED_COUNT);
}
}
// LEDs fades between saved colors
void ledFade() {
// Clamp target to range of 256-1792
uint16_t target = constrain(colors[targetColor][RGB], 256, 1792);
// Use shortest way to the target value
if ( directionRight ) {
fadeCurrent[RGB]++;
if ( fadeCurrent[RGB] > 1792 ) fadeCurrent[RGB] = 256;
}
else {
fadeCurrent[RGB]--;
if ( fadeCurrent[RGB] < 256 ) fadeCurrent[RGB] = 1792;
}
// Change target
if ( fadeCurrent[RGB] == target ) {
targetColor++; if ( targetColor > 3 ) targetColor = 0;
// Calculate distance from current to target
// going to right
int16_t directionRight = (1792 - fadeCurrent[RGB]) + (target - 256);
// going to left
int16_t directionLeft = fadeCurrent[RGB] - target;
if ( directionRight < directionLeft ) directionRight = true;
else directionRight = false;
}
getColorValues(fadeCurrent[RGB], fadeCurrent, false);
// Loop fadeCurrent R, G, B
for (uint8_t i = 0; i < 3; i++) {
// Set new values with target's white level
if (fadeCurrent[i] < colors[targetColor][W]) {
fadeCurrent[i]--;
}
if (fadeCurrent[i] > colors[targetColor][W]) {
fadeCurrent[i]++;
}
// Set new values with target's brightness level
if (fadeCurrent[Brightness] < colors[targetColor][Brightness]) {
fadeCurrent[Brightness]++;
}
if (fadeCurrent[Brightness] > colors[targetColor][Brightness]) {
fadeCurrent[Brightness]--;
}
fadeCurrent[i] = constrain(fadeCurrent[i] * fadeCurrent[Brightness] / 100, 0, 255);
}
NeoPixel.fill(NeoPixel.Color(fadeCurrent[R],
fadeCurrent[G],
fadeCurrent[B]), 0, LED_COUNT);
delay( 100 - settings[Speed]*10 );
}
// LEDs waves between saved colors
void ledWave() {
// Find next pixel for wave
selectedPixel++;
// Looped trough -> set next pixel as 0
if ( selectedPixel >= LED_COUNT) selectedPixel = 0;
uint16_t pixel;
// Offset waves depending on led amount and desired amount of waves of desired lenght
uint16_t waveOffset = (LED_COUNT-settings[WaveLenght]*settings[WaveAmount])
/ settings[WaveAmount];
uint8_t waveColor = 1;
// Generate diffrent colored waves at different position
for (uint8_t wave = 0; wave < settings[WaveAmount]; wave++) {
// Set new color for this wave iteration
waveColor++; if (waveColor > 3) waveColor = 1;
// Loop pixels to light up with that color
for (uint8_t i = 0; i < settings[WaveLenght]; i++) {
// Find pixel to light up
pixel = (selectedPixel + i) + (settings[WaveLenght]+waveOffset)*wave;
if (pixel >= LED_COUNT) pixel -= LED_COUNT;
NeoPixel.setPixelColor(pixel, NeoPixel.Color(colors[waveColor][R],
colors[waveColor][G],
colors[waveColor][B]));
}
// Find tailingPixel of this wave iteration
int32_t tailingPixel = pixel-settings[WaveLenght]; // Tail is behind the wave
if ( tailingPixel < 0 ) tailingPixel += LED_COUNT; // Tail is actually other end
// Remove tailing pixel (Sets as backround color which is another saved color)
NeoPixel.setPixelColor(tailingPixel, NeoPixel.Color(colors[0][R],
colors[0][G],
colors[0][B]));
}
delay(100 - settings[Speed]*80 );
}
// LEDs pulses with given color
void ledPulse() {
if ( addPulseVal ) {
if ( pulseVal < vals[Brightness] ) {
pulseVal += 2; if (pulseVal > 100) pulseVal = 100;
NeoPixel.fill(NeoPixel.Color(vals[R]*pulseVal/100,
vals[G]*pulseVal/100,
vals[B]*pulseVal/100), 0, LED_COUNT);
}
else {
pulseMinBrightness = random(constrain(vals[Brightness]-settings[Intensity],
0,
vals[Brightness]-settings[Intensity]),
vals[Brightness]);
addPulseVal = false;
}
delay(15-settings[Speed]);
}
else if ( pulseVal > pulseMinBrightness ) {
pulseVal--;
NeoPixel.fill(NeoPixel.Color(vals[R]*pulseVal/100,
vals[G]*pulseVal/100,
vals[B]*pulseVal/100), 0, LED_COUNT);
}
else {
addPulseVal = true;
}
}
// LEDs sparkles randomly with saved colors
void ledSparkle() {
NeoPixel.clear(); // TODO
}
/************************************************************************************
* Other
***********************************************************************************/
// Draw my logo
void drawLogo() {
display.clearDisplay();
display.drawBitmap(0, 0, pigeon_128x64, 128, 64, WHITE);
display.display();
delay(1000);
updateDisplay();
}
// Save current vals to color n
void saveColor(uint16_t color[]) {
for (uint8_t i = 0; i < valCount; i++){
color[i] = vals[i];
}
}