ES ordening

This commit is contained in:
Rens Pastoor
2025-05-27 23:20:05 +02:00
parent d9244f1ae3
commit 39269a71a7
49 changed files with 1952 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,3 @@
{
"idf.portWin": "COM11"
}

View File

@@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,16 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps = plerup/EspSoftwareSerial@^8.2.0

View File

@@ -0,0 +1,74 @@
#include "SerialProcess.h"
#include <Arduino.h>
// Constructor
SerialProcess::SerialProcess(int addr)
: address(addr), ndx(0), rc(0), newData(false), rcCheck(false) {
Serial.begin(115200);
}
// Processes Serial Input
void SerialProcess::SerialInput() {
while (Serial.available() > 0) {
rc = static_cast<char>(Serial.read());
if (rc == beginMarker) {
rcCheck = true; // Start reading after the begin marker
ndx = 0; // Reset index for new message
}
if (rcCheck) {
// Store the character if within bounds
if (ndx < numChars - 1) {
receivedChars[ndx++] = rc;
}
// Check for end marker
if (rc == endMarker) {
receivedChars[ndx] = '\0'; // Null-terminate the string
newData = true; // Mark new data as available
rcCheck = false; // Stop reading until the next begin marker
}
}
}
}
// Check if new data is available
bool SerialProcess::isNewDataAvailable() {
return newData;
}
// Get the received data
char* SerialProcess::getReceivedData() {
if (newData) {
newData = false; // Reset the flag after accessing the data
return receivedChars;
}
return nullptr; // No new data
}
// Process the received message
void SerialProcess::getPayload(char *payload) {
if (newData) {
uint8_t source;
uint8_t destination;
char data[255]; // Allocate a buffer for the data
int parsed = sscanf(receivedChars, "#%hhu:%hhu:%63s;", &source, &destination, data);
if (parsed == 3 && destination == address) { // Ensure all fields are parsed correctly
strcpy(payload, data); // Copy data to the provided buffer
newData = false; // Mark the data as processed
} else if (address != source) {
Serial.print(receivedChars); // Forward the message
}
}
}
// Send a message in the correct format
void SerialProcess::sendMessage(int receiver, const char* payload) {
Serial.printf("#%u:%u:%s;", address, receiver, payload);
}
void SerialProcess::changeAddress(int addr) {
address = addr; // Update the device address
}

View File

@@ -0,0 +1,39 @@
#include <Arduino.h>
#ifndef SERIALPROCESS_H
#define SERIALPROCESS_H
class SerialProcess {
private:
uint8_t ndx; // Current index for the buffer
const char beginMarker = '#'; // Marker to indicate the start of a message
const char endMarker = ';'; // Marker to indicate the end of a message
char rc; // Character read from Serial
int address; // Device address
bool newData; // Flag for new data availability
static const uint8_t numChars = 255; // Maximum size of the buffer
char receivedChars[numChars]; // Buffer for incoming data
bool rcCheck;
public:
// Constructor
explicit SerialProcess(int addr);
// Store Serial Input (if available)
void SerialInput();
// Check if new data is available
bool isNewDataAvailable();
// Get the received data
char* getReceivedData();
// Process the received message
void getPayload(char* payload);
// Send message in the correct format
void sendMessage(int receiver, const char* payload);
void changeAddress(int addr);
};
#endif // SERIALPROCESS_H

View File

