Initial Commit
This commit is contained in:
commit
349a62c4c0
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal 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
10
.vscode/extensions.json
vendored
Normal 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
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"cmake.configureOnOpen": false
|
||||
}
|
3
README.md
Normal file
3
README.md
Normal 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
39
include/README
Normal 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
46
lib/README
Normal 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
24
platformio.ini
Normal 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
585
src/main.cpp
Normal 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
11
test/README
Normal 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
|
Loading…
Reference in New Issue
Block a user