457 lines
11 KiB
C++
457 lines
11 KiB
C++
#include <Homie.h>
|
|
// homie lib from: https://github.com/marvinroger/homie-esp8266/
|
|
#include <Adafruit_NeoPixel.h>
|
|
#ifdef __AVR__
|
|
#include <avr/power.h>
|
|
#endif
|
|
|
|
#define PIN 2 //data pin for ws2812
|
|
|
|
#define NUMPIXELS 64
|
|
|
|
#define FPS 15
|
|
|
|
uint8_t effect=0;
|
|
#define EFFECT_NONE 0
|
|
#define EFFECT_SMOOTH 1
|
|
uint8_t movingPoint_x=3;
|
|
uint8_t movingPoint_y=3;
|
|
uint8_t wheelPos=0;
|
|
uint8_t wheelPosSlow=0; //for slower wheelPos increment than 1
|
|
int wheelSpeed=16; //16=+1/frame
|
|
int smoothing=80; //0 to 100. 100=no change (ultrasmooth), 0=no smoothing.
|
|
int strength=50; //how much pixels to apply color to
|
|
#define EFFECT_SPIRAL 2
|
|
#define EFFECT_RANDOMFADE 3
|
|
int fadespeedmax=5; //1 to 255
|
|
|
|
|
|
|
|
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
|
|
|
|
HomieNode homieNode("pixel", "commands");
|
|
|
|
uint8_t pixelR[NUMPIXELS];
|
|
uint8_t pixelG[NUMPIXELS];
|
|
uint8_t pixelB[NUMPIXELS];
|
|
//write to buffer, flip with showBuffer()
|
|
uint8_t pixelR_buffer[NUMPIXELS];
|
|
uint8_t pixelG_buffer[NUMPIXELS];
|
|
uint8_t pixelB_buffer[NUMPIXELS];
|
|
|
|
long lastMillis=0;
|
|
long fpsdelay=1000/FPS;
|
|
|
|
int xyToPos(int x, int y){ //convert x y pixel position to matrix position
|
|
if (y%2==0){
|
|
return (y*8+x);
|
|
}else{
|
|
return (y*8+(7-x));
|
|
}
|
|
}
|
|
|
|
int numToPos(int num){ //convert pixel number to actual 8x8 matrix position
|
|
int x=num%8;
|
|
int y=num/8;
|
|
return xyToPos(x,y);
|
|
}
|
|
|
|
uint32_t wheel(byte WheelPos) {
|
|
WheelPos = 255 - WheelPos;
|
|
if(WheelPos < 85) {
|
|
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
|
|
}
|
|
if(WheelPos < 170) {
|
|
WheelPos -= 85;
|
|
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
|
|
}
|
|
WheelPos -= 170;
|
|
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
|
|
}
|
|
|
|
|
|
void led_fill(uint32_t c)
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
strip.setPixelColor(i, c);
|
|
}
|
|
strip.show();
|
|
}
|
|
|
|
void led_random()
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
strip.setPixelColor(i, wheel(random(0,255)));
|
|
}
|
|
strip.show();
|
|
}
|
|
|
|
|
|
void showBuffer()
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
pixelR[i]=pixelR_buffer[i];
|
|
pixelG[i]=pixelG_buffer[i];
|
|
pixelB[i]=pixelB_buffer[i];
|
|
strip.setPixelColor(i, pixelR[i], pixelG[i], pixelB[i]);
|
|
}
|
|
strip.show();
|
|
}
|
|
|
|
uint8_t getAverage(uint8_t array[NUMPIXELS], uint8_t i, int x, int y)
|
|
{
|
|
uint16_t sum=0;
|
|
uint8_t count=0;
|
|
if (i>=8){ //up
|
|
sum+=array[i-8];
|
|
count++;
|
|
}
|
|
if (i<(64-8)){ //down
|
|
sum+=array[i+8];
|
|
count++;
|
|
}
|
|
if (i>=1){ //left
|
|
sum+=array[i-1];
|
|
count++;
|
|
}
|
|
if (i<(64-1)){ //right
|
|
sum+=array[i+1];
|
|
count++;
|
|
}
|
|
|
|
/*
|
|
if (i>=(8+1)){ //up left
|
|
sum+=array[i-8-1];
|
|
count++;
|
|
}
|
|
if (i<(64-8-1)){ //down left
|
|
sum+=array[i+8-1];
|
|
count++;
|
|
}
|
|
if (i>=(8-1)){ //up right
|
|
sum+=array[i-8+1];
|
|
count++;
|
|
}
|
|
if (i<(64-8+1)){ //down right
|
|
sum+=array[i+8+1];
|
|
count++;
|
|
}*/
|
|
return sum/count;
|
|
}
|
|
|
|
|
|
void led_smooth()
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
//uint8_t avgbrightness=pixelR_buffer[i]/3+pixelG_buffer[i]/3+pixelB_buffer[i]/3;
|
|
pixelR_buffer[i]=(smoothing/100.0)*pixelR[i] + (1.0-(smoothing/100.0))*getAverage(pixelR,i, 0,0);
|
|
pixelG_buffer[i]=(smoothing/100.0)*pixelG[i] + (1.0-(smoothing/100.0))*getAverage(pixelG,i, 0,0);
|
|
pixelB_buffer[i]=(smoothing/100.0)*pixelB[i] + (1.0-(smoothing/100.0))*getAverage(pixelB,i, 0,0);
|
|
|
|
}
|
|
showBuffer();
|
|
}
|
|
|
|
void led_movingPoint()
|
|
{
|
|
uint32_t c=wheel(wheelPos);
|
|
wheelPosSlow+=wheelSpeed;
|
|
wheelPos=(wheelPos+ (wheelPosSlow/10) )%255;
|
|
wheelPosSlow=wheelPosSlow%16;
|
|
|
|
uint8_t r = (uint8_t)(c >> 16);
|
|
uint8_t g = (uint8_t)(c >> 8);
|
|
uint8_t b = (uint8_t)c;
|
|
|
|
movingPoint_x=movingPoint_x+8+random(-random(0,1+1),random(0,1+1)+1);
|
|
movingPoint_y=movingPoint_y+8+random(-random(0,1+1),random(0,1+1)+1);
|
|
if (movingPoint_x<8){
|
|
movingPoint_x=8-movingPoint_x;
|
|
}else if (movingPoint_x>=16){
|
|
movingPoint_x=22-movingPoint_x;
|
|
}else{
|
|
movingPoint_x-=8;
|
|
}
|
|
|
|
if (movingPoint_y<8){
|
|
movingPoint_y=8-movingPoint_y;
|
|
}else if (movingPoint_y>=16){
|
|
movingPoint_y=22-movingPoint_y;
|
|
}else{
|
|
movingPoint_y-=8;
|
|
}
|
|
uint8_t startx=movingPoint_x;
|
|
uint8_t starty=movingPoint_y;
|
|
|
|
for (int i=0;i<strength;i++){
|
|
|
|
movingPoint_x=startx+8+random(-random(0,2+1),random(0,2+1)+1);
|
|
movingPoint_y=starty+8+random(-random(0,2+1),random(0,2+1)+1);
|
|
|
|
if (movingPoint_x<8){
|
|
movingPoint_x=8-movingPoint_x;
|
|
}else if (movingPoint_x>=16){
|
|
movingPoint_x=22-movingPoint_x;
|
|
}else{
|
|
movingPoint_x-=8;
|
|
}
|
|
|
|
if (movingPoint_y<8){
|
|
movingPoint_y=8-movingPoint_y;
|
|
}else if (movingPoint_y>=16){
|
|
movingPoint_y=22-movingPoint_y;
|
|
}else{
|
|
movingPoint_y-=8;
|
|
}
|
|
|
|
|
|
|
|
if (pixelR[xyToPos(movingPoint_x,movingPoint_y)]<r){
|
|
pixelR[xyToPos(movingPoint_x,movingPoint_y)]++;
|
|
}else if (pixelR[xyToPos(movingPoint_x,movingPoint_y)]>r){
|
|
pixelR[xyToPos(movingPoint_x,movingPoint_y)]--;
|
|
}
|
|
if (pixelG[xyToPos(movingPoint_x,movingPoint_y)]<g){
|
|
pixelG[xyToPos(movingPoint_x,movingPoint_y)]++;
|
|
}else if (pixelG[xyToPos(movingPoint_x,movingPoint_y)]>g){
|
|
pixelG[xyToPos(movingPoint_x,movingPoint_y)]--;
|
|
}
|
|
if (pixelB[xyToPos(movingPoint_x,movingPoint_y)]<b){
|
|
pixelB[xyToPos(movingPoint_x,movingPoint_y)]++;
|
|
}else if (pixelB[xyToPos(movingPoint_x,movingPoint_y)]>b){
|
|
pixelB[xyToPos(movingPoint_x,movingPoint_y)]--;
|
|
}
|
|
}
|
|
|
|
|
|
//pixelR[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelR[xyToPos(movingPoint_x,movingPoint_y)]+0.5*r;
|
|
//pixelG[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelG[xyToPos(movingPoint_x,movingPoint_y)]+0.5*g;
|
|
//pixelB[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelB[xyToPos(movingPoint_x,movingPoint_y)]+0.5*b;
|
|
|
|
movingPoint_x=startx;
|
|
movingPoint_y=starty;
|
|
|
|
}
|
|
void bufferClear()
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
pixelR_buffer[i]=0;
|
|
pixelG_buffer[i]=0;
|
|
pixelB_buffer[i]=0;
|
|
}
|
|
}
|
|
|
|
void led_spiral()
|
|
{
|
|
wheelPos++;
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
//strip.setPixelColor(i,wheel((wheelPos+i*5)%255));
|
|
|
|
|
|
}
|
|
strip.show();
|
|
}
|
|
|
|
void led_randomfade()
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
pixelR_buffer[i]+=random(0,random(0,fadespeedmax+1)+1); //use buffer red channel for color wheel
|
|
strip.setPixelColor(i,wheel(pixelR_buffer[i]));
|
|
}
|
|
strip.show();
|
|
}
|
|
void set_randomBuffer()
|
|
{
|
|
for (int i=0; i < strip.numPixels(); i++) {
|
|
uint32_t c=wheel(random(0,256));
|
|
pixelR_buffer[i]=(uint8_t)(c >> 16);
|
|
pixelG_buffer[i]=(uint8_t)(c >> 8);
|
|
pixelB_buffer[i]=(uint8_t)c;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
uint32_t parseColor(String value){
|
|
if (value.charAt(0)=='#'){ //solid fill
|
|
String color=value.substring(1);
|
|
int number = (int) strtol( &color[0], NULL, 16);
|
|
|
|
|
|
// Split them up into r, g, b values
|
|
int r = number >> 16;
|
|
int g = number >> 8 & 0xFF;
|
|
int b = number & 0xFF;
|
|
Homie.getLogger() << "r=" << r << " g=" << g << " b=" << b << endl;
|
|
//Serial.print("r=");Serial.print(r);
|
|
//Serial.print(" g=");Serial.print(g);
|
|
//Serial.print(" b=");Serial.println(b);
|
|
return strip.Color(r, g, b);
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool effectHandler(const HomieRange& range, const String& value) {
|
|
Homie.getLogger() << "-> " << value << endl;
|
|
int sep = value.indexOf("|");
|
|
|
|
String command=value.substring(0,sep);
|
|
String parameters=value.substring(sep+1);
|
|
Homie.getLogger() << "command=" << command << " parameters=" << parameters << endl;
|
|
|
|
if (command.equals("fill")){
|
|
effect=EFFECT_NONE;
|
|
led_fill(parseColor(parameters));
|
|
}else if (command.equals("off")){
|
|
effect=EFFECT_NONE;
|
|
led_fill(strip.Color(0, 0, 0));
|
|
}else if (command.equals("random")){
|
|
effect=EFFECT_NONE;
|
|
led_random();
|
|
}else if (command.equals("set")){ //example: set|37#ff003a
|
|
effect=EFFECT_NONE;
|
|
int x=parameters.substring(0,1).toInt();
|
|
int y=parameters.substring(1,2).toInt();
|
|
String cstr=parameters.substring(2,9);
|
|
strip.setPixelColor(xyToPos(x,y), parseColor(cstr));
|
|
strip.show();
|
|
}else if (command.equals("smooth")){ //example: smooth|[wheelspeed]|[smoothing]|[strength] wheelspeed=1-255, smoothing=0-100, strength=1-255
|
|
int sepparam = parameters.indexOf("|");
|
|
int p1=parameters.substring(0,sepparam).toInt();
|
|
String parameters_part2=parameters.substring(sepparam+1);
|
|
sepparam = parameters_part2.indexOf("|");
|
|
int p2=parameters_part2.substring(0,sepparam).toInt();
|
|
int p3=parameters_part2.substring(sepparam+1).toInt();
|
|
wheelSpeed=16; //default, speed=+1 /frame
|
|
if (p1>0){
|
|
wheelSpeed=p1;
|
|
}
|
|
smoothing=80;
|
|
if (p2>0){
|
|
smoothing=p2;
|
|
}
|
|
strength=50;
|
|
if (p3>0){
|
|
strength=p3;
|
|
}
|
|
Homie.getLogger() << "-- p1=" << p1 << " p2=" << p2 << " p3=" << p3 << endl;
|
|
effect=EFFECT_SMOOTH;
|
|
bufferClear();
|
|
showBuffer();
|
|
strip.show();
|
|
}else if (command.equals("spiral")){
|
|
effect=EFFECT_SPIRAL;
|
|
bufferClear();
|
|
showBuffer();
|
|
strip.show();
|
|
}else if (command.equals("clearbuffer")){
|
|
bufferClear();
|
|
showBuffer();
|
|
strip.show();
|
|
}else if (command.equals("randomfade")){ //example: randomfade|5
|
|
int sepparam = parameters.indexOf("|");
|
|
int p1=parameters.substring(0,sepparam).toInt();
|
|
fadespeedmax=5;
|
|
if (p1>0){
|
|
fadespeedmax=p1;
|
|
}
|
|
effect=EFFECT_RANDOMFADE;
|
|
set_randomBuffer(); //initialize random
|
|
}else if (command.equals("randombuffer")){
|
|
set_randomBuffer(); //set random
|
|
showBuffer();
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool pixelsHandler(const HomieRange& range, const String& value) {
|
|
|
|
String remaining=value;
|
|
int i=0;
|
|
effect=EFFECT_NONE;
|
|
do{
|
|
String current=remaining.substring(0,7);
|
|
Homie.getLogger() << i << ":" << current << endl;
|
|
uint32_t currentcolor=parseColor(current);
|
|
|
|
strip.setPixelColor(i, currentcolor);
|
|
i++;
|
|
|
|
remaining=remaining.substring(7);
|
|
|
|
}while(remaining.length()>2 && (i<strip.numPixels()));
|
|
Homie.getLogger() << " filling rest with black" << endl;
|
|
while(i<strip.numPixels()){
|
|
strip.setPixelColor(numToPos(i), strip.Color(0,0,0));
|
|
i++;
|
|
}
|
|
|
|
strip.show();
|
|
|
|
return true;
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
Serial << endl << endl;
|
|
|
|
strip.begin();
|
|
strip.show(); // Initialize all pixels to 'off'
|
|
|
|
led_fill(strip.Color(100, 0, 0));
|
|
|
|
|
|
Homie_setFirmware("pixelprojektor", "1.0.0");
|
|
|
|
homieNode.advertise("effect").settable(effectHandler);
|
|
homieNode.advertise("pixels").settable(pixelsHandler);
|
|
|
|
strip.setPixelColor(0, strip.Color(100,0,0));
|
|
strip.show();
|
|
|
|
|
|
delay(21240); //wait, boot bug testfix
|
|
Homie.setup();
|
|
|
|
led_fill(strip.Color(0, 0, 0));
|
|
|
|
|
|
}
|
|
|
|
void loop() {
|
|
Homie.loop();
|
|
long currentMillis = millis();
|
|
|
|
|
|
if (lastMillis+fpsdelay<currentMillis){
|
|
|
|
switch(effect){
|
|
case EFFECT_SMOOTH:
|
|
led_movingPoint();
|
|
led_smooth();
|
|
break;
|
|
case EFFECT_SPIRAL:
|
|
led_spiral();
|
|
break;
|
|
case EFFECT_RANDOMFADE:
|
|
led_randomfade();
|
|
break;
|
|
}
|
|
|
|
lastMillis=currentMillis;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|