Ver 0.9.6
This commit is contained in:
parent
cc1c937458
commit
60294709c7
33 changed files with 4088 additions and 1313 deletions
|
@ -1,6 +1,26 @@
|
|||
Revisions history
|
||||
-----------------
|
||||
|
||||
|
||||
* 2020-12-09: Ver 0.9.6 - Luca
|
||||
...............................
|
||||
- Removed dependency from "AsyncSerialLib"
|
||||
- new class SerialCommand()
|
||||
- Remove delay() (blocking) in Countdown phase
|
||||
- new class SoftTimer()
|
||||
- adapted to Serial Protocol 1.0
|
||||
- added serial command "@" - Enter Configuration Mode
|
||||
(still respond to deprecated R0,R1 to enter cfg mode)
|
||||
- added serial command "K" - Set Physics parameters (weight, friction)
|
||||
- added serial command "W" - Write parameter set to EEPROM
|
||||
- updated command "B" (Pitlane cfg) to manage PERM parameter
|
||||
- updated command "A" (Slope cfg) to manage PERM parameter
|
||||
|
||||
* 2020-08-26: Ver 0.9.5 - Luca
|
||||
- Version number format changed (3 dot-separated integers) !!!
|
||||
- added serial command Get Software ID
|
||||
- added serial command Get Software Version
|
||||
|
||||
* 2020-07-28: Ver 0.9.d - Luca
|
||||
- Tested OK on Arduino Every
|
||||
- Configuration Values NOW SAVED in EEPROM to enable
|
||||
|
|
|
@ -1,471 +0,0 @@
|
|||
# Serial Protocol (OLRBoard←→Host)
|
||||
|
||||
|
||||
In the present doc the terms Board and Host indicate:
|
||||
- ***Board***: OLR Board - The microcontroller managing the led strip (Arduino)
|
||||
- ***Host***: The Host running the OpenLedRace COnfiguration software (PC, RPI, etc)
|
||||
|
||||
The Board is currently connected to the Host via Serial interface (USB)
|
||||
|
||||
## Implementation characteristic
|
||||
|
||||
1. Local Communication between Board and Host use plain ASCII (not binary).
|
||||
|
||||
2. Messages are kept very short (lightweight protocol for “speed” and low resources.)
|
||||
|
||||
|
||||
## Message formats
|
||||
- Messages are composed by 2 parts: **Command**, **Parameters**.
|
||||
- Messages with multiple parameters, the char **','** is used as **"Parameters Separator"**
|
||||
- Messages sending back command confirmation uses "**<i>command</i>OK**" and "**<i>command</i>NOK**"
|
||||
- Example: **CNOK** is the 'error' answer sent for a **C** command
|
||||
-Messages end with the "EOC" (End Of Command) char:
|
||||
- [EOC] - Line Feed = ASCII 10/0x0A = new line = ‘\n’
|
||||
|
||||
* * *
|
||||
|
||||
## Commands list
|
||||
Command | Description | Notes
|
||||
--------|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
\# | Protocol Handshaking | Host-Board handshake on startup
|
||||
@ | Reset | Host send a Reset to the Board
|
||||
$ | Get UID | Get Board Unique Id
|
||||
% | Get Version | Get Board Software version
|
||||
: | Set Unique ID | Set Board Unique ID
|
||||
! | Send log/error msg | Send a log/error message to peer
|
||||
**C** | **C**onfiguration Race | Set basic race configuration (Number of **laps** for a single race)
|
||||
**R** | Race phase | Command used to Change/Notify current Race phase
|
||||
**T** | Track length configuration | Command used to configure the Total Number of LEDs in the track.
|
||||
**B** | Box length configuration | Command used to configure the number of LEDS at the end of the LED Strip reserved for Boxes (Pitlane).
|
||||
**A** | Ramp configuration | Command used to configure the Ramp in the track.
|
||||
**D** | Load Track and ramp defult | Command used to Reset to Default parameters (Race, Ramp, Pitlane)
|
||||
**Q** | Query board cfg | Host request the current situation of the Config Parameters Set
|
||||
**p** | Car current position in OLR | Current position of the car in the OLR
|
||||
**w** | Car Win the Race | A car just win the current race
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
## Commands Description
|
||||
|
||||
|
||||
In the following sections the column "**Initiate**" contains the id of the board sending the message.
|
||||
- ***B*** - ***Board***: OLR Board (Arduino Nano)
|
||||
- ***H*** - ***Host***: Host where the OpenLedRace Manger program is running (Computer).
|
||||
|
||||
Same rule applies to the "***From***" column in "***Response***"
|
||||
|
||||
Some commands may be originated by both peers (ex: Handshake command)
|
||||
|
||||
The string **_[EOC]_** indicates the EOC (End of Command) char, currently = "line feed", (ASCII 0A)
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [#] - Protocol Handshaking
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|B, H|**#**_[EOC]_ | **Protocol Handshaking**
|
||||
|| | Sent to initialize a connection (Board and Host) |
|
||||
|**Response**| **From** | **Notes**
|
||||
|**#**_[EOC]_ |H, B | The connection opens succesfully when a “**#**” is received 'back' from the peer
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [@] - Reset [To be implemented]
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**@**_[EOC]_|**OLR Board Reset request**
|
||||
| | | Sent from Host to Reset the OLR Board to the initial state (before handshake)
|
||||
|**Response**| **From** | **Notes**
|
||||
| | | No response expected from Board
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [$] - Get Board UID
|
||||
|
||||
|
||||
| initiate | Syntax | Description |
|
||||
|----------|-------------|------------------------------------|
|
||||
| H | **$**_[EOC]_ | **OLR Board UID request** |
|
||||
| | | Sent from Host to get Board's UID |
|
||||
|
||||
|**Response**| **From** | **Notes**
|
||||
|------|--------|------------
|
||||
|**$**Id[EOC] | B | Send the UID strings
|
||||
|
||||
#### UID String format
|
||||
Unique Board Id (UID) string format:<br>
|
||||
**^[\x33-\x7E]{16}$**
|
||||
|
||||
- Lenght: 16 chars
|
||||
- Valid Chars: Ascii 7-bit Printable Chars excluding ‘space’=ASCII 32 (this means ASCII chars between 33 (0x21) and 126 (0x7E) inclusive
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**$[EOC]**| Host send a **get info** request
|
||||
|B|**?3179c3ec6e28a[EOC]**|Board answer: Id="3179c3ec6e28a"
|
||||
|
||||
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**?[EOC]**| Host send a **get info** request
|
||||
|B|**??????????[EOC]**|The Board send back an invalid UID (if you are looking at it in a Serial Console, you usually see a bunch of question marks or other chars / non-printable ASCII).<br> This usually happens when the UID is not set yet, so the Board send back the contents of the area of the EEPROM where the UID is supposed to be stored.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [%] - Get Software Version
|
||||
|
||||
Used to check software compatibility between Board and Host program's versions
|
||||
|
||||
| initiate | Syntax | Description |
|
||||
|----------|-------------|------------------------------------|
|
||||
| H | **%** [EOC] | **OLR Board software version request** |
|
||||
|
||||
|
||||
|**Response**| **From** | **Notes**
|
||||
|------|--------|------------
|
||||
|**%**Ver[EOC] | B | String representing the Software Version
|
||||
|
||||
|
||||
#### Software Version String format
|
||||
[0-9]+\.[0-9]+\.[0-9a-zA-Z]+
|
||||
|
||||
Two dot-separated decimal numbers plus a third part composed by numbers and/or letters.
|
||||
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**%[EOC]**| Host send a **get info** request
|
||||
|B|**%0.9.d[EOC]**|The message from the Board indicates Version="0.9.d"
|
||||
|
||||
#### Guidelines to Assign a version number to the Arduino Software:
|
||||
|
||||
|
||||
The three numbers represents the “Major.Minor.Patch” version.
|
||||
|
||||
• Major version zero (0.y.z) is for initial development. Anything MAY change at any time.
|
||||
• Version 1.0.0 defines first ‘Stable’ version
|
||||
• Increment:
|
||||
◦ MAJOR version when you make incompatible changes
|
||||
◦ MINOR version when you add functionality in a backwards compatible manner
|
||||
◦ PATCH version when you make backwards compatible bug fixes.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
|
||||
### [:] - Set board Unique Id
|
||||
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**:**id[EOC]|**OLR Board Set UniqueId request**
|
||||
|| | Sent from Host to Set Board's Unique Id
|
||||
|
||||
|Parameters | | |
|
||||
|----------------|---|---|
|
||||
| |id: | String representing the Unique Id. The string contains 16 characters max.
|
||||
|
||||
|
||||
|Response| |
|
||||
|---|---
|
||||
|**OK**[EOC] | Board sends "OK" string (ACK)
|
||||
|**NOK**[EOC] | Board indicates that something went wrong
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [!] - Send log/error message
|
||||
|
||||
The software running on the Board use this command to send messages to be written into the Host logfile.<br>
|
||||
The Host will log the message and decide what to do with the relay race according to the "Severity" parameter (nothing, stop it, etc.)
|
||||
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|B |**!**Severity,Message[EOC]|**OLR Board Sends an error message to Host**
|
||||
|
||||
|Parameters | | |
|
||||
|----------------|---|---|
|
||||
| Severity:[0-3] | single char|
|
||||
| | 1| **Log only** - Board want to log a message into the Host Message LogFile,<br> Sent usually in development/debug phase to trace the dialog between Board and Host |
|
||||
| | 2| **Warning** - Board send back a "warning" message<br>Sent by board on 'not blocking' errors like, for example, unknown commands or parameters |
|
||||
| | 3| **Blocking Error** - The boards have a Severe error condition and cannot proceed.<br> The Host will log the message into the Host Message LogFile and decide what to do (if the Host is running a RelayRace it will Stop the Race)<br>
|
||||
| | | |
|
||||
| Message:String |ASCII| The ASCII String containing the message.
|
||||
|
||||
|**Response**| **From** | **Notes**
|
||||
|------|--------|------------
|
||||
| | H | No answer sent from Host
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|B|**\!1,invalid Car=[3] in [t] command**| Board send a warning message about a previously received command
|
||||
|
||||
- - -
|
||||
|
||||
### [**C**] - Set basic race configuration
|
||||
|
||||
This configuration is stored in non-volatile memory.
|
||||
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**C**start.nlap.repeat.finish[EOC] |**Host Set basic race configuration**
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter |Format| Description
|
||||
|--------|---|---------
|
||||
| **start** | [0-1] | OLRNetwork Only **Always 1 for standalone OLR**
|
||||
| **nlap** | [1-9][0-9]? |Number of laps of a Race
|
||||
| | | max 2 chars (range 1-99)
|
||||
| **repeat** |[1-9][0-9]?| OLRNetwork Only **Always 1 for standalone OLR**
|
||||
| | |
|
||||
| **finish** |[0-1] | OLRNetwork Only **Always 1 for standalone OLR**
|
||||
|
||||
|
||||
|
||||
|Response| |
|
||||
|---|---
|
||||
|**OK**[EOC] | Board sends "OK" string (ACK)
|
||||
|**NOK**[EOC] |Board indicates that something went wrong
|
||||
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**C1,3,1,1**|**laps=3**: Each car will need to complete 3 laps before it can cross the Finish Line
|
||||
|B|**OK[EOC]**| This is the Response from the Board to the previous example(ACK)
|
||||
|||The message from the Board indicates that the value for Position,Laps, Repeat and Finish line has been set correctly as requested by the host.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [T] - **T**rack configuration - Total LEDs Number
|
||||
|
||||
This configuration is stored in non-volatile memory.
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**T**nled[EOC] |**Host Set Racetrack Length Configuration**
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter |Format| Description
|
||||
|--------|---|---------
|
||||
| **nled** | Total number of LEDs in the Track | Ex: 300 for a single 5mt - 60 LED/mt LED Strip <br>**Please Note:<br>**After changing the LEDs number ** you need to reboot the board **
|
||||
|
||||
|Response| |
|
||||
|---|---
|
||||
|**TOK**[EOC] | Board sends "OK" string (ACK)
|
||||
|**TNOK**[EOC] |Board indicates that something went wrong
|
||||
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**T600**| Total Length is 600 (2 x 300 LED Strip connected).
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [B] - **T**rack configuration - Pitlane Lenght (Boxes)
|
||||
|
||||
This configuration is stored in non-volatile memory.
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**B**nled[EOC] |**Host Set Pitlane (boxex) Length Configuration**
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter |Format| Description
|
||||
|--------|---|---------
|
||||
| **nled** | Total number of LEDs, at the end of the Racetrack, reserved for the Pitlane | Ex: 120
|
||||
|
||||
|Response| |
|
||||
|---|---
|
||||
|**BOK**[EOC] | Board sends "OK" string (ACK)
|
||||
|**BNOK**[EOC] |Board indicates that something went wrong
|
||||
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**B120**| Total Length for Pitlane is 120
|
||||
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [A] - r**A**ramp configuration
|
||||
|
||||
This configuration is stored in non-volatile memory.
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**A**start,center,end,high[EOC] |**Host Set basic Ramp configuration**
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter |Format| Description
|
||||
|--------|---|---------
|
||||
| **start** | | LED number where the ramp Starts
|
||||
| **center** | | LED Number where ramp is centered.
|
||||
| **end** | | LED number where the ramp ends
|
||||
| **height** | [ 0 - 1023] | Ramp elevation
|
||||
|
||||
|
||||
|Response| |
|
||||
|---|---
|
||||
|**AOK**[EOC] | Board sends "OK" string (ACK)
|
||||
|**ANOK**[EOC] |Board indicates that something went wrong
|
||||
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**A140,150,160,12**|: Set the ramp centered in led 150, starts 10 LED before it and ends 10 LEDs after it, Elevation 12.
|
||||
|
||||
- - -
|
||||
|
||||
### [D] - **D**efault configuration [ Implemented ]
|
||||
|
||||
Reset to default configuration parametrs (Track lenght, Ramp, Boxes and Race Laps).
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|H |**D**[EOC] |**Host Request Board to Reset configuration parameters to Default**
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
|
||||
### [Q] - Query current parameters set
|
||||
|
||||
|
||||
| initiate | Syntax | Description |
|
||||
|----------|-------------|------------------------------------|
|
||||
| H | **Q**_[EOC]_ | **Get Current Parameters request** |
|
||||
| | | Sent from Host to get Board's Cfg |
|
||||
|
||||
|
||||
#### Returned Parameters format
|
||||
|
||||
|
||||
|**Response**| **From** | **Notes**
|
||||
|------|--------|------------
|
||||
|Board issue 3 answers:<br>**TRACK:**a,b,c,d,e[EOC]<br> **RAMP:**a,b,c,d[EOC]<br> **RACE:**a,b,c,d[EOC]<br>| B | Send the Parameters Set
|
||||
| | | |
|
||||
| **TRACK** params | | |
|
||||
| | **nled_total** | Total number of LEDs in the Racetrack (**configurable with "T" command**) |
|
||||
| | nled_main | Internal parameter (when Pitlane is active: number of LEDs currently in the Main Path) |
|
||||
| | nled_aux | Internal parameter (when Pitlane is active: number of LEDs currently in the Pitlane Path)|
|
||||
| | nled_init_aux | Internal parameter (position of the Pitlane entrance) |
|
||||
| | **box_len** | Total number of LEDs, at the end of the Racetrack, reserved for the Pitlane (**configurable with "B" command**) |
|
||||
| **RAMP** params | | |
|
||||
|| **start** | LED number where the ramp Starts
|
||||
|| **center** | LED Number where ramp is centered.
|
||||
|| **end** | LED number where the ramp ends
|
||||
|| **height** | Ramp elevation
|
||||
| **RACE** params | | |
|
||||
|| **start** | OLRNetwork Only **Always 1 for standalone OLR**
|
||||
|| **nlap** |Number of laps of a Race (**Configurable with 2nd parameter of "C" Command**)
|
||||
|| **repeat** | OLRNetwork Only **Always 1 for standalone OLR**
|
||||
|| **finish** | OLRNetwork Only **Always 1 for standalone OLR**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|H|**Q[EOC]**| Host send a **get current paremeters Set** request
|
||||
|B|**TRACK:1200,1200,0,-1,60[EOC]<br>RAMP:180,190,200,15[EOC]<br>RACE:1,2,1,1[EOC]<br>**|Messages from the Board with the current cfg values
|
||||
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
|
||||
### [**p**] - Current Car **p**osition in Race
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|B|**p**NumTrackNlapRpos[EOC]|**Position for each car in the race**
|
||||
|||Sent during race for each car currently in this Board.
|
||||
|
||||
|Parameters | | |
|
||||
|--------------|---|---|
|
||||
| Num [1-9] | | One char representing Car Number.
|
||||
| Track [A-Z] | | One char representing the Track where the car is.
|
||||
| | M | Main Track |
|
||||
| | B | Box Track |
|
||||
| | U | Not Track |
|
||||
| | . | .... |
|
||||
| Nlap [1-99] | | Number of the Current Lap.
|
||||
| Rpos [00-99] | | Relative position in a track in percent.
|
||||
|
||||
|Response| From | |
|
||||
|---|--- |---
|
||||
| | H | No response from host
|
||||
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|B|**p**1B1.95**p**2M5.45[EOC]| Two cars are currentry "running" in the Board. Car "1" is in Track "B" in Lap number "1" Relative Lap Position 95%. Car "2" is in Track "M" in Lap number "5" Relative Lap Position 45%
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### [w] - Car Win te Race
|
||||
|
||||
|initiate| Syntax | Description
|
||||
|------|--------|------------
|
||||
|B|**w**Num[EOC]|**Car 'Num' just win the race**
|
||||
|||Sent by the circuit managing the "Finish Line" when a car cross it.
|
||||
|
||||
|Parameters | | |
|
||||
|----------------|---|---|
|
||||
| Num [1-9] | |One char representing Car Number
|
||||
|
||||
|Response| From | |
|
||||
|---|--- |---
|
||||
| | H | No response from host
|
||||
|
||||
|
||||
#### Examples
|
||||
|Origin|Command||
|
||||
|---|----|----|
|
||||
|B|**w**1| Car "1" win the race
|
||||
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
**Revisions:**
|
||||
- 2020 07 28
|
||||
- Command T: Syntax mofied
|
||||
- Command A: Syntax mofied
|
||||
- Command B: Added
|
||||
-
|
||||
|
||||
- - -
|
BIN
doc/OLRN_Protocol_Serial.pdf
Normal file
BIN
doc/OLRN_Protocol_Serial.pdf
Normal file
Binary file not shown.
|
@ -1,339 +0,0 @@
|
|||
/***************************************************
|
||||
Copyright (c) 2018 Luis Llamas
|
||||
(www.luisllamas.es)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
|
||||
****************************************************/
|
||||
|
||||
#include "AsyncSerialLib.h"
|
||||
|
||||
AsyncSerial::AsyncSerial(byte* buffer, size_t bufferLength,
|
||||
AsyncSerialCallback onRecievedOk, AsyncSerialCallback onTimeout = nullptr, AsyncSerialCallback onOverflow = nullptr)
|
||||
{
|
||||
Init(buffer, bufferLength, &Serial);
|
||||
OnRecievedOk = onRecievedOk;
|
||||
OnTimeout = onTimeout;
|
||||
OnOverflow = onOverflow;
|
||||
_startTime = millis();
|
||||
}
|
||||
|
||||
void AsyncSerial::Init(byte *buffer, size_t bufferLength, Stream* stream)
|
||||
{
|
||||
_status = RECIEVING_DATA;
|
||||
_startTime = millis();
|
||||
_stream = stream == NULL ? &Serial : stream;
|
||||
_buffer = buffer;
|
||||
_bufferLength = bufferLength;
|
||||
_bufferIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
AsyncSerial::Status AsyncSerial::AsyncRecieve()
|
||||
{
|
||||
if (_status == IDDLE) { return; }
|
||||
|
||||
if (IsExpired())
|
||||
{
|
||||
if (OnTimeout != nullptr) OnTimeout(*this);
|
||||
_status = TIMEOUT;
|
||||
}
|
||||
|
||||
if (_status >= MESSAGE_RECIEVED)
|
||||
{
|
||||
_startTime = millis();
|
||||
_status = AutoReset ? RECIEVING_DATA : IDDLE;
|
||||
}
|
||||
|
||||
if (_status == RECIEVING_DATA || _status == RECIEVING_DATA_OVERFLOW)
|
||||
{
|
||||
asyncRecieve();
|
||||
}
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
AsyncSerial::Status AsyncSerial::AsyncRecieve(int timeOut)
|
||||
{
|
||||
Timeout = timeOut;
|
||||
AsyncRecieve();
|
||||
return _status;
|
||||
}
|
||||
|
||||
AsyncSerial::Status AsyncSerial::Recieve(int timeOut)
|
||||
{
|
||||
_startTime = millis();
|
||||
_status = RECIEVING_DATA;
|
||||
|
||||
bool expired = false;
|
||||
while (!expired && _status < MESSAGE_RECIEVED)
|
||||
{
|
||||
AsyncRecieve();
|
||||
expired = ((unsigned long)(millis() - _startTime) >= Timeout);
|
||||
}
|
||||
|
||||
if (expired)
|
||||
{
|
||||
_status = TIMEOUT;
|
||||
if (OnTimeout != nullptr) OnTimeout(*this);
|
||||
}
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
|
||||
void AsyncSerial::AsyncSend(bool waitAck)
|
||||
{
|
||||
AsyncSend(_buffer, _bufferLength, waitAck);
|
||||
}
|
||||
|
||||
void AsyncSerial::AsyncSend(byte* data, size_t dataLength, bool waitAck)
|
||||
{
|
||||
if (_status == IDDLE) return;
|
||||
|
||||
if (_status == TIMEOUT)
|
||||
{
|
||||
_status = AutoReset ? SENDING_DATA : IDDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_status != WAITING_ACK)
|
||||
{
|
||||
_stream->write(data, dataLength);
|
||||
|
||||
if (waitAck)
|
||||
{
|
||||
_status = WAITING_ACK;
|
||||
_startTime = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
_status = AutoReset ? MESSAGE_SENDED : IDDLE;
|
||||
if (OnRecievedOk != nullptr) OnRecievedOk(*this);
|
||||
}
|
||||
}
|
||||
|
||||
if (_status == WAITING_ACK)
|
||||
{
|
||||
if (IsExpired())
|
||||
{
|
||||
_status = TIMEOUT;
|
||||
_startTime = millis();
|
||||
if (OnTimeout != nullptr) OnTimeout(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_stream->read() == AckChar)
|
||||
{
|
||||
_status = AutoReset ? MESSAGE_SENDED : IDDLE;
|
||||
if (OnRecievedOk != nullptr) OnRecievedOk(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncSerial::ProcessByte(byte data)
|
||||
{
|
||||
LastByte = data;
|
||||
|
||||
if (data == (byte)FinishChar) finishRecieve();
|
||||
else processNewData();
|
||||
}
|
||||
|
||||
void AsyncSerial::Send(bool waitAck)
|
||||
{
|
||||
Send(_buffer, _bufferLength, waitAck);
|
||||
}
|
||||
|
||||
void AsyncSerial::Send(byte* data, size_t dataLength, bool waitAck)
|
||||
{
|
||||
_stream->write(data, dataLength);
|
||||
|
||||
if (waitAck)
|
||||
{
|
||||
_startTime = millis();
|
||||
while (!IsExpired())
|
||||
{
|
||||
if (_stream->read() == AckChar)
|
||||
{
|
||||
_status = AutoReset ? MESSAGE_SENDED : IDDLE;
|
||||
if (OnRecievedOk != nullptr) OnRecievedOk(*this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_status = TIMEOUT;
|
||||
if (OnTimeout != nullptr) OnTimeout(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
_status = AutoReset ? MESSAGE_SENDED : IDDLE;
|
||||
if (OnRecievedOk != nullptr) OnRecievedOk(*this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
uint8_t AsyncSerial::GetLastIndex()
|
||||
{
|
||||
return (_bufferIndex - 1 + _bufferLength) % _bufferLength;
|
||||
}
|
||||
|
||||
byte AsyncSerial::GetLastData()
|
||||
{
|
||||
return _buffer[GetLastIndex()];
|
||||
}
|
||||
|
||||
byte * AsyncSerial::GetContent()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
uint8_t AsyncSerial::GetContentLength()
|
||||
{
|
||||
return _status == MESSAGE_RECIEVED_OVERFLOW ? _bufferLength : _bufferIndex;
|
||||
}
|
||||
|
||||
void AsyncSerial::OrderBuffer()
|
||||
{
|
||||
orderBuffer(_buffer, 0, _bufferLength - 1, GetLastIndex());
|
||||
}
|
||||
|
||||
void AsyncSerial::Start()
|
||||
{
|
||||
_status = RECIEVING_DATA;
|
||||
_bufferIndex = 0;
|
||||
_startTime = millis();
|
||||
}
|
||||
|
||||
void AsyncSerial::Stop()
|
||||
{
|
||||
_status = IDDLE;
|
||||
}
|
||||
|
||||
inline bool AsyncSerial::IsExpired()
|
||||
{
|
||||
if (Timeout == 0) return false;
|
||||
return ((unsigned long)(millis() - _startTime) > Timeout);
|
||||
}
|
||||
|
||||
|
||||
void AsyncSerial::asyncRecieve()
|
||||
{
|
||||
while (_stream->available())
|
||||
{
|
||||
byte newData = _stream->read();
|
||||
|
||||
ProcessByte(newData);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncSerial::processNewData()
|
||||
{
|
||||
if (LastByte != (byte)IgnoreChar)
|
||||
{
|
||||
if (OnByteProcessed != nullptr) OnByteProcessed(*this);
|
||||
|
||||
if (_bufferIndex >= _bufferLength)
|
||||
{
|
||||
_bufferIndex %= _bufferLength;
|
||||
if (_status != RECIEVING_DATA_OVERFLOW)
|
||||
{
|
||||
if (OnOverflow != nullptr) OnOverflow(*this);
|
||||
}
|
||||
_status = RECIEVING_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
_buffer[_bufferIndex] = LastByte;
|
||||
_bufferIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncSerial::finishRecieve()
|
||||
{
|
||||
_status = (_status == RECIEVING_DATA_OVERFLOW ? MESSAGE_RECIEVED_OVERFLOW : MESSAGE_RECIEVED);
|
||||
|
||||
if (_status == MESSAGE_RECIEVED)
|
||||
{
|
||||
if (OnRecievedOk != nullptr) OnRecievedOk(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowOverflow)
|
||||
{
|
||||
OrderBuffer();
|
||||
if (OnRecievedOk != nullptr) OnRecievedOk(*this);
|
||||
if (SendAck) _stream->write(AckChar);
|
||||
}
|
||||
}
|
||||
|
||||
_bufferIndex = 0;
|
||||
}
|
||||
|
||||
void AsyncSerial::orderBuffer(byte buffer[], size_t start, size_t end, size_t index)
|
||||
{
|
||||
size_t leftBlockLength = index - start + 1;
|
||||
size_t rigthBlockLength = end - index;
|
||||
|
||||
while (leftBlockLength != 0 && rigthBlockLength != 0)
|
||||
{
|
||||
if (leftBlockLength <= rigthBlockLength)
|
||||
{
|
||||
// RIGHT BLOCK SHIFT
|
||||
swapBufferBlock(buffer, start, leftBlockLength);
|
||||
start += leftBlockLength;
|
||||
index += leftBlockLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LEFT BLOCK SHIFT
|
||||
swapBufferBlock(buffer, index - rigthBlockLength + 1, rigthBlockLength);
|
||||
end -= rigthBlockLength;
|
||||
index -= rigthBlockLength;
|
||||
}
|
||||
leftBlockLength = index - start + 1;
|
||||
rigthBlockLength = end - index;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncSerial::swapBufferBlock(byte buffer[], size_t start, size_t length)
|
||||
{
|
||||
byte temp;
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
temp = buffer[start + i];
|
||||
buffer[start + i] = buffer[start + i + length];
|
||||
buffer[start + i + length] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AsyncSerial::debugStatus()
|
||||
{
|
||||
switch (_status)
|
||||
{
|
||||
IDDLE: _stream->println("IDDLE"); break;
|
||||
RECIEVING_DATA: _stream->println("RECIEVING_DATA"); break;
|
||||
RECIEVING_DATA_OVERFLOW: _stream->println("RECIEVING_DATA_OVERFLOW"); break;
|
||||
MESSAGE_RECIEVED: _stream->println("MESSAGE_RECIEVED"); break;
|
||||
MESSAGE_RECIEVED_OVERFLOW: _stream->println("MESSAGE_RECIEVED_OVERFLOW"); break;
|
||||
TIMEOUT: _stream->println("TIMEOUT"); break;
|
||||
WAITING_ACK: _stream->println("WAITING_ACK"); break;
|
||||
MESSAGE_SENDED: _stream->println(" "); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncSerial::debugBuffer()
|
||||
{
|
||||
for (int i = 0; i < _bufferLength; i++)
|
||||
{
|
||||
_stream->print((char)_buffer[i]);
|
||||
_stream->print("\t");
|
||||
}
|
||||
_stream->println();
|
||||
|
||||
for (int i = 0; i < _bufferLength; i++)
|
||||
_stream->print(i == GetLastIndex() ? "^" : "\t");
|
||||
_stream->println();
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/***************************************************
|
||||
Copyright (c) 2018 Luis Llamas
|
||||
(www.luisllamas.es)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
|
||||
****************************************************/
|
||||
|
||||
#ifndef _ASYNCSERIALLIB_h
|
||||
#define _ASYNCSERIALLIB_h
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
const char CARRIAGE_RETURN = '\r';
|
||||
const char NEW_LINE = '\n';
|
||||
const char ACK = 0x06;
|
||||
|
||||
class AsyncSerial
|
||||
{
|
||||
typedef void(*AsyncSerialCallback)(AsyncSerial& sender);
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
IDDLE,
|
||||
RECIEVING_DATA,
|
||||
RECIEVING_DATA_OVERFLOW,
|
||||
MESSAGE_RECIEVED,
|
||||
MESSAGE_RECIEVED_OVERFLOW,
|
||||
TIMEOUT,
|
||||
SENDING_DATA,
|
||||
WAITING_ACK,
|
||||
MESSAGE_SENDED
|
||||
} Status;
|
||||
|
||||
AsyncSerial(byte *buffer, size_t bufferLength,
|
||||
AsyncSerialCallback OnRecievedOk, AsyncSerialCallback OnOverflow = nullptr, AsyncSerialCallback OnTimeout = nullptr );
|
||||
|
||||
void Init(byte *buffer, size_t bufferLength, Stream* stream = NULL);
|
||||
|
||||
Status AsyncRecieve();
|
||||
AsyncSerial::Status AsyncRecieve(int timeOut);
|
||||
AsyncSerial::Status Recieve(int timeOut);
|
||||
|
||||
void AsyncSend(bool waitAck = false);
|
||||
void AsyncSend(byte* data, size_t dataLength, bool waitAck = false);
|
||||
void ProcessByte(byte data);
|
||||
void Send(bool waitAck = false);
|
||||
void Send(byte* data, size_t dataLength, bool waitAck = false);
|
||||
|
||||
|
||||
uint8_t GetLastIndex();
|
||||
uint8_t GetLastData();
|
||||
byte* GetContent();
|
||||
uint8_t GetContentLength();
|
||||
void OrderBuffer();
|
||||
void Start();
|
||||
void Stop();
|
||||
inline bool IsExpired();
|
||||
|
||||
AsyncSerialCallback OnRecievedOk;
|
||||
AsyncSerialCallback OnOverflow;
|
||||
AsyncSerialCallback OnTimeout;
|
||||
AsyncSerialCallback OnByteProcessed;
|
||||
|
||||
unsigned long Timeout = 0;
|
||||
bool AutoReset = true;
|
||||
bool AllowOverflow = false;
|
||||
bool SendAck;
|
||||
byte LastByte;
|
||||
char FinishChar = NEW_LINE;
|
||||
char IgnoreChar = CARRIAGE_RETURN;
|
||||
char AckChar = ACK;
|
||||
|
||||
|
||||
protected:
|
||||
Stream* _stream;
|
||||
byte *_buffer;
|
||||
size_t _bufferIndex;
|
||||
size_t _bufferLength;
|
||||
|
||||
unsigned long _startTime;
|
||||
bool _sendAck = false;
|
||||
Status _status;
|
||||
|
||||
void asyncRecieve();
|
||||
void processNewData();
|
||||
void finishRecieve();
|
||||
static void orderBuffer(byte buffer[], size_t start, size_t end, size_t index);
|
||||
static void swapBufferBlock(byte buffer[], size_t start, size_t length);
|
||||
|
||||
void debugBuffer();
|
||||
void debugStatus();
|
||||
};
|
||||
|
||||
#endif
|
74
open-led-race/SerialCommand.cpp
Normal file
74
open-led-race/SerialCommand.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "SerialCommand.h"
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
SerialCommand::SerialCommand() {
|
||||
_initialized = false;
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
SerialCommand::SerialCommand(char *buf, int blen, char eoc, Stream* stream) {
|
||||
init(buf, blen, eoc, stream);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void SerialCommand::init(char *buf, int blen, char eoc, Stream* stream) {
|
||||
if(_initialized) return;
|
||||
|
||||
_stream = stream == NULL ? &Serial : stream;
|
||||
_buf = buf;
|
||||
_bufLen = blen;
|
||||
_bufIdx = 0;
|
||||
_eoc = eoc;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int SerialCommand::checkSerial() {
|
||||
if( !_initialized) return(-1);
|
||||
|
||||
while (_stream->available()) {
|
||||
if(_bufIdx < _bufLen - 2) {
|
||||
char data = _stream->read();
|
||||
if(data == _eoc) {
|
||||
int cmsSize=_bufIdx;
|
||||
_buf[_bufIdx++] = '\0';
|
||||
_bufIdx=0;
|
||||
return(cmsSize);
|
||||
} else {
|
||||
_buf[_bufIdx++] = data;
|
||||
}
|
||||
} else {
|
||||
// buffer full
|
||||
// re4set and retunn error
|
||||
_buf[_bufIdx++] = '\0';
|
||||
_bufIdx=0;
|
||||
return(-2);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SerialCommand::sendCommand(char* str) {
|
||||
// get command length
|
||||
int dlen=0;
|
||||
// for(; dlen<_bufLen; dlen++ ) { // limit transmitted command length to received command buffer
|
||||
for(; dlen<80; dlen++ ) { // limit transmitted command length to received command buffer
|
||||
if(*(str+dlen) == _eoc ){
|
||||
dlen++; // send EOC
|
||||
break;
|
||||
}
|
||||
}
|
||||
_stream->write(str, dlen);
|
||||
return;
|
||||
}
|
29
open-led-race/SerialCommand.h
Normal file
29
open-led-race/SerialCommand.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef SerialCommand_h
|
||||
#define SerialCommand_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class SerialCommand {
|
||||
|
||||
public:
|
||||
SerialCommand();
|
||||
SerialCommand(char *buf, int bufLen, char eoc, Stream* stream);
|
||||
void init(char *buf, int bufLen, char eoc, Stream* stream);
|
||||
int checkSerial(void);
|
||||
void sendCommand(char *);
|
||||
|
||||
int overflow;
|
||||
|
||||
protected:
|
||||
Stream* _stream;
|
||||
char *_buf;
|
||||
int _bufIdx;
|
||||
int _bufLen;
|
||||
char _eoc; // EndOfCommand char
|
||||
|
||||
bool _initialized;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
36
open-led-race/SoftTimer.cpp
Normal file
36
open-led-race/SoftTimer.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "SoftTimer.h"
|
||||
|
||||
SoftTimer::SoftTimer(unsigned long tout) {
|
||||
this->set(tout);
|
||||
}
|
||||
|
||||
SoftTimer::SoftTimer() {
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void SoftTimer::set(unsigned long tout) {
|
||||
this->timeout=tout;
|
||||
}
|
||||
|
||||
|
||||
void SoftTimer::start() {
|
||||
this->startTime=millis();
|
||||
}
|
||||
|
||||
void SoftTimer::start(unsigned long tout) {
|
||||
this->set(tout);
|
||||
this->start();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
boolean SoftTimer::elapsed(){
|
||||
if((millis() - this->startTime) > this->timeout) {
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
23
open-led-race/SoftTimer.h
Normal file
23
open-led-race/SoftTimer.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef SoftTimer_h
|
||||
#define SoftTimer_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class SoftTimer {
|
||||
|
||||
public:
|
||||
|
||||
SoftTimer(void);
|
||||
SoftTimer(unsigned long);
|
||||
void set(unsigned long);
|
||||
void start(void);
|
||||
void start(unsigned long);
|
||||
boolean elapsed(void);
|
||||
|
||||
private:
|
||||
unsigned long startTime=0;
|
||||
unsigned long timeout=0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#ifndef _OLR_CONTROLLER_LIB_h
|
||||
#define _OLR_CONTROLLER_LIB_h
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
|
||||
|
||||
void process_main_track( track_t* tck, car_t* car );
|
||||
void process_aux_track( track_t* tck, car_t* car );
|
||||
|
||||
void car_init( car_t* car, controller_t* ct, uint32_t color ) {
|
||||
car->ct = ct;
|
||||
car->color = color;
|
||||
|
@ -63,19 +60,22 @@ void process_aux_track( track_t* tck, car_t* car ){
|
|||
|
||||
void process_main_track( track_t* tck, car_t* car ) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
|
||||
if ( tck->rampactive ) {
|
||||
struct cfgramp const* r = &tck->cfg.ramp;
|
||||
int const pos = (int)car->dist % cfg->nled_main;
|
||||
if ( pos >= r->init && pos < r->center )
|
||||
car->speed -= cfg->kg * r->high * ( pos - r->init );
|
||||
// car->speed -= cfg->kg * r->high * ( pos - r->init );
|
||||
car->speed -= cfg->kg * r->high ;
|
||||
|
||||
if ( pos <= r->end && pos > r->center )
|
||||
car->speed += cfg->kg * r->high * ( pos - r->center );
|
||||
}
|
||||
|
||||
//car->speed += cfg->kg * r->high * ( pos - r->center );
|
||||
car->speed += cfg->kg * r->high ;
|
||||
}
|
||||
|
||||
car->speed -= car->speed * cfg->kf;
|
||||
car->dist += car->speed;
|
||||
|
||||
}
|
||||
|
||||
void ramp_init( track_t* tck ) {
|
||||
|
@ -115,14 +115,30 @@ int tracklen_configure( track_t* tck, int nled ) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int boxlen_configure( track_t* tck, int box_len ) {
|
||||
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if ( boxalwaysOn != 0 && boxalwaysOn != 1 ) return -1;
|
||||
if( box_len <= 0 || box_len >= cfg->nled_total ) return -1;
|
||||
cfg->box_len = box_len;
|
||||
cfg->box_alwaysOn = boxalwaysOn;
|
||||
|
||||
// Update track->boxactive
|
||||
tck->boxactive = boxalwaysOn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int physic_configure( track_t* tck, float kgp, float kfp ){
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if( kgp <= 0.0 || kgp >= 2.0 ) return -1;
|
||||
if( kfp <= 0.0 || kfp >= 2.0 ) return -1;
|
||||
cfg->kf = kfp;
|
||||
cfg->kg = kgp;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int track_configure( track_t* tck, int init_box ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
@ -135,18 +151,28 @@ int track_configure( track_t* tck, int init_box ) {
|
|||
}
|
||||
|
||||
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high ) {
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high, int alwaysOn ) {
|
||||
struct cfgramp* ramp = &tck->cfg.ramp;
|
||||
|
||||
if ( init >= tck->cfg.track.nled_main || init <= 0 ) return -1;
|
||||
if ( center >= tck->cfg.track.nled_main || center <= 0 ) return -2;
|
||||
if ( end >= tck->cfg.track.nled_main || end <= 0 ) return -3;
|
||||
if ( ! (center > init && center < end) ) return -4;
|
||||
if ( alwaysOn != 0 && alwaysOn != 1 ) return -5;
|
||||
|
||||
ramp->init = init;
|
||||
ramp->center = center;
|
||||
ramp->end = end;
|
||||
ramp->high = high;
|
||||
ramp->alwaysOn = alwaysOn;
|
||||
|
||||
// Update track->rampactive
|
||||
/**
|
||||
boolean rampactive = &tck->rampactive;
|
||||
rampactive = alwaysOn;
|
||||
**/
|
||||
tck->rampactive = alwaysOn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,13 +72,19 @@ void car_resetPosition( car_t* car);
|
|||
|
||||
void update_track( track_t* tck, car_t* car );
|
||||
|
||||
void process_main_track( track_t* tck, car_t* car );
|
||||
|
||||
void process_aux_track( track_t* tck, car_t* car );
|
||||
|
||||
void box_init( track_t* tck );
|
||||
|
||||
bool box_isactive( track_t* tck );
|
||||
|
||||
int tracklen_configure( track_t* tck, int nled );
|
||||
|
||||
int boxlen_configure( track_t* tck, int box_len );
|
||||
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn );
|
||||
|
||||
int physic_configure( track_t* tck, float kgp, float kfp );
|
||||
|
||||
int track_configure( track_t* tck, int init_box );
|
||||
|
||||
|
@ -86,7 +92,7 @@ void ramp_init( track_t* tck );
|
|||
|
||||
bool ramp_isactive( track_t* tck );
|
||||
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high );
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high, int alwaysOn );
|
||||
|
||||
int race_configure( track_t* tck, int startline, int nlap, int nrepeat, int finishline );
|
||||
|
||||
|
|
|
@ -11,13 +11,16 @@ void param_setdefault( struct cfgparam* cfg ) {
|
|||
cfg->ramp.init = 80;
|
||||
cfg->ramp.center = 90;
|
||||
cfg->ramp.end = 100;
|
||||
cfg->ramp.high = 3;
|
||||
cfg->ramp.high = 6;
|
||||
cfg->ramp.alwaysOn = false;
|
||||
|
||||
cfg->track.nled_total = MAXLED;
|
||||
cfg->track.nled_main = MAXLED; // 240 when boxes length = 60
|
||||
cfg->track.nled_aux = 0; // 60
|
||||
cfg->track.init_aux = -1; // 239
|
||||
cfg->track.box_len = BOXLEN;
|
||||
cfg->track.kf = 0.015; //friction constant
|
||||
cfg->track.kg = 0.003; //gravity constant
|
||||
cfg->track.box_alwaysOn = false;
|
||||
|
||||
cfg->track.kf = 0.015; // friction constant
|
||||
cfg->track.kg = 0.006; // gravity constant - Used in Slope
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
#ifndef _OLR_SERIAL_LIB_h
|
||||
#define _OLR_SERIAL_LIB_h
|
||||
#ifndef _OLR_PARAM_LIB_h
|
||||
#define _OLR_PARAM_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
|
@ -19,7 +18,7 @@ extern "C"{
|
|||
|
||||
enum{
|
||||
LEN_UID = 16,
|
||||
CFG_VER = 4, // "4" in V0.9.d
|
||||
CFG_VER = 5, // "5" in V0.9.6 (manage "permanent" param for Box and Slope)
|
||||
};
|
||||
|
||||
struct cfgrace{
|
||||
|
@ -39,6 +38,8 @@ struct cfgtrack {
|
|||
// it's not possible to implicitly store it in nled_main,nled_aux
|
||||
// because, if these are different to the default, box gets always activated
|
||||
// (the software does not chek "box_isactive" to draw car position)
|
||||
bool box_alwaysOn; // added in ver 0.9.6
|
||||
|
||||
|
||||
float kf;
|
||||
float kg;
|
||||
|
@ -50,6 +51,7 @@ struct cfgramp {
|
|||
int center;
|
||||
int end;
|
||||
int high;
|
||||
bool alwaysOn; // added in ver 0.9.6
|
||||
};
|
||||
|
||||
struct brdinfo {
|
||||
|
|
File diff suppressed because it is too large
Load diff
74
open-led-race_0.0/SerialCommand.cpp
Normal file
74
open-led-race_0.0/SerialCommand.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "SerialCommand.h"
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
SerialCommand::SerialCommand() {
|
||||
_initialized = false;
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
SerialCommand::SerialCommand(char *buf, int blen, char eoc, Stream* stream) {
|
||||
init(buf, blen, eoc, stream);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void SerialCommand::init(char *buf, int blen, char eoc, Stream* stream) {
|
||||
if(_initialized) return;
|
||||
|
||||
_stream = stream == NULL ? &Serial : stream;
|
||||
_buf = buf;
|
||||
_bufLen = blen;
|
||||
_bufIdx = 0;
|
||||
_eoc = eoc;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int SerialCommand::getCommand() {
|
||||
if( !_initialized) return(-1);
|
||||
|
||||
while (_stream->available()) {
|
||||
if(_bufIdx < _bufLen - 2) {
|
||||
char data = _stream->read();
|
||||
if(data == _eoc) {
|
||||
int cmsSize=_bufIdx;
|
||||
_buf[_bufIdx++] = '\0';
|
||||
_bufIdx=0;
|
||||
return(cmsSize);
|
||||
} else {
|
||||
_buf[_bufIdx++] = data;
|
||||
}
|
||||
} else {
|
||||
// buffer full
|
||||
// re4set and retunn error
|
||||
_buf[_bufIdx++] = '\0';
|
||||
_bufIdx=0;
|
||||
return(-2);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SerialCommand::sendCommand(char* str) {
|
||||
// get command length
|
||||
int dlen=0;
|
||||
// for(; dlen<_bufLen; dlen++ ) { // limit transmitted command length to received command buffer
|
||||
for(; dlen<80; dlen++ ) { // limit transmitted command length to received command buffer
|
||||
if(*(str+dlen) == _eoc ){
|
||||
dlen++; // send EOC
|
||||
break;
|
||||
}
|
||||
}
|
||||
_stream->write(str, dlen);
|
||||
return;
|
||||
}
|
29
open-led-race_0.0/SerialCommand.h
Normal file
29
open-led-race_0.0/SerialCommand.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef SerialCommand_h
|
||||
#define SerialCommand_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class SerialCommand {
|
||||
|
||||
public:
|
||||
SerialCommand();
|
||||
SerialCommand(char *buf, int bufLen, char eoc, Stream* stream);
|
||||
void init(char *buf, int bufLen, char eoc, Stream* stream);
|
||||
int getCommand(void);
|
||||
void sendCommand(char *);
|
||||
|
||||
int overflow;
|
||||
|
||||
protected:
|
||||
Stream* _stream;
|
||||
char *_buf;
|
||||
int _bufIdx;
|
||||
int _bufLen;
|
||||
char _eoc; // EndOfCommand char
|
||||
|
||||
bool _initialized;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
36
open-led-race_0.0/SoftTimer.cpp
Normal file
36
open-led-race_0.0/SoftTimer.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "SoftTimer.h"
|
||||
|
||||
SoftTimer::SoftTimer(unsigned long tout) {
|
||||
this->set(tout);
|
||||
}
|
||||
|
||||
SoftTimer::SoftTimer() {
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void SoftTimer::set(unsigned long tout) {
|
||||
this->timeout=tout;
|
||||
}
|
||||
|
||||
|
||||
void SoftTimer::start() {
|
||||
this->startTime=millis();
|
||||
}
|
||||
|
||||
void SoftTimer::start(unsigned long tout) {
|
||||
this->set(tout);
|
||||
this->start();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
boolean SoftTimer::elapsed(){
|
||||
if((millis() - this->startTime) > this->timeout) {
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
23
open-led-race_0.0/SoftTimer.h
Normal file
23
open-led-race_0.0/SoftTimer.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef SoftTimer_h
|
||||
#define SoftTimer_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class SoftTimer {
|
||||
|
||||
public:
|
||||
|
||||
SoftTimer(void);
|
||||
SoftTimer(unsigned long);
|
||||
void set(unsigned long);
|
||||
void start(void);
|
||||
void start(unsigned long);
|
||||
boolean elapsed(void);
|
||||
|
||||
private:
|
||||
unsigned long startTime=0;
|
||||
unsigned long timeout=0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
75
open-led-race_0.0/olr-controller.c
Normal file
75
open-led-race_0.0/olr-controller.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "olr-controller.h"
|
||||
|
||||
enum {
|
||||
DELTA_ANALOG = 5,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void controller_init( controller_t* ct, enum ctr_type mode, int pin ) {
|
||||
ct->mode = mode;
|
||||
ct->pin = pin;
|
||||
ct->delta_analog = DELTA_ANALOG;
|
||||
}
|
||||
|
||||
|
||||
byte controller_getStatus( controller_t* ct ) {
|
||||
|
||||
if( ct->mode == DIGITAL_MODE ){
|
||||
return digitalRead( ct->pin );
|
||||
}
|
||||
else if( ct->mode == ANALOG_MODE ){
|
||||
ct->adc = analogRead( ct->pin );
|
||||
if( abs( ct->badc - ct->adc ) > ct->delta_analog ){
|
||||
ct->badc = ct->adc;
|
||||
return 1;
|
||||
}
|
||||
ct->badc = ct->adc;
|
||||
}
|
||||
else if( ct->mode == DEBUG_MODE ){
|
||||
ct->adc++;
|
||||
if( ct->adc >= 60){
|
||||
ct->adc = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
float controller_getSpeed( controller_t* ct) {
|
||||
float speed = 0.0;
|
||||
if ( (ct->flag_sw == 1 ) && (controller_getStatus( ct ) == 0) ) {
|
||||
ct->flag_sw = 0;
|
||||
speed = ACEL;
|
||||
}
|
||||
|
||||
if ( (ct->flag_sw == 0 ) && (controller_getStatus( ct ) == 1 ) ) {
|
||||
ct->flag_sw = 1;
|
||||
}
|
||||
return speed;
|
||||
}
|
||||
|
||||
float controller_getAccel ( void ) {
|
||||
return ACEL;
|
||||
}
|
||||
|
||||
bool controller_isActive( int pin ) {
|
||||
return !digitalRead( pin );
|
||||
}
|
58
open-led-race_0.0/olr-controller.h
Normal file
58
open-led-race_0.0/olr-controller.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef _OLR_CONTROLLER_LIB_h
|
||||
#define _OLR_CONTROLLER_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#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{
|
||||
NOT_DEFINED = 0,
|
||||
DIGITAL_MODE,
|
||||
ANALOG_MODE,
|
||||
DEBUG_MODE,
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
enum ctr_type mode;
|
||||
int pin;
|
||||
int adc;
|
||||
int badc;
|
||||
int delta_analog;
|
||||
byte flag_sw;
|
||||
}controller_t;
|
||||
|
||||
void controller_setup( void );
|
||||
|
||||
void controller_init( controller_t* ct, enum ctr_type mode, int pin );
|
||||
|
||||
byte controller_getStatus( controller_t* ct );
|
||||
|
||||
float controller_getSpeed( controller_t* ct );
|
||||
|
||||
float controller_getAccel ( void );
|
||||
|
||||
bool controller_isActive( int pin );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
197
open-led-race_0.0/olr-lib.c
Normal file
197
open-led-race_0.0/olr-lib.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include "Arduino.h"
|
||||
#include "olr-lib.h"
|
||||
|
||||
|
||||
//void process_main_track( track_t* tck, car_t* car );
|
||||
//void process_aux_track( track_t* tck, car_t* car );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void car_init( car_t* car, controller_t* ct, uint32_t color ) {
|
||||
car->ct = ct;
|
||||
car->color = color;
|
||||
car->trackID = TRACK_MAIN;
|
||||
car->speed=0;
|
||||
car->dist=0;
|
||||
car->dist_aux=0;
|
||||
}
|
||||
|
||||
void car_updateController( car_t* car ) {
|
||||
car->speed += controller_getSpeed( car->ct );
|
||||
}
|
||||
|
||||
void update_track( track_t* tck, car_t* car ) {
|
||||
controller_t* ct = car->ct;
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
|
||||
if ( car->trackID == TRACK_MAIN
|
||||
&& (int)car->dist % cfg->nled_main == (cfg->init_aux-(cfg->nled_aux))
|
||||
// && controller_getStatus( ct ) == 0 ) { //change track by switch
|
||||
&& (car->speed <= SPD_MIN_TRACK_AUX )) { //change track by low speed
|
||||
|
||||
car->trackID = TRACK_AUX;
|
||||
car->dist_aux = 0;
|
||||
}
|
||||
else if( car->trackID == TRACK_AUX
|
||||
&& car->dist_aux > cfg->nled_aux ) {
|
||||
|
||||
car->trackID = TRACK_MAIN;
|
||||
car->dist += cfg->nled_aux;
|
||||
}
|
||||
|
||||
/* Update car position in the current track */
|
||||
if ( car->trackID == TRACK_AUX ) process_aux_track( tck, car );
|
||||
else if ( car->trackID == TRACK_MAIN ) process_main_track( tck, car );
|
||||
|
||||
/* Update car lap */
|
||||
if ( car->dist > ( cfg->nled_main*car->nlap -1) ) car->nlap++;
|
||||
}
|
||||
|
||||
void process_aux_track( track_t* tck, car_t* car ){
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
if ( (int)car->dist_aux == tck->ledcoin
|
||||
&& car->speed <= controller_getAccel() ) {
|
||||
car->speed = controller_getAccel ()*50;
|
||||
tck->ledcoin = COIN_RESET;
|
||||
};
|
||||
|
||||
car->speed -= car->speed * cfg->kf;
|
||||
car->dist_aux += car->speed;
|
||||
}
|
||||
|
||||
|
||||
void process_main_track( track_t* tck, car_t* car ) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
if ( tck->rampactive ) {
|
||||
struct cfgramp const* r = &tck->cfg.ramp;
|
||||
int const pos = (int)car->dist % cfg->nled_main;
|
||||
if ( pos >= r->init && pos < r->center )
|
||||
// car->speed -= cfg->kg * r->high * ( pos - r->init );
|
||||
car->speed -= cfg->kg * r->high ;
|
||||
|
||||
if ( pos <= r->end && pos > r->center )
|
||||
//car->speed += cfg->kg * r->high * ( pos - r->center );
|
||||
car->speed += cfg->kg * r->high ;
|
||||
}
|
||||
|
||||
car->speed -= car->speed * cfg->kf;
|
||||
car->dist += car->speed;
|
||||
|
||||
}
|
||||
|
||||
void ramp_init( track_t* tck ) {
|
||||
tck->rampactive = true;
|
||||
}
|
||||
|
||||
|
||||
bool ramp_isactive( track_t* tck ) {
|
||||
return tck->rampactive;
|
||||
}
|
||||
|
||||
|
||||
void car_resetPosition( car_t* car) {
|
||||
|
||||
car->trackID = TRACK_MAIN;
|
||||
car->speed = 0;
|
||||
car->dist = 0;
|
||||
car->dist_aux = 0;
|
||||
car->nlap = 1;
|
||||
car->leaving = false;
|
||||
}
|
||||
|
||||
void box_init( track_t* tck ) {
|
||||
tck->boxactive = true;
|
||||
}
|
||||
|
||||
bool box_isactive( track_t* tck ) {
|
||||
return tck->boxactive;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int tracklen_configure( track_t* tck, int nled ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
if( nled <= 0 ) return -1;
|
||||
cfg->nled_total = nled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if ( boxalwaysOn != 0 && boxalwaysOn != 1 ) return -1;
|
||||
if( box_len <= 0 || box_len >= cfg->nled_total ) return -1;
|
||||
cfg->box_len = box_len;
|
||||
cfg->box_alwaysOn = boxalwaysOn;
|
||||
|
||||
// Update track->boxactive
|
||||
tck->boxactive = boxalwaysOn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int physic_configure( track_t* tck, float kgp, float kfp ){
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if( kgp <= 0.0 || kgp >= 2.0 ) return -1;
|
||||
if( kfp <= 0.0 || kfp >= 2.0 ) return -1;
|
||||
cfg->kf = kfp;
|
||||
cfg->kg = kgp;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int track_configure( track_t* tck, int init_box ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if(init_box >= cfg->nled_total ) return -1;
|
||||
cfg->nled_main = ( init_box == 0 ) ? cfg->nled_total : init_box;
|
||||
cfg->nled_aux = ( init_box == 0 ) ? 0 : cfg->nled_total - init_box;
|
||||
cfg->init_aux = init_box - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high, int alwaysOn ) {
|
||||
struct cfgramp* ramp = &tck->cfg.ramp;
|
||||
|
||||
if ( init >= tck->cfg.track.nled_main || init <= 0 ) return -1;
|
||||
if ( center >= tck->cfg.track.nled_main || center <= 0 ) return -2;
|
||||
if ( end >= tck->cfg.track.nled_main || end <= 0 ) return -3;
|
||||
if ( ! (center > init && center < end) ) return -4;
|
||||
if ( alwaysOn != 0 && alwaysOn != 1 ) return -5;
|
||||
|
||||
ramp->init = init;
|
||||
ramp->center = center;
|
||||
ramp->end = end;
|
||||
ramp->high = high;
|
||||
ramp->alwaysOn = alwaysOn;
|
||||
|
||||
// Update track->rampactive
|
||||
/**
|
||||
boolean rampactive = &tck->rampactive;
|
||||
rampactive = alwaysOn;
|
||||
**/
|
||||
tck->rampactive = alwaysOn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int race_configure( track_t* tck, int startline, int nlap, int nrepeat, int finishline ) {
|
||||
struct cfgrace* race = &tck->cfg.race;
|
||||
|
||||
if ( startline != 0 && startline != 1 ) return -1;
|
||||
if ( finishline != 0 && finishline != 1 ) return -1;
|
||||
race->startline = startline;
|
||||
race->finishline = finishline;
|
||||
race->nlap = nlap;
|
||||
race->nrepeat = nrepeat;
|
||||
return 0;
|
||||
}
|
103
open-led-race_0.0/olr-lib.h
Normal file
103
open-led-race_0.0/olr-lib.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
|
||||
#ifndef _OLR_LIB_h
|
||||
#define _OLR_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "olr-controller.h"
|
||||
#include "olr-param.h"
|
||||
|
||||
#define SPD_MIN_TRACK_AUX 0.8
|
||||
|
||||
enum stcoin{
|
||||
COIN_RESET = -2,
|
||||
COIN_WAIT = -1,
|
||||
};
|
||||
|
||||
enum{
|
||||
NOT_TRACK = 0,
|
||||
TRACK_MAIN,
|
||||
TRACK_AUX,
|
||||
TRACK_IN,
|
||||
TRACK_OUT,
|
||||
NUM_TRACKS,
|
||||
};
|
||||
|
||||
|
||||
enum status{
|
||||
CAR_WAITING = 0,
|
||||
CAR_COMING,
|
||||
CAR_ENTER,
|
||||
CAR_RACING,
|
||||
CAR_LEAVING,
|
||||
CAR_GO_OUT,
|
||||
CAR_FINISH
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
controller_t* ct;
|
||||
float speed;
|
||||
float dist;
|
||||
float dist_aux;
|
||||
byte nlap;
|
||||
byte repeats;
|
||||
uint32_t color;
|
||||
int trackID;
|
||||
enum status st;
|
||||
bool leaving;
|
||||
}car_t;
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct cfgparam cfg;
|
||||
int ledcoin; //LED_SPEED_COIN
|
||||
uint32_t ledtime;
|
||||
bool rampactive;
|
||||
bool boxactive;
|
||||
}track_t;
|
||||
|
||||
|
||||
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 update_track( track_t* tck, car_t* car );
|
||||
|
||||
void process_main_track( track_t* tck, car_t* car );
|
||||
|
||||
void process_aux_track( track_t* tck, car_t* car );
|
||||
|
||||
void box_init( track_t* tck );
|
||||
|
||||
bool box_isactive( track_t* tck );
|
||||
|
||||
int tracklen_configure( track_t* tck, int nled );
|
||||
|
||||
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn );
|
||||
|
||||
int physic_configure( track_t* tck, float kgp, float kfp );
|
||||
|
||||
int track_configure( track_t* tck, int init_box );
|
||||
|
||||
void ramp_init( track_t* tck );
|
||||
|
||||
bool ramp_isactive( track_t* tck );
|
||||
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high, int alwaysOn );
|
||||
|
||||
int race_configure( track_t* tck, int startline, int nlap, int nrepeat, int finishline );
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
26
open-led-race_0.0/olr-param.c
Normal file
26
open-led-race_0.0/olr-param.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "olr-param.h"
|
||||
|
||||
void param_setdefault( struct cfgparam* cfg ) {
|
||||
cfg->setted = true;
|
||||
|
||||
cfg->race.startline = true;
|
||||
cfg->race.nlap = NUMLAP;
|
||||
cfg->race.nrepeat = 1;
|
||||
cfg->race.finishline = true;
|
||||
|
||||
cfg->ramp.init = 80;
|
||||
cfg->ramp.center = 90;
|
||||
cfg->ramp.end = 100;
|
||||
cfg->ramp.high = 6;
|
||||
cfg->ramp.alwaysOn = false;
|
||||
|
||||
cfg->track.nled_total = MAXLED;
|
||||
cfg->track.nled_main = MAXLED; // 240 when boxes length = 60
|
||||
cfg->track.nled_aux = 0; // 60
|
||||
cfg->track.init_aux = -1; // 239
|
||||
cfg->track.box_len = BOXLEN;
|
||||
cfg->track.box_alwaysOn = false;
|
||||
|
||||
cfg->track.kf = 0.015; // friction constant
|
||||
cfg->track.kg = 0.006; // gravity constant - Used in Slope
|
||||
}
|
77
open-led-race_0.0/olr-param.h
Normal file
77
open-led-race_0.0/olr-param.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef _OLR_SERIAL_LIB_h
|
||||
#define _OLR_SERIAL_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAXLED 300
|
||||
#define BOXLEN 60
|
||||
#define NUMLAP 5
|
||||
|
||||
|
||||
|
||||
enum{
|
||||
LEN_UID = 16,
|
||||
CFG_VER = 5, // "5" in V0.9.6 (manage "permanent" param for Box and Slope)
|
||||
};
|
||||
|
||||
struct cfgrace{
|
||||
bool startline; // Used only in OLRNetwork
|
||||
int nlap;
|
||||
int nrepeat; // Used only in OLRNetwork
|
||||
bool finishline; // Used only in OLRNetwork
|
||||
};
|
||||
|
||||
//
|
||||
struct cfgtrack {
|
||||
int nled_total;
|
||||
int nled_main;
|
||||
int nled_aux;
|
||||
int init_aux;
|
||||
int box_len; // used to hold the Box Length if the default get changed.
|
||||
// it's not possible to implicitly store it in nled_main,nled_aux
|
||||
// because, if these are different to the default, box gets always activated
|
||||
// (the software does not chek "box_isactive" to draw car position)
|
||||
bool box_alwaysOn; // added in ver 0.9.6
|
||||
|
||||
|
||||
float kf;
|
||||
float kg;
|
||||
};
|
||||
|
||||
// ramp centred in LED 100 with 10 led fordward and 10 backguard
|
||||
struct cfgramp {
|
||||
int init;
|
||||
int center;
|
||||
int end;
|
||||
int high;
|
||||
bool alwaysOn; // added in ver 0.9.6
|
||||
};
|
||||
|
||||
struct brdinfo {
|
||||
char uid[LEN_UID + 1];
|
||||
};
|
||||
|
||||
struct cfgparam {
|
||||
bool setted;
|
||||
struct cfgrace race; // added in ver 0.9.d
|
||||
struct cfgtrack track;
|
||||
struct cfgramp ramp;
|
||||
struct brdinfo info;
|
||||
};
|
||||
|
||||
|
||||
void param_setdefault( struct cfgparam* cfg );
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
945
open-led-race_0.0/open-led-race_0.0.ino
Normal file
945
open-led-race_0.0/open-led-race_0.0.ino
Normal file
|
@ -0,0 +1,945 @@
|
|||
/*
|
||||
* ____ _ ______ _____ _____
|
||||
/ __ \ | | | ____| __ \ | __ \
|
||||
| | | |_ __ ___ _ __ | | | |__ | | | | | |__) |__ _ ___ ___
|
||||
| | | | '_ \ / _ \ '_ \ | | | __| | | | | | _ // _` |/ __/ _ \
|
||||
| |__| | |_) | __/ | | | | |____| |____| |__| | | | \ \ (_| | (_| __/
|
||||
\____/| .__/ \___|_| |_| |______|______|_____/ |_| \_\__,_|\___\___|
|
||||
| |
|
||||
|_|
|
||||
Open LED Race
|
||||
An minimalist cars race for LED strip
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
First public version by:
|
||||
Angel Maldonado (https://gitlab.com/angeljmc)
|
||||
Gerardo Barbarov (gbarbarov AT singulardevices DOT com)
|
||||
|
||||
Basen on original idea and 2 players code by:
|
||||
Gerardo Barbarov for Arduino day Seville 2019
|
||||
https://github.com/gbarbarov/led-race
|
||||
|
||||
|
||||
Public Repository for this code:
|
||||
https://gitlab.com/open-led-race/olr-arduino
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// 2020/12/10 - Ver 0.9.6
|
||||
// --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.6";
|
||||
|
||||
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include <EEPROM.h>
|
||||
#include "olr-lib.h"
|
||||
#include "olr-param.h"
|
||||
#include "SoftTimer.h"
|
||||
#include "SerialCommand.h"
|
||||
|
||||
#define PIN_LED 2 // R 500 ohms to DI pin for WS2812 and WS2813, for WS2813 BI pin of first LED to GND , CAP 1000 uF to VCC 5v/GND,power supplie 5V 2A
|
||||
#define PIN_AUDIO 3 // through CAP 2uf to speaker 8 ohms
|
||||
|
||||
#define 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(0,255,255)
|
||||
#define COLOR_BOXMARKS track.Color(64,64,0)
|
||||
#define LED_SEMAPHORE 12
|
||||
|
||||
#define CONTDOWN_PHASE_DURATION 2000
|
||||
#define CONTDOWN_STARTSOUND_DURATION 40
|
||||
|
||||
#define NEWRACE_DELAY 5000
|
||||
|
||||
#define REC_COMMAND_BUFLEN 32 // Received command buffer length
|
||||
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;
|
||||
|
||||
char txbuff[64];
|
||||
|
||||
|
||||
|
||||
|
||||
static unsigned long lastmillis = 0;
|
||||
|
||||
SoftTimer customDelay = SoftTimer(); // non blocking delay()
|
||||
|
||||
// Used to manage countdown phases
|
||||
int countdown_phase=1;
|
||||
bool countdown_new_phase=true;
|
||||
|
||||
|
||||
int win_music[] = {
|
||||
2637, 2637, 0, 2637,
|
||||
0, 2093, 2637, 0,
|
||||
3136
|
||||
};
|
||||
|
||||
//int TBEEP=3;
|
||||
|
||||
char tracksID[ NUM_TRACKS ][2] ={"U","M","B","I","O"};
|
||||
|
||||
/* ----------- Function prototypes ------------------- */
|
||||
|
||||
void sendResponse( ack_t *ack);
|
||||
//ack_t parseCommands(AsyncSerial &serial);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
char cmd[REC_COMMAND_BUFLEN]; // Stores command received by ReadSerialComand()
|
||||
SerialCommand serialCommand = SerialCommand(cmd, REC_COMMAND_BUFLEN, EOL, &Serial); // get complete command from serial
|
||||
|
||||
|
||||
Adafruit_NeoPixel track;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
randomSeed( analogRead(A6) + analogRead(A7) );
|
||||
controller_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 );
|
||||
|
||||
race.numcars = 2;
|
||||
|
||||
if( controller_isActive( DIG_CONTROL_3 )) {
|
||||
controller_init( &switchs[2], DIGITAL_MODE, DIG_CONTROL_3 );
|
||||
car_init( &cars[2], &switchs[2], COLOR3 );
|
||||
++race.numcars;
|
||||
}
|
||||
|
||||
if( controller_isActive( DIG_CONTROL_4 )) {
|
||||
controller_init( &switchs[3], DIGITAL_MODE, DIG_CONTROL_4 );
|
||||
car_init( &cars[3], &switchs[3], COLOR4 );
|
||||
++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 || tck.cfg.track.box_alwaysOn ) { //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 );
|
||||
} else{
|
||||
track_configure( &tck, 0 );
|
||||
}
|
||||
|
||||
if( digitalRead(DIG_CONTROL_1)==0 || tck.cfg.ramp.alwaysOn ) { //push switch 1 on reset for activate physics
|
||||
ramp_init( &tck );
|
||||
draw_ramp( &tck );
|
||||
track.show();
|
||||
delay(2000);
|
||||
if ( digitalRead( DIG_CONTROL_1 ) == 0 ) { //retain push switch on reset for activate FX sound
|
||||
SMOTOR=1;
|
||||
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;
|
||||
|
||||
customDelay.start(0); // first race starts with no delay
|
||||
race.phase = READY;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void loop() {
|
||||
|
||||
// look for commands received on serial
|
||||
ack_t ack = manageSerialCommand();
|
||||
if(ack.rp != NOTHING){
|
||||
sendResponse(&ack);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// string sent by the host is longer, it gets lost)
|
||||
// In other phases (READY, RACING, etc) ONLY 2 bytes are guaranteed to be
|
||||
// succesfully received - So "Enter Configuration Mode" command is just one byte (@)
|
||||
|
||||
if ( race.phase == CONFIG ) {
|
||||
if( race.newcfg ) {
|
||||
race.newcfg = false;
|
||||
countdownReset();
|
||||
customDelay.start(0);
|
||||
race.phase = READY;
|
||||
send_phase( race.phase );
|
||||
}
|
||||
}
|
||||
else if ( race.phase == READY ) {
|
||||
|
||||
if(customDelay.elapsed()) {
|
||||
for( int i = 0; i < race.numcars; ++i) {
|
||||
car_resetPosition( &cars[i] );
|
||||
cars[i].repeats = 0;
|
||||
}
|
||||
tck.ledcoin = COIN_RESET;
|
||||
race.phase = COUNTDOWN;
|
||||
send_phase( race.phase );
|
||||
}
|
||||
}
|
||||
else if( race.phase == COUNTDOWN ) {
|
||||
|
||||
if( race.cfg.startline ){
|
||||
// Countdown: semaphore and tones
|
||||
if(start_race_done()) {
|
||||
// Countdown done
|
||||
for( int i = 0; i < race.numcars; ++i ) {
|
||||
cars[i].st = CAR_ENTER;
|
||||
}
|
||||
race.phase = RACING;
|
||||
send_phase( race.phase );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( race.phase == RACING ) {
|
||||
|
||||
strip_clear( &tck );
|
||||
|
||||
if( box_isactive( &tck ) ) {
|
||||
if( tck.ledcoin == COIN_RESET ) {
|
||||
tck.ledcoin = COIN_WAIT;
|
||||
tck.ledtime = millis() + random(2000,7000);
|
||||
}
|
||||
if( tck.ledcoin > 0 )
|
||||
draw_coin( &tck );
|
||||
else if( millis() > tck.ledtime )
|
||||
tck.ledcoin = random( 20, tck.cfg.track.nled_aux - 20 );
|
||||
}
|
||||
|
||||
if( ramp_isactive( &tck ) )
|
||||
draw_ramp( &tck );
|
||||
if( box_isactive( &tck ) )
|
||||
draw_box_entrypoint( &tck );
|
||||
|
||||
for( int i = 0; i < race.numcars; ++i ) {
|
||||
run_racecycle( &cars[i], i );
|
||||
if( cars[i].st == CAR_FINISH ) {
|
||||
race.phase = COMPLETE;
|
||||
race.winner = i;
|
||||
send_phase( race.phase );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
track.show();
|
||||
if (SMOTOR==1) tone(PIN_AUDIO,FBEEP+int(cars[0].speed*440*1)+int(cars[1].speed*440*2)+int(cars[2].speed*440*3)+int(cars[3].speed*440*4));
|
||||
if (TBEEP>0) {TBEEP--;} else {FBEEP=0;};
|
||||
|
||||
/* Print p command!!! */
|
||||
unsigned long nowmillis = millis();
|
||||
if( abs( nowmillis - lastmillis ) > 100 ){
|
||||
lastmillis = nowmillis;
|
||||
print_cars_positions( cars );
|
||||
}
|
||||
/* ---------------- */
|
||||
}
|
||||
else if( race.phase == COMPLETE ) {
|
||||
strip_clear( &tck );
|
||||
track.show();
|
||||
if ( race.cfg.finishline ){
|
||||
draw_winner( &tck, cars[race.winner].color );
|
||||
sound_winner( &tck, race.winner );
|
||||
strip_clear( &tck );
|
||||
}
|
||||
track.show();
|
||||
customDelay.start(NEWRACE_DELAY);
|
||||
race.phase = READY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void send_phase( int phase ) {
|
||||
sprintf(txbuff, "R%d%c",phase,EOL);
|
||||
serialCommand.sendCommand(txbuff);
|
||||
}
|
||||
|
||||
|
||||
void run_racecycle( car_t *car, int i ) {
|
||||
struct cfgtrack const* cfg = &tck.cfg.track;
|
||||
|
||||
if( car->st == CAR_ENTER ) {
|
||||
car_resetPosition( car );
|
||||
if( car->repeats < race.cfg.nrepeat )
|
||||
car->st = CAR_RACING;
|
||||
else
|
||||
car->st = CAR_GO_OUT;
|
||||
}
|
||||
|
||||
if( car->st == CAR_RACING ) {
|
||||
update_track( &tck, car );
|
||||
car_updateController( car );
|
||||
draw_car( &tck, car );
|
||||
|
||||
if( car->nlap == race.cfg.nlap
|
||||
&& !car->leaving
|
||||
&& 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;
|
||||
car->st = CAR_GO_OUT;
|
||||
}
|
||||
|
||||
if( car->repeats >= race.cfg.nrepeat
|
||||
&& race.cfg.finishline ) {
|
||||
car->st = CAR_FINISH;
|
||||
}
|
||||
}
|
||||
|
||||
if ( car->st == CAR_FINISH ){
|
||||
car->trackID = NOT_TRACK;
|
||||
sprintf( txbuff, "w%d%c", i + 1, EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
|
||||
car_resetPosition( car );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int get_relative_position( car_t* car ) {
|
||||
enum{
|
||||
MIN_RPOS = 0,
|
||||
MAX_RPOS = 99,
|
||||
};
|
||||
struct cfgtrack const* cfg = &tck.cfg.track;
|
||||
int trackdist = 0;
|
||||
int pos = 0;
|
||||
|
||||
switch ( car->trackID ){
|
||||
case TRACK_MAIN:
|
||||
trackdist = (int)car->dist % cfg->nled_main;
|
||||
pos = map(trackdist, 0, cfg->nled_main -1, MIN_RPOS, MAX_RPOS);
|
||||
break;
|
||||
case TRACK_AUX:
|
||||
trackdist = (int)car->dist_aux;
|
||||
pos = map(trackdist, 0, cfg->nled_aux -1, MIN_RPOS, MAX_RPOS);
|
||||
break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
void print_cars_positions( car_t* cars ) {
|
||||
|
||||
bool outallcar = true;
|
||||
for( int i = 0; i < race.numcars; ++i)
|
||||
outallcar &= cars[i].st == CAR_WAITING;
|
||||
|
||||
if ( outallcar ) return;
|
||||
|
||||
for( int i = 0; i < race.numcars; ++i ) {
|
||||
int const rpos = get_relative_position( &cars[i] );
|
||||
sprintf( txbuff, "p%d%s%d,%d%c", i + 1, tracksID[cars[i].trackID], cars[i].nlap, rpos, EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* non-blocking version
|
||||
*/
|
||||
boolean start_race_done( ) {
|
||||
if(countdown_new_phase){
|
||||
countdown_new_phase=false;
|
||||
customDelay.start(CONTDOWN_PHASE_DURATION);
|
||||
strip_clear( &tck );
|
||||
if(ramp_isactive( &tck )) draw_ramp( &tck );
|
||||
if(box_isactive( &tck )) draw_box_entrypoint( &tck );
|
||||
switch(countdown_phase) {
|
||||
case 1:
|
||||
tone(PIN_AUDIO,400);
|
||||
track.setPixelColor(LED_SEMAPHORE, track.Color(255,0,0));
|
||||
break;
|
||||
case 2:
|
||||
tone(PIN_AUDIO,600);
|
||||
track.setPixelColor(LED_SEMAPHORE, track.Color(0,0,0));
|
||||
track.setPixelColor(LED_SEMAPHORE-1, track.Color(255,255,0));
|
||||
break;
|
||||
case 3:
|
||||
tone(PIN_AUDIO,1200);
|
||||
track.setPixelColor(LED_SEMAPHORE-1, track.Color(0,0,0));
|
||||
track.setPixelColor(LED_SEMAPHORE-2, track.Color(0,255,0));
|
||||
break;
|
||||
case 4:
|
||||
customDelay.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));
|
||||
break;
|
||||
case 5:
|
||||
noTone(PIN_AUDIO);
|
||||
countdownReset(); // reset for next countdown
|
||||
return(true);
|
||||
}
|
||||
track.show();
|
||||
}
|
||||
if(customDelay.elapsed()) {
|
||||
noTone(PIN_AUDIO);
|
||||
countdown_new_phase=true;
|
||||
countdown_phase++;
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void countdownReset() {
|
||||
countdown_phase=1;
|
||||
countdown_new_phase=true;
|
||||
}
|
||||
|
||||
|
||||
void sound_winner( track_t* tck, int winner ) {
|
||||
int const msize = sizeof(win_music) / sizeof(int);
|
||||
for (int note = 0; note < msize; note++) {
|
||||
tone(PIN_AUDIO, win_music[note],200);
|
||||
delay(230);
|
||||
noTone(PIN_AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void strip_clear( track_t* tck ) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
for( int i=0; i < cfg->nled_main; i++)
|
||||
track.setPixelColor( i, track.Color(0,0,0) );
|
||||
|
||||
for( int i=0; i < cfg->nled_aux; i++)
|
||||
track.setPixelColor( cfg->nled_main+i, track.Color(0,0,0) );
|
||||
}
|
||||
|
||||
|
||||
void draw_coin( track_t* tck ) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
track.setPixelColor( 1 + cfg->nled_main + cfg->nled_aux - tck->ledcoin,COLOR_COIN );
|
||||
}
|
||||
|
||||
void draw_winner( track_t* tck, uint32_t color) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
for(int i=16; i < cfg->nled_main; i=i+(8 * cfg->nled_main / 300 )){
|
||||
track.setPixelColor( i , color );
|
||||
track.setPixelColor( i-16 ,0 );
|
||||
track.show();
|
||||
}
|
||||
}
|
||||
|
||||
void draw_car( track_t* tck, car_t* car ) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
switch ( car->trackID ){
|
||||
case TRACK_MAIN:
|
||||
for(int i=0; i<= car->nlap; ++i )
|
||||
track.setPixelColor( ((word)car->dist % cfg->nled_main) + i, car->color );
|
||||
break;
|
||||
case TRACK_AUX:
|
||||
for(int i=0; i<= car->nlap; ++i )
|
||||
track.setPixelColor( (word)(cfg->nled_main + cfg->nled_aux - car->dist_aux) + i, car->color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display on LED Strip current values for Slope and Pitlane
|
||||
*
|
||||
*/
|
||||
void show_cfgpars_onstrip(){
|
||||
strip_clear( &tck );
|
||||
if( ramp_isactive( &tck ) )
|
||||
draw_ramp( &tck );
|
||||
if( box_isactive( &tck ) )
|
||||
draw_box_entrypoint( &tck );
|
||||
track.show();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void draw_ramp( track_t* _tck ) {
|
||||
struct cfgramp const* r = &_tck->cfg.ramp;
|
||||
byte dist = 0;
|
||||
byte intensity = 0;
|
||||
for( int i = r->init; i <= r->center; ++i ) {
|
||||
dist = r->center - r->init;
|
||||
intensity = ( 32 * (i - r->init) ) / dist;
|
||||
track.setPixelColor( i, track.Color( intensity,0,intensity ) );
|
||||
}
|
||||
for( int i = r->center; i <= r->end; ++i ) {
|
||||
dist = r->end - r->center;
|
||||
intensity = ( 32 * ( r->end - i ) ) / dist;
|
||||
track.setPixelColor( i, track.Color( intensity,0,intensity ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void draw_box_entrypoint( track_t* _tck ) {
|
||||
struct cfgtrack const* cfg = &_tck->cfg.track;
|
||||
int out = cfg->nled_total - cfg->box_len; // Pit lane exit (race start)
|
||||
int in = out - cfg->box_len; // Pit lane Entrance
|
||||
track.setPixelColor(in ,COLOR_BOXMARKS );
|
||||
track.setPixelColor(out ,COLOR_BOXMARKS );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Check Serial to see if there is a command ready to be processed
|
||||
*
|
||||
*/
|
||||
ack_t manageSerialCommand() {
|
||||
|
||||
ack_t ack = { .rp = NOTHING, .type = '\0' };
|
||||
|
||||
int clen = serialCommand.getCommand();
|
||||
|
||||
|
||||
if(clen == 0) {
|
||||
// No commands received
|
||||
return ack;
|
||||
} else if(clen < 0) {
|
||||
// Error receiving command
|
||||
sprintf( txbuff, "Error reading serial command:[%d]",clen);
|
||||
printdebug( txbuff, WARNING );
|
||||
}
|
||||
|
||||
// clen > 0 ---> Command with length=clen stored in cmd[].
|
||||
|
||||
ack.rp=NOK;
|
||||
|
||||
|
||||
if( cmd[0] == '#' ) {
|
||||
ack.type = cmd[0];
|
||||
sprintf( txbuff, "#%c", EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
ack.rp = NOTHING;
|
||||
}
|
||||
|
||||
else if( cmd[0] == '@' ) { // Enter "Configuration Mode" status
|
||||
ack.type = cmd[0];
|
||||
if(race.phase == CONFIG) { // Board already in "Configure Mode"
|
||||
; // ignore command (will return @OK)
|
||||
} else {
|
||||
race.phase = CONFIG;
|
||||
enter_configuration_mode();
|
||||
}
|
||||
ack.rp = OK;
|
||||
}
|
||||
|
||||
else if( cmd[0] == '~' ) { // Exit "Configure Mode"
|
||||
ack.type = cmd[0];
|
||||
if(race.phase == CONFIG) {
|
||||
race.newcfg = true;
|
||||
} else { // Board NOT in configuration mode
|
||||
; // ignore command (will return ~OK)
|
||||
}
|
||||
ack.rp = OK;
|
||||
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'R' ) {
|
||||
ack.type = cmd[0];
|
||||
int const phase = atoi( cmd + 1);
|
||||
if( 0 > phase || RACE_PHASES <= phase) return ack;
|
||||
race.phase = (enum phases) phase;
|
||||
ack.rp = OK;
|
||||
if ( race.phase == CONFIG ) { // accept R1 as a EnterConfigurationMode command - DEPRECATED
|
||||
enter_configuration_mode();
|
||||
}
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'C' ) { //Parse race configuration -> C1,2,3,0
|
||||
ack.type = cmd[0];
|
||||
|
||||
char * pch = strtok (cmd,"C");
|
||||
if( !pch ) return ack;
|
||||
|
||||
pch = strtok (pch, "," );
|
||||
if( !pch ) return ack;
|
||||
int startline = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, ",");
|
||||
if( !pch ) return ack;
|
||||
int nlap = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, ",");
|
||||
if( !pch ) return ack;
|
||||
int nrepeat = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, ",");
|
||||
if( !pch ) return ack;
|
||||
//cfg.finishline = atoi( pch );
|
||||
int finishline = atoi( pch );
|
||||
|
||||
int err = race_configure( &tck, startline, nlap, nrepeat, finishline);
|
||||
if( err ) return ack;
|
||||
|
||||
race.cfg.startline = tck.cfg.race.startline;
|
||||
race.cfg.nlap = tck.cfg.race.nlap;
|
||||
race.cfg.nrepeat = tck.cfg.race.nrepeat;
|
||||
race.cfg.finishline = tck.cfg.race.finishline;
|
||||
|
||||
ack.rp = OK;
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'T' ) { //Parse Track configuration -> Track length
|
||||
ack.type = cmd[0];
|
||||
|
||||
char * pch = strtok (cmd,"T");
|
||||
if( !pch ) return ack;
|
||||
|
||||
int nled = atoi( cmd + 1 );
|
||||
int err = tracklen_configure( &tck, nled);
|
||||
if( err ) return ack;
|
||||
track_configure( &tck, 0);
|
||||
if( err ) return ack;
|
||||
|
||||
ack.rp = OK;
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'B' ) { //Parse BoxLenght Configuration -> Alen,perm
|
||||
ack.type = cmd[0];
|
||||
|
||||
char * pch = strtok (cmd,"B");
|
||||
if( !pch ) return ack;
|
||||
|
||||
pch = strtok (pch, "," );
|
||||
if( !pch ) return ack;
|
||||
int boxlen = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, "," );
|
||||
if( !pch ) return ack;
|
||||
int boxperm = atoi( pch );
|
||||
|
||||
int err = boxlen_configure( &tck, boxlen, boxperm );
|
||||
if( err ) return ack;
|
||||
|
||||
ack.rp = OK;
|
||||
|
||||
// Force Pitlane ON, so "show_cfgpars_onstrip()"
|
||||
// will show the new values, even if AlwaysON=false
|
||||
box_init(&tck);
|
||||
show_cfgpars_onstrip();
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'A' ) { // Parse Ramp configuration -> Astart,center,end,high,perm
|
||||
ack.type = cmd[0];
|
||||
|
||||
char * pch = strtok (cmd,"A");
|
||||
if( !pch ) return ack;
|
||||
|
||||
pch = strtok (pch, "," );
|
||||
if( !pch ) return ack;
|
||||
int init = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, "," );
|
||||
if( !pch ) return ack;
|
||||
int center = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, "," );
|
||||
if( !pch ) return ack;
|
||||
int end = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, ",");
|
||||
if( !pch ) return ack;
|
||||
int high = atoi( pch );
|
||||
|
||||
pch = strtok (NULL, ",");
|
||||
if( !pch ) return ack;
|
||||
int slopeperm = atoi( pch );
|
||||
|
||||
int err = ramp_configure( &tck, init, center, end, high, slopeperm );
|
||||
if( err ) return ack;
|
||||
ack.rp = OK;
|
||||
|
||||
// Force Ramp ON, so "show_cfgpars_onstrip()"
|
||||
// will show the new values, even if AlwaysON=false
|
||||
ramp_init(&tck);
|
||||
|
||||
show_cfgpars_onstrip();
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'K' ) { // Parse Physic simulation parameters
|
||||
ack.type = cmd[0];
|
||||
|
||||
char * pch = strtok (cmd,"K");
|
||||
if( !pch ) return ack;
|
||||
|
||||
pch = strtok (pch, "," );
|
||||
if( !pch ) return ack;
|
||||
float kgp = atof( pch );
|
||||
|
||||
pch = strtok (NULL, "," );
|
||||
if( !pch ) return ack;
|
||||
float kfp = atof( pch );
|
||||
|
||||
int err = physic_configure( &tck, kgp, kfp );
|
||||
if( err ) return ack;
|
||||
|
||||
ack.rp = OK;
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'D') {
|
||||
ack.type = cmd[0];
|
||||
param_setdefault( &tck.cfg );
|
||||
EEPROM.put( eeadrInfo, tck.cfg ); // Save immediately
|
||||
|
||||
printdebug( "Load default parameter values", LOG );
|
||||
|
||||
ack.rp = OK;
|
||||
|
||||
// Update box/slope active in current Track Struct with values
|
||||
// just loaded (for show_cfgpars_onstrip())
|
||||
struct cfgparam const* cfg = &tck.cfg;
|
||||
tck.boxactive = cfg->track.box_alwaysOn;
|
||||
tck.rampactive = cfg->ramp.alwaysOn;
|
||||
|
||||
show_cfgpars_onstrip();
|
||||
|
||||
}
|
||||
|
||||
else if( cmd[0] == ':' ) { // Set board Unique Id
|
||||
struct brdinfo* info = &tck.cfg.info;
|
||||
ack.type = cmd[0];
|
||||
if( strlen(cmd + 1) > LEN_UID ) return ack;
|
||||
strcpy( info->uid, cmd + 1 );
|
||||
EEPROM.put( eeadrInfo, tck.cfg ); // Save immediately
|
||||
ack.rp = OK;
|
||||
}
|
||||
|
||||
else if( cmd[0] == '$' ) { // Get Board UID
|
||||
sprintf( txbuff, "%s%s%c", "$", tck.cfg.info.uid, EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
ack.rp = NOTHING;
|
||||
}
|
||||
|
||||
else if( cmd[0] == '?' ) { // Get Software Id
|
||||
sprintf( txbuff, "%s%s%c", "?", softwareId, EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
ack.rp = NOTHING;
|
||||
}
|
||||
|
||||
else if( cmd[0] == '%' ) { // Get Software Version
|
||||
sprintf( txbuff, "%s%s%c", "%", version, EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
ack.rp = NOTHING;
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'Q' ) { // Get configuration Info
|
||||
struct cfgparam const* cfg = &tck.cfg;
|
||||
|
||||
sprintf( txbuff, "%s:%d,%d,%d,%d,%d,%d,%d.%03d,%d.%03d%c", "QTRACK",
|
||||
cfg->track.nled_total,
|
||||
cfg->track.nled_main,
|
||||
cfg->track.nled_aux,
|
||||
cfg->track.init_aux,
|
||||
cfg->track.box_len,
|
||||
cfg->track.box_alwaysOn,
|
||||
(int)cfg->track.kg, (int)(cfg->track.kg*1000)%1000, // std arduino sprintf() missing %f
|
||||
(int)cfg->track.kf, (int)(cfg->track.kf*1000)%1000, // std arduino sprintf() missing %f
|
||||
EOL );
|
||||
|
||||
serialCommand.sendCommand(txbuff);
|
||||
|
||||
sprintf( txbuff, "%s:%d,%d,%d,%d,%d%c", "QRAMP",
|
||||
cfg->ramp.init,
|
||||
cfg->ramp.center,
|
||||
cfg->ramp.end,
|
||||
cfg->ramp.high,
|
||||
cfg->ramp.alwaysOn,
|
||||
EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
|
||||
sprintf( txbuff, "%s:%d,%d,%d,%d%c", "QRACE",
|
||||
cfg->race.startline,
|
||||
cfg->race.nlap,
|
||||
cfg->race.nrepeat,
|
||||
cfg->race.finishline,
|
||||
EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
|
||||
ack.rp = NOTHING;
|
||||
}
|
||||
|
||||
else if( cmd[0] == 'W' ) { // Write configuration to EEPROM
|
||||
ack.type = cmd[0];
|
||||
EEPROM.put( eeadrInfo, tck.cfg );
|
||||
ack.rp = OK;
|
||||
}
|
||||
|
||||
return(ack);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void sendResponse( ack_t *ack) {
|
||||
if(ack->type=='\0'){
|
||||
sprintf(txbuff, "%s%c", ack->rp==OK? "OK":"NOK" , EOL );
|
||||
} else {
|
||||
sprintf(txbuff, "%c%s%c", ack->type, ack->rp==OK? "OK":"NOK" , EOL );
|
||||
}
|
||||
serialCommand.sendCommand(txbuff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send Log/Warning/Error messages to host
|
||||
*/
|
||||
void printdebug( const char * msg, int errlevel ) {
|
||||
char tmp [80];
|
||||
sprintf( tmp, "!%d,%s%c",errlevel, msg, EOL );
|
||||
serialCommand.sendCommand(tmp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* reset race parameters
|
||||
* stop sound
|
||||
*/
|
||||
void enter_configuration_mode(){
|
||||
noTone(PIN_AUDIO);
|
||||
strip_clear( &tck );
|
||||
track.show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void param_load( struct cfgparam* cfg ) {
|
||||
int cfgversion;
|
||||
int eeAdress = eeadrInfo;
|
||||
EEPROM.get( eeAdress, tck.cfg );
|
||||
eeAdress += sizeof( cfgparam );
|
||||
EEPROM.get( eeAdress, cfgversion );
|
||||
|
||||
sprintf( txbuff, "%s:%d%c", "Parameters Loaded from EEPROM - Cfg ver", cfgversion, EOL );
|
||||
//Serial.print(txbuff);
|
||||
serialCommand.sendCommand(txbuff);
|
||||
|
||||
|
||||
if ( cfgversion != CFG_VER ) {
|
||||
param_setdefault( &tck.cfg );
|
||||
eeAdress = 0;
|
||||
EEPROM.put( eeAdress, tck.cfg );
|
||||
eeAdress += sizeof( cfgparam );
|
||||
EEPROM.put( eeAdress, CFG_VER );
|
||||
sprintf( txbuff, "%s%c", "DEFAULT PAREMETRS LOADED (and Stored in EEPROM)", EOL );
|
||||
serialCommand.sendCommand(txbuff);
|
||||
|
||||
}
|
||||
|
||||
}
|
75
open-led-race_OK/olr-controller.c
Normal file
75
open-led-race_OK/olr-controller.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "olr-controller.h"
|
||||
|
||||
enum {
|
||||
DELTA_ANALOG = 5,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void controller_init( controller_t* ct, enum ctr_type mode, int pin ) {
|
||||
ct->mode = mode;
|
||||
ct->pin = pin;
|
||||
ct->delta_analog = DELTA_ANALOG;
|
||||
}
|
||||
|
||||
|
||||
byte controller_getStatus( controller_t* ct ) {
|
||||
|
||||
if( ct->mode == DIGITAL_MODE ){
|
||||
return digitalRead( ct->pin );
|
||||
}
|
||||
else if( ct->mode == ANALOG_MODE ){
|
||||
ct->adc = analogRead( ct->pin );
|
||||
if( abs( ct->badc - ct->adc ) > ct->delta_analog ){
|
||||
ct->badc = ct->adc;
|
||||
return 1;
|
||||
}
|
||||
ct->badc = ct->adc;
|
||||
}
|
||||
else if( ct->mode == DEBUG_MODE ){
|
||||
ct->adc++;
|
||||
if( ct->adc >= 60){
|
||||
ct->adc = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
float controller_getSpeed( controller_t* ct) {
|
||||
float speed = 0.0;
|
||||
if ( (ct->flag_sw == 1 ) && (controller_getStatus( ct ) == 0) ) {
|
||||
ct->flag_sw = 0;
|
||||
speed = ACEL;
|
||||
}
|
||||
|
||||
if ( (ct->flag_sw == 0 ) && (controller_getStatus( ct ) == 1 ) ) {
|
||||
ct->flag_sw = 1;
|
||||
}
|
||||
return speed;
|
||||
}
|
||||
|
||||
float controller_getAccel ( void ) {
|
||||
return ACEL;
|
||||
}
|
||||
|
||||
bool controller_isActive( int pin ) {
|
||||
return !digitalRead( pin );
|
||||
}
|
58
open-led-race_OK/olr-controller.h
Normal file
58
open-led-race_OK/olr-controller.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef _OLR_CONTROLLER_LIB_h
|
||||
#define _OLR_CONTROLLER_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#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{
|
||||
NOT_DEFINED = 0,
|
||||
DIGITAL_MODE,
|
||||
ANALOG_MODE,
|
||||
DEBUG_MODE,
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
enum ctr_type mode;
|
||||
int pin;
|
||||
int adc;
|
||||
int badc;
|
||||
int delta_analog;
|
||||
byte flag_sw;
|
||||
}controller_t;
|
||||
|
||||
void controller_setup( void );
|
||||
|
||||
void controller_init( controller_t* ct, enum ctr_type mode, int pin );
|
||||
|
||||
byte controller_getStatus( controller_t* ct );
|
||||
|
||||
float controller_getSpeed( controller_t* ct );
|
||||
|
||||
float controller_getAccel ( void );
|
||||
|
||||
bool controller_isActive( int pin );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
189
open-led-race_OK/olr-lib.c
Normal file
189
open-led-race_OK/olr-lib.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
#include "Arduino.h"
|
||||
#include "olr-lib.h"
|
||||
|
||||
|
||||
|
||||
void car_init( car_t* car, controller_t* ct, uint32_t color ) {
|
||||
car->ct = ct;
|
||||
car->color = color;
|
||||
car->trackID = TRACK_MAIN;
|
||||
car->speed=0;
|
||||
car->dist=0;
|
||||
car->dist_aux=0;
|
||||
}
|
||||
|
||||
void car_updateController( car_t* car ) {
|
||||
car->speed += controller_getSpeed( car->ct );
|
||||
}
|
||||
|
||||
void update_track( track_t* tck, car_t* car ) {
|
||||
controller_t* ct = car->ct;
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
|
||||
if ( car->trackID == TRACK_MAIN
|
||||
&& (int)car->dist % cfg->nled_main == (cfg->init_aux-(cfg->nled_aux))
|
||||
// && controller_getStatus( ct ) == 0 ) { //change track by switch
|
||||
&& (car->speed <= SPD_MIN_TRACK_AUX )) { //change track by low speed
|
||||
|
||||
car->trackID = TRACK_AUX;
|
||||
car->dist_aux = 0;
|
||||
}
|
||||
else if( car->trackID == TRACK_AUX
|
||||
&& car->dist_aux > cfg->nled_aux ) {
|
||||
|
||||
car->trackID = TRACK_MAIN;
|
||||
car->dist += cfg->nled_aux;
|
||||
}
|
||||
|
||||
/* Update car position in the current track */
|
||||
if ( car->trackID == TRACK_AUX ) process_aux_track( tck, car );
|
||||
else if ( car->trackID == TRACK_MAIN ) process_main_track( tck, car );
|
||||
|
||||
/* Update car lap */
|
||||
if ( car->dist > ( cfg->nled_main*car->nlap -1) ) car->nlap++;
|
||||
}
|
||||
|
||||
void process_aux_track( track_t* tck, car_t* car ){
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
if ( (int)car->dist_aux == tck->ledcoin
|
||||
&& car->speed <= controller_getAccel() ) {
|
||||
car->speed = controller_getAccel ()*50;
|
||||
tck->ledcoin = COIN_RESET;
|
||||
};
|
||||
|
||||
car->speed -= car->speed * cfg->kf;
|
||||
car->dist_aux += car->speed;
|
||||
}
|
||||
|
||||
|
||||
void process_main_track( track_t* tck, car_t* car ) {
|
||||
struct cfgtrack const* cfg = &tck->cfg.track;
|
||||
|
||||
if ( tck->rampactive ) {
|
||||
struct cfgramp const* r = &tck->cfg.ramp;
|
||||
int const pos = (int)car->dist % cfg->nled_main;
|
||||
if ( pos >= r->init && pos < r->center )
|
||||
// car->speed -= cfg->kg * r->high * ( pos - r->init );
|
||||
car->speed -= cfg->kg * r->high ;
|
||||
|
||||
if ( pos <= r->end && pos > r->center )
|
||||
//car->speed += cfg->kg * r->high * ( pos - r->center );
|
||||
car->speed += cfg->kg * r->high ;
|
||||
}
|
||||
|
||||
car->speed -= car->speed * cfg->kf;
|
||||
car->dist += car->speed;
|
||||
|
||||
}
|
||||
|
||||
void ramp_init( track_t* tck ) {
|
||||
tck->rampactive = true;
|
||||
}
|
||||
|
||||
|
||||
bool ramp_isactive( track_t* tck ) {
|
||||
return tck->rampactive;
|
||||
}
|
||||
|
||||
|
||||
void car_resetPosition( car_t* car) {
|
||||
|
||||
car->trackID = TRACK_MAIN;
|
||||
car->speed = 0;
|
||||
car->dist = 0;
|
||||
car->dist_aux = 0;
|
||||
car->nlap = 1;
|
||||
car->leaving = false;
|
||||
}
|
||||
|
||||
void box_init( track_t* tck ) {
|
||||
tck->boxactive = true;
|
||||
}
|
||||
|
||||
bool box_isactive( track_t* tck ) {
|
||||
return tck->boxactive;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int tracklen_configure( track_t* tck, int nled ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
if( nled <= 0 ) return -1;
|
||||
cfg->nled_total = nled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if ( boxalwaysOn != 0 && boxalwaysOn != 1 ) return -1;
|
||||
if( box_len <= 0 || box_len >= cfg->nled_total ) return -1;
|
||||
cfg->box_len = box_len;
|
||||
cfg->box_alwaysOn = boxalwaysOn;
|
||||
|
||||
// Update track->boxactive
|
||||
tck->boxactive = boxalwaysOn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int physic_configure( track_t* tck, float kgp, float kfp ){
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if( kgp <= 0.0 || kgp >= 2.0 ) return -1;
|
||||
if( kfp <= 0.0 || kfp >= 2.0 ) return -1;
|
||||
cfg->kf = kfp;
|
||||
cfg->kg = kgp;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int track_configure( track_t* tck, int init_box ) {
|
||||
struct cfgtrack* cfg = &tck->cfg.track;
|
||||
|
||||
if(init_box >= cfg->nled_total ) return -1;
|
||||
cfg->nled_main = ( init_box == 0 ) ? cfg->nled_total : init_box;
|
||||
cfg->nled_aux = ( init_box == 0 ) ? 0 : cfg->nled_total - init_box;
|
||||
cfg->init_aux = init_box - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high, int alwaysOn ) {
|
||||
struct cfgramp* ramp = &tck->cfg.ramp;
|
||||
|
||||
if ( init >= tck->cfg.track.nled_main || init <= 0 ) return -1;
|
||||
if ( center >= tck->cfg.track.nled_main || center <= 0 ) return -2;
|
||||
if ( end >= tck->cfg.track.nled_main || end <= 0 ) return -3;
|
||||
if ( ! (center > init && center < end) ) return -4;
|
||||
if ( alwaysOn != 0 && alwaysOn != 1 ) return -5;
|
||||
|
||||
ramp->init = init;
|
||||
ramp->center = center;
|
||||
ramp->end = end;
|
||||
ramp->high = high;
|
||||
ramp->alwaysOn = alwaysOn;
|
||||
|
||||
// Update track->rampactive
|
||||
/**
|
||||
boolean rampactive = &tck->rampactive;
|
||||
rampactive = alwaysOn;
|
||||
**/
|
||||
tck->rampactive = alwaysOn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int race_configure( track_t* tck, int startline, int nlap, int nrepeat, int finishline ) {
|
||||
struct cfgrace* race = &tck->cfg.race;
|
||||
|
||||
if ( startline != 0 && startline != 1 ) return -1;
|
||||
if ( finishline != 0 && finishline != 1 ) return -1;
|
||||
race->startline = startline;
|
||||
race->finishline = finishline;
|
||||
race->nlap = nlap;
|
||||
race->nrepeat = nrepeat;
|
||||
return 0;
|
||||
}
|
103
open-led-race_OK/olr-lib.h
Normal file
103
open-led-race_OK/olr-lib.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
|
||||
#ifndef _OLR_LIB_h
|
||||
#define _OLR_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "olr-controller.h"
|
||||
#include "olr-param.h"
|
||||
|
||||
#define SPD_MIN_TRACK_AUX 0.8
|
||||
|
||||
enum stcoin{
|
||||
COIN_RESET = -2,
|
||||
COIN_WAIT = -1,
|
||||
};
|
||||
|
||||
enum{
|
||||
NOT_TRACK = 0,
|
||||
TRACK_MAIN,
|
||||
TRACK_AUX,
|
||||
TRACK_IN,
|
||||
TRACK_OUT,
|
||||
NUM_TRACKS,
|
||||
};
|
||||
|
||||
|
||||
enum status{
|
||||
CAR_WAITING = 0,
|
||||
CAR_COMING,
|
||||
CAR_ENTER,
|
||||
CAR_RACING,
|
||||
CAR_LEAVING,
|
||||
CAR_GO_OUT,
|
||||
CAR_FINISH
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
controller_t* ct;
|
||||
float speed;
|
||||
float dist;
|
||||
float dist_aux;
|
||||
byte nlap;
|
||||
byte repeats;
|
||||
uint32_t color;
|
||||
int trackID;
|
||||
enum status st;
|
||||
bool leaving;
|
||||
}car_t;
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct cfgparam cfg;
|
||||
int ledcoin; //LED_SPEED_COIN
|
||||
uint32_t ledtime;
|
||||
bool rampactive;
|
||||
bool boxactive;
|
||||
}track_t;
|
||||
|
||||
|
||||
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 update_track( track_t* tck, car_t* car );
|
||||
|
||||
void process_main_track( track_t* tck, car_t* car );
|
||||
|
||||
void process_aux_track( track_t* tck, car_t* car );
|
||||
|
||||
void box_init( track_t* tck );
|
||||
|
||||
bool box_isactive( track_t* tck );
|
||||
|
||||
int tracklen_configure( track_t* tck, int nled );
|
||||
|
||||
int boxlen_configure( track_t* tck, int box_len, int boxalwaysOn );
|
||||
|
||||
int physic_configure( track_t* tck, float kgp, float kfp );
|
||||
|
||||
int track_configure( track_t* tck, int init_box );
|
||||
|
||||
void ramp_init( track_t* tck );
|
||||
|
||||
bool ramp_isactive( track_t* tck );
|
||||
|
||||
int ramp_configure( track_t* tck, int init, int center, int end, int high, int alwaysOn );
|
||||
|
||||
int race_configure( track_t* tck, int startline, int nlap, int nrepeat, int finishline );
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
26
open-led-race_OK/olr-param.c
Normal file
26
open-led-race_OK/olr-param.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "olr-param.h"
|
||||
|
||||
void param_setdefault( struct cfgparam* cfg ) {
|
||||
cfg->setted = true;
|
||||
|
||||
cfg->race.startline = true;
|
||||
cfg->race.nlap = NUMLAP;
|
||||
cfg->race.nrepeat = 1;
|
||||
cfg->race.finishline = true;
|
||||
|
||||
cfg->ramp.init = 80;
|
||||
cfg->ramp.center = 90;
|
||||
cfg->ramp.end = 100;
|
||||
cfg->ramp.high = 6;
|
||||
cfg->ramp.alwaysOn = false;
|
||||
|
||||
cfg->track.nled_total = MAXLED;
|
||||
cfg->track.nled_main = MAXLED; // 240 when boxes length = 60
|
||||
cfg->track.nled_aux = 0; // 60
|
||||
cfg->track.init_aux = -1; // 239
|
||||
cfg->track.box_len = BOXLEN;
|
||||
cfg->track.box_alwaysOn = false;
|
||||
|
||||
cfg->track.kf = 0.015; // friction constant
|
||||
cfg->track.kg = 0.006; // gravity constant - Used in Slope
|
||||
}
|
77
open-led-race_OK/olr-param.h
Normal file
77
open-led-race_OK/olr-param.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef _OLR_PARAM_LIB_h
|
||||
#define _OLR_PARAM_LIB_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAXLED 300
|
||||
#define BOXLEN 60
|
||||
#define NUMLAP 5
|
||||
|
||||
|
||||
|
||||
enum{
|
||||
LEN_UID = 16,
|
||||
CFG_VER = 5, // "5" in V0.9.6 (manage "permanent" param for Box and Slope)
|
||||
};
|
||||
|
||||
struct cfgrace{
|
||||
bool startline; // Used only in OLRNetwork
|
||||
int nlap;
|
||||
int nrepeat; // Used only in OLRNetwork
|
||||
bool finishline; // Used only in OLRNetwork
|
||||
};
|
||||
|
||||
//
|
||||
struct cfgtrack {
|
||||
int nled_total;
|
||||
int nled_main;
|
||||
int nled_aux;
|
||||
int init_aux;
|
||||
int box_len; // used to hold the Box Length if the default get changed.
|
||||
// it's not possible to implicitly store it in nled_main,nled_aux
|
||||
// because, if these are different to the default, box gets always activated
|
||||
// (the software does not chek "box_isactive" to draw car position)
|
||||
bool box_alwaysOn; // added in ver 0.9.6
|
||||
|
||||
|
||||
float kf;
|
||||
float kg;
|
||||
};
|
||||
|
||||
// ramp centred in LED 100 with 10 led fordward and 10 backguard
|
||||
struct cfgramp {
|
||||
int init;
|
||||
int center;
|
||||
int end;
|
||||
int high;
|
||||
bool alwaysOn; // added in ver 0.9.6
|
||||
};
|
||||
|
||||
struct brdinfo {
|
||||
char uid[LEN_UID + 1];
|
||||
};
|
||||
|
||||
struct cfgparam {
|
||||
bool setted;
|
||||
struct cfgrace race; // added in ver 0.9.d
|
||||
struct cfgtrack track;
|
||||
struct cfgramp ramp;
|
||||
struct brdinfo info;
|
||||
};
|
||||
|
||||
|
||||
void param_setdefault( struct cfgparam* cfg );
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
1065
open-led-race_OK/open-led-race_OK.ino
Normal file
1065
open-led-race_OK/open-led-race_OK.ino
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue