Merge branch 'lab'
This commit is contained in:
commit
631062ac31
16 changed files with 834 additions and 299 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
00_note_sviluppo.txt
|
||||||
.pio
|
.pio
|
||||||
.vscode/.browse.c_cpp.db*
|
.vscode/.browse.c_cpp.db*
|
||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
|
@ -5,3 +6,4 @@
|
||||||
.vscode/ipch
|
.vscode/ipch
|
||||||
|
|
||||||
\.vscode/
|
\.vscode/
|
||||||
|
|
||||||
|
|
87
README.md
87
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
|
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
Revisions history
|
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
|
- Branch Lab merged to Master
|
||||||
|
|
||||||
* 2021-11-20: Ver 0.9.7 - Luca - Branch: "lab"
|
* 2021-11-20: Ver 0.9.7 - Luca - Branch: "lab"
|
||||||
|
|
Binary file not shown.
BIN
doc/OLR_Protocol_Serial.pdf
Normal file
BIN
doc/OLR_Protocol_Serial.pdf
Normal file
Binary file not shown.
BIN
doc/docSrc/OLR_Protocol_Serial.odt
Normal file
BIN
doc/docSrc/OLR_Protocol_Serial.odt
Normal file
Binary file not shown.
|
@ -48,7 +48,7 @@ int SerialCommand::checkSerial() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// buffer full
|
// buffer full
|
||||||
// re4set and retunn error
|
// reset and retunn error
|
||||||
_buf[_bufIdx++] = '\0';
|
_buf[_bufIdx++] = '\0';
|
||||||
_bufIdx=0;
|
_bufIdx=0;
|
||||||
return(-2);
|
return(-2);
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
#include "olr-controller.h"
|
#include "olr-controller.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DELTA_ANALOG = 5,
|
DELTA_ANALOG = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int DIGITAL_CTRL[MAX_PLAYERS];
|
||||||
|
|
||||||
static float const ACEL = 0.2;
|
static float const ACEL = 0.2;
|
||||||
|
|
||||||
void controller_setup( void ) {
|
void controller_setup( void ) {
|
||||||
|
|
||||||
|
/***
|
||||||
if( DIGITAL_MODE == false ){
|
if( DIGITAL_MODE == false ){
|
||||||
pinMode(PIN_VCC_ADC1, OUTPUT);
|
pinMode(PIN_VCC_ADC1, OUTPUT);
|
||||||
pinMode(PIN_VCC_ADC2, OUTPUT);
|
pinMode(PIN_VCC_ADC2, OUTPUT);
|
||||||
digitalWrite(PIN_VCC_ADC1, HIGH);
|
digitalWrite(PIN_VCC_ADC1, HIGH);
|
||||||
digitalWrite(PIN_VCC_ADC2, HIGH);
|
digitalWrite(PIN_VCC_ADC2, HIGH);
|
||||||
}
|
}
|
||||||
|
***/
|
||||||
|
|
||||||
pinMode( DIG_CONTROL_1, INPUT_PULLUP); //pull up in adc
|
DIGITAL_CTRL[CTRL_1]= DIG_CTRL_1_PIN;
|
||||||
pinMode( DIG_CONTROL_2, INPUT_PULLUP);
|
DIGITAL_CTRL[CTRL_2]= DIG_CTRL_2_PIN;
|
||||||
pinMode( DIG_CONTROL_3, INPUT_PULLUP);
|
DIGITAL_CTRL[CTRL_3]= DIG_CTRL_3_PIN;
|
||||||
pinMode( DIG_CONTROL_4, INPUT_PULLUP);
|
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 ) {
|
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;
|
ct->badc = ct->adc;
|
||||||
}
|
}
|
||||||
else if( ct->mode == DEBUG_MODE ){
|
else if( ct->mode == DEMO_MODE ){
|
||||||
ct->adc++;
|
ct->adc++;
|
||||||
if( ct->adc >= 60){
|
int rnd = rand();
|
||||||
|
long threshold = 1800 - rnd;
|
||||||
|
if( ct->adc >= (threshold)){
|
||||||
ct->adc = 0;
|
ct->adc = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,24 +6,34 @@
|
||||||
extern "C"{
|
extern "C"{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Arduino.h"
|
#include <Arduino.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.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_ADC1 6
|
||||||
#define PIN_VCC_ADC2 7
|
#define PIN_VCC_ADC2 7
|
||||||
|
|
||||||
enum ctr_type{
|
enum ctr_type {
|
||||||
NOT_DEFINED = 0,
|
NOT_DEFINED = 0,
|
||||||
DIGITAL_MODE,
|
DIGITAL_MODE,
|
||||||
ANALOG_MODE,
|
ANALOG_MODE,
|
||||||
DEBUG_MODE,
|
DEMO_MODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "olr-lib.h"
|
#include "olr-lib.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void car_init( car_t* car, controller_t* ct, uint32_t color ) {
|
void car_init( car_t* car, controller_t* ct, uint32_t color ) {
|
||||||
car->ct = ct;
|
car->ct = ct;
|
||||||
car->color = color;
|
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 ) {
|
void process_main_track( track_t* tck, car_t* car ) {
|
||||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
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->trackID = TRACK_MAIN;
|
||||||
car->speed = 0;
|
if(reset_speed) car->speed = 0 ;
|
||||||
car->dist = 0;
|
car->dist = 0;
|
||||||
car->dist_aux = 0;
|
car->dist_aux = 0;
|
||||||
car->nlap = 1;
|
car->nlap = 1;
|
||||||
car->leaving = false;
|
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 ) {
|
void box_init( track_t* tck ) {
|
||||||
tck->boxactive = true;
|
tck->boxactive = true;
|
||||||
}
|
}
|
||||||
|
@ -152,14 +158,18 @@ int tracklen_configure( track_t* tck, int nled ) {
|
||||||
return 0;
|
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);
|
param_option_set(&tck->cfg, AUTOSTART_MODE_OPTION, (boolean) autostart);
|
||||||
return 0;
|
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){
|
switch(val){
|
||||||
case 2 :
|
case 2 :
|
||||||
param_option_set(&tck->cfg, PLAYER_3_OPTION, false);
|
param_option_set(&tck->cfg, PLAYER_3_OPTION, false);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#ifndef _OLR_LIB_h
|
#ifndef _OLR_LIB_h
|
||||||
#define _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_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 );
|
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 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 );
|
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn );
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ void param_setdefault( struct cfgparam* cfg ) {
|
||||||
|
|
||||||
param_option_set(cfg, PLAYER_3_OPTION, PLAYER_3);
|
param_option_set(cfg, PLAYER_3_OPTION, PLAYER_3);
|
||||||
param_option_set(cfg, PLAYER_4_OPTION, PLAYER_4);
|
param_option_set(cfg, PLAYER_4_OPTION, PLAYER_4);
|
||||||
|
param_option_set(cfg, DEMO_MODE_OPTION, DEMO_MODE_ST);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ extern "C"{
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "olr-settings.h"
|
||||||
|
|
||||||
// Default values loaded on "D" command received (Serial Protocol)
|
// Default values loaded on "D" command received (Serial Protocol)
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
#define MAXLED 300
|
#define MAXLED 300
|
||||||
|
@ -21,6 +23,7 @@ extern "C"{
|
||||||
#define SLOPE_ALWAYS_ON false
|
#define SLOPE_ALWAYS_ON false
|
||||||
#define PLAYER_3 false
|
#define PLAYER_3 false
|
||||||
#define PLAYER_4 false
|
#define PLAYER_4 false
|
||||||
|
#define DEMO_MODE_ST false
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,27 +36,28 @@ enum cfgparam_option_bit {
|
||||||
SLOPE_MODE_OPTION = 3,
|
SLOPE_MODE_OPTION = 3,
|
||||||
PLAYER_3_OPTION = 4,
|
PLAYER_3_OPTION = 4,
|
||||||
PLAYER_4_OPTION = 5,
|
PLAYER_4_OPTION = 5,
|
||||||
NOT_USED_3_OPTION = 6,
|
DEMO_MODE_OPTION = 6,
|
||||||
NOT_USED_4_OPTION = 7,
|
NOT_USED_7_OPTION = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum cfgpar {
|
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
|
// 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,
|
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 nlap;
|
||||||
uint8_t nrepeat; // Used only in OLRNetwork
|
uint8_t nrepeat; // Standalone mode: Always 1
|
||||||
bool finishline; // Used only in OLRNetwork
|
bool finishline; // Standalone mode: Always 1
|
||||||
};
|
} cfgrace_t;
|
||||||
|
|
||||||
struct cfgbattery{ // added in ver 0.9.7
|
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 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 min; // Battery charge does not goes below this "min" percentage
|
||||||
uint8_t speed_boost_scaler;
|
uint8_t speed_boost_scaler;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
@ -86,7 +90,8 @@ struct brdinfo {
|
||||||
struct cfgparam {
|
struct cfgparam {
|
||||||
uint8_t ver; // Version of this [cfgparam] struct
|
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)
|
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 cfgbattery battery;
|
||||||
struct cfgtrack track;
|
struct cfgtrack track;
|
||||||
struct cfgramp ramp;
|
struct cfgramp ramp;
|
||||||
|
|
140
open-led-race/olr-settings.h
Normal file
140
open-led-race/olr-settings.h
Normal 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
|
95
open-led-race/open-led-race.h
Normal file
95
open-led-race/open-led-race.h
Normal 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
|
|
@ -15,11 +15,12 @@
|
||||||
the Free Software Foundation; either version 3 of the License, or
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
First public version by:
|
Current Version by:
|
||||||
Angel Maldonado (https://gitlab.com/angeljmc)
|
LucaBuka (https://gitlab.com/lucabuka)
|
||||||
Gerardo Barbarov (gbarbarov AT singulardevices DOT com)
|
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
|
Gerardo Barbarov for Arduino day Seville 2019
|
||||||
https://github.com/gbarbarov/led-race
|
https://github.com/gbarbarov/led-race
|
||||||
|
|
||||||
|
@ -28,156 +29,73 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ____________HARDWARE CONFIGURATION____________________
|
||||||
|
* __ __
|
||||||
|
* __ Compile-time settings (PIN_LED, PIN_AUDIO, etc) __
|
||||||
|
* __ are defined in [olr-settings.h] file __
|
||||||
|
* ______________________________________________________
|
||||||
|
*/
|
||||||
|
|
||||||
// 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 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 <Adafruit_NeoPixel.h>
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include "olr-lib.h"
|
#include "olr-lib.h"
|
||||||
|
#include "olr-controller.h"
|
||||||
#include "olr-param.h"
|
#include "olr-param.h"
|
||||||
#include "SoftTimer.h"
|
#include "SoftTimer.h"
|
||||||
#include "SerialCommand.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[] = {
|
int win_music[] = {
|
||||||
2637, 2637, 0, 2637,
|
2637, 2637, 0, 2637,
|
||||||
0, 2093, 2637, 0,
|
0, 2093, 2637, 0,
|
||||||
3136
|
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()
|
char cmd[REC_COMMAND_BUFLEN]; // Stores command received by ReadSerialComand()
|
||||||
SerialCommand serialCommand = SerialCommand(cmd, REC_COMMAND_BUFLEN, EOL, &Serial); // get complete command from serial
|
SerialCommand serialCommand = SerialCommand(cmd, REC_COMMAND_BUFLEN, EOL, &Serial); // get complete command from serial
|
||||||
|
|
||||||
char txbuff[TX_COMMAND_BUFLEN];
|
char txbuff[TX_COMMAND_BUFLEN];
|
||||||
|
|
||||||
Adafruit_NeoPixel track;
|
Adafruit_NeoPixel track;
|
||||||
|
|
||||||
|
static uint32_t car_color[]={
|
||||||
|
COLOR1,
|
||||||
|
COLOR2,
|
||||||
|
COLOR3,
|
||||||
|
COLOR4
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -191,31 +109,34 @@ void setup() {
|
||||||
|
|
||||||
track = Adafruit_NeoPixel( tck.cfg.track.nled_total, PIN_LED, NEO_GRB + NEO_KHZ800 );
|
track = Adafruit_NeoPixel( tck.cfg.track.nled_total, PIN_LED, NEO_GRB + NEO_KHZ800 );
|
||||||
|
|
||||||
controller_init( &switchs[0], DIGITAL_MODE, DIG_CONTROL_1 );
|
// First 2 controllers always active (Red, Green)
|
||||||
car_init( &cars[0], &switchs[0], COLOR1 );
|
|
||||||
controller_init( &switchs[1], DIGITAL_MODE, DIG_CONTROL_2 );
|
|
||||||
car_init( &cars[1], &switchs[1], COLOR2 );
|
|
||||||
|
|
||||||
race.numcars = 2;
|
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) ) {
|
// Calculate actual players number
|
||||||
controller_init( &switchs[2], DIGITAL_MODE, DIG_CONTROL_3 );
|
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) ) {
|
||||||
car_init( &cars[2], &switchs[2], COLOR3 );
|
++race.numcars;
|
||||||
|
}
|
||||||
|
if( controller_isActive( DIGITAL_CTRL[CTRL_4] ) || param_option_is_active(&tck.cfg, PLAYER_4_OPTION)) {
|
||||||
++race.numcars;
|
++race.numcars;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( controller_isActive( DIG_CONTROL_4 ) || param_option_is_active(&tck.cfg, PLAYER_4_OPTION)) {
|
// Check if DEMO mode is configured
|
||||||
controller_init( &switchs[3], DIGITAL_MODE, DIG_CONTROL_4 );
|
race.demo_mode = param_option_is_active(&tck.cfg, DEMO_MODE_OPTION);
|
||||||
car_init( &cars[3], &switchs[3], COLOR4 );
|
enum ctr_type current_mode = (race.demo_mode == true) ? DEMO_MODE : DIGITAL_MODE;
|
||||||
++race.numcars;
|
|
||||||
}
|
// !!! 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();
|
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
|
// 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 );
|
box_init( &tck );
|
||||||
track_configure( &tck, tck.cfg.track.nled_total - tck.cfg.track.box_len );
|
track_configure( &tck, tck.cfg.track.nled_total - tck.cfg.track.box_len );
|
||||||
draw_box_entrypoint( &tck );
|
draw_box_entrypoint( &tck );
|
||||||
|
@ -223,23 +144,31 @@ void setup() {
|
||||||
track_configure( &tck, 0 );
|
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 );
|
ramp_init( &tck );
|
||||||
draw_ramp( &tck );
|
draw_ramp( &tck );
|
||||||
track.show();
|
track.show();
|
||||||
delay(2000);
|
delay(2000);
|
||||||
if ( digitalRead( DIG_CONTROL_1 ) == 0 ) { //retain push switch on reset for activate FX sound
|
if ( controller_isActive( DIGITAL_CTRL[CTRL_1] ) ) { //retain push switch on reset for activate FX sound
|
||||||
SMOTOR=1;
|
s_motor=1;
|
||||||
tone(PIN_AUDIO,100);}
|
tone(PIN_AUDIO,100);}
|
||||||
}
|
}
|
||||||
|
|
||||||
race.cfg.startline = tck.cfg.race.startline;// true;
|
race.network_race = false; // always starts in standalone mode
|
||||||
race.cfg.nlap = tck.cfg.race.nlap;// NUMLAP;
|
race.demo_mode_on_received = false;
|
||||||
race.cfg.nrepeat = tck.cfg.race.nrepeat;// 1;
|
race.demo_mode_off_received = false;
|
||||||
race.cfg.finishline = tck.cfg.race.finishline;// true;
|
|
||||||
|
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);
|
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:
|
// PLEASE NOTE:
|
||||||
// DO NOT call "track.show()" in the loop() while in configuration mode !!!
|
// 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
|
// It would mess up with Serial communication (receives only 2 bytes - if the
|
||||||
|
@ -264,61 +204,75 @@ void loop() {
|
||||||
|
|
||||||
case CONFIG:
|
case CONFIG:
|
||||||
{
|
{
|
||||||
if( race.newcfg ) {
|
if( race.newcfg ) { // Exit_Config command received
|
||||||
race.newcfg = false;
|
race.newcfg = false;
|
||||||
countdownReset();
|
countdownReset();
|
||||||
customDelay.start(0);
|
startRace_delay.start(0);
|
||||||
race.phase = READY;
|
// 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 );
|
send_phase( race.phase );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case READY:
|
case CONFIG_OK: // OLR Network only
|
||||||
{
|
{
|
||||||
if(param_option_is_active(&tck.cfg, AUTOSTART_MODE_OPTION)){ // Auto-Start Mode ON
|
; // In a Relay Race the configuration is sent (via 'C' command) by the
|
||||||
if(customDelay.elapsed()) {
|
// Host ("Nerwork Client" program running on another Computer)
|
||||||
for( int i = 0; i < race.numcars; ++i) {
|
// When the board reach the CONFIG_OK status...it does nothing but wait for
|
||||||
car_resetPosition( &cars[i] );
|
// the next Command coming form the Host.
|
||||||
cars[i].repeats = 0;
|
// 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
|
||||||
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 );}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case COUNTDOWN:
|
||||||
{
|
{
|
||||||
if( race.cfg.startline ){
|
if( race.cfg.startline ) { // Standalone: Always true - Network mode: only racetrack where race starts
|
||||||
// Countdown: semaphore and tones
|
// Countdown: semaphore and tones
|
||||||
if(start_race_done()) {
|
if(start_race_done()) { // Countdown done
|
||||||
// Countdown done
|
|
||||||
for( int i = 0; i < race.numcars; ++i ) {
|
for( int i = 0; i < race.numcars; ++i ) {
|
||||||
cars[i].st = CAR_ENTER;
|
cars[i].st = CAR_ENTER;
|
||||||
}
|
}
|
||||||
|
@ -331,12 +285,7 @@ void loop() {
|
||||||
|
|
||||||
case RACING:
|
case RACING:
|
||||||
{
|
{
|
||||||
strip_clear( &tck );
|
strip_clear( &tck, true );
|
||||||
|
|
||||||
if( ramp_isactive( &tck ) )
|
|
||||||
draw_ramp( &tck );
|
|
||||||
if( box_isactive( &tck ) )
|
|
||||||
draw_box_entrypoint( &tck );
|
|
||||||
|
|
||||||
if( box_isactive( &tck ) ) {
|
if( box_isactive( &tck ) ) {
|
||||||
if( tck.ledcoin == COIN_RESET ) {
|
if( tck.ledcoin == COIN_RESET ) {
|
||||||
|
@ -365,39 +314,47 @@ void loop() {
|
||||||
run_racecycle( &cars[i], i );
|
run_racecycle( &cars[i], i );
|
||||||
if( cars[i].st == CAR_FINISH ) {
|
if( cars[i].st == CAR_FINISH ) {
|
||||||
race.phase = COMPLETE;
|
race.phase = COMPLETE;
|
||||||
race.winner = i;
|
race.winner = (byte) i;
|
||||||
send_phase( race.phase );
|
send_phase( race.phase );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
track.show();
|
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 (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 (TBEEP>0) {TBEEP--;} else {FBEEP=0;};
|
if (t_beep>0) {t_beep--;} else {f_beep=0;};
|
||||||
|
|
||||||
// Print p command!!!
|
|
||||||
unsigned long nowmillis = millis();
|
// Send Telemetry data
|
||||||
if( abs( nowmillis - lastmillis ) > 250 ){
|
if(telemetry_delay.elapsed()) {
|
||||||
lastmillis = nowmillis;
|
|
||||||
print_cars_positions( cars );
|
print_cars_positions( cars );
|
||||||
|
telemetry_delay.start(TELEMETRY_DELAY);
|
||||||
}
|
}
|
||||||
// ----------------
|
// ----------------
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMPLETE :
|
case COMPLETE :
|
||||||
{
|
{
|
||||||
strip_clear( &tck );
|
strip_clear( &tck, false );
|
||||||
track.show();
|
track.show();
|
||||||
if ( race.cfg.finishline ){
|
draw_winner( &tck, cars[race.winner].color );
|
||||||
draw_winner( &tck, cars[race.winner].color );
|
sound_winner( &tck, race.winner );
|
||||||
sound_winner( &tck, race.winner );
|
strip_clear( &tck, false );
|
||||||
strip_clear( &tck );
|
|
||||||
}
|
|
||||||
track.show();
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -411,17 +368,91 @@ 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 ) {
|
void send_phase( int phase ) {
|
||||||
sprintf(txbuff, "R%d%c",phase,EOL);
|
sprintf(txbuff, "R%d%c",phase,EOL);
|
||||||
serialCommand.sendCommand(txbuff);
|
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;
|
struct cfgtrack const* cfg = &tck.cfg.track;
|
||||||
|
|
||||||
|
// if( car->st == CAR_COMING ) { // OLR Network only
|
||||||
|
// // To be implemented
|
||||||
|
// }
|
||||||
|
|
||||||
if( car->st == CAR_ENTER ) {
|
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 )
|
if( car->repeats < race.cfg.nrepeat )
|
||||||
car->st = CAR_RACING;
|
car->st = CAR_RACING;
|
||||||
else
|
else
|
||||||
|
@ -451,13 +482,29 @@ 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", 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 ){
|
if ( car->st == CAR_FINISH ){
|
||||||
car->trackID = NOT_TRACK;
|
car->trackID = NOT_TRACK;
|
||||||
sprintf( txbuff, "w%d%c", i + 1, EOL );
|
sprintf( txbuff, "w%d%c", caridx + 1, EOL );
|
||||||
serialCommand.sendCommand(txbuff);
|
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( ) {
|
boolean start_race_done( ) {
|
||||||
if(countdown_new_phase){
|
if(countdown_new_phase){
|
||||||
countdown_new_phase=false;
|
countdown_new_phase=false;
|
||||||
customDelay.start(CONTDOWN_PHASE_DURATION);
|
startRace_delay.start(CONTDOWN_PHASE_DURATION);
|
||||||
strip_clear( &tck );
|
strip_clear( &tck , true);
|
||||||
if(ramp_isactive( &tck )) draw_ramp( &tck );
|
|
||||||
if(box_isactive( &tck )) draw_box_entrypoint( &tck );
|
|
||||||
switch(countdown_phase) {
|
switch(countdown_phase) {
|
||||||
case 1:
|
case 1:
|
||||||
tone(PIN_AUDIO,400);
|
tone(PIN_AUDIO,400);
|
||||||
|
@ -528,7 +573,7 @@ boolean start_race_done( ) {
|
||||||
track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,255,0));
|
track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,255,0));
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
customDelay.start(CONTDOWN_STARTSOUND_DURATION);
|
startRace_delay.start(CONTDOWN_STARTSOUND_DURATION);
|
||||||
tone(PIN_AUDIO,880);
|
tone(PIN_AUDIO,880);
|
||||||
track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,0,0));
|
track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,0,0));
|
||||||
track.setPixelColor(0, track.Color(255,255,255));
|
track.setPixelColor(0, track.Color(255,255,255));
|
||||||
|
@ -540,7 +585,7 @@ boolean start_race_done( ) {
|
||||||
}
|
}
|
||||||
track.show();
|
track.show();
|
||||||
}
|
}
|
||||||
if(customDelay.elapsed()) {
|
if(startRace_delay.elapsed()) {
|
||||||
noTone(PIN_AUDIO);
|
noTone(PIN_AUDIO);
|
||||||
countdown_new_phase=true;
|
countdown_new_phase=true;
|
||||||
countdown_phase++;
|
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);
|
int const msize = sizeof(win_music) / sizeof(int);
|
||||||
for (int note = 0; note < msize; note++) {
|
for (int note = 0; note < msize; note++) {
|
||||||
tone(PIN_AUDIO, win_music[note],200);
|
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;
|
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||||
for( int i=0; i < cfg->nled_main; i++)
|
for( int i=0; i < cfg->nled_main; i++)
|
||||||
track.setPixelColor( i, track.Color(0,0,0) );
|
track.setPixelColor( i, track.Color(0,0,0) );
|
||||||
|
|
||||||
for( int i=0; i < cfg->nled_aux; i++)
|
for( int i=0; i < cfg->nled_aux; i++)
|
||||||
track.setPixelColor( cfg->nled_main+i, track.Color(0,0,0) );
|
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(){
|
void show_cfgpars_onstrip(){
|
||||||
strip_clear( &tck );
|
strip_clear( &tck, true );
|
||||||
if( ramp_isactive( &tck ) )
|
|
||||||
draw_ramp( &tck );
|
|
||||||
if( box_isactive( &tck ) )
|
|
||||||
draw_box_entrypoint( &tck );
|
|
||||||
track.show();
|
track.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,6 +742,7 @@ ack_t manageSerialCommand() {
|
||||||
|
|
||||||
int clen = serialCommand.checkSerial();
|
int clen = serialCommand.checkSerial();
|
||||||
if(clen == 0) return ack; // No commands received
|
if(clen == 0) return ack; // No commands received
|
||||||
|
|
||||||
if(clen < 0) { // Error receiving command
|
if(clen < 0) { // Error receiving command
|
||||||
sprintf( txbuff, "Error reading serial command:[%d]",clen);
|
sprintf( txbuff, "Error reading serial command:[%d]",clen);
|
||||||
printdebug( txbuff, WARNING );
|
printdebug( txbuff, WARNING );
|
||||||
|
@ -702,7 +751,16 @@ ack_t manageSerialCommand() {
|
||||||
// clen > 0 ---> Command with length=clen ready in cmd[]
|
// clen > 0 ---> Command with length=clen ready in cmd[]
|
||||||
ack.rp=NOK;
|
ack.rp=NOK;
|
||||||
|
|
||||||
|
|
||||||
|
// Debug only
|
||||||
|
//if(race.network_race == true) {
|
||||||
|
// sprintf( txbuff, "Recv cmd:%s", cmd);
|
||||||
|
// printdebug( txbuff, WARNING );
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
switch (cmd[0]) {
|
switch (cmd[0]) {
|
||||||
|
|
||||||
case '#': // Handshake
|
case '#': // Handshake
|
||||||
{
|
{
|
||||||
ack.type = cmd[0];
|
ack.type = cmd[0];
|
||||||
|
@ -736,16 +794,72 @@ ack_t manageSerialCommand() {
|
||||||
case 'R' : // Set Race Phase
|
case 'R' : // Set Race Phase
|
||||||
{
|
{
|
||||||
ack.type = cmd[0];
|
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;
|
if( 0 > phase || RACE_PHASES <= phase) return ack;
|
||||||
race.phase = (enum phases) phase;
|
race.phase = (enum phases) phase;
|
||||||
ack.rp = OK;
|
ack.rp = OK;
|
||||||
if ( race.phase == CONFIG ) { // accept R1 as a EnterConfigurationMode command - DEPRECATED
|
if ( race.phase == CONFIG ) { // accept R1 as a EnterConfigurationMode command - DEPRECATED
|
||||||
enter_configuration_mode();
|
enter_configuration_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
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
|
case 'C' : //Parse race configuration -> C1,2,3,0
|
||||||
{
|
{
|
||||||
ack.type = cmd[0];
|
ack.type = cmd[0];
|
||||||
|
@ -853,7 +967,7 @@ ack_t manageSerialCommand() {
|
||||||
if( !pch ) return ack;
|
if( !pch ) return ack;
|
||||||
int slopeperm = atoi( pch );
|
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;
|
if( err ) return ack;
|
||||||
ack.rp = OK;
|
ack.rp = OK;
|
||||||
|
|
||||||
|
@ -874,51 +988,73 @@ ack_t manageSerialCommand() {
|
||||||
|
|
||||||
pch = strtok (pch, "," );
|
pch = strtok (pch, "," );
|
||||||
if( !pch ) return ack;
|
if( !pch ) return ack;
|
||||||
int delta = atoi( pch );
|
uint8_t delta = atoi( pch );
|
||||||
|
|
||||||
pch = strtok (NULL, "," );
|
pch = strtok (NULL, "," );
|
||||||
if( !pch ) return ack;
|
if( !pch ) return ack;
|
||||||
int min = atoi( pch );
|
uint8_t min = atoi( pch );
|
||||||
|
|
||||||
pch = strtok (NULL, "," );
|
pch = strtok (NULL, "," );
|
||||||
if( !pch ) return ack;
|
if( !pch ) return ack;
|
||||||
int boost = atoi( pch );
|
uint8_t boost = atoi( pch );
|
||||||
|
|
||||||
pch = strtok (NULL, ",");
|
pch = strtok (NULL, ",");
|
||||||
if( !pch ) return ack;
|
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;
|
if( err ) return ack;
|
||||||
ack.rp = OK;
|
ack.rp = OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'G' : //Parse Autostart configuration -> Gautostart
|
case 'G' : // Parse Autostart configuration -> Gautostart
|
||||||
{
|
{
|
||||||
ack.type = cmd[0];
|
ack.type = cmd[0];
|
||||||
|
|
||||||
char * pch = strtok (cmd,"G");
|
char * pch = strtok (cmd,"G");
|
||||||
if( !pch ) return ack;
|
if( !pch ) return ack;
|
||||||
|
|
||||||
int autostart = atoi( cmd + 1 );
|
uint8_t autostart = atoi( cmd + 1 );
|
||||||
int err = autostart_configure( &tck, autostart);
|
uint8_t err = autostart_configure( &tck, autostart);
|
||||||
if( err ) return ack;
|
if( err ) return ack;
|
||||||
|
|
||||||
ack.rp = OK;
|
ack.rp = OK;
|
||||||
}
|
}
|
||||||
break;
|
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];
|
ack.type = cmd[0];
|
||||||
|
|
||||||
char * pch = strtok (cmd,"P");
|
char * pch = strtok (cmd,"P");
|
||||||
if( !pch ) return ack;
|
if( !pch ) return ack;
|
||||||
|
|
||||||
int autostart = atoi( cmd + 1 );
|
uint8_t players_n = atoi( cmd + 1 );
|
||||||
int err = players_n_configure( &tck, autostart);
|
uint8_t err = players_n_configure( &tck, players_n);
|
||||||
if( err ) return ack;
|
if( err ) return ack;
|
||||||
|
|
||||||
ack.rp = OK;
|
ack.rp = OK;
|
||||||
|
@ -950,6 +1086,20 @@ ack_t manageSerialCommand() {
|
||||||
}
|
}
|
||||||
break;
|
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
|
case 'D' : // Load Default Parameters and store them in from EEPROM
|
||||||
{
|
{
|
||||||
|
@ -1004,6 +1154,16 @@ ack_t manageSerialCommand() {
|
||||||
}
|
}
|
||||||
break;
|
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
|
case 'Q': // Get current configuration Info
|
||||||
{
|
{
|
||||||
struct cfgparam const* cfg = &tck.cfg;
|
struct cfgparam const* cfg = &tck.cfg;
|
||||||
|
@ -1039,13 +1199,16 @@ ack_t manageSerialCommand() {
|
||||||
EOL );
|
EOL );
|
||||||
serialCommand.sendCommand(txbuff);
|
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.startline,
|
||||||
cfg->race.nlap,
|
cfg->race.nlap,
|
||||||
cfg->race.nrepeat,
|
cfg->race.nrepeat,
|
||||||
cfg->race.finishline,
|
cfg->race.finishline,
|
||||||
param_option_is_active(&tck.cfg, PLAYER_3_OPTION),
|
param_option_is_active(&tck.cfg, PLAYER_3_OPTION),
|
||||||
param_option_is_active(&tck.cfg, PLAYER_4_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 );
|
EOL );
|
||||||
serialCommand.sendCommand(txbuff);
|
serialCommand.sendCommand(txbuff);
|
||||||
|
|
||||||
|
@ -1097,7 +1260,7 @@ void printdebug( const char * msg, int errlevel ) {
|
||||||
*/
|
*/
|
||||||
void enter_configuration_mode(){
|
void enter_configuration_mode(){
|
||||||
noTone(PIN_AUDIO);
|
noTone(PIN_AUDIO);
|
||||||
strip_clear( &tck );
|
strip_clear( &tck, false );
|
||||||
track.show();
|
track.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,8 +1278,8 @@ void param_load( struct cfgparam* cfg ) {
|
||||||
|
|
||||||
EEPROM.get( eeadrInfo, tck.cfg );
|
EEPROM.get( eeadrInfo, tck.cfg );
|
||||||
|
|
||||||
sprintf( txbuff, "%s:%d%c", "EEPROM-v", tck.cfg.ver, EOL );
|
// sprintf( txbuff, "%s:%d%c", "EEPROM-v", tck.cfg.ver, EOL );
|
||||||
serialCommand.sendCommand(txbuff);
|
// serialCommand.sendCommand(txbuff);
|
||||||
|
|
||||||
if ( tck.cfg.ver != CFGPARAM_VER ) { // [cfgparam.ver] read form EEPROM != [#define CFGPARAM_VER] in the code
|
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
|
// Each time a new version of the code modify the [cfgparam] struct, [#define CFGPARAM_VER] is also
|
||||||
|
|
Loading…
Add table
Reference in a new issue