initial commit

This commit is contained in:
2018-11-15 17:50:18 +01:00
parent f79f869d55
commit acfad93691
11 changed files with 814 additions and 56 deletions

View File

@@ -0,0 +1,471 @@
/**
* Original NeoPattern code by Bill Earl
* https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
*
* TODO
* - cleanup the mess
* - fnc table for patterns to replace switch case
*
* Custom modifications by 0x1d:
* - default OnComplete callback that sets pattern to reverse
* - separate animation update from timer; Update now updates directly, UpdateScheduled uses timer
*/
#ifndef __NeoPattern_INCLUDED__
#define __NeoPattern_INCLUDED__
#include <Adafruit_NeoPixel.h>
using namespace std;
using namespace std::placeholders;
// Pattern types supported:
enum pattern
{
NONE = 0,
RAINBOW_CYCLE = 1,
THEATER_CHASE = 2,
COLOR_WIPE = 3,
SCANNER = 4,
FADE = 5,
FIRE = 6
};
// 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 = RAINBOW_CYCLE; // which pattern is running
direction Direction = FORWARD; // direction to run the pattern
unsigned long Interval = 150; // milliseconds between updates
unsigned long lastUpdate = 0; // last update of position
uint32_t Color1 = 0;
uint32_t Color2 = 0; // What colors are in use
uint16_t TotalSteps = 32; // total number of steps in the pattern
uint16_t Index; // current step within the pattern
uint16_t completed = 0;
// FIXME return current NeoPatternState
void (*OnComplete)(int); // Callback on completion of pattern
uint8_t *frameBuffer;
int bufferSize = 0;
// 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)
{
frameBuffer = (uint8_t *)malloc(768);
OnComplete = callback;
TotalSteps = numPixels();
begin();
}
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type)
: Adafruit_NeoPixel(pixels, pin, type)
{
frameBuffer = (uint8_t *)malloc(768);
TotalSteps = numPixels();
begin();
}
void handleStream(uint8_t *data, size_t len)
{
//const uint16_t *data16 = (uint16_t *)data;
bufferSize = len;
memcpy(frameBuffer, data, len);
}
void drawFrameBuffer(int w, uint8_t *frame, int length)
{
for (int i = 0; i < length; i++)
{
uint8_t r = frame[i];
uint8_t g = frame[i + 1];
uint8_t b = frame[i + 2];
setPixelColor(i, r, g, b);
}
}
void onCompleteDefault(int pixels)
{
//Serial.println("onCompleteDefault");
// FIXME no specific code
if (ActivePattern == THEATER_CHASE)
{
return;
}
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;
case FIRE:
Fire(50, 120);
break;
default:
if (bufferSize > 0)
{
drawFrameBuffer(TotalSteps, frameBuffer, bufferSize);
}
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
{
onCompleteDefault(numPixels());
}
}
}
else // Direction == REVERSE
{
--Index;
if (Index <= 0)
{
Index = TotalSteps - 1;
completed = 1;
if (OnComplete != NULL)
{
OnComplete(numPixels()); // call the comlpetion callback
}
else
{
onCompleteDefault(numPixels());
}
}
}
}
// 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);
}
}
/**
* Effects from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
void Fire(int Cooling, int Sparking)
{
byte heat[numPixels()];
int cooldown;
// Step 1. Cool down every cell a little
for (int i = 0; i < numPixels(); i++)
{
cooldown = random(0, ((Cooling * 10) / numPixels()) + 2);
if (cooldown > heat[i])
{
heat[i] = 0;
}
else
{
heat[i] = heat[i] - cooldown;
}
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for (int k = numPixels() - 1; k >= 2; k--)
{
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
}
// Step 3. Randomly ignite new 'sparks' near the bottom
if (random(255) < Sparking)
{
int y = random(7);
heat[y] = heat[y] + random(160, 255);
//heat[y] = random(160,255);
}
// Step 4. Convert heat to LED colors
for (int j = 0; j < numPixels(); j++)
{
setPixelHeatColor(j, heat[j]);
}
showStrip();
}
void setPixelHeatColor(int Pixel, byte temperature)
{
// Scale 'heat' down from 0-255 to 0-191
byte t192 = round((temperature / 255.0) * 191);
// calculate ramp up from
byte heatramp = t192 & 0x3F; // 0..63
heatramp <<= 2; // scale up to 0..252
// figure out which third of the spectrum we're in:
if (t192 > 0x80)
{ // hottest
setPixel(Pixel, 255, 255, heatramp);
}
else if (t192 > 0x40)
{ // middle
setPixel(Pixel, 255, heatramp, 0);
}
else
{ // coolest
setPixel(Pixel, heatramp, 0, 0);
}
}
void setPixel(int Pixel, byte red, byte green, byte blue)
{
setPixelColor(Pixel, Color(red, green, blue));
}
void showStrip()
{
show();
}
};
#endif

View File

@@ -0,0 +1,64 @@
#ifndef __NEOPATTERN_STATE__
#define __NEOPATTERN_STATE__
#include <ArduinoJson.h>
#include "utils/utils_print.h"
#include "JsonStruct.h"
struct NeoPixelConfig : public JsonStruct {
// FIXME constants!
int pin = 4;
int length = 8;
int brightness = 100;
int updateInterval = 100;
int defaultColor = 100; // FIXME remove unused
void mapJsonObject(JsonObject& root) {
root["pin"] = pin;
root["length"] = length;
root["brightness"] = brightness;
root["updateInterval"] = updateInterval;
root["defaultColor"] = defaultColor;
}
void fromJsonObject(JsonObject& json) {
pin = getIntAttrFromJson(json, "pin", pin);
length = getIntAttrFromJson(json, "length", length);
brightness = getIntAttrFromJson(json, "brightness", brightness);
updateInterval = getIntAttrFromJson(json, "updateInterval", updateInterval);
defaultColor = getIntAttrFromJson(json, "defaultColor", defaultColor);
}
};
struct NeoPatternState : public JsonStruct {
uint pattern = 0;
uint color= 0;
uint color2= 0;
uint totalSteps = 16;
uint brightness = 64;
void mapJsonObject(JsonObject& root) {
root["pattern"] = pattern;
root["color"] = color;
root["color2"] = color2;
root["totalSteps"] = totalSteps;
root["brightness"] = brightness;
}
// Map a json object to this struct.
void fromJsonObject(JsonObject& json){
if(!verifyJsonObject(json)){
PRINT_MSG(Serial, "fromJsonObject", "cannot parse JSON");
valid = 0;
return;
}
color = getIntAttrFromJson(json, "color", color);
color2 = getIntAttrFromJson(json, "color2", color2);
pattern = getIntAttrFromJson(json, "pattern", pattern);
brightness = getIntAttrFromJson(json, "brightness", brightness);
totalSteps = getIntAttrFromJson(json, "totalSteps", totalSteps);
valid = 1;
};
};
#endif

View File

@@ -0,0 +1,39 @@
#ifndef __NEOPATTERN_STATE__
#define __NEOPATTERN_STATE__
#include <ArduinoJson.h>
#include "JsonStruct.h"
#include "utils/utils_print.h"
struct NeoPatternState : public JsonStruct {
uint pattern = 0;
uint color= 0;
uint color2= 0;
uint totalSteps = 16;
uint brightness = 64;
void mapJsonObject(JsonObject& root) {
root["pattern"] = pattern;
root["color"] = color;
root["color2"] = color2;
root["totalSteps"] = totalSteps;
root["brightness"] = brightness;
}
// Map a json object to this struct.
void fromJsonObject(JsonObject& json){
if(!verifyJsonObject(json)){
PRINT_MSG(Serial, "fromJsonObject", "cannot parse JSON");
valid = 0;
return;
}
color = getIntAttrFromJson(json, "color", color);
color2 = getIntAttrFromJson(json, "color2", color2);
pattern = getIntAttrFromJson(json, "pattern", pattern);
brightness = getIntAttrFromJson(json, "brightness", brightness);
totalSteps = getIntAttrFromJson(json, "totalSteps", totalSteps);
valid = 1;
};
};
#endif

View File

@@ -0,0 +1,33 @@
#ifndef __NEOPIXEL_CONFIG__
#define __NEOPIXEL_CONFIG__
#include <ArduinoJson.h>
#include "JsonStruct.h"
struct NeoPixelConfig : public JsonStruct
{
// FIXME constants!
int pin = 4;
int length = 8;
int brightness = 100;
int updateInterval = 100;
int defaultColor = 100; // FIXME remove unused
void mapJsonObject(JsonObject &root)
{
root["pin"] = pin;
root["length"] = length;
root["brightness"] = brightness;
root["updateInterval"] = updateInterval;
root["defaultColor"] = defaultColor;
}
void fromJsonObject(JsonObject &json)
{
pin = getIntAttrFromJson(json, "pin", pin);
length = getIntAttrFromJson(json, "length", length);
brightness = getIntAttrFromJson(json, "brightness", brightness);
updateInterval = getIntAttrFromJson(json, "updateInterval", updateInterval);
defaultColor = getIntAttrFromJson(json, "defaultColor", defaultColor);
}
};
#endif

View File

@@ -1,7 +1,7 @@
{
"name": "sprocket-plugin-template",
"name": "sprocket-plugin-neopixel",
"keywords": "sprocket, plugin",
"description": "Template for a sprocket plugin",
"description": "Sprocket plugin for NeoPixels",
"authors":
{
"name": "Patrick Balsiger",
@@ -11,7 +11,7 @@
"repository":
{
"type": "git",
"url": "https://gitlab.com/wirelos/sprocket-plugin-template"
"url": "https://gitlab.com/wirelos/sprocket-plugin-neopixel"
},
"frameworks": "arduino",
"platforms": "espressif8266, esp32",

130
src/PixelPlugin.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include "PixelPlugin.h"
PixelPlugin::PixelPlugin(NeoPattern *neoPattern)
{
pixels = neoPattern;
loadConfigFromFile();
applyConfig(pixelConfig);
defaultAnimation();
}
PixelPlugin::PixelPlugin(PixelConfig cfg)
{
pixelConfig.brightness = cfg.brightness;
pixelConfig.pin = cfg.pin;
pixelConfig.length = cfg.length;
pixelConfig.updateInterval = cfg.updateInterval;
loadConfigFromFile();
pixels = new NeoPattern(pixelConfig.length, pixelConfig.pin, NEO_GRB + NEO_KHZ800);
applyConfig(pixelConfig);
defaultAnimation();
}
PixelPlugin::PixelPlugin()
{
loadConfigFromFile();
pixels = new NeoPattern(pixelConfig.length, pixelConfig.pin, NEO_GRB + NEO_KHZ800);
applyConfig(pixelConfig);
defaultAnimation();
}
void PixelPlugin::loadConfigFromFile(){
if (SPIFFS.begin()){
pixelConfig.fromFile(PIXEL_CONFIG_FILE);
}
}
void PixelPlugin::applyConfig(NeoPixelConfig cfg)
{
pixels->setBrightness(pixelConfig.brightness);
}
void PixelPlugin::defaultAnimation() {
pixels->RainbowCycle(pixelConfig.updateInterval);
animate();
}
void PixelPlugin::activate(Scheduler *userScheduler)
{
animation.set(TASK_MILLISECOND * pixelConfig.updateInterval, TASK_FOREVER, bind(&PixelPlugin::animate, this));
userScheduler->addTask(animation);
animation.enable();
subscribe("pixels/colorWheel", bind(&PixelPlugin::colorWheel, this, _1));
subscribe("pixels/color", bind(&PixelPlugin::setColor, this, _1));
subscribe("pixels/color2", bind(&PixelPlugin::setColor2, this, _1));
subscribe("pixels/pattern", bind(&PixelPlugin::setPattern, this, _1));
subscribe("pixels/totalSteps", bind(&PixelPlugin::setTotalSteps, this, _1));
subscribe("pixels/brightness", bind(&PixelPlugin::setBrightness, this, _1));
subscribe("pixels/state", bind(&PixelPlugin::setState, this, _1));
PRINT_MSG(Serial, "PIXELS", "NeoPixels activated");
}
void PixelPlugin::setState(String msg)
{
PRINT_MSG(Serial, "PIXELS", msg.c_str());
state.fromJsonString(msg);
//pixels->setBrightness(state.brightness);
//pixels->ColorSet(state.color);
pixels->Index = 0;
pixels->Color1 = state.color;
pixels->Color2 = state.color2;
pixels->TotalSteps = state.totalSteps;
pixels->ActivePattern = (pattern)state.pattern;
pixels->Direction = FORWARD;
}
void PixelPlugin::colorWheel(String msg)
{
int color = atoi(msg.c_str());
pixels->ActivePattern = NONE;
pixels->ColorSet(pixels->Wheel(color));
}
void PixelPlugin::setTotalSteps(String msg)
{
pixels->TotalSteps = atoi(msg.c_str());
}
void PixelPlugin::setBrightness(String msg)
{
int inVal = atoi(msg.c_str());
pixels->setBrightness(inVal);
pixels->show();
}
void PixelPlugin::setColor(String msg)
{
pixels->ActivePattern = NONE;
pixels->Color1 = atoi(msg.c_str());
//if(pixels->ActivePattern == NONE){
pixels->ColorSet(pixels->Color1);
//}
}
void PixelPlugin::setColor2(String msg)
{
pixels->Color2 = atoi(msg.c_str());
}
void PixelPlugin::setPattern(String msg)
{
pixels->Index = 0;
pixels->Direction = FORWARD;
pixels->ActivePattern = (pattern)atoi(msg.c_str());
}
void PixelPlugin::animate()
{
pixels->Update();
yield();
}
void PixelPlugin::enable()
{
animation.enable();
}
void PixelPlugin::disable()
{
animation.disable();
}

59
src/PixelPlugin.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef __PIXEL_PLUGIN__
#define __PIXEL_PLUGIN__
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STD_FUNCTION
#include "TaskSchedulerDeclarations.h"
#include "MeshNet.h"
#include "Plugin.h"
#include "NeoPixelConfig.h"
#include "NeoPatternState.h"
#include "NeoPattern.cpp"
#include "config.h"
using namespace std;
using namespace std::placeholders;
#ifndef PIXEL_CONFIG_FILE
#define PIXEL_CONFIG_FILE "/pixelConfig.json"
#endif
struct PixelConfig
{
int pin;
int length;
int brightness;
int updateInterval;
};
class PixelPlugin : public Plugin
{
private:
NeoPixelConfig pixelConfig;
NeoPattern *pixels;
NeoPatternState state;
public:
Task animation;
PixelPlugin(PixelConfig cfg);
PixelPlugin(NeoPattern *neoPattern);
PixelPlugin();
void loadConfigFromFile();
void applyConfig(NeoPixelConfig cfg);
void applyConfigFromFile();
void activate(Scheduler *userScheduler);
void defaultAnimation();
void setState(String msg);
void colorWheel(String msg);
void setTotalSteps(String msg);
void setBrightness(String msg);
void setColor(String msg);
void setColor2(String msg);
void setPattern(String msg);
void animate();
void enable();
void disable();
};
#endif

View File

@@ -1,11 +0,0 @@
#include "TemplatePlugin.h"
TemplatePlugin::TemplatePlugin(TemplateConfig cfg)
{
templateConfig = cfg;
}
void TemplatePlugin::activate(Scheduler *scheduler)
{
PRINT_MSG(Serial, "TEMPLATE", "plugin activated");
}

View File

@@ -1,26 +0,0 @@
#ifndef __TEMPLATE_PLUGIN__
#define __TEMPLATE_PLUGIN__
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STD_FUNCTION
#include <Plugin.h>
#include "utils/utils_print.h"
using namespace std;
using namespace std::placeholders;
struct TemplateConfig
{
const char *var;
};
class TemplatePlugin : public Plugin
{
public:
TemplateConfig templateConfig;
TemplatePlugin(TemplateConfig cfg);
void activate(Scheduler *scheduler);
};
#endif

View File

@@ -21,4 +21,11 @@
#define HOSTNAME "sprocket"
#define CONNECT_TIMEOUT 10000
// NeoPixel conig
#define LED_STRIP_PIN D2
#define LED_STRIP_LENGTH 8
#define LED_STRIP_BRIGHTNESS 48
#define LED_STRIP_UPDATE_INTERVAL 200
#define LED_STRIP_DEFAULT_COLOR 100
#endif

View File

@@ -1,26 +1,18 @@
#include "config.h"
#include "WiFiNet.h"
#include "Sprocket.h"
#include "TemplatePlugin.h"
#include "PixelPlugin.h"
WiFiNet *network;
Sprocket *sprocket;
void setup()
{
sprocket = new Sprocket({STARTUP_DELAY, SERIAL_BAUD_RATE});
sprocket->addPlugin(new TemplatePlugin({"sprocket"}));
network = new WiFiNet(
SPROCKET_MODE,
STATION_SSID,
STATION_PASSWORD,
AP_SSID,
AP_PASSWORD,
HOSTNAME,
CONNECT_TIMEOUT);
network->connect();
sprocket = new Sprocket(
{STARTUP_DELAY, SERIAL_BAUD_RATE});
sprocket->addPlugin(new PixelPlugin(
{LED_STRIP_PIN,
LED_STRIP_LENGTH,
LED_STRIP_BRIGHTNESS,
LED_STRIP_UPDATE_INTERVAL}));
sprocket->activate();
}