ws2812-achterbahn/achterbahn.ino

543 lines
16 KiB
Arduino
Raw Normal View History

2018-06-10 17:23:11 +00:00
//flash as Wemos D1 R2 & mini
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#include <vector>
#include "wagon.h"
#include "effect.h"
2018-07-10 22:08:14 +00:00
#include "fx_scanner.h"
2018-07-15 15:26:07 +00:00
#include "fx_flash.h"
#define PIN D2
2018-05-06 16:14:11 +00:00
#define NUMPIXELS 600
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
long lastPixelUpdate=0;
2018-05-06 16:14:11 +00:00
#define PIXELUPDATETIME 20
long lastRoutineUpdate=0;
2018-05-06 16:14:11 +00:00
#define ROUTINEUPDATETIME 20
2018-05-06 19:41:33 +00:00
long lastCheckspawn=0;
#define CHECKSPAWNDELAY 4000 //delay in ms to check random spawn
2018-05-11 22:24:02 +00:00
#define SPAWNCHANCE 20 //1 out of x times wagon will spawn
2018-05-06 19:41:33 +00:00
#define SPAWNCHANCEDOUBLE 5 //change of spawning a two trains simultaneously
2018-07-15 21:04:20 +00:00
long lastCheckspawnEffect=0;
#define CHECKSPAWNDELAY_EFFECT 2000 //delay in ms to check random effect
2018-07-15 21:11:54 +00:00
#define SPAWNCHANCE_EFFECT_SCANNER 600 //1 out of x times spawn effect
#define SPAWNCHANCE_EFFECT_FLASH 600 //1 out of x times spawn effect
2018-05-16 18:48:55 +00:00
#define BRIGHTNESS_RUN 200
#define BRIGHTNESS_DEBUG 150
long loopmillis=0;
uint8_t height[NUMPIXELS];
2018-05-06 16:14:11 +00:00
uint8_t heightraw[NUMPIXELS]; //uninterpolated values
#define MAXHEIGHT 254
std::vector <Wagon> wagon_arr;
uint8_t maxid=0;
2018-05-06 19:41:33 +00:00
bool configmode=false;
2018-05-06 16:14:11 +00:00
int selectedpixel=-1; //-1 = none
2018-05-06 19:41:33 +00:00
uint8_t wagoncount=0;
Effect* effect = NULL;
2018-07-10 22:08:14 +00:00
2018-05-06 19:41:33 +00:00
//define config
//#define RESPAWNWAGON
#define MAXWAGONS 5 //maximum number of wagons
2018-05-06 16:14:11 +00:00
void setup() {
Serial.begin(115200);
strip.begin();
2018-05-16 18:48:55 +00:00
strip.setBrightness(BRIGHTNESS_RUN); //150
strip.show(); // Initialize all pixels to 'off'
Serial.println("Started");
2018-05-06 16:14:11 +00:00
resetHeightmap();
2018-05-06 19:41:33 +00:00
//fixed heightmap
2018-06-10 17:23:11 +00:00
heightraw[0]=254;
heightraw[20]=253;
heightraw[40]=254;
heightraw[67]=241;
heightraw[79]=240;
heightraw[90]=237;
heightraw[130]=218;
heightraw[144]=217;
heightraw[159]=218;
heightraw[196]=232;
heightraw[228]=197;
heightraw[236]=191;
heightraw[245]=197;
heightraw[275]=235;
heightraw[300]=210;
heightraw[345]=178;
heightraw[362]=176;
heightraw[369]=176;
heightraw[381]=169;
heightraw[421]=112;
heightraw[460]=80;
heightraw[474]=78;
heightraw[489]=80;
heightraw[524]=112;
heightraw[534]=121;
heightraw[542]=122;
heightraw[561]=123;
heightraw[580]=145;
heightraw[599]=172;
2018-05-24 23:10:06 +00:00
2018-05-16 18:48:55 +00:00
2018-05-06 19:41:33 +00:00
2018-05-06 16:14:11 +00:00
2018-05-06 16:14:11 +00:00
interpolateHeightValues();
/*
Serial.println();
for (int i=0;i<NUMPIXELS;i++){
Serial.print(i);
Serial.print(": ");
Serial.println(height[i]);
}*/
//previewHeightmap(2000);
2018-05-06 19:41:33 +00:00
//spawnWagon();
2018-05-19 18:37:06 +00:00
spawnWagon();
2018-05-06 16:14:11 +00:00
}
void resetHeightmap(){
for (int i=0;i<NUMPIXELS;i++){
heightraw[i]=255; //255 means value need to be interpolated
}
heightraw[0]=0;
heightraw[NUMPIXELS-1]=0;
}
2018-05-06 19:41:33 +00:00
void printHeightmapRaw() {
Serial.println();
for (int i=0;i<NUMPIXELS;i++){
if (heightraw[i]!=255){
Serial.print("heightraw[");
Serial.print(i);
Serial.print("]=");
Serial.print(heightraw[i]);
Serial.println(";");
2018-05-16 18:48:55 +00:00
delay(10);
2018-05-06 19:41:33 +00:00
}
}
}
2018-05-06 16:14:11 +00:00
void interpolateHeightValues(){
for (int i=0;i<NUMPIXELS;i++){ //copy heightraw to height
height[i]=heightraw[i];
}
//interpolate every part with height value 255
for (int interpolateStartpos=0;interpolateStartpos<NUMPIXELS-1;interpolateStartpos++){
if (height[interpolateStartpos]==255){ //interpolation part starts
int interpolateEndpos=interpolateStartpos+1;
while (interpolateEndpos<NUMPIXELS && height[interpolateEndpos]==255){
interpolateEndpos++;
}
interpolateEndpos--;
//interpolateStartpos index of first 255 value
//interpolateEndpos index of last 255 value
uint8_t interpolateStartvalue=height[interpolateStartpos-1];
uint8_t interpolateEndvalue=height[interpolateEndpos+1];
int interpolateLength=interpolateEndpos-interpolateStartpos+1; //one 255 element -> length 1
float interpolateStep= ((int)(interpolateEndvalue)-(int)(interpolateStartvalue))*1.0 /(interpolateLength+1);
Serial.println();
Serial.print("interpolateStep=");
Serial.print("(");
Serial.print(interpolateEndvalue);
Serial.print("-");
Serial.print(interpolateStartvalue);
Serial.print(")/");
Serial.print(interpolateLength+1);
Serial.print("=");
Serial.println(interpolateStep);
int interpolateStepCounter=1;
Serial.println();
Serial.print("interpolateStartpos=");
Serial.println(interpolateStartpos);
Serial.print("interpolateEndpos=");
Serial.println(interpolateEndpos);
Serial.print("interpolateStartvalue=");
Serial.println(interpolateStartvalue);
Serial.print("interpolateEndvalue=");
Serial.println(interpolateEndvalue);
Serial.print("interpolateLength=");
Serial.println(interpolateLength);
Serial.print("interpolateStep=");
Serial.println(interpolateStep,6);
for (int setinti=interpolateStartpos;setinti<=interpolateEndpos;setinti++) { //for all coherent elements to interpolate
height[setinti]=height[interpolateStartpos-1]+(int)(interpolateStep*interpolateStepCounter);
/*Serial.print(height[interpolateStartpos-1]);
Serial.print("+(");
Serial.print(interpolateStep);
Serial.print("*");
Serial.print(interpolateStepCounter);
Serial.print(")=");
Serial.println(height[setinti]);*/
interpolateStepCounter++;
}
interpolateStartpos=interpolateEndpos;
}
}
}
void previewHeightmap(int waittime){
for (int i=0;i<NUMPIXELS;i++){
//uint32_t c=Wheel(height[i]*255/45);
uint8_t b=height[i]*255.0/MAXHEIGHT;
2018-05-06 16:14:11 +00:00
//uint32_t c=strip.Color(255-b,b,0);
uint32_t c=Wheel(b/1.2);
if (height[i]==255){
c=strip.Color(0,0,0);
}
strip.setPixelColor(i,c);
}
2018-05-06 16:14:11 +00:00
if (waittime>0){
strip.show();
delay(waittime);
}
}
void spawnWagon(){
2018-04-08 17:47:34 +00:00
//Wagon tmpr = Wagon(maxid++,NUMPIXELS,&strip, height, 35, 6, 0.5,0); //spawn new wagon
2018-05-16 18:48:55 +00:00
// pos, wagonlength, startvel, startacc, trainmass, wagoncolor
2018-05-06 19:41:33 +00:00
//Wagon tmpr = Wagon(maxid++,NUMPIXELS,&strip, height, random(0, 20), random(3,20), random(0.2, 50)/10.0, 0 , random(5,100) , Wheel(random(0,256))); //spawn new wagon
2018-05-24 23:10:06 +00:00
int _randomlength=random(3,40); //Zelt: 3-> minimum vel 10, 40 -> minium vel 30
2018-05-16 18:48:55 +00:00
// pos, wagonlength, startvel , startacc, trainmass, wagoncolor
2018-06-10 17:23:11 +00:00
//Wagon tmpr = Wagon(maxid++,NUMPIXELS,&strip, height, random(0, 20), _randomlength, random(map(_randomlength,3,40,1,1), map(_randomlength,3,40, 13,40))/10.0 , 0 , 5 , Wheel((uint8_t)random(0,255))); //spawn new wagon
Wagon tmpr = Wagon(maxid++,NUMPIXELS,&strip, height, NUMPIXELS+_randomlength, _randomlength, -random(map(_randomlength,3,40,10,20), map(_randomlength,3,40, 22,60))/10.0 , 0 , 2.0 , Wheel((uint8_t)random(0,255))); //spawn new wagon
2018-04-08 17:47:34 +00:00
2018-06-10 17:59:01 +00:00
//special spawns
if (random(0,50)==0){
tmpr.setType(1); //make rainbow
tmpr.setLength(_randomlength*random(1,3));
}
wagon_arr.push_back(tmpr);
Serial.println("Spawned Wagon");
}
2018-05-06 16:14:11 +00:00
void spawnWagon(float pos, float wagonlength,float startvel, float startacc, float mass, uint8_t wheelcolor){
//Wagon tmpr = Wagon(maxid++,NUMPIXELS,&strip, height, 35, 6, 0.5,0); //spawn new wagon
// pos, wagonlength, startvel, startacc, wagonmass, wagoncolor
Wagon tmpr = Wagon(maxid++,NUMPIXELS,&strip, height, pos, wagonlength, startvel, startacc , mass , Wheel(wheelcolor)); //spawn new wagon
wagon_arr.push_back(tmpr);
Serial.println("Spawned Custom Wagon");
}
2018-04-08 17:47:34 +00:00
void loop() {
loopmillis=millis();
2018-05-06 16:14:11 +00:00
checkSerial();
if (configmode){
loop_configmode();
}else{
loop_achterbahn();
}
}
void checkSerial(){
static String serialstring_temp="";
String serialstring="";
while(Serial.available()){
if (Serial.available()>0){
char c = Serial.read();
if (c!='\n') {
serialstring_temp+=c;
Serial.print(c); //echo
}else{
Serial.println(""); //new line
serialstring=serialstring_temp;
serialstring_temp="";
}
}
}
if (serialstring.length()>0) {
Serial.println("String:"+serialstring);
2018-05-06 19:41:33 +00:00
2018-05-06 16:14:11 +00:00
if (serialstring.equals("run")){
2018-05-16 18:48:55 +00:00
strip.setBrightness(BRIGHTNESS_RUN);
2018-05-06 16:14:11 +00:00
configmode=false;
}else if (serialstring.equals("debug")){
2018-05-16 18:48:55 +00:00
strip.setBrightness(BRIGHTNESS_DEBUG);
2018-05-06 16:14:11 +00:00
configmode=true;
2018-05-06 19:41:33 +00:00
}else if (serialstring.equals("print")){
printHeightmapRaw();
2018-05-06 16:14:11 +00:00
}else if (serialstring.equals("remove")){
removeAllWagons();
}else if (serialstring.equals("clear")){
resetHeightmap();
interpolateHeightValues();
previewHeightmap(0); //show heightmap
strip.show();
}else if (serialstring.startsWith("spawn=")){
String rest=serialstring.substring(serialstring.indexOf('=')+1); //part after =
int spawnpos=rest.substring(0,rest.indexOf(',')).toInt(); //part to next ,
rest=rest.substring(rest.indexOf(',')+1); //part after ,
int spawnlength=rest.substring(0,rest.indexOf(',')).toInt(); //part to next ,
rest=rest.substring(rest.indexOf(',')+1); //part after ,
int spawnstartvel=rest.substring(0,rest.indexOf(',')).toInt(); //part to next ,
rest=rest.substring(rest.indexOf(',')+1); //part after ,
int spawnstartacc=rest.substring(0,rest.indexOf(',')).toInt(); //part to next ,
rest=rest.substring(rest.indexOf(',')+1); //part after ,
float spawnmass=rest.substring(0,rest.indexOf(',')).toInt()/1000.0; //part to next , //mass in gramm
2018-05-06 16:14:11 +00:00
rest=rest.substring(rest.indexOf(',')+1); //part after ,
int spawncolor=rest.substring(0).toInt(); //part to next ,
Serial.print("spawning ");
Serial.print(spawnpos);
Serial.print(",");
Serial.println(spawnlength);
Serial.print(",");
Serial.println(spawnstartvel); //startvel will be /10
Serial.print(",");
Serial.println(spawnstartacc); //startacc will be /10
Serial.print(",");
Serial.println(spawnmass);
Serial.print(",");
Serial.println(spawncolor);
spawnWagon(spawnpos,spawnlength,spawnstartvel/10.0,spawnstartacc/10.0,spawnmass,spawncolor);
}else if (serialstring.equals("spawn")){
spawnWagon(); //random
}else if (serialstring.startsWith("setpx=")){
String pixelnumberstring=serialstring.substring(serialstring.indexOf('=')+1,serialstring.indexOf(',')); //part between = and ,
String pixelvaluestring=serialstring.substring(serialstring.indexOf(',')+1); //part after ,
int pixelnumber=pixelnumberstring.toInt();
int pixelvalue=pixelvaluestring.toInt();
Serial.print("set pixel ");
Serial.print(pixelnumber);
Serial.print("=");
Serial.println(pixelvalue);
if (pixelnumber>=0 && pixelnumber<NUMPIXELS && pixelvalue>=0 && pixelvalue<=255){
heightraw[pixelnumber]=pixelvalue;
}else{
Serial.println("Error: Value out of range!");
}
interpolateHeightValues();
Serial.println();
for (int i=0;i<NUMPIXELS;i++){
Serial.print(i);
Serial.print(": ");
Serial.print(height[i]);
Serial.print(" (");
Serial.print(heightraw[i]);
Serial.println(")");
}
previewHeightmap(0); //show heightmap
strip.show();
}else if (serialstring.startsWith("px=")){
String pixelnumberstring=serialstring.substring(serialstring.indexOf('=')+1); //part between = and ,
int pixelnumber=pixelnumberstring.toInt();
Serial.print("show pixel ");
Serial.print(pixelnumber);
if (pixelnumber<NUMPIXELS){
selectedpixel=pixelnumber;
}else{
Serial.println("Error: Value too high!");
}
2018-07-15 15:26:07 +00:00
}else if (serialstring.startsWith("fx_")){
2018-07-10 22:08:14 +00:00
Serial.println("Effect Scanner");
if (effect!=NULL){
delete effect;
}
2018-07-15 15:26:07 +00:00
if (serialstring.equals("fx_scanner")){
Serial.println("Effect Scanner");
effect=new FX_Scanner(NUMPIXELS,&strip,height,255,-200,strip.Color(100,0,0));
}else if (serialstring.equals("fx_flash")){
Serial.println("Effect Flash");
2018-07-15 21:04:20 +00:00
effect=new FX_Flash(NUMPIXELS,&strip,height,strip.Color(200,200,200));
2018-07-15 15:26:07 +00:00
}
2018-05-06 16:14:11 +00:00
}
}
}
void loop_configmode(){
if (lastPixelUpdate+PIXELUPDATETIME<loopmillis){
lastPixelUpdate=loopmillis;
previewHeightmap(0);
2018-05-06 19:41:33 +00:00
if (selectedpixel>=0){
2018-05-06 16:14:11 +00:00
uint32_t c=strip.Color(255,255,255);
strip.setPixelColor(selectedpixel,c);
2018-05-06 19:41:33 +00:00
if (selectedpixel>=1){
2018-05-06 16:14:11 +00:00
uint32_t c=strip.Color(0,0,0);
strip.setPixelColor(selectedpixel-1,c);
}
if (selectedpixel<NUMPIXELS-1){
uint32_t c=strip.Color(0,0,0);
strip.setPixelColor(selectedpixel+1,c);
}
}
strip.show();
}
}
void loop_achterbahn(){
//######################### Update LED Output
if (lastPixelUpdate+PIXELUPDATETIME<loopmillis){
lastPixelUpdate=loopmillis;
for (int i=0;i<NUMPIXELS;i++){ //all black
uint32_t c=strip.Color(0,0,0);
strip.setPixelColor(i,c);
}
2018-07-10 22:08:14 +00:00
//Wagons
for (std::vector<Wagon>::iterator it = wagon_arr.begin(); it != wagon_arr.end(); ++it) //all wagons
{
Wagon & w = *it;
w.updateGraphics();
}
2018-07-10 22:08:14 +00:00
//Effects
if (effect != NULL ){
if (effect->active()){
effect->updateGraphics();
}
2018-07-10 22:08:14 +00:00
}
strip.show();
}
if (lastRoutineUpdate+ROUTINEUPDATETIME<loopmillis-ROUTINEUPDATETIME){
2018-05-06 16:14:11 +00:00
Serial.println("Behind!");
}
//######################### Update Physics
if (lastRoutineUpdate+ROUTINEUPDATETIME<loopmillis){
lastRoutineUpdate=loopmillis;
2018-05-06 19:41:33 +00:00
wagoncount=0;
for (std::vector<Wagon>::iterator it = wagon_arr.begin(); it != wagon_arr.end(); ++it) //all wagons
{
2018-05-06 19:41:33 +00:00
Wagon & w = *it;
w.updatePhysics(ROUTINEUPDATETIME);
if (!w.alive())
{
it = wagon_arr.erase(it); // After erasing, it is now pointing the next element.
--it;
2018-05-06 19:41:33 +00:00
#ifdef RESPAWNWAGON
spawnWagon(); //spawn new one
#endif
}else{ //wagon is alive
wagoncount++;
}
}
2018-07-10 22:08:14 +00:00
//Effects
if (effect != NULL ){
if (effect->active()){
effect->updateRoutine(ROUTINEUPDATETIME);
}else{ //effect not active anymore
delete effect;
effect=NULL;
}
2018-07-10 22:08:14 +00:00
}
2018-05-06 19:41:33 +00:00
}
//Check spawning
if (lastCheckspawn+CHECKSPAWNDELAY<loopmillis) {
lastCheckspawn=loopmillis;
Serial.print("Checking Spawning, wagons ");
Serial.println(wagoncount);
if (random(0,SPAWNCHANCE)==0 && wagoncount<MAXWAGONS) { //by chance, exclusive SPAWNCHANCE
spawnWagon();
if (random(0,SPAWNCHANCEDOUBLE)==0){
spawnWagon();
}
2018-07-15 21:04:20 +00:00
}
}
//Check Effect Spawning
if (effect==NULL && lastCheckspawnEffect+CHECKSPAWNDELAY_EFFECT<loopmillis) {
lastCheckspawnEffect=loopmillis;
if (random(0,SPAWNCHANCE_EFFECT_SCANNER)==0){
effect=new FX_Scanner(NUMPIXELS,&strip,height,255,-200,strip.Color(100,0,0));
}else if (random(0,SPAWNCHANCE_EFFECT_FLASH)==0){
effect=new FX_Flash(NUMPIXELS,&strip,height,strip.Color(200,200,200));
}
}
}
2018-05-06 16:14:11 +00:00
void removeAllWagons(){
for (std::vector<Wagon>::iterator it = wagon_arr.begin(); it != wagon_arr.end(); ++it) //all wagons
{
Wagon & w = *it;
w.updatePhysics(ROUTINEUPDATETIME);
it = wagon_arr.erase(it); // After erasing, it is now pointing the next element.
--it;
}
}
// 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) {
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);
}