@@ -0,0 +1,186 @@
#include <SoftwareSerial.h>
SoftwareSerial mySerial(16, 17); // RX, TX
const int red = 14;
const int yellow = 33;
const int green = 12;
const unsigned long greenDuration = 5000;
const unsigned long yellowDuration = 2000;
const unsigned long redDuration = 5000;
const unsigned long transitionDuration = 2000;
const unsigned long heartbeatInterval = 1000;
const unsigned long heartbeatTimeout = 3000;
const unsigned long blinkInterval = 500;
String receivedData = "";
bool receiving = false;
unsigned long previousMillis = 0;
unsigned long lastHeartbeatMillis = 0;
unsigned long lastBlinkMillis = 0;
bool blinkState = false;
bool isError = false;
enum State { GREEN, YELLOW, RED, TRANSITION, ERROR };
State currentState = GREEN;
#define REPEAT_SEND false
void sendCommand(String cmd) {
mySerial.print("<");
mySerial.print(cmd);
mySerial.println(">");
Serial.print("Sent: ");
Serial.println(cmd);
}
void sendHeartbeat() {
mySerial.print("<HB>");
Serial.println("Sent: Heartbeat");
}
void ledRed() {
digitalWrite(red, HIGH);
digitalWrite(yellow, LOW);
digitalWrite(green, LOW);
}
void ledYellow() {
digitalWrite(red, LOW);
digitalWrite(yellow, HIGH);
digitalWrite(green, LOW);
}
void ledGreen() {
digitalWrite(red, LOW);
digitalWrite(yellow, LOW);
digitalWrite(green, HIGH);
}
void updateLights() {
switch (currentState) {
case GREEN:
ledGreen();
sendCommand("G");
break;
case YELLOW:
ledYellow();
sendCommand("Y");
break;
case RED:
ledRed();
sendCommand("R");
break;
case TRANSITION:
sendCommand("T");
break;
case ERROR:
if (millis() - lastBlinkMillis >= blinkInterval) {
blinkState = !blinkState;
digitalWrite(yellow, blinkState ? HIGH : LOW);
lastBlinkMillis = millis();
}
break;
}
}
void setup() {
pinMode(green, OUTPUT);
pinMode(yellow, OUTPUT);
pinMode(red, OUTPUT);
mySerial.begin(115200);
Serial.begin(115200);
previousMillis = millis();
lastHeartbeatMillis = millis();
updateLights();
}
void loop() {
unsigned long currentMillis = millis();
// Send heartbeat regularly
if (currentMillis - lastHeartbeatMillis >= heartbeatInterval) {
sendHeartbeat();
lastHeartbeatMillis = currentMillis;
}
// Heartbeat timeout check
if (currentMillis - lastHeartbeatMillis >= heartbeatTimeout) {
if (!isError) {
isError = true;
}
}
// State transitions
switch (currentState) {
case GREEN:
if (currentMillis - previousMillis >= greenDuration) {
currentState = YELLOW;
previousMillis = currentMillis;
updateLights();
}
break;
case YELLOW:
if (currentMillis - previousMillis >= yellowDuration) {
currentState = RED;
previousMillis = currentMillis;
updateLights();
}
break;
case RED:
if (currentMillis - previousMillis >= redDuration) {
currentState = TRANSITION;
previousMillis = currentMillis;
updateLights();
}
break;
case TRANSITION:
if (currentMillis - previousMillis >= transitionDuration) {
currentState = GREEN;
previousMillis = millis();
updateLights();
}
break;
case ERROR:
updateLights();
break;
}
// Handle received data
while (mySerial.available()) {
char incomingByte = mySerial.read();
if (incomingByte == '<') {
receivedData = "";
receiving = true;
} else if (incomingByte == '>') {
receiving = false;
Serial.print("Received Data: ");
Serial.println(receivedData);
if (receivedData == "HB") {
lastHeartbeatMillis = millis();
if (isError) {
digitalWrite(yellow, LOW);
}
isError = false;
}
receivedData = "";
} else if (receiving) {
if (incomingByte >= 32 && incomingByte <= 126) {
receivedData += incomingByte;
}
}
}
// Blinking yellow light in error state
if (isError) {
if (currentMillis - lastBlinkMillis >= blinkInterval) {
blinkState = !blinkState;
digitalWrite(yellow, blinkState ? HIGH : LOW);
lastBlinkMillis = currentMillis;
}
}
}

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,39 @@
#include <Arduino.h>
#ifndef SERIALPROCESS_H
#define SERIALPROCESS_H
class SerialProcess {
private:
uint8_t ndx; // Current index for the buffer
const char beginMarker = '#'; // Marker to indicate the start of a message
const char endMarker = ';'; // Marker to indicate the end of a message
char rc; // Character read from Serial
int address; // Device address
bool newData; // Flag for new data availability
static const uint8_t numChars = 255; // Maximum size of the buffer
char receivedChars[numChars]; // Buffer for incoming data
bool rcCheck;
public:
// Constructor
explicit SerialProcess(int addr);
// Store Serial Input (if available)
void SerialInput();
// Check if new data is available
bool isNewDataAvailable();
// Get the received data
char* getReceivedData();
// Process the received message
void getPayload(char* payload);
// Send message in the correct format
void sendMessage(int receiver, const char* payload);
void changeAddress(int addr);
};
#endif // SERIALPROCESS_H

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,16 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps = plerup/EspSoftwareSerial@^8.2.0

