Initial Commit

This commit is contained in:
Junior 2023-11-16 14:41:47 -05:00
commit 349a62c4c0
9 changed files with 730 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
# Vim
*.swp
*.swo

10
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"cmake.configureOnOpen": false
}

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# LEDChooser Hardware LED Controller
This project is designed to run on an ESP-32 microcontroller dev board. It is used in a on-off custom designed hardare device for interfacing with LEDController driven LED strips.

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

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

24
platformio.ini Normal file
View File

@ -0,0 +1,24 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_port = com4
monitor_speed = 115200
upload_protocol = espota
upload_port = 192.168.2.214
lib_deps =
fastled/FastLED@^3.5.0
https://github.com/tzapu/WiFiManager.git
adafruit/Adafruit SSD1306@^2.5.3
adafruit/Adafruit GFX Library@^1.11.1
madhephaestus/ESP32Encoder@^0.9.2

585
src/main.cpp Normal file
View File

@ -0,0 +1,585 @@
#include <Arduino.h>
#include <FastLED.h>
#include <WiFiManager.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_I2CDevice.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Encoder.h>
#include <ArduinoOTA.h>
// Pins Used:
// 4, 13, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 36, 39
#define DEBUGMODE true
#define TRIGGERENABLED false
#define REDPIN 36
#define GREENPIN 39
#define BLUEPIN 34
#define LEDPIN 32
#define NUMLEDS 6
#define LEDUPDATEMS 100
#define MESSAGERATEMS 250
#define MESSAGETRIGGERPIN 33
#define MESSAGETOGGLEPIN 25
#define ZONEPIN_CABU 15
#define ZONEPIN_CABD 16
#define ZONEPIN_WALL 17
#define ZONEPIN_CLDN 18
#define ZONEPIN_GZBO 19
#define ZONEPIN_EXTR 26
#define NUMZONES 6
#define DEBOUNCEMS 15
#define OLED_SCL 22
#define OLED_SDA 21
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define ROTARYPBPIN 23
#define ROTARYPIN1 13
#define ROTARYPIN2 27
#define COLORMODEPIN 4
struct ZoneStruct {
unsigned int pin;
unsigned int id;
bool active;
ZoneStruct() {
pin = 0;
id = 0;
active = false;
}
ZoneStruct(unsigned int p, unsigned int i) {
this->pin = p;
this->id = i;
this->active = false;
}
};
// Create OLED display object
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Yeah, yeah. The zones and zone IDs are hard coded.
unsigned int dimmerPins[3] = {REDPIN, GREENPIN, BLUEPIN};
ZoneStruct zones[NUMZONES] = {
ZoneStruct(ZONEPIN_CABU, 1),
ZoneStruct(ZONEPIN_CABD, 2),
ZoneStruct(ZONEPIN_WALL, 3),
ZoneStruct(ZONEPIN_GZBO, 5),
ZoneStruct(ZONEPIN_CLDN, 4),
ZoneStruct(ZONEPIN_EXTR, 6)
};
// Define the array for the addressable LEDs
CRGB leds[NUMLEDS];
unsigned int red = 0, green = 0, blue = 0;
unsigned int lastRed = 0, lastGreen = 0, lastBlue = 0;
// A couple main loop variables to keep track of timing triggers
// Only the "trigger" button is debounced since the zone switches
// and "toggle" switch are not time sensitive
unsigned long lastDimmerSample, lastDebugMessage, lastTriggerDebounceTime;
// Variable to track the state of the "trigger" button. Button/switch
// inputs are pulled HIGH so a LOW state means the button is pressed
unsigned short triggerButtonState = HIGH, lastTriggerButtonState = HIGH;
// Triggered messages should only be sent once per press.
// Same for pattern messages. These variables tracks that
bool triggerMessageSent = false, patternMessageSent = false;
// Variable to track the state of the rotary encoder push button
// inputs are pulled HIGH so a LOW state means the button is pressed
unsigned short rotaryPBState = HIGH, lastRotaryPBState = HIGH;
unsigned long lastRPBDebounceTime;
// The Rotary Encoder for the pattern menu
ESP32Encoder encoder;
unsigned int encoderIndex = 0;
unsigned int maxEncoderIndex = 0;
// Keep track of color patterns pulled from server
unsigned int patternCount = 0;
char patterns[100][11];
unsigned short colorModeState = HIGH, lastColorModeState = HIGH;
unsigned long lastColorModeDebounceTime;
// Variable for pulse-repaint of OLED
bool needRepopulate = false;
unsigned long repopulateTime;
// Variable to monitor if we are in "Demo" mode
// which means no WiFi connection attempt and no HTTP messages
bool demoModeEnabled = false;
// A simple function to get the dimmer/POT values
// and return them as a mapped value for FastLED colors
unsigned int getColorValue(unsigned int dimmerPin) {
// Take ten samples of the dimmer and average them.
// This smooths out the abysmal ADC built into the ESP32
unsigned long sum = 0;
for ( int i=0; i<10; i++ ) {
sum += analogRead(dimmerPin);
}
sum = sum/10;
// Map the sampled 10bit values to 8bit FastLED colors
return map(sum, 0, 1023, 0, 255);
}
// Function to send messages to the back-end LED controller
// for setting the current pattern active.
void sendPatternMessage() {
// Build our GET message for seting pattern
char webMsg[255] = "";
strcat(webMsg, "http://192.168.2.2/l/setpattern.php?");
// This varieble will only be set to true
// if any of the zone switches are on
bool haveTarget = false;
// Add all the target IDs
// This is done using the "[]" notation
// for URL array parameters native to PHP
for ( int i=0; i<NUMZONES; i++ ) {
if ( zones[i].active == true ) {
char target[3] = "";
sprintf(target, "%d", zones[i].id);
strcat(webMsg, "t[]=");
strcat(webMsg, target);
strcat(webMsg, "&");
haveTarget = true;
}
}
// Bail out of all the targets are false
if ( !haveTarget ) return;
strcat(webMsg, "p=");
strcat(webMsg, patterns[encoderIndex]);
// Send the message if not in demo mode
if ( !demoModeEnabled ) {
HTTPClient http;
http.begin(webMsg);
http.GET();
http.end();
}
// Set patternMessageSent to true if it wasn't and the trigger button is pressed
if ( !rotaryPBState && !patternMessageSent ) patternMessageSent = true;
if ( DEBUGMODE ) Serial.println(webMsg);
}
// Function to send messages to the back-end LED controller
// We could send UDP messages from here, but it's better to
// rely on the back end so this code doesn't need to change
// if the LED control message format changes.
void sendColorMessage() {
// Our variables to hold the nex values
char redHex[3] = "", greenHex[3] = "", blueHex[3] = "";
// Set the hex values for the three colors from the global variables
sprintf(redHex, "%02x", red);
sprintf(greenHex, "%02x", green);
sprintf(blueHex, "%02x", blue);
// Build our GET message for setting colors
char webMsg[255] = "";
strcat(webMsg, "http://192.168.2.2/l/setcolor.php?");
// This varieble will only be set to true
// if any of the zone switches are on
bool haveTarget = false;
// Add all the target IDs
// This is done using the "[]" notation
// for URL array parameters native to PHP
for ( int i=0; i<NUMZONES; i++ ) {
if ( zones[i].active == true ) {
char target[3] = "";
sprintf(target, "%d", zones[i].id);
strcat(webMsg, "t[]=");
strcat(webMsg, target);
strcat(webMsg, "&");
haveTarget = true;
}
}
// Bail out of all the targets are false
if ( !haveTarget ) return;
// Add the converted hex color value
strcat(webMsg, "c=");
strcat(webMsg, redHex);
strcat(webMsg, greenHex);
strcat(webMsg, blueHex);
// Send the message if not in demo mode
if ( !demoModeEnabled ) {
HTTPClient http;
http.begin(webMsg);
http.GET();
http.end();
}
// Set triggerMessageSent to true if it wasn't and the trigger button is pressed
if ( !triggerButtonState && !triggerMessageSent ) triggerMessageSent = true;
if ( DEBUGMODE ) Serial.println(webMsg);
}
unsigned int getEncoderIndex() {
// If we aren't in "pattern" mode then
// reset the current encoder count and return
if ( colorModeState == HIGH ) {
encoder.setCount(encoderIndex);
return encoderIndex;
}
double currentIndex = encoder.getCount();
if ( currentIndex > maxEncoderIndex ) {
currentIndex = maxEncoderIndex;
encoder.setCount(maxEncoderIndex);
} else if ( currentIndex < 0 ) {
currentIndex = 0;
encoder.setCount(0);
}
return (unsigned int)currentIndex;
}
void getColorPatterns() {
if ( demoModeEnabled ) {
// If in demo mode make some dummy patterns
strcpy(patterns[0], "Demo Time!");
strcpy(patterns[1], "Happyness");
strcpy(patterns[2], "Hilarious");
strcpy(patterns[3], "California");
strcpy(patterns[4], "Tortoise");
patternCount = 5;
maxEncoderIndex = patternCount - 1;
return;
}
// Make the web call to get the color patterns
HTTPClient http;
http.begin("http://192.168.2.2/l/getpatterns.php");
int httpCode = http.GET();
String payload = http.getString();
http.end();
// If we don't have a good HTTP return code
// or the payload is empty, then bail
if ( (httpCode == 0) || (payload.length() == 0) ) return;
// Parse the payload delimited by commas
char values[payload.length() + 1];
memset(values, '\0', sizeof(values));
strcpy(values, payload.c_str());
char *token = strtok(values, ",");
while ( token != NULL ) {
//strncpy(patterns[patternCount], token, strlen(token));
strcpy(patterns[patternCount], token);
patternCount++;
token = strtok(NULL, ",");
}
// Update the maximum encoder value
maxEncoderIndex = patternCount - 1;
}
void paintLEDs() {
if ( colorModeState == LOW ) {
leds[0] = CRGB(128, 0, 0);
leds[1] = CRGB(128, 0, 0);
leds[2] = CRGB(0, 128, 0);
leds[3] = CRGB(0, 128, 0);
leds[4] = CRGB(0, 0, 128);
leds[5] = CRGB(0, 0, 128);
} else {
// Grab the current color values
red = getColorValue(REDPIN);
green = getColorValue(GREENPIN);
blue = getColorValue(BLUEPIN);
// Set all the LEDs to the current color
for ( int i=0; i<NUMLEDS; i++ ) {
leds[i] = CRGB(red, green, blue);
}
}
FastLED.show();
}
void populateOLED(bool inverted = false) {
unsigned short foreground = WHITE, background = BLACK;
if ( inverted ) {
foreground = BLACK;
background = WHITE;
needRepopulate = true;
repopulateTime = millis() + 150;
}
oled.clearDisplay();
oled.setCursor(0, 0);
if ( colorModeState == HIGH ) {
// State HIGH means in "single color mode"
oled.setTextColor(foreground, background);
oled.println("TURN DIALS");
oled.println("PICK COLOR");
oled.println("HIT BUTTON");
oled.println("BE AMAZED!");
} else {
// State LOW means in "pattern mode"
unsigned int startingIndex = 0;
if ( encoderIndex > 2 ) {
startingIndex = encoderIndex - 2;
}
unsigned int endingIndex = ((startingIndex + 3) > patternCount) ? patternCount : (startingIndex + 3);
for ( unsigned int i=startingIndex; i<=endingIndex; i++ ) {
if ( i == encoderIndex ) {
oled.setTextColor(background, foreground);
} else {
oled.setTextColor(foreground, background);
}
char line[11] = "";
// Buffer variable for string padding
char spaceBuffer[] = " ";
strcpy(line, patterns[i]);
strncat(line, spaceBuffer, (10-strlen(patterns[i])));
oled.println(line);
}
}
oled.display();
}
// This function gets called if the board goes into
// AP Captive Portal mode to provide WiFi configuration
void configModeCallback (WiFiManager *myWiFiManager) {
oled.clearDisplay();
oled.setTextSize(2);
oled.setTextColor(WHITE);
oled.setCursor(0, 0);
oled.println("-- SSID --");
oled.println("LEDChooser");
oled.println("Use Phone");
oled.println("Setup WiFi");
oled.display();
}
void setup() {
// Set COLORMODEPIN to INTPUT_PULLUP
pinMode(COLORMODEPIN, INPUT_PULLUP);
colorModeState = digitalRead(COLORMODEPIN);
lastColorModeState = colorModeState;
// Initialize color pattern name array
memset(patterns, '\0', sizeof(patterns));
// Set up the serial console
Serial.begin(115200);
// Set up the Rotary Encoder push button input
pinMode(ROTARYPBPIN, INPUT_PULLUP);
//ESP32Encoder::useInternalWeakPullResistors=UP;
encoder.attachSingleEdge(ROTARYPIN1, ROTARYPIN2);
encoder.setCount(encoderIndex);
// Set up the two pins for initiating a set color message to network
pinMode(MESSAGETRIGGERPIN, INPUT_PULLUP);
pinMode(MESSAGETOGGLEPIN, INPUT_PULLUP);
// Set up the pins for tracking the zone enable/disable switches
for ( int i=0; i<NUMZONES; i++ ) {
pinMode(zones[i].pin, INPUT_PULLUP);
}
// Set up our in-box addressable LEDs and set them all to off
FastLED.addLeds<WS2812B, LEDPIN, GRB>(leds, NUMLEDS);
fill_solid(leds, NUMLEDS, CRGB::Black);
FastLED.show();
// Turn on OLED
if ( !oled.begin(SSD1306_SWITCHCAPVCC, 0x3C) ) {
Serial.println(F("Failed to start OLED Display!"));
while (1);
}
// Print the MAC Address
Serial.println("MAC Address: " + WiFi.macAddress());
// Clear the OLED display
oled.clearDisplay();
oled.display();
// Test if we are in "Demo" mode. If the Rotary Push Button and
// the "Trigger" button are held during boot the system will go
// into demo mode. In demo mode no WiFi connection will be made
// and no HTTP messages will be sent. If demo mode is detected
// the system will cycle the addressable LED strip until both
// buttons are released before continuing.
if ( (digitalRead(ROTARYPBPIN) == LOW) && ((digitalRead(MESSAGETRIGGERPIN) == LOW) || !TRIGGERENABLED) ) {
demoModeEnabled = true;
short activeLED = 0;
bool countingUp = true;
oled.clearDisplay();
oled.setCursor(0, 0);
oled.setTextSize(2);
oled.setTextColor(WHITE);
oled.println("DEMO MODE");
oled.println(" DETECTED ");
oled.println("----------");
oled.println(" RELEASE! ");
oled.display();
// Wait until both buttons have been released before continuing
// Cycle the LEDs like KITT while waiting :)
while ( (digitalRead(ROTARYPBPIN) == LOW) || (digitalRead(MESSAGETRIGGERPIN) == LOW) ) {
leds[activeLED] = CRGB::Red;
FastLED.show();
delay(200);
leds[activeLED] = CRGB::Black;
FastLED.show();
if ( countingUp ) {
activeLED += 1;
if ( activeLED == NUMLEDS ) {
countingUp = false;
activeLED = NUMLEDS - 2;
}
} else {
activeLED -= 1;
if ( activeLED == -1 ) {
countingUp = true;
activeLED = 1;
}
}
}
fill_solid(leds, NUMLEDS, CRGB::Black);
FastLED.show();
}
// Set the analog resolution for color dials to 10 bits (0-1023)
analogReadResolution(10);
// Join the network or start the captive portal AP for configuration
// Turn the first LED blue while configuring WiFi so we know when it's done
// Only do this if not in demo mode
oled.clearDisplay();
oled.setTextSize(2);
oled.setTextColor(WHITE);
oled.setCursor(0, 0);
oled.println("CONNECTING");
oled.println(" TO");
oled.println(" WiFi");
oled.println("..........");
oled.display();
fill_rainbow(leds, NUMLEDS, 0, 42);
FastLED.show();
if ( !demoModeEnabled ) {
WiFiManager wifiManager;
wifiManager.setAPCallback(configModeCallback);
wifiManager.autoConnect("LEDChooser");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start OTA updating " + type);
oled.clearDisplay();
oled.setTextColor(WHITE);
oled.setCursor(0, 0);
oled.println(" UPDATING ");
oled.println(" FIRMWARE ");
oled.println(" ");
oled.display();
})
.onEnd([]() {
Serial.println("\nOTA Update Completed");
oled.clearDisplay();
oled.setTextColor(WHITE);
oled.setCursor(0, 0);
oled.println(" FIRMWARE ");
oled.println(" UPDATE ");
oled.println(" COMPLETE ");
oled.display();
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("OTA Update Progress: %u%%\r", (progress / (total / 100)));
oled.setCursor(40, 48);
oled.print(" ");
oled.display();
oled.setCursor(40, 48);
oled.print(progress / (total / 100));
oled.print("%");
oled.display();
})
.onError([](ota_error_t error) {
Serial.printf("OTA Update Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
} else {
// If in demo mode wait for 3 seconds to show
// WiFi connection attempt on OLED and LEDs
delay(3000);
}
fill_solid(leds, NUMLEDS, CRGB::Black);
FastLED.show();
getColorPatterns();
paintLEDs();
populateOLED();
}
void loop() {
// Check the state of the "trigger" button and debounce if needed
unsigned short triggerCheck = digitalRead(MESSAGETRIGGERPIN);
if ( triggerCheck != lastTriggerButtonState ) lastTriggerDebounceTime = millis();
// If the button state has settled/debounced then take action
if ( millis() > (lastTriggerDebounceTime + DEBOUNCEMS) ) {
if ( triggerCheck != triggerButtonState ) {
triggerButtonState = triggerCheck;
// If the trigger button was released set the message tracker to false
if ( triggerButtonState == HIGH ) triggerMessageSent = false;
}
}
lastTriggerButtonState = triggerCheck;
// Check the state of the "pattern mode" switch
unsigned short colorModeCheck = digitalRead(COLORMODEPIN);
if ( colorModeCheck != lastColorModeState ) lastColorModeDebounceTime = millis();
// If the pattern mode switch state has settled/debounced then take action
if ( millis() > (lastColorModeDebounceTime + DEBOUNCEMS) ) {
if ( colorModeCheck != colorModeState ) {
colorModeState = colorModeCheck;
if ( colorModeState == LOW ) {
paintLEDs();
}
populateOLED();
}
}
lastColorModeState = colorModeCheck;
// Check the state of the RotaryPB and debounce if needed
unsigned short rotaryPBCheck = digitalRead(ROTARYPBPIN);
if ( rotaryPBCheck != lastRotaryPBState ) lastRPBDebounceTime = millis();
// If the rotary PB state has settled/debounced then take action
if ( millis() > (lastRPBDebounceTime + DEBOUNCEMS) ) {
if ( rotaryPBCheck != rotaryPBState ) {
rotaryPBState = rotaryPBCheck;
if ( ((colorModeState == LOW) || !TRIGGERENABLED) && (rotaryPBState == LOW) ) {
// Poll all the zone switches
for ( int i=0; i<NUMZONES; i++ ) {
zones[i].active = (digitalRead(zones[i].pin) == LOW) ? true : false;
}
populateOLED(true);
if ( colorModeState == LOW ) {
sendPatternMessage();
} else {
sendColorMessage();
}
}
}
}
lastRotaryPBState = rotaryPBCheck;
// Only sample the dimmers and update LED colors every LEDUPDATEMS milliseconds
// Or if the trigger button has been pressed and no message sent yet
if ( (colorModeState == HIGH) && ((!triggerButtonState && !triggerMessageSent) || (millis() > (lastDimmerSample + LEDUPDATEMS))) ) {
paintLEDs();
lastDimmerSample = millis();
// If the "trigger" button has been pressed, or the "toggle" switch for
// continuous message sending is turned on then we should send a set color
// messages to the back-end. A triggered message will only be sent once
// per press of the button. Continuous messages will only be sent every
// LEDUPDATEMS milliseconds (i.e. the same rate as the sampling)
if ( !digitalRead(MESSAGETOGGLEPIN) || (!digitalRead(MESSAGETRIGGERPIN) && !triggerButtonState && !triggerMessageSent) ) {
for ( int i=0; i<NUMZONES; i++ ) {
zones[i].active = (digitalRead(zones[i].pin) == LOW) ? true : false;
}
if ( !triggerButtonState && !triggerMessageSent ) populateOLED(true);
sendColorMessage();
}
lastRed = red;
lastGreen = green;
lastBlue = blue;
}
// Check if the rotary encoder has been turned and take action
unsigned int currentEncoderIndex = getEncoderIndex();
if ( (colorModeState == LOW) && (currentEncoderIndex != encoderIndex) ) {
encoderIndex = currentEncoderIndex;
populateOLED();
if ( DEBUGMODE ) Serial.println("Encoder: " + String(encoderIndex));
}
// Send state message to serial monitor port if DEBUGMODE is enabled
if ( DEBUGMODE && (millis() > (lastDebugMessage + 1000)) ) {
//Serial.println("Colors: " + String(red) + ", " + String(green) + ", " + String(blue) + ", " + String(getColorValue(REDPIN)));
lastDebugMessage = millis();
}
// If we need to repopulate/repaint due to "pulse invert"
if ( needRepopulate && (millis() > repopulateTime) ) {
populateOLED();
}
if ( !demoModeEnabled ) ArduinoOTA.handle();
}

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html