diff --git a/README.md b/README.md index c2aa05d..66025f6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,86 @@ -# OLR-Arduino +# OLR-Arduino - Minimalist race game with LED strip +Software running on OLR Board (Arduino) implementing a 'Car Race' where a 'car' is a light moving along a LED Strip (WS2812/WS2813). +Manage up to 4 'cars' and optionals Slope (with gravity effect), Pitlane, Electric cars mode (need to recharge battery in Pitlane), etc +Players use push-button controllers to move the 'cars' - The quicker you push the button the faster the light (car) moves along the LED Strip. +The software can receive configuration commands via Serial interface: (--see: doc/OLR_Protocol_Serial.pdf) + +## Standalone mode +The software running on OLR Board (Arduino) implements a race as described above where up to 4 people can play using the push buttons. + +## Network mode / Relay Race +A "NetworkClient" software, running on an external 'Host' (Computer, SBC,..) controls the Software running on OLR Board (Arduino). +Together they implements a Relay Race where 'cars' starts a Race in Racetrack 'A', than goes to Racetrack 'B', etc. +-- see: https://gitlab.com/open-led-race-network/networkclient + +## Hardware +Tested on [Arduino Nano] and [Arduino Every]. + +Change settings in [olr-settings.h] to adapt to your hardware configuration. + +``` +/////////////////////////////////////////////////////////////////////////////// +// __________________________LED Strip____________________________________ // +// +// For WS2812 LED Strip: +// _______ __ ____[WS2812 LED Strip connector]____ +// |__Arduino_ | | | +// | +5V |>---------------------->| V+ (usually Red cable) | +// | GND |>---------------------->| GND (usually White cable) | +// | PIN_LED |>---->[R 500 ohms]----->| DI (data in - usually Green cable)| +// \_________/ \__________________________________/ +// +// +// For WS2813 LED Strip: +// WS2813 have a 4th cable: Backup data lines (BI- usually Blue cable) +// Connect BI cable to GND +// ___________ ____[WS2813 LED Strip connector]_____ +// |__Arduino_ | | | +// | +5V |>---------------------->| V+ (usually Red cable) | +// | GND |>----------------o----->| GND (usually White cable) | +// | | \---->| BI (backup in - usually Blue cable)| +// | PIN_LED |>---->[R 500 ohms]----->| DI (data in - usually Green cable) | +// \_________/ \___________________________________/ +// +// __________________________Loudspeaker___________________________________ // +// +// ___________ _____________ +// |__Arduino_ | | | +// | GND |>--------------------->o| Loudspeaker | +// | PIN_AUDIO |>----->[CAP 2uF]------>o| | +// \_________/ |_____________| +// +/////////////////////////////////////////////////////////////////////////////// +enum hw_setup { // If you have a custom hardware (i.e not the OLR PCB), + PIN_LED = 2, // set PIN_LED and PIN_AUDIO accordingly + PIN_AUDIO = 3, +}; + + +/////////////////////////////////////////////////////////////////////////////// +// __________________ Digital Controllers (Buttons)_______________________ // +// +// ________________ +// |____Arduino____ | ________________ +// | GND |>-------->o| Button 1 (Red) | +// | DIG_CTRL_1_PIN |>-------->o|________________| +// | | __________________ +// | GND |>-------->o| Button 2 (Green) | +// | DIG_CTRL_2_PIN |>-------->o|__________________| +// | | _________________ +// | GND |>-------->o| Button 3 (Blue) | +// | DIG_CTRL_3_PIN |>-------->o|_________________| +// | | __________________ +// | GND |>-------->o| Button 4 (White) | +// | DIG_CTRL_4_PIN |>-------->o|__________________| +// \______________/ +// +/////////////////////////////////////////////////////////////////////////////// +#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 + +``` + - Minimalist race game with an LED strip. - 4 Players and Pit Lane Version diff --git a/changelog.txt b/changelog.txt index 8446e72..b15f82c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,14 @@ Revisions history ----------------- + * 2023-08-19: Ver 0.9.9 - Luca - Branch: "lab" ---> "master" + - Implements Network (Relay Race) functionality: + - Works as usual in Standalone mode + - Works with NetworkClient [0.9.8] (Relay Race) + - Demo mode now force a temporary [Autostart] mode when active + - Added [olr-settings.h] file containing Compile-time settings + - PIN_LED, PIN_AUDIO, etc + * 2023-07-20: Ver 0.9.8 - Luca - Branch: "lab" - Added "Demo" mode: - When Board has [demo_mode=ON] in the EEPROM configuration, diff --git a/open-led-race/olr-controller.c b/open-led-race/olr-controller.c index bf04421..bb0dc4b 100644 --- a/open-led-race/olr-controller.c +++ b/open-led-race/olr-controller.c @@ -1,10 +1,9 @@ #include "olr-controller.h" - enum { DELTA_ANALOG = 5, }; -int DIGITAL_CTRL[4]; +int DIGITAL_CTRL[MAX_PLAYERS]; static float const ACEL = 0.2; @@ -24,7 +23,6 @@ void controller_setup( void ) { 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); @@ -53,7 +51,9 @@ byte controller_getStatus( controller_t* ct ) { } else if( ct->mode == DEMO_MODE ){ ct->adc++; - if( ct->adc >= 15){ + int rnd = rand(); + long threshold = 1800 - rnd; + if( ct->adc >= (threshold)){ ct->adc = 0; return 1; } diff --git a/open-led-race/olr-controller.h b/open-led-race/olr-controller.h index 679050b..a5fb9ee 100644 --- a/open-led-race/olr-controller.h +++ b/open-led-race/olr-controller.h @@ -6,18 +6,15 @@ extern "C"{ #endif -#include "Arduino.h" +#include #include #include -extern int DIGITAL_CTRL[]; // Global Array containig PINs used for the Digital Controllers (ex: Push Buttons) +#include "olr-settings.h" -#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 - +extern int DIGITAL_CTRL[MAX_PLAYERS]; // Global Array containig PINs used for the Digital Controllers (ex: Push Buttons) +//int DIGITAL_CTRL[MAX_PLAYERS]; // Global Array containig PINs used for the Digital Controllers (ex: Push Buttons) enum ctr_idx { // Used to access controller by "name" (and not via zero-offset index) @@ -29,7 +26,6 @@ enum ctr_idx { // Used to access controller by "name" (and not via zero-offset - #define PIN_VCC_ADC1 6 #define PIN_VCC_ADC2 7 diff --git a/open-led-race/olr-lib.c b/open-led-race/olr-lib.c index a18b057..5f25358 100644 --- a/open-led-race/olr-lib.c +++ b/open-led-race/olr-lib.c @@ -2,7 +2,6 @@ #include "olr-lib.h" - void car_init( car_t* car, controller_t* ct, uint32_t color ) { car->ct = ct; car->color = color; @@ -63,6 +62,7 @@ void process_aux_track( track_t* tck, car_t* car ){ } + void process_main_track( track_t* tck, car_t* car ) { struct cfgtrack const* cfg = &tck->cfg.track; @@ -127,12 +127,12 @@ bool ramp_isactive( track_t* tck ) { void car_resetPosition( car_t* car, bool reset_speed) { car->trackID = TRACK_MAIN; - car->speed = 0; + if(reset_speed) car->speed = 0 ; car->dist = 0; car->dist_aux = 0; car->nlap = 1; car->leaving = false; - car->battery = 100; + car->battery = 100; // Todo: propagate car's battery status in relay races !!! } void car_setSpeed( car_t* car, float speed) { diff --git a/open-led-race/olr-lib.h b/open-led-race/olr-lib.h index 1492d73..075a188 100644 --- a/open-led-race/olr-lib.h +++ b/open-led-race/olr-lib.h @@ -1,4 +1,3 @@ - #ifndef _OLR_LIB_h #define _OLR_LIB_h diff --git a/open-led-race/olr-param.h b/open-led-race/olr-param.h index fc1256c..3c02088 100644 --- a/open-led-race/olr-param.h +++ b/open-led-race/olr-param.h @@ -10,6 +10,8 @@ extern "C"{ #include #include +#include "olr-settings.h" + // Default values loaded on "D" command received (Serial Protocol) ////////////////////////////////////////////////////////////////// #define MAXLED 300 @@ -45,16 +47,17 @@ enum cfgpar { LEN_UID = 16, }; -struct cfgrace{ - bool startline; // Used only in OLRNetwork + +typedef struct cfgrace{ + bool startline; // Standalone mode: Always 1 uint8_t nlap; - uint8_t nrepeat; // Used only in OLRNetwork - bool finishline; // Used only in OLRNetwork -}; + uint8_t nrepeat; // Standalone mode: Always 1 + bool finishline; // Standalone mode: Always 1 +} cfgrace_t; struct cfgbattery{ // added in ver 0.9.7 - uint8_t delta; // unsigned char value [1-254] / will be divided by 100 [0.01-2.54] - uint8_t min; // Battery does not descharge below this "min" percentage + uint8_t delta; // unsigned char value [1-254] / will be divided by 100 [0.01-2.54] + uint8_t min; // Battery charge does not goes below this "min" percentage uint8_t speed_boost_scaler; } ; @@ -87,7 +90,8 @@ struct brdinfo { struct cfgparam { uint8_t ver; // Version of this [cfgparam] struct uint8_t option; // Bit-mapped byte to store 'active' on|off for options (Battery, AutoStart, BoxalwaysOn, etc) - struct cfgrace race; // added in ver 0.9.d + //struct cfgrace race; // added in ver 0.9.d + cfgrace_t race; // added in ver 0.9.d struct cfgbattery battery; struct cfgtrack track; struct cfgramp ramp; diff --git a/open-led-race/olr-settings.h b/open-led-race/olr-settings.h new file mode 100644 index 0000000..c654973 --- /dev/null +++ b/open-led-race/olr-settings.h @@ -0,0 +1,140 @@ +#ifndef _OLR_COMMON__h +#define _OLR_COMMON__h + +#ifdef __cplusplus + +extern "C"{ +#endif + +//////////////////// +// Hardware Setup // +//////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// __________________________LED Strip____________________________________ // +// +// For WS2812 LED Strip: +// _______ __ ____[WS2812 LED Strip connector]____ +// |__Arduino_ | | | +// | +5V |>---------------------->| V+ (usually Red cable) | +// | GND |>---------------------->| GND (usually White cable) | +// | PIN_LED |>---->[R 500 ohms]----->| DI (data in - usually Green cable)| +// \_________/ \__________________________________/ +// +// +// For WS2813 LED Strip: +// WS2813 have a 4th cable: Backup data lines (BI- usually Blue cable) +// Connect BI cable to GND +// ___________ ____[WS2813 LED Strip connector]_____ +// |__Arduino_ | | | +// | +5V |>---------------------->| V+ (usually Red cable) | +// | GND |>----------------o----->| GND (usually White cable) | +// | | \---->| BI (backup in - usually Blue cable)| +// | PIN_LED |>---->[R 500 ohms]----->| DI (data in - usually Green cable) | +// \_________/ \___________________________________/ +// +// __________________________Loudspeaker___________________________________ // +// +// ___________ _____________ +// |__Arduino_ | | | +// | GND |>--------------------->o| Loudspeaker | +// | PIN_AUDIO |>----->[CAP 2uF]------>o| | +// \_________/ |_____________| +// +/////////////////////////////////////////////////////////////////////////////// +enum hw_setup { // If you have a custom hardware (i.e not the OLR PCB), + PIN_LED = 2, // set PIN_LED and PIN_AUDIO accordingly + PIN_AUDIO = 3, +}; + + +/////////////////////////////////////////////////////////////////////////////// +// __________________ Digital Controllers (Buttons)_______________________ // +// +// ________________ +// |____Arduino____ | ________________ +// | GND |>-------->o| Button 1 (Red) | +// | DIG_CTRL_1_PIN |>-------->o|________________| +// | | __________________ +// | GND |>-------->o| Button 2 (Green) | +// | DIG_CTRL_2_PIN |>-------->o|__________________| +// | | _________________ +// | GND |>-------->o| Button 3 (Blue) | +// | DIG_CTRL_3_PIN |>-------->o|_________________| +// | | __________________ +// | GND |>-------->o| Button 4 (White) | +// | DIG_CTRL_4_PIN |>-------->o|__________________| +// \______________/ +// +/////////////////////////////////////////////////////////////////////////////// +#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 + + +//////////////////// +// Software Setup // +//////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// __________________Colors setup (Racing lights, Ramp, etc)________________ // +// +#define COLOR1 track.Color(255,0,0) // Light controlled by DIG_CTRL_1_PIN +#define COLOR2 track.Color(0,255,0) // Light controlled by DIG_CTRL_2_PIN +#define COLOR3 track.Color(0,0,255) // Light controlled by DIG_CTRL_3_PIN +#define COLOR4 track.Color(255,255,255) // Light controlled by DIG_CTRL_4_PIN + +#define COLOR_RAMP track.Color(64,0,64) +#define COLOR_COIN track.Color(40,34,0) +#define COLOR_BOXMARKS track.Color(64,64,0) +#define WARNING_BLINK_COLOR track.Color(32,20,0) + +#define LED_SEMAPHORE 12 // LED in the Stip used as a Semaphore (Countdown phase) + +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// _______________________________Delays ___________________________________ // +// +enum delays_setup { // If you have a custom hardware (i.e not the OLR PCB), + CONTDOWN_PHASE_DURATION = 2000, // (mSec) + CONTDOWN_STARTSOUND_DURATION = 40, // (mSec) + NEWRACE_DELAY = 5000, // (mSec) + INACTIVITY_TIMEOUT_DELAY = 300, // (Sec) When demo_mode is active, board goes into demo mode after this inactivity time + TELEMETRY_DELAY = 250, // (mSec) Telemetry data sent every TELEMETRY_DELAY mSec +}; +/////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // +// !!!!!!!!!!!!!!! DO NOT CHANGE ANYTHING BELOW !!!!!!!!!!!!!!!!! // +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // +//////////////////////////////////////////////////////////////////// + + + +enum internal_setup { + REC_COMMAND_BUFLEN = 32,// received command buffer size + // At the moment, the largest received command is RAMP CONFIGURATION (A) + // ex: A1400,1430,1460,12,0[EOC] (for a 1500 LED strip) + // 21 CHAR + + TX_COMMAND_BUFLEN = 48, // send command buffer size + // At the moment, the largest send command is Q + // ex: QTK:1500,1500,0,-1,60,0,0.006,0.015,1[EOC] (for a 1500 LED strip) + // 37 CHAR + + MAX_PLAYERS = 4, // DO NOT Change: Current software supports max 4 controllers +}; + + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/open-led-race/open-led-race.h b/open-led-race/open-led-race.h new file mode 100644 index 0000000..3f6e550 --- /dev/null +++ b/open-led-race/open-led-race.h @@ -0,0 +1,95 @@ +#ifndef _OPEN_LED_RACE_h +#define _OPEN_LED_RACE_h + +#ifdef __cplusplus + +extern "C"{ +#endif + +#include "olr-settings.h" +#include "olr-lib.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Definitons related to Serial Communication Protocol +///////////////////////////////////////////////////////////////////////////////////////////////////// +// !!! A change in definitions below requires a corresponding modification in: +// !!! - Protocol definition doc +// !!! - Every Host Software used with this Firmware (Configuration App, Network Race Enabler, ...) +///////////////////////////////////////////////////////////////////////////////////////////////////// + +enum loglevel { // used in Serial Protocol "!" command (send log/error message) + LOG = 1, + WARNING = 2, + ERROR = 3 +}; + +// Race Phases +enum phases { + IDLE = 0, + CONFIG, + CONFIG_OK, + READY, + COUNTDOWN, + RACING, + PAUSE, + RESUME, + COMPLETE, + RACE_PHASES +}; + +#define EOL '\n' // End of Command char used in Protocol + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + + +// Types for ack.type +enum resp{ // + NOK = -1, + NOTHING = 0, + OK = 1 +}; + +// Answer sent after processing a command received from Host +typedef struct ack{ + enum resp rp; + char type; +}ack_t; + + +typedef struct cfgcircuit{ + uint8_t outtunnel; +} cfgcircuit_t; + +typedef struct race { + cfgrace_t cfg; + cfgcircuit_t circ; + bool newcfg; + enum phases phase; + byte numcars; + byte winner; + bool demo_mode; + bool demo_mode_on_received; + bool demo_mode_off_received; + bool network_race; +} race_t; + + +/* ----------- Function prototypes ------------------- */ + +void sendResponse( ack_t *ack); + +ack_t manageSerialCommand(); + +void printdebug( const char * msg, int errlevel ); +void print_cars_positions( car_t* cars); +void run_racecycle( void ); +void draw_winner( track_t* tck, uint32_t color); + + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/open-led-race/open-led-race.ino b/open-led-race/open-led-race.ino index 4de1854..6c22154 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 @@ -18,12 +18,10 @@ 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) + Angel Maldonado (https://gitlab.com/angeljmc) - Basen on original idea and 2 players code by: + Basen on an original idea and 2 players code by: Gerardo Barbarov for Arduino day Seville 2019 https://github.com/gbarbarov/led-race @@ -33,163 +31,65 @@ */ +/** + * ____________HARDWARE CONFIGURATION____________________ + * __ __ + * __ Compile-time settings (PIN_LED, PIN_AUDIO, etc) __ + * __ are defined in [olr-settings.h] file __ + * ______________________________________________________ + */ + char const softwareId[] = "A4P0"; // A4P -> A = Open LED Race, 4P0 = Game ID (4P = 4 Players, 0=Type 0) -char const version[] = "0.9.8"; +char const version[] = "0.9.9"; +#include "open-led-race.h" #include #include #include "olr-lib.h" +#include "olr-controller.h" #include "olr-param.h" #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 supply 5V 2A -#define PIN_AUDIO 3 // through CAP 2uf to speaker 8 ohms - -#define REC_COMMAND_BUFLEN 32 // received command buffer size - // At the moment, the largest received command is RAMP CONFIGURATION (A) - // ex: A1400,1430,1460,12,0[EOC] (for a 1500 LED strip) - // 21 CHAR -#define TX_COMMAND_BUFLEN 48 // send command buffer size - // At the moment, the largest send command is Q - // ex: QTK:1500,1500,0,-1,60,0,0.006,0.015,1[EOC] (for a 1500 LED strip) - // 37 CHAR - -#define EOL '\n' // End of Command char used in Protocol - -#define COLOR1 track.Color(255,0,0) -#define COLOR2 track.Color(0,255,0) -#define COLOR3 track.Color(0,0,255) -#define COLOR4 track.Color(255,255,255) - -#define COLOR_RAMP track.Color(64,0,64) -#define COLOR_COIN track.Color(40,34,0) -#define COLOR_BOXMARKS track.Color(64,64,0) -#define LED_SEMAPHORE 12 -#define WARNING_BLINK_COLOR track.Color(32,20,0) - - -#define CONTDOWN_PHASE_DURATION 2000 -#define CONTDOWN_STARTSOUND_DURATION 40 - -#define NEWRACE_DELAY 5000 - -#define INACTIVITY_TIMEOUT_DELAY 15000 - - -enum{ - MAX_CARS = 4, -}; - - -enum loglevel { // used in Serial Protocol "!" command (send log/error messageS) - ECHO = 0, - DISABLE = 0, - LOG = 1, - WARNING = 2, - ERROR = 3 -}; - - -enum resp{ - NOK = -1, - NOTHING = 0, - OK = 1 -}; - -typedef struct ack{ - enum resp rp; - char type; -}ack_t; - - - -struct cfgcircuit{ - uint8_t outtunnel; -}; - -enum phases{ - IDLE = 0, - CONFIG, - CONFIG_OK, - READY, - COUNTDOWN, - RACING, - PAUSE, - RESUME, - COMPLETE, - RACE_PHASES -}; - -struct race{ - struct cfgrace cfg; - struct cfgcircuit circ; - bool newcfg; - enum phases phase; - byte numcars; - byte winner; - bool demo_mode; - bool demo_mode_on_received; - bool demo_mode_off_received; - bool network_race; -}; - - -byte SMOTOR=0; -int TBEEP=0; -int FBEEP=0; - /*------------------------------------------------------*/ -enum loglevel verbose = DISABLE; - -static struct race race; -static car_t cars[ MAX_CARS ]; -static controller_t switchs[ MAX_CARS ]; -static track_t tck; - -static int const eeadrInfo = 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 - - -// Used to manage countdown phases -int countdown_phase=1; -bool countdown_new_phase=true; - - int win_music[] = { 2637, 2637, 0, 2637, 0, 2093, 2637, 0, 3136 }; - -char tracksID[ NUM_TRACKS ][2] ={"U","M","B","I","O"}; +/*------------------------------------------------------*/ -/* ----------- Function prototypes ------------------- */ +static race_t race; +static car_t cars[ MAX_PLAYERS ]; +static controller_t switchs[ MAX_PLAYERS ]; +static track_t tck; +char tracksID[ NUM_TRACKS ][2] ={"U","M","B","I","O"}; -void sendResponse( ack_t *ack); +static int countdown_phase=1; +static bool countdown_new_phase=true; -ack_t manageSerialCommand(); +static int const eeadrInfo = 0; + +// non blocking delays +SoftTimer startRace_delay = SoftTimer(); // Autostart, Countdown +SoftTimer demoMode_delay = SoftTimer(); // Activate Demo Mode on inactivity +SoftTimer telemetry_delay = SoftTimer(0); // Send Telemetry data + +static byte s_motor=0; +static int t_beep=0; +static int f_beep=0; -void printdebug( const char * msg, int errlevel ); -void print_cars_positions( car_t* cars); -void run_racecycle( void ); -void draw_winner( track_t* tck, uint32_t color); char cmd[REC_COMMAND_BUFLEN]; // Stores command received by ReadSerialComand() SerialCommand serialCommand = SerialCommand(cmd, REC_COMMAND_BUFLEN, EOL, &Serial); // get complete command from serial - char txbuff[TX_COMMAND_BUFLEN]; Adafruit_NeoPixel track; + static uint32_t car_color[]={ COLOR1, COLOR2, @@ -235,10 +135,10 @@ void setup() { init_cars(race.numcars); track.begin(); - strip_clear( &tck ); + strip_clear( &tck , false); // Check Box before Physic/Sound to allow user to have Box and Physics with no sound - 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) + if(controller_isActive( DIGITAL_CTRL[CTRL_2]) || 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 ); @@ -246,13 +146,13 @@ void setup() { track_configure( &tck, 0 ); } - if( digitalRead(DIGITAL_CTRL[CTRL_1])==0 || param_option_is_active(&tck.cfg, SLOPE_MODE_OPTION) ) { // push switch 1 on reset for activate physics + if( controller_isActive( DIGITAL_CTRL[CTRL_3]) || 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( DIGITAL_CTRL[CTRL_1] ) == 0 ) { //retain push switch on reset for activate FX sound - SMOTOR=1; + if ( controller_isActive( DIGITAL_CTRL[CTRL_1] ) ) { //retain push switch on reset for activate FX sound + s_motor=1; tone(PIN_AUDIO,100);} } @@ -268,7 +168,9 @@ void setup() { 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(); +// last_activity_millis = millis(); + + } /* @@ -287,7 +189,7 @@ void loop() { exit_demo_mode(); } // If demo_mode option is set in board configuration - // -> Enter demo mode after INACTIVITY_TIMEOUT_DELAY + // -> Enter demo mode after INACTIVITY_TIMEOUT_DELAY sec 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(); } @@ -304,63 +206,73 @@ void loop() { case CONFIG: { - if( race.newcfg ) { + if( race.newcfg ) { // Exit_Config command received race.newcfg = false; countdownReset(); startRace_delay.start(0); - // for Standalone mode, gets into READY status - // for Network races gets into CONFIGURATION OK statue + // for Network races gets into CONFIGURATION OK status race.phase = ( race.network_race == false ) ? READY : CONFIG_OK; - send_phase( race.phase ); } } break; - - case READY: + case CONFIG_OK: // OLR Network only { - if(param_option_is_active(&tck.cfg, AUTOSTART_MODE_OPTION)){ // Auto-Start Mode ON - if(startRace_delay.elapsed()) { - for( int i = 0; i < race.numcars; ++i) { - car_resetPosition( &cars[i], true ); - cars[i].repeats = 0; - } - tck.ledcoin = COIN_RESET; - race.phase = COUNTDOWN; - send_phase( race.phase ); - } - } else { - int pstart=0; - strip_clear( &tck ); - if( ramp_isactive( &tck ) ) - draw_ramp( &tck ); - if( box_isactive( &tck ) ) - draw_box_entrypoint( &tck ); - for( int i = 0; i < race.numcars; ++i) { - if (controller_getStatus(cars[i].ct)==false){ - car_resetPosition( &cars[i], true ); - //Serial.println(i); - track.setPixelColor(i,cars[i].color); - cars[i].repeats = 0; - pstart++; - } - } - - track.setPixelColor(LED_SEMAPHORE , ((millis()/5)%64)*0x010100 ); - track.show(); - if (pstart==race.numcars){tck.ledcoin = COIN_RESET; - race.phase = COUNTDOWN; - send_phase( race.phase );} - - }; + ; // 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 Network mode (Relay races), some Status changes comes from the Host } break; + case READY: + { + bool goOn=false; + + if( race.cfg.startline ) { // Standalone: Always true - Network mode: only racetrack where race starts + + if(param_option_is_active(&tck.cfg,AUTOSTART_MODE_OPTION) || race.demo_mode ) { // Autostart parameters ON + if(startRace_delay.elapsed()) goOn=true; // Automatically start Countdown after a defined Delay + // Note: In DemoMode always use AutoStart + } else { // Autostart OFF: + int pstart=0; // Wait for every controller be active (button pressed) to start Countdown + strip_clear( &tck, true ); + for( int i = 0; i < race.numcars; ++i) { + if (controller_getStatus(cars[i].ct)==false) { + track.setPixelColor(i,cars[i].color); + pstart++; + } + } + track.setPixelColor(LED_SEMAPHORE , ((millis()/5)%64)*0x010100 ); + track.show(); + // if every controller activated -> Ready for Countdown + if (pstart==race.numcars) goOn=true; + }; + } + + if(goOn || (!race.cfg.startline)) { // Standalone mode is Ready for Countdown __OR__ Network mode and Race does not starts here + for( int i = 0; i < race.numcars; ++i) { + car_resetPosition( &cars[i], true ); + cars[i].repeats = 0; + cars[i].st = CAR_WAITING; // Network race -> cleanup status of previous race + } + tck.ledcoin = COIN_RESET; + race.phase = COUNTDOWN; + if(race.network_race != true) send_phase( race.phase ); + + srand((unsigned long) analogRead(A6) + analogRead(A7)); // used in demo_mode (see olr_controllers.h) + } + } + break; + + case COUNTDOWN: { - if( race.cfg.startline ){ + if( race.cfg.startline ) { // Standalone: Always true - Network mode: only racetrack where race starts // Countdown: semaphore and tones if(start_race_done()) { // Countdown done for( int i = 0; i < race.numcars; ++i ) { @@ -375,12 +287,7 @@ void loop() { case RACING: { - strip_clear( &tck ); - - if( ramp_isactive( &tck ) ) - draw_ramp( &tck ); - if( box_isactive( &tck ) ) - draw_box_entrypoint( &tck ); + strip_clear( &tck, true ); if( box_isactive( &tck ) ) { if( tck.ledcoin == COIN_RESET ) { @@ -416,14 +323,14 @@ void loop() { } track.show(); - 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;}; + if (s_motor==1) tone(PIN_AUDIO,f_beep+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 (t_beep>0) {t_beep--;} else {f_beep=0;}; + - // Print p command!!! - unsigned long nowmillis = millis(); - if( abs( nowmillis - last_telemetry_millis ) > 250 ){ - last_telemetry_millis = nowmillis; + // Send Telemetry data + if(telemetry_delay.elapsed()) { print_cars_positions( cars ); + telemetry_delay.start(TELEMETRY_DELAY); } // ---------------- } @@ -431,13 +338,11 @@ void loop() { case COMPLETE : { - strip_clear( &tck ); + strip_clear( &tck, false ); track.show(); - if ( race.cfg.finishline ){ - draw_winner( &tck, cars[race.winner].color ); - sound_winner( &tck, race.winner ); - strip_clear( &tck ); - } + draw_winner( &tck, cars[race.winner].color ); + sound_winner( &tck, race.winner ); + strip_clear( &tck, false ); track.show(); startRace_delay.start(NEWRACE_DELAY); @@ -448,15 +353,9 @@ void loop() { } 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 + ; // -- see comment in CONFIG_OK status } break; @@ -498,11 +397,11 @@ bool players_actity(uint8_t numcars ) { } /* - * Check if Controllers (players) were incative for more than INACTIVITY_TIMEOUT_DELAY + * Check if Controllers (players) were incative for more than INACTIVITY_TIMEOUT_DELAY sec */ bool ready_for_demo_mode(void) { if(players_actity(race.numcars)){ - demoMode_delay.start(INACTIVITY_TIMEOUT_DELAY); // Reset timeout when somebody is using controllers + demoMode_delay.start(INACTIVITY_TIMEOUT_DELAY * 1000); // Reset timeout when somebody is using controllers } return (demoMode_delay.elapsed()); } @@ -516,7 +415,7 @@ 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.winner=0; // force a fake winner (used in Status=Complete by draw_winner()) race.phase = COMPLETE; sprintf(txbuff, "%c%d%c", 'M', 1 , EOL ); @@ -531,7 +430,7 @@ 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.winner=0; // force a fake winner (used in Status=Complete by draw_winner()) race.phase = COMPLETE; sprintf(txbuff, "%c%d%c", 'M', 0 , EOL ); @@ -544,7 +443,7 @@ void send_phase( int phase ) { } -void run_racecycle( car_t *car, int i ) { +void run_racecycle( car_t *car, int caridx ) { struct cfgtrack const* cfg = &tck.cfg.track; // if( car->st == CAR_COMING ) { // OLR Network only @@ -552,29 +451,16 @@ void run_racecycle( car_t *car, int i ) { // } if( car->st == CAR_ENTER ) { - // 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 car->st = CAR_GO_OUT; } - + if( car->st == CAR_RACING ) { update_track( &tck, car ); car_updateController( car ); @@ -600,15 +486,15 @@ void run_racecycle( car_t *car, int i ) { if( car->st == CAR_LEAVING ) { // OLR Network only car->st = CAR_RACING; - sprintf( txbuff, "r%d%c", i + 1, EOL ); + sprintf( txbuff, "r%d%c", caridx + 1, EOL ); serialCommand.sendCommand(txbuff); } if( car->st == CAR_GO_OUT ) { // OLR Network only car->st = CAR_WAITING; - //#warning Insert function to map speed! + //map car number in 3 higher bits and car speed in 5 lower bits byte const speed = car->speed * 10; - byte const data = (i + 1) << 5 | ( 0b00011111 & speed ); + byte const data = (caridx + 1) << 5 | ( 0b00011111 & speed ); sprintf( txbuff, "s%c%c", data, EOL ); serialCommand.sendCommand(txbuff);; car_resetPosition( car, true ); @@ -617,7 +503,7 @@ void run_racecycle( car_t *car, int i ) { if ( car->st == CAR_FINISH ){ car->trackID = NOT_TRACK; - sprintf( txbuff, "w%d%c", i + 1, EOL ); + sprintf( txbuff, "w%d%c", caridx + 1, EOL ); serialCommand.sendCommand(txbuff); car_resetPosition(car, true); @@ -666,15 +552,13 @@ void print_cars_positions( car_t* cars ) { /* - * non-blocking version + * non-blocking */ boolean start_race_done( ) { if(countdown_new_phase){ countdown_new_phase=false; startRace_delay.start(CONTDOWN_PHASE_DURATION); - strip_clear( &tck ); - if(ramp_isactive( &tck )) draw_ramp( &tck ); - if(box_isactive( &tck )) draw_box_entrypoint( &tck ); + strip_clear( &tck , true); switch(countdown_phase) { case 1: tone(PIN_AUDIO,400); @@ -730,13 +614,20 @@ void sound_winner( track_t* tck, byte winner ) { } -void strip_clear( track_t* tck ) { +void strip_clear( track_t* tck, bool show_settings ) { struct cfgtrack const* cfg = &tck->cfg.track; for( int i=0; i < cfg->nled_main; i++) track.setPixelColor( i, track.Color(0,0,0) ); for( int i=0; i < cfg->nled_aux; i++) track.setPixelColor( cfg->nled_main+i, track.Color(0,0,0) ); + + if(show_settings) { + if( ramp_isactive( tck )) + draw_ramp( tck ); + if( box_isactive( tck ) ) + draw_box_entrypoint( tck ); + } } @@ -805,11 +696,7 @@ void draw_car( track_t* tck, car_t* car ) { * */ void show_cfgpars_onstrip(){ - strip_clear( &tck ); - if( ramp_isactive( &tck ) ) - draw_ramp( &tck ); - if( box_isactive( &tck ) ) - draw_box_entrypoint( &tck ); + strip_clear( &tck, true ); track.show(); } @@ -857,6 +744,7 @@ ack_t manageSerialCommand() { int clen = serialCommand.checkSerial(); if(clen == 0) return ack; // No commands received + if(clen < 0) { // Error receiving command sprintf( txbuff, "Error reading serial command:[%d]",clen); printdebug( txbuff, WARNING ); @@ -865,7 +753,16 @@ ack_t manageSerialCommand() { // clen > 0 ---> Command with length=clen ready in cmd[] ack.rp=NOK; + +// Debug only +//if(race.network_race == true) { +// sprintf( txbuff, "Recv cmd:%s", cmd); +// printdebug( txbuff, WARNING ); +//} + + switch (cmd[0]) { + case '#': // Handshake { ack.type = cmd[0]; @@ -900,10 +797,21 @@ ack_t manageSerialCommand() { { ack.type = cmd[0]; uint8_t const phase = atoi( cmd + 1); + /** + * Reintrodotto momentaneamente R1=enter config per testare RelayRace con Networkclient esistente // 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; + **/ + // Codice vecchio con R1=Enter Configuration + if( 0 > phase || RACE_PHASES <= phase) 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; @@ -917,8 +825,9 @@ ack_t manageSerialCommand() { cars[ncar-1].st = CAR_ENTER; cars[ncar-1].speed = (float) speed / 10; ack.rp = OK; + ack.rp = NOTHING; if( false ) { - sprintf( txbuff, "%s %d, %s %d, %s %d", "CAR: ", ncar, "STATUS: ", cars[ncar-1].st, "SPEED: ", (int)cars[ncar-1].speed * 10 ); + 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 ); } } @@ -938,6 +847,20 @@ ack_t manageSerialCommand() { } break; + case 'w' : // Car Wins the race - OLR Network only + // Standalone mode: Board _NEVER_ receives a 'w' command !!! + { // Network mode: 1) Command "w" sent by the Board where the race ends + // 2) Every other participant (Board) receives 'w' command + ack.type = cmd[0]; + byte const ncar = atoi( cmd + 1); + if( 0 >= ncar || race.numcars < ncar) return ack; + if( race.network_race && (!race.cfg.finishline)) { + race.winner = (byte) ncar-1; // Set the Winner + } + ack.rp = NOTHING; + } + break; + case 'C' : //Parse race configuration -> C1,2,3,0 { @@ -1339,7 +1262,7 @@ void printdebug( const char * msg, int errlevel ) { */ void enter_configuration_mode(){ noTone(PIN_AUDIO); - strip_clear( &tck ); + strip_clear( &tck, false ); track.show(); } @@ -1357,8 +1280,8 @@ void param_load( struct cfgparam* cfg ) { EEPROM.get( eeadrInfo, tck.cfg ); - sprintf( txbuff, "%s:%d%c", "EEPROM-v", tck.cfg.ver, EOL ); - serialCommand.sendCommand(txbuff); +// sprintf( txbuff, "%s:%d%c", "EEPROM-v", tck.cfg.ver, EOL ); +// serialCommand.sendCommand(txbuff); if ( tck.cfg.ver != CFGPARAM_VER ) { // [cfgparam.ver] read form EEPROM != [#define CFGPARAM_VER] in the code // Each time a new version of the code modify the [cfgparam] struct, [#define CFGPARAM_VER] is also