View File

@@ -0,0 +1,74 @@
#include "SerialProcess.h"
#include <Arduino.h>
// Constructor
SerialProcess::SerialProcess(int addr)
: address(addr), ndx(0), rc(0), newData(false), rcCheck(false) {
Serial.begin(115200);
}
// Processes Serial Input
void SerialProcess::SerialInput() {
while (Serial.available() > 0) {
rc = static_cast<char>(Serial.read());
if (rc == beginMarker) {
rcCheck = true; // Start reading after the begin marker
ndx = 0; // Reset index for new message
}
if (rcCheck) {
// Store the character if within bounds
if (ndx < numChars - 1) {
receivedChars[ndx++] = rc;
}
// Check for end marker
if (rc == endMarker) {
receivedChars[ndx] = '\0'; // Null-terminate the string
newData = true; // Mark new data as available
rcCheck = false; // Stop reading until the next begin marker
}
}
}
}
// Check if new data is available
bool SerialProcess::isNewDataAvailable() {
return newData;
}
// Get the received data
char* SerialProcess::getReceivedData() {
if (newData) {
newData = false; // Reset the flag after accessing the data
return receivedChars;
}
return nullptr; // No new data
}
// Process the received message
void SerialProcess::getPayload(char *payload) {
if (newData) {
uint8_t source;
uint8_t destination;
char data[255]; // Allocate a buffer for the data
int parsed = sscanf(receivedChars, "#%hhu:%hhu:%63s;", &source, &destination, data);
if (parsed == 3 && destination == address) { // Ensure all fields are parsed correctly
strcpy(payload, data); // Copy data to the provided buffer
newData = false; // Mark the data as processed
} else if (address != source) {
Serial.print(receivedChars); // Forward the message
}
}
}
// Send a message in the correct format
void SerialProcess::sendMessage(int receiver, const char* payload) {
Serial.printf("#%u:%u:%s;", address, receiver, payload);
}
void SerialProcess::changeAddress(int addr) {
address = addr; // Update the device address
}

View File

