Merge branch 'lab'

This commit is contained in:
Luca Borsari 2023-08-19 18:36:05 +02:00
commit 631062ac31
16 changed files with 834 additions and 299 deletions

2
.gitignore vendored
View file

@ -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/

View file

@ -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

View file

@ -1,10 +1,26 @@
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-10: Ver 0.9.7
* 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

Binary file not shown.

BIN
doc/OLR_Protocol_Serial.pdf Normal file

Binary file not shown.

Binary file not shown.

View file

@ -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);

View file

@ -1,24 +1,32 @@
#include "olr-controller.h"
enum {
DELTA_ANALOG = 5,
};
int DIGITAL_CTRL[MAX_PLAYERS];
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 +49,11 @@ 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){
int rnd = rand();
long threshold = 1800 - rnd;
if( ct->adc >= (threshold)){
ct->adc = 0;
return 1;
}

View file

@ -6,24 +6,34 @@
extern "C"{
#endif
#include "Arduino.h"
#include <Arduino.h>
#include <stdint.h>
#include <stdbool.h>
#include "olr-settings.h"
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)
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{

View file

@ -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;
@ -124,17 +124,23 @@ 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;
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) {
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);

View file

@ -1,4 +1,3 @@
#ifndef _OLR_LIB_h
#define _OLR_LIB_h
@ -75,7 +74,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 +88,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 );

View file

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

View file

@ -10,6 +10,8 @@ extern "C"{
#include <stdint.h>
#include <stdbool.h>
#include "olr-settings.h"
// Default values loaded on "D" command received (Serial Protocol)
//////////////////////////////////////////////////////////////////
#define MAXLED 300
@ -21,6 +23,7 @@ extern "C"{
#define SLOPE_ALWAYS_ON false
#define PLAYER_3 false
#define PLAYER_4 false
#define DEMO_MODE_ST false
//////////////////////////////////////////////////////////////////
@ -33,27 +36,28 @@ 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,
};
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;
} ;
@ -86,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;

View file

@ -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

View file

@ -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

View file

@ -15,11 +15,12 @@
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
First public version by:
Angel Maldonado (https://gitlab.com/angeljmc)
Current Version by:
LucaBuka (https://gitlab.com/lucabuka)
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
@ -28,156 +29,73 @@
*/
// 2021/07/20 - Ver 0.9.6 - lab branch
// --see changelog.txt
/**
* ____________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.7";
char const version[] = "0.9.9";
#include "open-led-race.h"
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>
#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 supplie 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
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{
int 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;
int winner;
};
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 lastmillis = 0;
SoftTimer customDelay = SoftTimer(); // non blocking delay()
// 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,
COLOR3,
COLOR4
};
/*
*
@ -190,32 +108,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 );
strip_clear( &tck , false);
// 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(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 );
@ -223,23 +144,31 @@ 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( 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( DIG_CONTROL_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);}
}
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
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();
customDelay.start(0); // first race starts with no delay
race.phase = READY;
}
/*
@ -253,6 +182,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 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();
}
// 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
@ -264,61 +204,75 @@ void loop() {
case CONFIG:
{
if( race.newcfg ) {
if( race.newcfg ) { // Exit_Config command received
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 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(customDelay.elapsed()) {
for( int i = 0; i < race.numcars; ++i) {
car_resetPosition( &cars[i] );
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] );
//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
if(start_race_done()) { // Countdown done
for( int i = 0; i < race.numcars; ++i ) {
cars[i].st = CAR_ENTER;
}
@ -331,12 +285,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 ) {
@ -365,39 +314,47 @@ 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;
}
}
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 - lastmillis ) > 250 ){
lastmillis = nowmillis;
// Send Telemetry data
if(telemetry_delay.elapsed()) {
print_cars_positions( cars );
telemetry_delay.start(TELEMETRY_DELAY);
}
// ----------------
// ----------------
}
break;
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();
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 IDLE: // OLR Network only
{
; // -- see comment in CONFIG_OK status
}
break;
default:
@ -411,23 +368,97 @@ 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 sec
*/
bool ready_for_demo_mode(void) {
if(players_actity(race.numcars)){
demoMode_delay.start(INACTIVITY_TIMEOUT_DELAY * 1000); // 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; // force a fake winner (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; // force a fake winner (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);
}
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
// // 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 );
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 );
@ -438,7 +469,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;
@ -450,14 +481,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", caridx + 1, EOL );
serialCommand.sendCommand(txbuff);
}
if( car->st == CAR_GO_OUT ) { // OLR Network only
car->st = CAR_WAITING;
//map car number in 3 higher bits and car speed in 5 lower bits
byte const speed = car->speed * 10;
byte const data = (caridx + 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 );
sprintf( txbuff, "w%d%c", caridx + 1, EOL );
serialCommand.sendCommand(txbuff);
//sendCommand(txbuff);
car_resetPosition( car );
car_resetPosition(car, true);
}
}
@ -503,15 +550,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;
customDelay.start(CONTDOWN_PHASE_DURATION);
strip_clear( &tck );
if(ramp_isactive( &tck )) draw_ramp( &tck );
if(box_isactive( &tck )) draw_box_entrypoint( &tck );
startRace_delay.start(CONTDOWN_PHASE_DURATION);
strip_clear( &tck , true);
switch(countdown_phase) {
case 1:
tone(PIN_AUDIO,400);
@ -528,7 +573,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));
@ -540,7 +585,7 @@ boolean start_race_done( ) {
}
track.show();
}
if(customDelay.elapsed()) {
if(startRace_delay.elapsed()) {
noTone(PIN_AUDIO);
countdown_new_phase=true;
countdown_phase++;
@ -557,7 +602,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);
@ -567,13 +612,20 @@ void sound_winner( track_t* tck, int 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 );
}
}
@ -642,11 +694,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();
}
@ -694,6 +742,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 );
@ -702,7 +751,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];
@ -736,16 +794,72 @@ ack_t manageSerialCommand() {
case 'R' : // Set Race Phase
{
ack.type = cmd[0];
int const phase = atoi( cmd + 1);
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;
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;
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) );
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 '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
{
ack.type = cmd[0];
@ -785,7 +899,7 @@ ack_t manageSerialCommand() {
case 'T' : //Parse Track configuration -> Track length
{
ack.type = cmd[0];
char * pch = strtok (cmd,"T");
if( !pch ) return ack;
@ -853,7 +967,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;
@ -874,51 +988,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;
@ -950,6 +1086,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
{
@ -1004,6 +1154,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;
@ -1039,13 +1199,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);
@ -1097,7 +1260,7 @@ void printdebug( const char * msg, int errlevel ) {
*/
void enter_configuration_mode(){
noTone(PIN_AUDIO);
strip_clear( &tck );
strip_clear( &tck, false );
track.show();
}
@ -1115,8 +1278,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