diff --git a/.gitignore b/.gitignore index 3bb8dce..84f68dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +00_note_sviluppo.txt .pio .vscode/.browse.c_cpp.db* .vscode/c_cpp_properties.json @@ -5,3 +6,4 @@ .vscode/ipch \.vscode/ + diff --git a/changelog.txt b/changelog.txt index 2b2efd7..8446e72 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,20 @@ Revisions history ----------------- + * 2023-07-20: Ver 0.9.8 - Luca - Branch: "lab" + - Added "Demo" mode: + - When Board has [demo_mode=ON] in the EEPROM configuration, + it plays a "Simulated" race where cars run without any real player. + Useful to test the code and to get people attention in Fairs, etc + On user activity (somebody uses the controllers) the boards jumps + back automatically to demo=off mode (Users play races via Controllers) + - Start implementing Relay Race funcionality + + + * 2023-07-10: Ver 0.9.7 - Lab ---> Master + - Branch Lab merged to Master + * 2021-11-20: Ver 0.9.7 - Luca - Branch: "lab" - ............................................... - Changed Version Number to 0.9.7 - ready to merge with master branch - ver 0.9.7 managed by companion desktopApp diff --git a/doc/OLRN_Protocol_Serial.pdf b/doc/OLRN_Protocol_Serial.pdf deleted file mode 100644 index 96f5017..0000000 Binary files a/doc/OLRN_Protocol_Serial.pdf and /dev/null differ diff --git a/doc/OLR_Protocol_Serial.pdf b/doc/OLR_Protocol_Serial.pdf new file mode 100644 index 0000000..4851784 Binary files /dev/null and b/doc/OLR_Protocol_Serial.pdf differ diff --git a/doc/docSrc/OLR_Protocol_Serial.odt b/doc/docSrc/OLR_Protocol_Serial.odt new file mode 100644 index 0000000..d51dcf6 Binary files /dev/null and b/doc/docSrc/OLR_Protocol_Serial.odt differ diff --git a/open-led-race/SerialCommand.cpp b/open-led-race/SerialCommand.cpp index bae07a7..d96b9a0 100644 --- a/open-led-race/SerialCommand.cpp +++ b/open-led-race/SerialCommand.cpp @@ -48,7 +48,7 @@ int SerialCommand::checkSerial() { } } else { // buffer full - // re4set and retunn error + // reset and retunn error _buf[_bufIdx++] = '\0'; _bufIdx=0; return(-2); diff --git a/open-led-race/olr-controller.c b/open-led-race/olr-controller.c index 311e9cf..bf04421 100644 --- a/open-led-race/olr-controller.c +++ b/open-led-race/olr-controller.c @@ -4,21 +4,31 @@ enum { DELTA_ANALOG = 5, }; +int DIGITAL_CTRL[4]; + static float const ACEL = 0.2; void controller_setup( void ) { - + + /*** if( DIGITAL_MODE == false ){ pinMode(PIN_VCC_ADC1, OUTPUT); pinMode(PIN_VCC_ADC2, OUTPUT); digitalWrite(PIN_VCC_ADC1, HIGH); digitalWrite(PIN_VCC_ADC2, HIGH); } - - pinMode( DIG_CONTROL_1, INPUT_PULLUP); //pull up in adc - pinMode( DIG_CONTROL_2, INPUT_PULLUP); - pinMode( DIG_CONTROL_3, INPUT_PULLUP); - pinMode( DIG_CONTROL_4, INPUT_PULLUP); + ***/ + + DIGITAL_CTRL[CTRL_1]= DIG_CTRL_1_PIN; + DIGITAL_CTRL[CTRL_2]= DIG_CTRL_2_PIN; + DIGITAL_CTRL[CTRL_3]= DIG_CTRL_3_PIN; + DIGITAL_CTRL[CTRL_4]= DIG_CTRL_4_PIN; + + + pinMode( DIG_CTRL_1_PIN, INPUT_PULLUP); //pull up in adc + pinMode( DIG_CTRL_2_PIN, INPUT_PULLUP); + pinMode( DIG_CTRL_3_PIN, INPUT_PULLUP); + pinMode( DIG_CTRL_4_PIN, INPUT_PULLUP); } void controller_init( controller_t* ct, enum ctr_type mode, int pin ) { @@ -41,9 +51,9 @@ byte controller_getStatus( controller_t* ct ) { } ct->badc = ct->adc; } - else if( ct->mode == DEBUG_MODE ){ + else if( ct->mode == DEMO_MODE ){ ct->adc++; - if( ct->adc >= 60){ + if( ct->adc >= 15){ ct->adc = 0; return 1; } diff --git a/open-led-race/olr-controller.h b/open-led-race/olr-controller.h index 6eed6d3..679050b 100644 --- a/open-led-race/olr-controller.h +++ b/open-led-race/olr-controller.h @@ -10,20 +10,34 @@ extern "C"{ #include #include +extern int DIGITAL_CTRL[]; // Global Array containig PINs used for the Digital Controllers (ex: Push Buttons) + + +#define DIG_CTRL_1_PIN A2 // switch player 1 to PIN and GND +#define DIG_CTRL_2_PIN A0 // switch player 2 to PIN and GND +#define DIG_CTRL_3_PIN A3 // switch player 3 to PIN and GND +#define DIG_CTRL_4_PIN A1 // switch player 4 to PIN and GND + + + +enum ctr_idx { // Used to access controller by "name" (and not via zero-offset index) + CTRL_1 = 0, // Ex: DIGITAL_CTRL[CTRL_2] + CTRL_2 , + CTRL_3 , + CTRL_4 +}; + + -#define DIG_CONTROL_1 A2 // switch player 1 to PIN and GND -#define DIG_CONTROL_2 A0 // switch player 2 to PIN and GND -#define DIG_CONTROL_3 A3 // switch player 3 to PIN and GND -#define DIG_CONTROL_4 A1 // switch player 4 to PIN and GND #define PIN_VCC_ADC1 6 #define PIN_VCC_ADC2 7 -enum ctr_type{ +enum ctr_type { NOT_DEFINED = 0, DIGITAL_MODE, ANALOG_MODE, - DEBUG_MODE, + DEMO_MODE, }; typedef struct{ diff --git a/open-led-race/olr-lib.c b/open-led-race/olr-lib.c index 75da1c3..a18b057 100644 --- a/open-led-race/olr-lib.c +++ b/open-led-race/olr-lib.c @@ -124,7 +124,7 @@ bool ramp_isactive( track_t* tck ) { } -void car_resetPosition( car_t* car) { +void car_resetPosition( car_t* car, bool reset_speed) { car->trackID = TRACK_MAIN; car->speed = 0; @@ -135,6 +135,12 @@ void car_resetPosition( car_t* car) { car->battery = 100; } +void car_setSpeed( car_t* car, float speed) { + car->speed = speed; +} + + + void box_init( track_t* tck ) { tck->boxactive = true; } @@ -152,14 +158,18 @@ int tracklen_configure( track_t* tck, int nled ) { return 0; } -int autostart_configure( track_t* tck, int autostart ) { +int autostart_configure( track_t* tck, uint8_t autostart ) { param_option_set(&tck->cfg, AUTOSTART_MODE_OPTION, (boolean) autostart); return 0; } +int demo_configure( track_t* tck, uint8_t demo ) { + param_option_set(&tck->cfg, DEMO_MODE_OPTION, (boolean) demo); + return 0; +} -int players_n_configure( track_t* tck, int val ) { +int players_n_configure( track_t* tck, uint8_t val ) { switch(val){ case 2 : param_option_set(&tck->cfg, PLAYER_3_OPTION, false); diff --git a/open-led-race/olr-lib.h b/open-led-race/olr-lib.h index 48889cb..1492d73 100644 --- a/open-led-race/olr-lib.h +++ b/open-led-race/olr-lib.h @@ -75,7 +75,7 @@ void car_init( car_t* car, controller_t* ct, uint32_t color ); void car_updateController( car_t* car ); -void car_resetPosition( car_t* car); +void car_resetPosition( car_t* car, bool reset_speed); void update_track( track_t* tck, car_t* car ); @@ -89,9 +89,11 @@ bool box_isactive( track_t* tck ); int tracklen_configure( track_t* tck, int nled ); -int autostart_configure( track_t* tck, int autostart ); +int autostart_configure( track_t* tck, uint8_t autostart ); -int players_n_configure( track_t* tck, int val ); +int demo_configure( track_t* tck, uint8_t demo ) ; + +int players_n_configure( track_t* tck, uint8_t val ); int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn ); diff --git a/open-led-race/olr-param.c b/open-led-race/olr-param.c index 0b897e0..544ccd4 100644 --- a/open-led-race/olr-param.c +++ b/open-led-race/olr-param.c @@ -33,6 +33,8 @@ void param_setdefault( struct cfgparam* cfg ) { param_option_set(cfg, PLAYER_3_OPTION, PLAYER_3); param_option_set(cfg, PLAYER_4_OPTION, PLAYER_4); + param_option_set(cfg, DEMO_MODE_OPTION, DEMO_MODE_ST); + } diff --git a/open-led-race/olr-param.h b/open-led-race/olr-param.h index 260be89..fc1256c 100644 --- a/open-led-race/olr-param.h +++ b/open-led-race/olr-param.h @@ -21,6 +21,7 @@ extern "C"{ #define SLOPE_ALWAYS_ON false #define PLAYER_3 false #define PLAYER_4 false +#define DEMO_MODE_ST false ////////////////////////////////////////////////////////////////// @@ -33,14 +34,14 @@ enum cfgparam_option_bit { SLOPE_MODE_OPTION = 3, PLAYER_3_OPTION = 4, PLAYER_4_OPTION = 5, - NOT_USED_3_OPTION = 6, - NOT_USED_4_OPTION = 7, + DEMO_MODE_OPTION = 6, + NOT_USED_7_OPTION = 7, }; enum cfgpar { - CFGPARAM_VER = 6, // Change this value (+=1) every time the [cfgparam] struct is modified + CFGPARAM_VER = 7, // Change this value (+=1) every time the [cfgparam] struct is modified // This will force an update with the new [struct] to the settings - // stored in EEPROM with an old (invalid) struct + // stored in EEPROM with an old (invalid) struct LEN_UID = 16, }; diff --git a/open-led-race/open-led-race.ino b/open-led-race/open-led-race.ino index 8673cdd..4de1854 100644 --- a/open-led-race/open-led-race.ino +++ b/open-led-race/open-led-race.ino @@ -4,7 +4,7 @@ | | | |_ __ ___ _ __ | | | |__ | | | | | |__) |__ _ ___ ___ | | | | '_ \ / _ \ '_ \ | | | __| | | | | | _ // _` |/ __/ _ \ | |__| | |_) | __/ | | | | |____| |____| |__| | | | \ \ (_| | (_| __/ - \____/| .__/ \___|_| |_| |______|______|_____/ |_| \_\__,_|\___\___| + \____/| .__/ \___|_| |_| |______|______|_____/ |_| \_\__,_file:///home/buka/Desktop/OpenLedRace/Code/olr-arduino/Current/open-led-race|\___\___| | | |_| Open LED Race @@ -16,6 +16,9 @@ (at your option) any later version. + Current Version by: + LucaBuka (https://gitlab.com/lucabuka) + First public version by: Angel Maldonado (https://gitlab.com/angeljmc) Gerardo Barbarov (gbarbarov AT singulardevices DOT com) @@ -30,12 +33,9 @@ */ - -// 2021/07/20 - Ver 0.9.6 - lab branch -// --see changelog.txt char const softwareId[] = "A4P0"; // A4P -> A = Open LED Race, 4P0 = Game ID (4P = 4 Players, 0=Type 0) -char const version[] = "0.9.7"; +char const version[] = "0.9.8"; #include @@ -45,7 +45,7 @@ char const version[] = "0.9.7"; #include "SoftTimer.h" #include "SerialCommand.h" -#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_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 supply 5V 2A #define PIN_AUDIO 3 // through CAP 2uf to speaker 8 ohms #define REC_COMMAND_BUFLEN 32 // received command buffer size @@ -76,6 +76,9 @@ char const version[] = "0.9.7"; #define NEWRACE_DELAY 5000 +#define INACTIVITY_TIMEOUT_DELAY 15000 + + enum{ MAX_CARS = 4, }; @@ -104,7 +107,7 @@ typedef struct ack{ struct cfgcircuit{ - int outtunnel; + uint8_t outtunnel; }; enum phases{ @@ -126,7 +129,11 @@ struct race{ bool newcfg; enum phases phase; byte numcars; - int winner; + byte winner; + bool demo_mode; + bool demo_mode_on_received; + bool demo_mode_off_received; + bool network_race; }; @@ -145,9 +152,12 @@ static track_t tck; static int const eeadrInfo = 0; -static unsigned long lastmillis = 0; +static unsigned long last_telemetry_millis = 0; +unsigned long last_activity_millis = 0; + +SoftTimer startRace_delay = SoftTimer(); // non blocking delay() for Autostart, Countdown +SoftTimer demoMode_delay = SoftTimer(); // non blocking delay() for activate Demo Mode on inactivity -SoftTimer customDelay = SoftTimer(); // non blocking delay() // Used to manage countdown phases int countdown_phase=1; @@ -162,6 +172,7 @@ int win_music[] = { char tracksID[ NUM_TRACKS ][2] ={"U","M","B","I","O"}; + /* ----------- Function prototypes ------------------- */ void sendResponse( ack_t *ack); @@ -179,6 +190,13 @@ SerialCommand serialCommand = SerialCommand(cmd, REC_COMMAND_BUFLEN, EOL, &Seria char txbuff[TX_COMMAND_BUFLEN]; Adafruit_NeoPixel track; +static uint32_t car_color[]={ + COLOR1, + COLOR2, + COLOR3, + COLOR4 +}; + /* @@ -192,32 +210,35 @@ void setup() { param_load( &tck.cfg ); track = Adafruit_NeoPixel( tck.cfg.track.nled_total, PIN_LED, NEO_GRB + NEO_KHZ800 ); - - controller_init( &switchs[0], DIGITAL_MODE, DIG_CONTROL_1 ); - car_init( &cars[0], &switchs[0], COLOR1 ); - controller_init( &switchs[1], DIGITAL_MODE, DIG_CONTROL_2 ); - car_init( &cars[1], &switchs[1], COLOR2 ); - + + // First 2 controllers always active (Red, Green) race.numcars = 2; - - if( controller_isActive( DIG_CONTROL_3 ) || param_option_is_active(&tck.cfg, PLAYER_3_OPTION) || param_option_is_active(&tck.cfg, PLAYER_4_OPTION) ) { - controller_init( &switchs[2], DIGITAL_MODE, DIG_CONTROL_3 ); - car_init( &cars[2], &switchs[2], COLOR3 ); + + // Calculate actual players number + if( controller_isActive( DIGITAL_CTRL[CTRL_3] ) || param_option_is_active(&tck.cfg, PLAYER_3_OPTION) || param_option_is_active(&tck.cfg, PLAYER_4_OPTION) ) { + ++race.numcars; + } + if( controller_isActive( DIGITAL_CTRL[CTRL_4] ) || param_option_is_active(&tck.cfg, PLAYER_4_OPTION)) { ++race.numcars; } - if( controller_isActive( DIG_CONTROL_4 ) || param_option_is_active(&tck.cfg, PLAYER_4_OPTION)) { - controller_init( &switchs[3], DIGITAL_MODE, DIG_CONTROL_4 ); - car_init( &cars[3], &switchs[3], COLOR4 ); - ++race.numcars; - } + // Check if DEMO mode is configured + race.demo_mode = param_option_is_active(&tck.cfg, DEMO_MODE_OPTION); + enum ctr_type current_mode = (race.demo_mode == true) ? DEMO_MODE : DIGITAL_MODE; + + // !!! Eliminare var current_mode ...mettere if contratto direttamente in f() call + + // Initialize Controllers for very player + set_controllers_mode(race.numcars, current_mode ) ; + + // Initialize car for every player + init_cars(race.numcars); track.begin(); strip_clear( &tck ); - // Check Box before Physic/Sound to allow user to have Box and Physics with no sound - if(digitalRead(DIG_CONTROL_2)==0 || param_option_is_active(&tck.cfg, BOX_MODE_OPTION) ) { //push switch 2 on reset for activate boxes (pit lane) + if(digitalRead(DIGITAL_CTRL[CTRL_2])==0 || param_option_is_active(&tck.cfg, BOX_MODE_OPTION) ) { //push switch 2 on reset for activate boxes (pit lane) box_init( &tck ); track_configure( &tck, tck.cfg.track.nled_total - tck.cfg.track.box_len ); draw_box_entrypoint( &tck ); @@ -225,23 +246,29 @@ void setup() { track_configure( &tck, 0 ); } - if( digitalRead(DIG_CONTROL_1)==0 || param_option_is_active(&tck.cfg, SLOPE_MODE_OPTION) ) { // push switch 1 on reset for activate physics + if( digitalRead(DIGITAL_CTRL[CTRL_1])==0 || param_option_is_active(&tck.cfg, SLOPE_MODE_OPTION) ) { // push switch 1 on reset for activate physics ramp_init( &tck ); draw_ramp( &tck ); track.show(); delay(2000); - if ( digitalRead( DIG_CONTROL_1 ) == 0 ) { //retain push switch on reset for activate FX sound + if ( digitalRead( DIGITAL_CTRL[CTRL_1] ) == 0 ) { //retain push switch on reset for activate FX sound SMOTOR=1; tone(PIN_AUDIO,100);} } - race.cfg.startline = tck.cfg.race.startline;// true; - race.cfg.nlap = tck.cfg.race.nlap;// NUMLAP; - race.cfg.nrepeat = tck.cfg.race.nrepeat;// 1; - race.cfg.finishline = tck.cfg.race.finishline;// true; + race.network_race = false; // always starts in standalone mode + race.demo_mode_on_received = false; + race.demo_mode_off_received = false; + + race.cfg.startline = tck.cfg.race.startline; // always true for Standalone mode + race.cfg.nlap = tck.cfg.race.nlap; // NUMLAP; + race.cfg.nrepeat = tck.cfg.race.nrepeat; // always 1 for Standalone mode + race.cfg.finishline = tck.cfg.race.finishline; // always true for Standalone mode - customDelay.start(0); // first race starts with no delay - race.phase = READY; + startRace_delay.start(0); // first race starts with no delay + race.phase = READY; // READY is the first status for Standalone mode + + last_activity_millis = millis(); } /* @@ -255,6 +282,17 @@ void loop() { sendResponse(&ack); } + // Exit DEMO mode when a Player touch a controller + if( race.demo_mode_off_received || (race.demo_mode && players_actity(race.numcars)) ){ + exit_demo_mode(); + } + // If demo_mode option is set in board configuration + // -> Enter demo mode after INACTIVITY_TIMEOUT_DELAY + if( race.demo_mode_on_received || (param_option_is_active(&tck.cfg, DEMO_MODE_OPTION) && race.demo_mode==false && ready_for_demo_mode()) ) { + activate_demo_mode(); + } + + // PLEASE NOTE: // DO NOT call "track.show()" in the loop() while in configuration mode !!! // It would mess up with Serial communication (receives only 2 bytes - if the @@ -269,19 +307,24 @@ void loop() { if( race.newcfg ) { race.newcfg = false; countdownReset(); - customDelay.start(0); - race.phase = READY; + startRace_delay.start(0); + + // for Standalone mode, gets into READY status + // for Network races gets into CONFIGURATION OK statue + race.phase = ( race.network_race == false ) ? READY : CONFIG_OK; + send_phase( race.phase ); } } break; + case READY: { if(param_option_is_active(&tck.cfg, AUTOSTART_MODE_OPTION)){ // Auto-Start Mode ON - if(customDelay.elapsed()) { + if(startRace_delay.elapsed()) { for( int i = 0; i < race.numcars; ++i) { - car_resetPosition( &cars[i] ); + car_resetPosition( &cars[i], true ); cars[i].repeats = 0; } tck.ledcoin = COIN_RESET; @@ -297,7 +340,7 @@ void loop() { draw_box_entrypoint( &tck ); for( int i = 0; i < race.numcars; ++i) { if (controller_getStatus(cars[i].ct)==false){ - car_resetPosition( &cars[i] ); + car_resetPosition( &cars[i], true ); //Serial.println(i); track.setPixelColor(i,cars[i].color); cars[i].repeats = 0; @@ -319,8 +362,7 @@ void loop() { { if( race.cfg.startline ){ // Countdown: semaphore and tones - if(start_race_done()) { - // Countdown done + if(start_race_done()) { // Countdown done for( int i = 0; i < race.numcars; ++i ) { cars[i].st = CAR_ENTER; } @@ -367,7 +409,7 @@ void loop() { run_racecycle( &cars[i], i ); if( cars[i].st == CAR_FINISH ) { race.phase = COMPLETE; - race.winner = i; + race.winner = (byte) i; send_phase( race.phase ); break; } @@ -377,18 +419,18 @@ void loop() { if (SMOTOR==1) tone(PIN_AUDIO,FBEEP+int(cars[0].speed*440*1)+int(cars[1].speed*440*2)+int(cars[2].speed*440*3)+int(cars[3].speed*440*4)); if (TBEEP>0) {TBEEP--;} else {FBEEP=0;}; - // Print p command!!! + // Print p command!!! unsigned long nowmillis = millis(); - if( abs( nowmillis - lastmillis ) > 250 ){ - lastmillis = nowmillis; + if( abs( nowmillis - last_telemetry_millis ) > 250 ){ + last_telemetry_millis = nowmillis; print_cars_positions( cars ); } - // ---------------- + // ---------------- } break; case COMPLETE : - { + { strip_clear( &tck ); track.show(); if ( race.cfg.finishline ){ @@ -397,9 +439,25 @@ void loop() { strip_clear( &tck ); } track.show(); - customDelay.start(NEWRACE_DELAY); - race.phase = READY; - } + + startRace_delay.start(NEWRACE_DELAY); + + // for Standalone mode, gets into READY status + // for Network races gets into IDLE statue + race.phase = ( race.network_race == false ) ? READY : IDLE; + } + break; + + case CONFIG_OK: // OLR Network only + case IDLE: // OLR Network only + { + ; // In a Relay Race the configuration is sent (via 'C' command) by the + // Host ("Nerwork Client" program running on another Computer) + // When the board reach the CONFIG_OK status...it does nothing but wait for + // the next Command coming form the Host. + // Same thing for the IDLE status (reached at the end of a relay race) + // In other words, in Relay Races, some Status changes comes from the Host + } break; default: @@ -413,6 +471,73 @@ void loop() { } +/** + * + */ +void set_controllers_mode(uint8_t numctrl, uint8_t mode ) { + for( uint8_t i = 0; i < numctrl; ++i) { + controller_init( &switchs[i], mode, DIGITAL_CTRL[i] ); + } +} + +/** + * + */ +void init_cars(uint8_t numcars ) { + for( uint8_t i = 0; i < numcars; ++i) { + car_init( &cars[i], &switchs[i], car_color[i] ); + } +} + +bool players_actity(uint8_t numcars ) { + for( uint8_t i = 0; i < numcars; ++i) { + if(controller_isActive(DIGITAL_CTRL[i])) + return(true); + } + return(false); +} + +/* + * Check if Controllers (players) were incative for more than INACTIVITY_TIMEOUT_DELAY + */ +bool ready_for_demo_mode(void) { + if(players_actity(race.numcars)){ + demoMode_delay.start(INACTIVITY_TIMEOUT_DELAY); // Reset timeout when somebody is using controllers + } + return (demoMode_delay.elapsed()); +} + + + +/** + * + */ +void activate_demo_mode(void){ + race.demo_mode = true; + race.demo_mode_on_received = false; // reset flag + set_controllers_mode(race.numcars, DEMO_MODE ) ; + race.winner=0; // Fake set (used in Status=Complete by draw_winner()) + race.phase = COMPLETE; + + sprintf(txbuff, "%c%d%c", 'M', 1 , EOL ); + serialCommand.sendCommand(txbuff); + +} + +/** + * + */ +void exit_demo_mode(void){ + race.demo_mode = false; + race.demo_mode_off_received = false; // reset flag + set_controllers_mode(race.numcars, DIGITAL_MODE ) ; + race.winner=0; // Fake set (used in Status=Complete by draw_winner()) + race.phase = COMPLETE; + + sprintf(txbuff, "%c%d%c", 'M', 0 , EOL ); + serialCommand.sendCommand(txbuff); +} + void send_phase( int phase ) { sprintf(txbuff, "R%d%c",phase,EOL); serialCommand.sendCommand(txbuff); @@ -421,9 +546,29 @@ void send_phase( int phase ) { void run_racecycle( car_t *car, int i ) { struct cfgtrack const* cfg = &tck.cfg.track; + + // if( car->st == CAR_COMING ) { // OLR Network only + // // To be implemented + // } if( car->st == CAR_ENTER ) { - car_resetPosition( car ); + + // Standalone mode => On Race start the Speed get RESET (speed=0) + // Network race => Car speed set when receiving the Car_Enter Serial command + (race.network_race == true) ? car_resetPosition( car, false ) : car_resetPosition( car, true ); + + + // In DEMO_MODE Red car gets a different bost on start (allows to see all cars in the circuit) + if( switchs[0].mode == DEMO_MODE ){ + cars[0].speed = 1.2; + float dec = 0.4; + for( uint8_t i = 1; i < race.numcars; ++i ) { + cars[i].speed = 1.2 - dec; + dec += 0.4; + } + } + + if( car->repeats < race.cfg.nrepeat ) car->st = CAR_RACING; else @@ -440,7 +585,7 @@ void run_racecycle( car_t *car, int i ) { && car->dist > ( cfg->nled_main*car->nlap - race.circ.outtunnel ) ) { car->leaving = true; car->st = CAR_LEAVING; - } + } if( car->nlap > race.cfg.nlap ) { ++car->repeats; @@ -452,14 +597,30 @@ void run_racecycle( car_t *car, int i ) { car->st = CAR_FINISH; } } + + if( car->st == CAR_LEAVING ) { // OLR Network only + car->st = CAR_RACING; + sprintf( txbuff, "r%d%c", i + 1, EOL ); + serialCommand.sendCommand(txbuff); + } + + if( car->st == CAR_GO_OUT ) { // OLR Network only + car->st = CAR_WAITING; + //#warning Insert function to map speed! + byte const speed = car->speed * 10; + byte const data = (i + 1) << 5 | ( 0b00011111 & speed ); + sprintf( txbuff, "s%c%c", data, EOL ); + serialCommand.sendCommand(txbuff);; + car_resetPosition( car, true ); + car->trackID = NOT_TRACK; + } if ( car->st == CAR_FINISH ){ car->trackID = NOT_TRACK; sprintf( txbuff, "w%d%c", i + 1, EOL ); serialCommand.sendCommand(txbuff); - //sendCommand(txbuff); - car_resetPosition( car ); + car_resetPosition(car, true); } } @@ -510,7 +671,7 @@ void print_cars_positions( car_t* cars ) { boolean start_race_done( ) { if(countdown_new_phase){ countdown_new_phase=false; - customDelay.start(CONTDOWN_PHASE_DURATION); + startRace_delay.start(CONTDOWN_PHASE_DURATION); strip_clear( &tck ); if(ramp_isactive( &tck )) draw_ramp( &tck ); if(box_isactive( &tck )) draw_box_entrypoint( &tck ); @@ -530,7 +691,7 @@ boolean start_race_done( ) { track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,255,0)); break; case 4: - customDelay.start(CONTDOWN_STARTSOUND_DURATION); + startRace_delay.start(CONTDOWN_STARTSOUND_DURATION); tone(PIN_AUDIO,880); track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,0,0)); track.setPixelColor(0, track.Color(255,255,255)); @@ -542,7 +703,7 @@ boolean start_race_done( ) { } track.show(); } - if(customDelay.elapsed()) { + if(startRace_delay.elapsed()) { noTone(PIN_AUDIO); countdown_new_phase=true; countdown_phase++; @@ -559,7 +720,7 @@ void countdownReset() { } -void sound_winner( track_t* tck, int winner ) { +void sound_winner( track_t* tck, byte winner ) { int const msize = sizeof(win_music) / sizeof(int); for (int note = 0; note < msize; note++) { tone(PIN_AUDIO, win_music[note],200); @@ -738,16 +899,46 @@ ack_t manageSerialCommand() { case 'R' : // Set Race Phase { ack.type = cmd[0]; - int const phase = atoi( cmd + 1); - if( 0 > phase || RACE_PHASES <= phase) return ack; + uint8_t const phase = atoi( cmd + 1); + // Does not accept anymore R=1 as Enter Configuration / Use command @ instead + if( 0 > phase || RACE_PHASES <= phase || phase == CONFIG) return ack; race.phase = (enum phases) phase; ack.rp = OK; - if ( race.phase == CONFIG ) { // accept R1 as a EnterConfigurationMode command - DEPRECATED - enter_configuration_mode(); - } } break; + case 'u' : // Car Enter the Circuit - // OLR Network only + { + ack.type = cmd[0]; + byte const data = cmd[1]; + byte const ncar = 0b00000111 & ( data >> 5 ); + byte const speed = 0b00011111 & data; + if( 0 >= ncar || race.numcars < ncar) return ack; + cars[ncar-1].st = CAR_ENTER; + cars[ncar-1].speed = (float) speed / 10; + ack.rp = OK; + if( false ) { + sprintf( txbuff, "%s %d, %s %d, %s %d", "CAR: ", ncar, "STATUS: ", cars[ncar-1].st, "SPEED: ", (int)cars[ncar-1].speed * 10 ); + printdebug( txbuff, LOG ); + } + } + break; + + case 't' : // Car Coming into the Circuit - // OLR Network only + { + ack.type = cmd[0]; + byte const ncar = atoi( cmd + 1); + if( 0 >= ncar || race.numcars < ncar) return ack; + cars[ncar-1].st = CAR_COMING; + ack.rp = OK; + if ( false ) { + sprintf( txbuff, "%s %d, %s %d", "CAR: ", ncar, "STATUS: ", cars[ncar-1].st); + printdebug( txbuff, LOG ); + } + } + break; + + case 'C' : //Parse race configuration -> C1,2,3,0 { ack.type = cmd[0]; @@ -787,7 +978,7 @@ ack_t manageSerialCommand() { case 'T' : //Parse Track configuration -> Track length { ack.type = cmd[0]; - + char * pch = strtok (cmd,"T"); if( !pch ) return ack; @@ -855,7 +1046,7 @@ ack_t manageSerialCommand() { if( !pch ) return ack; int slopeperm = atoi( pch ); - int err = ramp_configure( &tck, init, center, end, high, slopeperm ); + uint8_t err = ramp_configure( &tck, init, center, end, high, slopeperm ); if( err ) return ack; ack.rp = OK; @@ -876,51 +1067,73 @@ ack_t manageSerialCommand() { pch = strtok (pch, "," ); if( !pch ) return ack; - int delta = atoi( pch ); + uint8_t delta = atoi( pch ); pch = strtok (NULL, "," ); if( !pch ) return ack; - int min = atoi( pch ); + uint8_t min = atoi( pch ); pch = strtok (NULL, "," ); if( !pch ) return ack; - int boost = atoi( pch ); + uint8_t boost = atoi( pch ); pch = strtok (NULL, ","); if( !pch ) return ack; - int active = atoi( pch ); + uint8_t active = atoi( pch ); - int err = battery_configure( &tck, delta, min, boost, active ); + uint8_t err = battery_configure( &tck, delta, min, boost, active ); if( err ) return ack; ack.rp = OK; } break; - case 'G' : //Parse Autostart configuration -> Gautostart + case 'G' : // Parse Autostart configuration -> Gautostart { ack.type = cmd[0]; char * pch = strtok (cmd,"G"); if( !pch ) return ack; - int autostart = atoi( cmd + 1 ); - int err = autostart_configure( &tck, autostart); + uint8_t autostart = atoi( cmd + 1 ); + uint8_t err = autostart_configure( &tck, autostart); if( err ) return ack; ack.rp = OK; - } + } break; - case 'P' : //Parse Player 3/4 configuration -> P[2|3|4] + case 'M' : // Parse DEMO mode configuration + { + ack.type = cmd[0]; + + char * pch = strtok (cmd,"M"); + if( !pch ) return ack; + + uint8_t demo = atoi( cmd + 1 ); + + uint8_t err = demo_configure( &tck, demo); + if( err ) return ack; + ack.rp = OK; + + if(demo == 0) { + race.demo_mode_off_received = true; + } else if( race.demo_mode){ + race.demo_mode_on_received = true; + } + + } + break; + + case 'P' : // Parse Player 3/4 configuration -> P[2|3|4] { ack.type = cmd[0]; char * pch = strtok (cmd,"P"); if( !pch ) return ack; - int autostart = atoi( cmd + 1 ); - int err = players_n_configure( &tck, autostart); + uint8_t players_n = atoi( cmd + 1 ); + uint8_t err = players_n_configure( &tck, players_n); if( err ) return ack; ack.rp = OK; @@ -952,6 +1165,20 @@ ack_t manageSerialCommand() { } break; + case 'H' : // Tunnel configuration - // OLR Network only + { + ack.type = cmd[0]; + uint8_t const dtunnel = atoi( cmd + 1); + if( 0 >= dtunnel || 254 < dtunnel) return ack; + race.circ.outtunnel = dtunnel; + ack.rp = OK; + if ( false ) { //VERBOSE + sprintf( txbuff, "%s %d", "TUNNEL: ", race.circ.outtunnel ); + printdebug( txbuff, LOG ); + } + } + break; + case 'D' : // Load Default Parameters and store them in from EEPROM { @@ -1006,6 +1233,16 @@ ack_t manageSerialCommand() { } break; + case 'n' : // Set "Network Race" mode (Relay race) + { + ack.type = cmd[0]; + race.network_race = true; + race.phase = COMPLETE; // Immediatly ends the current race (if any) + race.winner=0; // Set a fake winner (used in Status=Complete by draw_winner()) + ack.rp = OK; + } + break; + case 'Q': // Get current configuration Info { struct cfgparam const* cfg = &tck.cfg; @@ -1041,13 +1278,16 @@ ack_t manageSerialCommand() { EOL ); serialCommand.sendCommand(txbuff); - sprintf( txbuff, "%s:%d,%d,%d,%d,%d,%d%c", "QRC", + sprintf( txbuff, "%s:%d,%d,%d,%d,%d,%d,%d,%d%c", "QRC", cfg->race.startline, cfg->race.nlap, cfg->race.nrepeat, cfg->race.finishline, param_option_is_active(&tck.cfg, PLAYER_3_OPTION), param_option_is_active(&tck.cfg, PLAYER_4_OPTION), + param_option_is_active(&tck.cfg, DEMO_MODE_OPTION), + //race.demo_mode, + race.network_race, EOL ); serialCommand.sendCommand(txbuff);