@@ -0,0 +1,238 @@
#include <Arduino.h>
#include "SerialProcess.h"
#define LEDRED 14
#define LEDORANGE 13
#define LEDGREEN 12
unsigned long previousMillis = 0;
const unsigned long greenDuration = 5000;
const unsigned long yellowDuration = 2000;
const unsigned long redDuration = 5000;
const unsigned long transitionDuration = 2000;
const unsigned long heartbeatInterval = 1000;
const unsigned long heartbeatTimeout = 3000;
const unsigned long blinkInterval = 500;
unsigned long lastHeartbeatMillis = 0;
unsigned long lastBlinkMillis = 0;
bool blinkState = false;
enum State { GREEN, YELLOW, RED, TRANSITION, ERROR };
State currentState = GREEN;
const char *slavecheck = "#slvchck;";
const char *slaveack = "#slvack;";
const char *masterack = "#mstack;";
const char *turnRed = "tr";
const char *turnOrange = "to";
const char *turnGreen = "tg";
const char *heartbeat = "hb";
int node; // other bord addres number
int address = 0; // Device address
void setRed(){
digitalWrite(LEDRED, HIGH);
digitalWrite(LEDORANGE, LOW);
digitalWrite(LEDGREEN, LOW);
}
void setOrange(){
digitalWrite(LEDRED, LOW);
digitalWrite(LEDORANGE, HIGH);
digitalWrite(LEDGREEN, LOW);
}
void setGreen(){
digitalWrite(LEDRED, LOW);
digitalWrite(LEDORANGE, LOW);
digitalWrite(LEDGREEN, HIGH);
}
SerialProcess serialcom(0);
bool MasterCheck() {
SerialProcess serialcomchecker(1000);
const unsigned long timeout = 2000;
unsigned long startTime = millis();
bool isMaster = true;
bool checkSent = false;
bool gotResponse = false;
delay(random(100, 600)); // Randomize startup slightly
Serial.print(slavecheck); // Send check to see if someone replies
checkSent = true;
while (millis() - startTime < timeout) {
if (Serial.available()) {
serialcomchecker.SerialInput();
char* payload = serialcomchecker.getReceivedData();
if (strcmp(payload, slavecheck) == 0) {
// Got a check while we also sent a check — respond and become SLAVE
Serial.print(slaveack);
isMaster = false;
break;
} else if (strcmp(payload, slaveack) == 0) {
// Got an ACK from the other side — we are master
Serial.print(masterack);
isMaster = true;
break;
} else if (strcmp(payload, masterack) == 0) {
// Got master ack — we must be slave
isMaster = false;
break;
}
}
}
// Failsafe: no response at all
if (!Serial.available() && !gotResponse) {
// Assume we are master
isMaster = true;
Serial.print(masterack);
}
return isMaster;
}
void updateLights() {
switch (currentState) {
case GREEN:
setGreen();
serialcom.sendMessage(node, turnGreen);
break;
case YELLOW:
setOrange();
serialcom.sendMessage(node, turnOrange);
break;
case RED:
setGreen();
serialcom.sendMessage(node, turnRed);
break;
case TRANSITION:
serialcom.sendMessage(node, turnOrange);
break;
case ERROR:
if (millis() - lastBlinkMillis >= blinkInterval) {
blinkState = !blinkState;
digitalWrite(LEDORANGE, blinkState ? HIGH : LOW);
lastBlinkMillis = millis();
}
break;
}
}
void sendHeartbeat() {
serialcom.sendMessage(node,heartbeat);
Serial.println("Sent: Heartbeat");
}
void master(){
bool running = true;
while (running){
unsigned long currentMillis = millis();
if (currentMillis - lastHeartbeatMillis >= heartbeatInterval) {
sendHeartbeat();
lastHeartbeatMillis = currentMillis;
}
if (currentMillis - lastHeartbeatMillis > heartbeatTimeout) {
currentState = ERROR;
updateLights();
return;
}
switch (currentState) {
case GREEN:
if (currentMillis - previousMillis >= greenDuration) {
currentState = YELLOW;
previousMillis = currentMillis;
updateLights();
}
break;
case YELLOW:
if (currentMillis - previousMillis >= yellowDuration) {
currentState = RED;
previousMillis = currentMillis;
updateLights();
}
break;
case RED:
if (currentMillis - previousMillis >= redDuration) {
currentState = TRANSITION;
previousMillis = currentMillis;
updateLights();
}
break;
case TRANSITION:
if (currentMillis - previousMillis >= transitionDuration) {
currentState = GREEN;
previousMillis = millis();
updateLights();
}
break;
case ERROR:
updateLights();
break;
}
}
}
void setup() {
Serial.begin(115200);
pinMode(LEDGREEN, OUTPUT);
pinMode(LEDORANGE, OUTPUT);
pinMode(LEDRED, OUTPUT);
bool MorS = MasterCheck(); // Check if master or slave
//set address for master or slave
if (MorS == false){
address = 2; // Set address for this slave
node = 1; // Set address for master
} else {
address = 1; // Set address for this master
node = 2; // Set address for slave
}
previousMillis = millis();
lastHeartbeatMillis = millis();
updateLights();
setRed();
serialcom.changeAddress(address); // Set address for serial communication
}
void slave(){
bool running = true;
char *command;
while (running && Serial.available() > 0){
serialcom.SerialInput();
serialcom.getPayload(command);
if (strcmp(command, turnRed)) setRed();
else if (strcmp(command, turnOrange)) setOrange();
else if (strcmp(command, turnGreen)) setGreen();
else if (strcmp(command, heartbeat)) {
lastHeartbeatMillis = millis();
digitalWrite(LEDRED, LOW); // Reset the orange blink pattern
digitalWrite(LEDORANGE, LOW);
}
else {
Serial.print("slave: unknown command");
}
}
if (millis() - lastHeartbeatMillis > heartbeatTimeout) {
// Blink the red and yellow LEDs to indicate an error (orange light)
if (millis() - lastBlinkMillis >= blinkInterval) {
blinkState = !blinkState;
digitalWrite(LEDORANGE, blinkState ? HIGH : LOW);
lastBlinkMillis = millis();
}
}
}
void loop(){
serialcom.changeAddress(2);
if (address == 1) slave(); //master
else if (address == 2) slave(); //slave
else Serial.print("master slave issue");
}

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

