esp-pixelprojektor/pixelprojektor/pixelprojektor.ino

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;
}
}