/* * ____ _ ______ _____ _____ / __ \ | | | ____| __ \ | __ \ | | | |_ __ ___ _ __ | | | |__ | | | | | |__) |__ _ ___ ___ | | | | '_ \ / _ \ '_ \ | | | __| | | | | | _ // _` |/ __/ _ \ | |__| | |_) | __/ | | | | |____| |____| |__| | | | \ \ (_| | (_| __/ \____/| .__/ \___|_| |_| |______|______|_____/ |_| \_\__,_|\___\___| | | |_| Open LED Race An minimalist cars race for LED strip This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. by gbarbarov@singulardevices.com for Arduino day Seville 2019 https://www.hackster.io/gbarbarov/open-led-race-a0331a https://twitter.com/openledrace https://gitlab.com/open-led-race https://openledrace.net/open-software/ */ //version Basic for PCB Rome Edition // 2 Player , without Boxes Track char const softwareId[] = "A2P0"; // A2P0: "A"=OpenLEDRace Team, "2P0"=Game ID (2P=2 Players, 0=Type 0 w slope w/ box) char const version[] = "1.0.0"; #include #define MAXLED 60 // MAX LEDs actives on strip #define PIN_LED 2 // R 500 ohms to DI pin for WS2812 and WS2813, for WS2813 BI pin of first LED to GND , CAP 1000 uF to VCC 5v/GND,power supplie 5V 2A #define PIN_P1 3 // switch player 1 to PIN and GND #define PIN_P2 4 // switch player 2 to PIN and GND #define PIN_AUDIO 9 // through CAP 2uf to speaker 8 ohms #define INI_RAMP 80 #define MED_RAMP 90 #define END_RAMP 100 #define HIGH_RAMP 16 bool ENABLE_RAMP=0; bool VIEW_RAMP=0; int NPIXELS=MAXLED; // leds on track int cont_print=0; #define COLOR1 Color(255,0,0) #define COLOR2 Color(0,255,0) #define COLOR1_tail Color(i*3,0,0) #define COLOR2_tail Color(0,i*3,0) // Serial Communications #define EOL '\n' // End of Command char used in Protocol #define REC_COMMAND_BUFLEN 32 char cmd[REC_COMMAND_BUFLEN]; // Stores command received by ReadSerialComand() #define TX_COMMAND_BUFLEN 64 char txbuff[TX_COMMAND_BUFLEN]; // to prepare command strings to send int win_music[] = { 2637, 2637, 0, 2637, 0, 2093, 2637, 0, 3136 }; byte gravity_map[MAXLED]; int TBEEP=0; int FBEEP=0; byte SMOTOR=0; float speed1=0; float speed2=0; float dist1=0; float dist2=0; byte loop1=0; byte loop2=0; byte leader=0; byte loop_max=5; //total laps race float ACEL=0.02; float kf=0.015; //friction constant float kg=0.003; //gravity constant byte flag_sw1=0; byte flag_sw2=0; byte draworder=0; unsigned long timestamp=0; Adafruit_NeoPixel track = Adafruit_NeoPixel(MAXLED, PIN_LED, NEO_GRB + NEO_KHZ800); int tdelay = 5; void set_ramp(byte H,byte a,byte b,byte c) {for(int i=0;i<(b-a);i++){gravity_map[a+i]=127-i*((float)H/(b-a));}; gravity_map[b]=127; for(int i=0;i<(c-b);i++){gravity_map[b+i+1]=127+H-i*((float)H/(c-b));}; } void set_loop(byte H,byte a,byte b,byte c) {for(int i=0;i<(b-a);i++){gravity_map[a+i]=127-i*((float)H/(b-a));}; gravity_map[b]=255; for(int i=0;i<(c-b);i++){gravity_map[b+i+1]=127+H-i*((float)H/(c-b));}; } void setup() { Serial.begin(115200); for(int i=0;i127) speed1+=kg*((gravity_map[(word)dist1 % NPIXELS])-127); speed1-=speed1*kf; if ( (flag_sw2==1) && (digitalRead(PIN_P2)==0) ) {flag_sw2=0;speed2+=ACEL;}; if ( (flag_sw2==0) && (digitalRead(PIN_P2)==1) ) {flag_sw2=1;}; if ((gravity_map[(word)dist2 % NPIXELS])<127) speed2-=kg*(127-(gravity_map[(word)dist2 % NPIXELS])); if ((gravity_map[(word)dist2 % NPIXELS])>127) speed2+=kg*((gravity_map[(word)dist2 % NPIXELS])-127); speed2-=speed2*kf; dist1+=speed1; dist2+=speed2; if (dist1>dist2) {if (leader==2) {FBEEP=440;TBEEP=10;} leader=1;} if (dist2>dist1) {if (leader==1) {FBEEP=440*2;TBEEP=10;} leader=2;}; if (dist1>NPIXELS*loop1) {loop1++;TBEEP=10;FBEEP=440;}; if (dist2>NPIXELS*loop2) {loop2++;TBEEP=10;FBEEP=440*2;}; if (loop1>loop_max) {sprintf( txbuff, "w1%c", EOL ); sendSerialCommand(txbuff); // Send Winner=1 command for(int i=0;iloop_max) {sprintf( txbuff, "w2%c", EOL ); sendSerialCommand(txbuff); // Send Winner=2 command for(int i=0;iabs(round(speed2*100))) {draworder=1;}; if (abs(round(speed2*100))>abs(round(speed1*100))) {draworder=0;}; if ( draworder==0 ) { draw_car1(); draw_car2(); } else { draw_car2(); draw_car1(); } track.show(); if (SMOTOR==1) tone(PIN_AUDIO,FBEEP+int(speed1*440*2)+int(speed2*440*3)); delay(tdelay); if (TBEEP>0) {TBEEP--;} else {FBEEP=0;}; cont_print++; if (cont_print>100) {print_cars_position();cont_print=0;} } /* * */ void checkSerialCommand(){ int clen = checkSerial(cmd); if(clen == 0) return ; // No commands received if(clen < 0) { // Error receiving command sprintf( txbuff, "!1Error reading serial command:[%d]",clen); // Send a warning to host sendSerialCommand(txbuff); return; } // clen > 0 ---> Command with length=clen ready in cmd[] switch (cmd[0]) { case '#': // Handshake -> send back { sprintf( txbuff, "#%c", EOL ); sendSerialCommand(txbuff); } return; case '@' : // Enter "Configuration Mode" { // send back @OK // No real cfg mode here, but send @OK so the Desktop app (Upload, configure) // can send a GET SOFTWARE Type/Ver command and identify this software sprintf( txbuff, "@OK%c", EOL ); sendSerialCommand(txbuff); } return; case '?' : // Get Software Id { sprintf( txbuff, "%s%s%c", "?", softwareId, EOL ); sendSerialCommand(txbuff); } return; case '%' : // Get Software Version { sprintf( txbuff, "%s%s%c", "%", version, EOL ); sendSerialCommand(txbuff); } return; } // if we get here, the command it's not managed by this software -> Answer NOK sprintf(txbuff, "%cNOK%c", cmd[0], EOL ); sendSerialCommand(txbuff); return; } /* * */ void send_race_phase( int phase ) { sprintf(txbuff, "R%d%c",phase,EOL); sendSerialCommand(txbuff); } // Command Send/Receive base functions /////////////////////////////////////// // vars used only in these functions Stream* _stream = &Serial; int _bufIdx; /* Non blocking: call these function in main loop() * * If there are bytes available in Serial, READ JUST ONE char * and ADD it to the 'internal' cmd buffer * The char just read is an END OF COMMAND char ? * N: Return 0 (no complete command available yet) * Y: Return buffer length (the caller will find the command in [buf] */ int checkSerial(char * buf) { while (_stream->available()) { if(_bufIdx < REC_COMMAND_BUFLEN - 2) { char data = _stream->read(); if(data == EOL) { int cmsSize=_bufIdx; buf[_bufIdx++] = '\0'; _bufIdx=0; return(cmsSize); } else { buf[_bufIdx++] = data; } } else { // buffer full // reset and retunn error buf[_bufIdx++] = '\0'; _bufIdx=0; return(-2); } } return(0); } /* * */ void sendSerialCommand(char* str) { // get command length int dlen=0; for(; dlenwrite(str, dlen); return; }