View File

@@ -0,0 +1,112 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" version="26.2.7">
<diagram name="Pagina-1" id="CR9P-yaCSiBfdL3vZK3D">
<mxGraphModel grid="1" page="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="dF3u_iEBEdzJW1drsnO6-13" value="GREEN" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-460" y="720" width="300" height="280" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-14" value="Entry:&amp;nbsp;&lt;div&gt;&lt;br&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Set the &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;215&quot; data-start=&quot;208&quot;&gt;green&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; light HIGH, &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;236&quot; data-start=&quot;228&quot;&gt;yellow&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; and &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;246&quot; data-start=&quot;241&quot;&gt;red&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; lights LOW.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Send the &quot;G&quot; command to the serial port.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-13">
<mxGeometry y="30" width="300" height="90" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-15" value="Do:&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Stay in the GREEN state for &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;362&quot; data-start=&quot;347&quot;&gt;greenDuration&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; (5 seconds).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Send heartbeats at intervals&lt;/span&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;.&lt;/span&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-13">
<mxGeometry y="120" width="300" height="90" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-16" value="Exit:&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Transition to the YELLOW state when the &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;517&quot; data-start=&quot;502&quot;&gt;greenDuration&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; has elapsed.&lt;/span&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-13">
<mxGeometry y="210" width="300" height="70" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-24" value="YELLOW" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="50" y="720" width="300" height="280" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-25" value="Entry:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Set the &lt;code data-end=&quot;494&quot; data-start=&quot;486&quot;&gt;yellow&lt;/code&gt; light HIGH, &lt;code data-end=&quot;514&quot; data-start=&quot;507&quot;&gt;green&lt;/code&gt; and &lt;code data-end=&quot;524&quot; data-start=&quot;519&quot;&gt;red&lt;/code&gt; lights&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;LOW. Send the &quot;Y&quot; command to the serial port.&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-24">
<mxGeometry y="30" width="300" height="90" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-26" value="Do:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Stay in the YELLOW state for &lt;code data-end=&quot;633&quot; data-start=&quot;617&quot;&gt;yellowDuration&lt;/code&gt; (2 seconds).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Send heartbeats at intervals.&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-24">
<mxGeometry y="120" width="300" height="90" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-27" value="&lt;p class=&quot;&quot; data-end=&quot;790&quot; data-start=&quot;447&quot;&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Exit:&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;790&quot; data-start=&quot;447&quot;&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Transition to the RED state when the &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;777&quot; data-start=&quot;761&quot;&gt;yellowDuration&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; has elapsed.&lt;/span&gt;&lt;/p&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-24">
<mxGeometry y="210" width="300" height="70" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-28" value="ERROR" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-90" y="1140" width="300" height="330" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-29" value="Entry:&amp;nbsp;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Blink the yellow light (on and off).&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Set the error flag to true and stop normal state transitions.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-28">
<mxGeometry y="30" width="300" height="100" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-30" value="Do:&lt;div&gt;&lt;br&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Continuously blink the yellow light at &lt;/span&gt;&lt;code style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot; data-end=&quot;1829&quot; data-start=&quot;1814&quot;&gt;blinkInterval&lt;/code&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt; while in the ERROR state.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Monitor for incoming heartbeat data to clear the error state.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-28">
<mxGeometry y="130" width="300" height="130" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-31" value="Exit:&lt;div&gt;&lt;br&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;Exit the ERROR state when a valid heartbeat is received.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-28">
<mxGeometry y="260" width="300" height="70" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-32" value="RED" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="540" y="720" width="300" height="280" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-33" value="Entry:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Set the &lt;code data-end=&quot;838&quot; data-start=&quot;833&quot;&gt;red&lt;/code&gt; light HIGH, &lt;code data-end=&quot;858&quot; data-start=&quot;851&quot;&gt;green&lt;/code&gt; and &lt;code data-end=&quot;871&quot; data-start=&quot;863&quot;&gt;yellow&lt;/code&gt; lights LOW.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Send the &quot;R&quot; command to the serial port.&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-32">
<mxGeometry y="30" width="300" height="100" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-34" value="Do:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Stay in the RED state for &lt;code data-end=&quot;974&quot; data-start=&quot;961&quot;&gt;redDuration&lt;/code&gt; (5 seconds).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Send heartbeats at intervals.&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-32">
<mxGeometry y="130" width="300" height="80" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-35" value="Exit:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Transition to the TRANSITION state when the &lt;code data-end=&quot;1122&quot; data-start=&quot;1109&quot;&gt;redDuration&lt;/code&gt; has elapsed.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-32">
<mxGeometry y="210" width="300" height="70" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-37" value="TRANSITION" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="540" y="1110" width="300" height="280" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-38" value="Entry:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Send the &quot;T&quot; command to the serial port (for transitioning).&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-37">
<mxGeometry y="30" width="300" height="70" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-39" value="Do:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Stay in the TRANSITION state for &lt;code data-end=&quot;1301&quot; data-start=&quot;1281&quot;&gt;transitionDuration&lt;/code&gt; (2 seconds).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Send heartbeats at intervals.&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-37">
<mxGeometry y="100" width="300" height="110" as="geometry" />
</mxCell>
<mxCell id="dF3u_iEBEdzJW1drsnO6-40" value="Exit:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Transition to the GREEN state when the &lt;code data-end=&quot;1451&quot; data-start=&quot;1431&quot;&gt;transitionDuration&lt;/code&gt; has elapsed.&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="dF3u_iEBEdzJW1drsnO6-37">
<mxGeometry y="210" width="300" height="70" as="geometry" />
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-1" value="" style="endArrow=classic;html=1;rounded=0;exitX=1.019;exitY=0.159;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-15">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="340" y="250" as="sourcePoint" />
<mxPoint x="47" y="860" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-3" value="" style="endArrow=classic;html=1;rounded=0;exitX=1.017;exitY=0.196;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.012;entryY=0.096;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-26" target="dF3u_iEBEdzJW1drsnO6-34">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="340" y="250" as="sourcePoint" />
<mxPoint x="490" y="860" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-5" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.463;entryY=-0.002;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-35" target="dF3u_iEBEdzJW1drsnO6-37">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="100" y="1200" as="sourcePoint" />
<mxPoint x="150" y="1150" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-6" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0;exitY=0;exitDx=0;exitDy=0;entryX=0.703;entryY=1.057;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-28" target="dF3u_iEBEdzJW1drsnO6-16">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="100" y="1200" as="sourcePoint" />
<mxPoint x="150" y="1150" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-7" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.579;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.41;entryY=1.057;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-28" target="dF3u_iEBEdzJW1drsnO6-27">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="120" y="1200" as="sourcePoint" />
<mxPoint x="170" y="1150" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-8" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=1;exitY=0;exitDx=0;exitDy=0;entryX=0.006;entryY=0.943;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-28" target="dF3u_iEBEdzJW1drsnO6-35">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="120" y="1200" as="sourcePoint" />
<mxPoint x="170" y="1150" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OWCZAN_wvuP4CP33OBUB-9" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=1.006;exitY=0.22;exitDx=0;exitDy=0;exitPerimeter=0;entryX=-0.014;entryY=0.743;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="dF3u_iEBEdzJW1drsnO6-29" target="dF3u_iEBEdzJW1drsnO6-38">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="120" y="1200" as="sourcePoint" />
<mxPoint x="170" y="1150" as="targetPoint" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>