new M,n commands - start integration of Network mode

This commit is contained in:
Luca Borsari 2023-07-19 18:13:10 +02:00
parent 65f9f15f51
commit 88755ac355
13 changed files with 398 additions and 105 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,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

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

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

View file

@ -10,20 +10,34 @@ extern "C"{
#include <stdint.h>
#include <stdbool.h>
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{

View file

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

View file

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

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

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

View file

@ -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 <Adafruit_NeoPixel.h>
@ -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);