basic led code

This commit is contained in:
2018-09-02 19:10:32 +02:00
parent bafc541cbb
commit e9ba25c4db
20 changed files with 910 additions and 1 deletions

View File

@@ -0,0 +1,335 @@
#ifndef __NeoPattern_INCLUDED__
#define __NeoPattern_INCLUDED__
#include <Adafruit_NeoPixel.h>
using namespace std;
using namespace std::placeholders;
/**
* Original NeoPattern code by Bill Earl
* https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
*
* Custom modifications by 0x1d:
* - default OnComplete callback that sets pattern to reverse
* - separate animation update from timer; Update now updates directly, UpdateScheduled uses timer
*/
// Pattern types supported:
enum pattern { NONE = 0, RAINBOW_CYCLE = 1, THEATER_CHASE = 2, COLOR_WIPE = 3, SCANNER = 4, FADE = 5 };
// Patern directions supported:
enum direction { FORWARD, REVERSE };
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPattern : public Adafruit_NeoPixel
{
public:
// Member Variables:
pattern ActivePattern; // which pattern is running
direction Direction; // direction to run the pattern
unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update of position
uint32_t Color1, Color2; // What colors are in use
uint16_t TotalSteps; // total number of steps in the pattern
uint16_t Index; // current step within the pattern
uint16_t completed = 0;
void (*OnComplete)(int); // Callback on completion of pattern
// Constructor - calls base-class constructor to initialize strip
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(int))
:Adafruit_NeoPixel(pixels, pin, type)
{
OnComplete = callback;
}
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type)
:Adafruit_NeoPixel(pixels, pin, type)
{
}
void onCompleteDefault(int pixels) {
if(ActivePattern != RAINBOW_CYCLE){
Reverse();
}
Serial.println("pattern completed");
}
// Update the pattern
void Update()
{
switch(ActivePattern)
{
case RAINBOW_CYCLE:
RainbowCycleUpdate();
break;
case THEATER_CHASE:
TheaterChaseUpdate();
break;
case COLOR_WIPE:
ColorWipeUpdate();
break;
case SCANNER:
ScannerUpdate();
break;
case FADE:
FadeUpdate();
break;
default:
break;
}
}
void UpdateScheduled()
{
if((millis() - lastUpdate) > Interval) // time to update
{
lastUpdate = millis();
Update();
}
}
// Increment the Index and reset at the end
void Increment()
{
completed = 0;
if (Direction == FORWARD)
{
Index++;
if (Index >= TotalSteps)
{
Index = 0;
completed = 1;
if (OnComplete != NULL)
{
OnComplete(numPixels()); // call the comlpetion callback
} else {
Reverse();
}
}
}
else // Direction == REVERSE
{
--Index;
if (Index <= 0)
{
Index = TotalSteps-1;
completed = 1;
if (OnComplete != NULL)
{
OnComplete(numPixels()); // call the comlpetion callback
} else {
Reverse();
}
}
}
}
// Reverse pattern direction
void Reverse()
{
if (Direction == FORWARD)
{
Direction = REVERSE;
Index = TotalSteps-1;
}
else
{
Direction = FORWARD;
Index = 0;
}
}
// Initialize for a RainbowCycle
void RainbowCycle(uint8_t interval, direction dir = FORWARD)
{
ActivePattern = RAINBOW_CYCLE;
Interval = interval;
TotalSteps = 255;
Index = 0;
Direction = dir;
}
// Update the Rainbow Cycle Pattern
void RainbowCycleUpdate()
{
for(int i=0; i< numPixels(); i++)
{
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
}
show();
Increment();
}
// Initialize for a Theater Chase
void TheaterChase(uint32_t color1, uint32_t color2, uint16_t interval, direction dir = FORWARD)
{
ActivePattern = THEATER_CHASE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}
// Update the Theater Chase Pattern
void TheaterChaseUpdate()
{
for(int i=0; i< numPixels(); i++)
{
if ((i + Index) % 3 == 0)
{
setPixelColor(i, Color1);
}
else
{
setPixelColor(i, Color2);
}
}
show();
Increment();
}
// Initialize for a ColorWipe
void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = COLOR_WIPE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color;
Index = 0;
Direction = dir;
}
// Update the Color Wipe Pattern
void ColorWipeUpdate()
{
setPixelColor(Index, Color1);
show();
Increment();
}
// Initialize for a SCANNNER
void Scanner(uint32_t color1, uint8_t interval)
{
ActivePattern = SCANNER;
Interval = interval;
TotalSteps = (numPixels() - 1) * 2;
Color1 = color1;
Index = 0;
}
// Update the Scanner Pattern
void ScannerUpdate()
{
for (int i = 0; i < numPixels(); i++)
{
if (i == Index) // Scan Pixel to the right
{
setPixelColor(i, Color1);
}
else if (i == TotalSteps - Index) // Scan Pixel to the left
{
setPixelColor(i, Color1);
}
else // Fading tail
{
setPixelColor(i, DimColor(getPixelColor(i)));
}
}
show();
Increment();
}
// Initialize for a Fade
void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = FADE;
Interval = interval;
TotalSteps = steps;
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}
// Update the Fade Pattern
void FadeUpdate()
{
// Calculate linear interpolation between Color1 and Color2
// Optimise order of operations to minimize truncation error
uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
ColorSet(Color(red, green, blue));
show();
Increment();
}
// Calculate 50% dimmed version of a color (used by ScannerUpdate)
uint32_t DimColor(uint32_t color)
{
// Shift R, G and B components one bit to the right
uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}
// Set all pixels to a color (synchronously)
void ColorSet(uint32_t color)
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, color);
}
show();
}
// Returns the Red component of a 32-bit color
uint8_t Red(uint32_t color)
{
return (color >> 16) & 0xFF;
}
// Returns the Green component of a 32-bit color
uint8_t Green(uint32_t color)
{
return (color >> 8) & 0xFF;
}
// Returns the Blue component of a 32-bit color
uint8_t Blue(uint32_t color)
{
return color & 0xFF;
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos)
{
if(WheelPos == 0) return Color(0,0,0);
WheelPos = 255 - WheelPos;
if(WheelPos < 85)
{
return Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if(WheelPos < 170)
{
WheelPos -= 85;
return Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
else
{
WheelPos -= 170;
return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
}
};
#endif

View File

@@ -0,0 +1,49 @@
#ifndef __NEOPATTERN_STATE__
#define __NEOPATTERN_STATE__
#include <ArduinoJson.h>
#include "NeoPattern_api_json.h"
#include "NeoPattern_api_modes.cpp"
#include "utils_print.h"
#include "JsonStruct.h"
// TODO move ARRAY_LENGTH to core lib
#define ARRAY_LENGTH(array) sizeof(array)/sizeof(array[0])
struct NeoPixelConfig {
int pin;
int length;
int brightness;
int updateInterval;
int defaultColor;
};
struct NeoPatternDto : public JsonStruct {
uint mode;
uint value;
const char* valueStr;
// ------------------------------------------------------------------------------------------
//Check if given object is valid and contains fields: JSON_MODE_NODE, JSON_VALUE
int verifyJsonObject(JsonObject& json){
return json.success()
&& json.containsKey(JSON_MODE_NODE)
&& json.containsKey(JSON_VALUE);
};
void mapJsonObject(JsonObject& root) {
root[JSON_MODE_NODE] = mode;
root[JSON_VALUE] = value;
}
// Map a json object to this struct.
void fromJsonObject(JsonObject& json){
if(!verifyJsonObject(json)){
PRINT_MSG(Serial, "PatternState.fromJsonObject", "cannot parse JSON");
return;
}
mode = atoi(json[JSON_MODE_NODE]);
mode = mode < ARRAY_LENGTH(PIXEL_FNCS) ? mode : 0;
value = json[JSON_VALUE];
valueStr = json[JSON_VALUE];
};
};
#endif

View File

@@ -0,0 +1,16 @@
#ifndef __PIXEL_JSON_API__
#define __PIXEL_JSON_API__
/*
modes: PIXELS_OFF = 0, COLOR_WHEEL_MODE = 1, COLOR_MODE = 2, PATTERN_MODE = 3
patterns: NONE = 0, RAINBOW_CYCLE = 1, THEATER_CHASE = 2, COLOR_WIPE = 3, SCANNER = 4, FADE = 5
{
"mode": int,
"value": int || String
}
*/
#define JSON_MODE_NODE "pixelMode"
#define JSON_VALUE "pixelValue"
#define JSON_ACTION_NODE "action"
#endif

View File

@@ -0,0 +1,66 @@
#ifndef __NEOPATTERN_API_MODES__
#define __NEOPATTERN_API_MODES__
#include "NeoPattern.cpp"
enum PIXEL_MODE { PIXELS_OFF = 0, COLOR_WHEEL_MODE = 1, COLOR_MODE = 2, PATTERN_MODE = 3};
typedef void (*PIXEL_FP)(NeoPattern*, const char *);
/*
Array of function pointers to be used as lookup table using the int values of PIXEL_MODE.
TODO header file + separate functions instead of lambdas
*/
const PIXEL_FP PIXEL_FNCS[] = {
/*
PIXESL_OFF
Sets all pixels to black.
*/
[](NeoPattern* pixels, const char *color){
pixels->clear();
pixels->ColorSet(0);
},
/*
COLOR_WHEEL_MODE
Input: integer color from 0 to 155
Uses the color wheel to set a color.
If given integer is <= 1, set the color to black.
By using this function, Color1 and Color2 is set on the pixels;
Color1 = new color, Color2 = last color.
*/
[](NeoPattern* pixels, const char *color){
int c1 = atoi(color);
int c2 = pixels->Color1;
pixels->Color1 = c1;
pixels->Color2 = c2;
pixels->ActivePattern = NONE;
if(c1 <= 1) {
pixels->ColorSet(0);
return;
}
pixels->ColorSet(pixels->Wheel(c1));
},
/*
COLOR_MODE
Input: rgb hex color without #
parses the hex string to r,g,b and sets all pixels accordingly
*/
[](NeoPattern* pixels, const char *color){
int r, g, b;
sscanf(color, "%02x%02x%02x", &r, &g, &b);
pixels->ColorSet(pixels->Color(r,g,b));
},
/*
PATTERN_MODE
Input: id of the pattern
Sets the active pattern on the strip.
As every pattern has another API, all fields need to be set before, for example by using COLOR_WHEEL_MODE.
*/
[](NeoPattern* pixels, const char *id){
pattern p = (pattern)atoi(id);
pixels->Interval = 50;
pixels->TotalSteps = pixels->numPixels();
pixels->ActivePattern = p;
}
};
#endif

41
lib/readme.txt Normal file
View File

@@ -0,0 +1,41 @@
This directory is intended for the project specific (private) libraries.
PlatformIO will compile them to static libraries and link to executable file.
The source code of each library should be placed in separate directory, like
"lib/private_lib/[here are source files]".
For example, see how can be organized `Foo` and `Bar` libraries:
|--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.txt --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Then in `src/main.c` you should use:
#include <Foo.h>
#include <Bar.h>
// rest H/C/CPP code
PlatformIO will find your libraries automatically, configure preprocessor's
include paths and build them.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,25 @@
#include "utils_print.h"
int FORMAT_BUFFER_SIZE(const char* format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf(NULL, 0, format, args);
va_end(args);
return result + 1; // safe byte for \0
}
void PRINT_MSG(Print &out, const char* prefix, const char* format, ...) {
if(SPROCKET_PRINT){
out.print(String(prefix) + String(": "));
char formatString[128], *ptr;
strncpy_P( formatString, format, sizeof(formatString) ); // copy in from program mem
// null terminate - leave last char since we might need it in worst case for result's \0
formatString[ sizeof(formatString)-2 ]='\0';
ptr=&formatString[ strlen(formatString)+1 ]; // our result buffer...
va_list args;
va_start (args,format);
vsnprintf(ptr, sizeof(formatString)-1-strlen(formatString), formatString, args );
va_end (args);
formatString[ sizeof(formatString)-1 ]='\0';
out.print(ptr);
}
}

View File

@@ -0,0 +1,15 @@
#ifndef __SPROCKET_UTILS__
#define __SPROCKET_UTILS__
#include <Arduino.h>
#ifndef SPROCKET_PRINT
#define SPROCKET_PRINT 1
#endif
// TODO move to sprocket
int FORMAT_BUFFER_SIZE(const char* format, ...);
void PRINT_MSG(Print &out, const char* prefix, const char* format, ...);
#endif