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.

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>

Binary file not shown.

View File

@@ -0,0 +1,85 @@
#include "BME280.h"
// I2C read 1 byte
uint8_t readRegister(uint8_t reg) {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 1);
return Wire.read();
}
// I2C write 1 byte
void writeRegister(uint8_t reg, uint8_t value) {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}
// Public functions
uint8_t BME280_GetID() {
return readRegister(BME280_REG_ID);
}
void BME280_Reset() {
writeRegister(BME280_REG_RESET, BME280_RESET_CMD);
}
uint8_t BME280_CtrlHum() {
return readRegister(BME280_REG_CTRL_HUM);
}
void BME280_CtrlHum(uint8_t bitpattern) {
writeRegister(BME280_REG_CTRL_HUM, bitpattern);
}
uint8_t BME280_CtrlMeas() {
return readRegister(BME280_REG_CTRL_MEAS);
}
void BME280_CtrlMeas(uint8_t bitpattern) {
writeRegister(BME280_REG_CTRL_MEAS, bitpattern);
}
long BME280_ReadTemperature() {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(BME280_REG_TEMP_MSB);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 3);
long msb = Wire.read();
long lsb = Wire.read();
long xlsb = Wire.read();
long adc_T = ((msb << 16) | (lsb << 8) | xlsb) >> 4;
return adc_T; // Raw value (compensation needed)
}
int BME280_ReadHumidity() {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(BME280_REG_HUM_MSB);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 2);
int msb = Wire.read();
int lsb = Wire.read();
int adc_H = (msb << 8) | lsb;
return adc_H; // Raw value
}
long BME280_ReadPressure() {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(BME280_REG_PRESS_MSB);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 3);
long msb = Wire.read();
long lsb = Wire.read();
long xlsb = Wire.read();
long adc_P = ((msb << 16) | (lsb << 8) | xlsb) >> 4;
return adc_P; // Raw value
}

View File

@@ -0,0 +1,43 @@
#ifndef BME280_H
#define BME280_H
#include <Arduino.h>
#include <Wire.h>
// I2C address for BME280 (default)
#define BME280_ADDRESS 0x76
// Register addresses from datasheet
#define BME280_REG_ID 0xD0
#define BME280_REG_RESET 0xE0
#define BME280_REG_CTRL_HUM 0xF2
#define BME280_REG_STATUS 0xF3
#define BME280_REG_CTRL_MEAS 0xF4
#define BME280_REG_CONFIG 0xF5
#define BME280_REG_PRESS_MSB 0xF7
#define BME280_REG_TEMP_MSB 0xFA
#define BME280_REG_HUM_MSB 0xFD
// Reset command
#define BME280_RESET_CMD 0xB6
// Function declarations
uint8_t BME280_GetID();
void BME280_Reset();
uint8_t BME280_CtrlHum();
void BME280_CtrlHum(uint8_t bitpattern);
uint8_t BME280_CtrlMeas();
void BME280_CtrlMeas(uint8_t bitpattern);
long BME280_ReadTemperature();
int BME280_ReadHumidity();
long BME280_ReadPressure();
#endif

View File

@@ -0,0 +1,37 @@
#include <Arduino.h>
#include <Wire.h>
#include "BME280.h"
void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("Initializing BME280...");
uint8_t id = BME280_GetID();
Serial.print("Sensor ID: 0x");
Serial.println(id, HEX);
BME280_Reset();
// Set oversampling for humidity = x1 (00000001)
BME280_CtrlHum(0x01);
// Set oversampling for temp and pressure = x1, mode = normal (00100111)
BME280_CtrlMeas(0x27);
Serial.println("BME280 Setup Done.");
}
void loop() {
long temp_raw = BME280_ReadTemperature();
int hum_raw = BME280_ReadHumidity();
long press_raw = BME280_ReadPressure();
Serial.print("Raw Temperature: ");
Serial.print(temp_raw);
Serial.print(" | Raw Humidity: ");
Serial.print(hum_raw);
Serial.print(" | Raw Pressure: ");
Serial.println(press_raw);
}

View File

@@ -0,0 +1,30 @@
#include <Wire.h>
#include <Arduino.h>
uint8_t count = 0;
void setup() {
Wire.begin(); // Master
Serial.begin(9600);
}
void loop() {
// Send count to slave
Wire.beginTransmission(0x42);
Wire.write(count);
Wire.endTransmission();
Serial.print("Sent: ");
Serial.println(count);
// Request response from slave
Wire.requestFrom(0x42, 1);
if (Wire.available()) {
uint8_t response = Wire.read();
Serial.print("Received: ");
Serial.println(response);
}
count++;
}

View File

@@ -0,0 +1,29 @@
#include <Arduino.h>
#include <Wire.h>
volatile uint8_t receivedValue = 0;
void setup() {
Wire.begin(0x42);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
Serial.begin(9600);
}
void loop() {
}
void receiveEvent(int howMany) {
if (howMany > 0) {
receivedValue = Wire.read();
Serial.print("Received from master: ");
Serial.println(receivedValue);
}
}
void requestEvent() {
uint8_t reply = (receivedValue > 100) ? 2 : 4;
Wire.write(reply);
Serial.print("Sent to master: ");
Serial.println(reply);
}

View File

@@ -0,0 +1,85 @@
#include "BME280.h"
// I2C read 1 byte
uint8_t readRegister(uint8_t reg) {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 1);
return Wire.read();
}
// I2C write 1 byte
void writeRegister(uint8_t reg, uint8_t value) {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}
// Public functions
uint8_t BME280_GetID() {
return readRegister(BME280_REG_ID);
}
void BME280_Reset() {
writeRegister(BME280_REG_RESET, BME280_RESET_CMD);
}
uint8_t BME280_CtrlHum() {
return readRegister(BME280_REG_CTRL_HUM);
}
void BME280_CtrlHum(uint8_t bitpattern) {
writeRegister(BME280_REG_CTRL_HUM, bitpattern);
}
uint8_t BME280_CtrlMeas() {
return readRegister(BME280_REG_CTRL_MEAS);
}
void BME280_CtrlMeas(uint8_t bitpattern) {
writeRegister(BME280_REG_CTRL_MEAS, bitpattern);
}
long BME280_ReadTemperature() {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(BME280_REG_TEMP_MSB);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 3);
long msb = Wire.read();
long lsb = Wire.read();
long xlsb = Wire.read();
long adc_T = ((msb << 16) | (lsb << 8) | xlsb) >> 4;
return adc_T; // Raw value (compensation needed)
}
int BME280_ReadHumidity() {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(BME280_REG_HUM_MSB);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 2);
int msb = Wire.read();
int lsb = Wire.read();
int adc_H = (msb << 8) | lsb;
return adc_H; // Raw value
}
long BME280_ReadPressure() {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(BME280_REG_PRESS_MSB);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS, 3);
long msb = Wire.read();
long lsb = Wire.read();
long xlsb = Wire.read();
long adc_P = ((msb << 16) | (lsb << 8) | xlsb) >> 4;
return adc_P; // Raw value
}

View File

@@ -0,0 +1,43 @@
#ifndef BME280_H
#define BME280_H
#include <Arduino.h>
#include <Wire.h>
// I2C address for BME280 (default)
#define BME280_ADDRESS 0x76
// Register addresses from datasheet
#define BME280_REG_ID 0xD0
#define BME280_REG_RESET 0xE0
#define BME280_REG_CTRL_HUM 0xF2
#define BME280_REG_STATUS 0xF3
#define BME280_REG_CTRL_MEAS 0xF4
#define BME280_REG_CONFIG 0xF5
#define BME280_REG_PRESS_MSB 0xF7
#define BME280_REG_TEMP_MSB 0xFA
#define BME280_REG_HUM_MSB 0xFD
// Reset command
#define BME280_RESET_CMD 0xB6
// Function declarations
uint8_t BME280_GetID();
void BME280_Reset();
uint8_t BME280_CtrlHum();
void BME280_CtrlHum(uint8_t bitpattern);
uint8_t BME280_CtrlMeas();
void BME280_CtrlMeas(uint8_t bitpattern);
long BME280_ReadTemperature();
int BME280_ReadHumidity();
long BME280_ReadPressure();
#endif

View File

@@ -0,0 +1,61 @@
#include <Arduino.h>
#include <Wire.h>
#include "BME280.h"
#define PARALLEL_SLAVE_ADDR 0x77
void writeToRegister(uint8_t deviceAddr, uint8_t reg, uint8_t value) {
Wire.beginTransmission(deviceAddr);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}
uint8_t readFromRegister(uint8_t deviceAddr, uint8_t reg) {
Wire.beginTransmission(deviceAddr);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(deviceAddr, 1);
return Wire.available() ? Wire.read() : 0xFF;
}
void setup() {
Serial.begin(9600);
Wire.begin();
// BME280 initialisatie
BME280_Reset();
BME280_CtrlHum(0x01); // x1 oversampling
BME280_CtrlMeas(0x27); // temp/press x1, mode normal
Serial.println("Setup done.");
}
void loop() {
// === Lezen van BME280 ===
long temp = BME280_ReadTemperature();
long press = BME280_ReadPressure();
int hum = BME280_ReadHumidity();
Serial.println("--- BME280 Readings ---");
Serial.print("Raw Temp: "); Serial.println(temp);
Serial.print("Raw Press: "); Serial.println(press);
Serial.print("Raw Hum: "); Serial.println(hum);
// === Testen van parallelle slave ===
uint8_t a = random(0, 100);
uint8_t b = random(0, 100);
writeToRegister(PARALLEL_SLAVE_ADDR, 0x21, a); // INA
writeToRegister(PARALLEL_SLAVE_ADDR, 0x22, b); // INB
uint8_t minVal = readFromRegister(PARALLEL_SLAVE_ADDR, 0x23); // MIN
uint8_t maxVal = readFromRegister(PARALLEL_SLAVE_ADDR, 0x24); // MAX
Serial.println("--- Parallel Slave ---");
Serial.print("a: "); Serial.print(a);
Serial.print(", b: "); Serial.print(b);
Serial.print(" => MIN: "); Serial.print(minVal);
Serial.print(", MAX: "); Serial.println(maxVal);
}

View File

@@ -0,0 +1,65 @@
#include <Arduino.h>
#include <Wire.h>
#define SLAVE_ADDRESS 0x77
uint8_t registers[256] = {0}; // Simuleer registermap
uint8_t regAddr = 0; // Registeradres voor read/write
void setup() {
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
Serial.begin(9600);
Serial.println("Slave ready.");
}
void loop() {
}
// Ontvangen van master (write)
void receiveEvent(int howMany) {
if (howMany < 1) return;
regAddr = Wire.read(); // Eerste byte is altijd registeradres
if (howMany > 1) {
// Daarna komen data bytes
int bytesToRead = howMany - 1;
for (int i = 0; i < bytesToRead; i++) {
if (Wire.available()) {
uint8_t val = Wire.read();
registers[regAddr + i] = val;
Serial.print("Reg ");
Serial.print(regAddr + i);
Serial.print(" written with ");
Serial.println(val);
}
}
// Update min/max registers als 0x21 of 0x22 is gewijzigd
if (regAddr == 0x21 || regAddr == 0x22) {
uint8_t a = registers[0x21];
uint8_t b = registers[0x22];
registers[0x23] = (a < b) ? a : b; // MIN
registers[0x24] = (a > b) ? a : b; // MAX
Serial.print("Updated MIN (0x23): ");
Serial.println(registers[0x23]);
Serial.print("Updated MAX (0x24): ");
Serial.println(registers[0x24]);
}
}
// else alleen registeradres ontvangen, niks schrijven (read setup)
}
// Master vraagt data op (read)
void requestEvent() {
uint8_t val = registers[regAddr];
Wire.write(val);
Serial.print("Sent reg ");
Serial.print(regAddr, HEX);
Serial.print(": ");
Serial.println(val);
}

View File

@@ -0,0 +1,60 @@
#include <Arduino.h>
#include <Wire.h>
#define DEVICE_ADDRESS 0x50
// Virtual hardware registers
uint8_t reg_INA = 0;
uint8_t reg_INB = 0;
uint8_t reg_pointer = 0; // acts as address register for reads
// Called when master writes data
void receiveEvent(int howMany) {
if (howMany < 1) return;
// First byte is always register address
reg_pointer = Wire.read();
howMany--;
if (howMany == 1) {
// Second byte is data to write
uint8_t value = Wire.read();
if (reg_pointer == 0x21) {
reg_INA = value;
Serial.println("INA set");
} else if (reg_pointer == 0x22) {
reg_INB = value;
Serial.println("INB set");
} else {
// Writes to read-only registers are ignored
Serial.println("Write to read-only register ignored");
}
}
}
// Called when master reads data
void requestEvent() {
uint8_t value = 0;
switch (reg_pointer) {
case 0x21: value = reg_INA; break;
case 0x22: value = reg_INB; break;
case 0x23: value = min(reg_INA, reg_INB); break;
case 0x24: value = max(reg_INA, reg_INB); break;
default: value = 0xFF; break; // undefined register
}
Wire.write(value);
}
void setup() {
Wire.begin(DEVICE_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
Serial.begin(9600);
}
void loop() {
}

View File

@@ -0,0 +1,65 @@
#include <Arduino.h>
#include <Wire.h>
#define SLAVE_ADDRESS 0x77
uint8_t registers[256] = {0}; // Simuleer registermap
uint8_t regAddr = 0; // Registeradres voor read/write
void setup() {
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
Serial.begin(9600);
Serial.println("Slave ready.");
}
void loop() {
}
// Ontvangen van master (write)
void receiveEvent(int howMany) {
if (howMany < 1) return;
regAddr = Wire.read(); // Eerste byte is altijd registeradres
if (howMany > 1) {
// Daarna komen data bytes
int bytesToRead = howMany - 1;
for (int i = 0; i < bytesToRead; i++) {
if (Wire.available()) {
uint8_t val = Wire.read();
registers[regAddr + i] = val;
Serial.print("Reg ");
Serial.print(regAddr + i);
Serial.print(" written with ");
Serial.println(val);
}
}
// Update min/max registers als 0x21 of 0x22 is gewijzigd
if (regAddr == 0x21 || regAddr == 0x22) {
uint8_t a = registers[0x21];
uint8_t b = registers[0x22];
registers[0x23] = (a < b) ? a : b; // MIN
registers[0x24] = (a > b) ? a : b; // MAX
Serial.print("Updated MIN (0x23): ");
Serial.println(registers[0x23]);
Serial.print("Updated MAX (0x24): ");
Serial.println(registers[0x24]);
}
}
// else alleen registeradres ontvangen, niks schrijven (read setup)
}
// Master vraagt data op (read)
void requestEvent() {
uint8_t val = registers[regAddr];
Wire.write(val);
Serial.print("Sent reg ");
Serial.print(regAddr, HEX);
Serial.print(": ");
Serial.println(val);
}

View File

@@ -0,0 +1,37 @@
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 convention is to give header files names that end with `.h'.
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 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,15 @@
; 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:uno]
platform = atmelavr
board = uno
framework = arduino
lib_deps = adafruit/Adafruit BusIO@^1.17.1

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,71 @@
#include <Arduino.h>
#define DEBOUNCE_DELAY 50
#define BLINK_INTERVAL 500
unsigned long lastBlinkTime = 0;
bool blinkState = false;
bool isPressed(uint8_t pinMask, volatile uint8_t* pinReg) {
static unsigned long lastDebounceTime[8] = {0};
static bool lastStableState[8] = {true};
static bool lastReadState[8] = {true};
uint8_t pinIndex = 0;
while ((pinMask >> pinIndex) != 1) pinIndex++; // bepaalt welk bit van toepassing is
bool reading = (*pinReg & pinMask); // leest huidige status van de pin
if (reading != lastReadState[pinIndex]) {
lastDebounceTime[pinIndex] = millis(); // bij verandering: debounce timer starten
lastReadState[pinIndex] = reading;
}
if ((millis() - lastDebounceTime[pinIndex]) > DEBOUNCE_DELAY) {
lastStableState[pinIndex] = reading; // status pas bijwerken na stabiele periode
}
return lastStableState[pinIndex];
}
void setup() {
DDRD |= _BV(DDD5) | _BV(DDD6);
Serial.begin(9600);
}
void loop() {
// Knopstatussen uitlezen met debounce
bool Button1Pressed = isPressed(_BV(PINB2), &PINB); // D10
bool Button2Pressed = isPressed(_BV(PINB3), &PINB); // D11
if (Button1Pressed && !Button2Pressed) {
// Alleen Button1 ingedrukt: LED D5 aan, D6 uit
PORTD |= _BV(PORTD5);
PORTD &= ~_BV(PORTD6);
} else if (!Button1Pressed && Button2Pressed) {
// Alleen Button2 ingedrukt: LED D6 aan, D5 uit en "Hello World!" printen
PORTD &= ~_BV(PORTD5);
PORTD |= _BV(PORTD6);
static unsigned long lastHelloTime = 0;
if (millis() - lastHelloTime > 100) {
Serial.println("Hello World!\n");
lastHelloTime = millis();
}
} else if (Button1Pressed && Button2Pressed) {
// Beide knoppen ingedrukt: LEDs knipperen om en om
if (millis() - lastBlinkTime >= BLINK_INTERVAL) {
blinkState = !blinkState;
lastBlinkTime = millis();
}
if (blinkState) {
PORTD |= _BV(PORTD5);
PORTD &= ~_BV(PORTD6);
} else {
PORTD &= ~_BV(PORTD5);
PORTD |= _BV(PORTD6);
}
} else {
// Geen knop ingedrukt: beide LEDs uit
PORTD &= ~(_BV(PORTD5) | _BV(PORTD6));
}
}

View File

@@ -0,0 +1,88 @@
#include <Arduino.h>
#define BLINK_INTERVAL 500
#define DEBOUNCE_DELAY 50
volatile bool Button1 = false;
volatile bool Button2 = false;
volatile unsigned long lastDebounceTimeButton1 = 0;
volatile unsigned long lastDebounceTimeButton2 = 0;
bool lastButton1State = true;
bool lastButton2State = true;
unsigned long lastBlinkTime = 0;
bool blinkState = false;
void setup() {
// LEDs op D5 en D6 als output
DDRD |= _BV(DDD5) | _BV(DDD6);
// Knoppen D10 (PB2) en D11 (PB3) als input met pull-up
DDRB &= ~(_BV(DDB2) | _BV(DDB3));
PORTB |= _BV(PORTB2) | _BV(PORTB3);
// Pin Change Interrupts activeren voor PB2 en PB3
PCICR |= _BV(PCIE0); // Enable PCINT[7:0] (PORTB)
PCMSK0 |= _BV(PCINT2) | _BV(PCINT3);
sei();
Serial.begin(9600);
}
ISR(PCINT0_vect) {
unsigned long now = millis();
// Lees huidige toestand
bool currentButton1 = PINB & _BV(PINB2);
bool currentButton2 = PINB & _BV(PINB3);
// Detecteer falling edge voor button1 (hoog -> laag)
if (lastButton1State && !currentButton1 && (now - lastDebounceTimeButton1 > DEBOUNCE_DELAY)) {
Button1 = !Button1; // toggle status
lastDebounceTimeButton1 = now;
}
// Detecteer falling edge voor button 2
if (lastButton2State && !currentButton2 && (now - lastDebounceTimeButton2 > DEBOUNCE_DELAY)) {
Button2 = !Button2; // toggle status
lastDebounceTimeButton2 = now;
}
// Update vorige staat
lastButton1State = currentButton1;
lastButton2State = currentButton2;
}
void loop() {
if (Button1 && !Button2) {
PORTD |= _BV(PORTD5);
PORTD &= ~_BV(PORTD6);
} else if (!Button1 && Button2) {
PORTD &= ~_BV(PORTD5);
PORTD |= _BV(PORTD6);
static unsigned long lastHelloTime = 0;
if (millis() - lastHelloTime > 100) {
Serial.println("Hello World!");
lastHelloTime = millis();
}
} else if (Button1 && Button2) {
if (millis() - lastBlinkTime >= BLINK_INTERVAL) {
blinkState = !blinkState;
lastBlinkTime = millis();
}
if (blinkState) {
PORTD |= _BV(PORTD5);
PORTD &= ~_BV(PORTD6);
} else {
PORTD &= ~_BV(PORTD5);
PORTD |= _BV(PORTD6);
}
} else {
PORTD &= ~(_BV(PORTD5) | _BV(PORTD6));
}
}

View File

@@ -0,0 +1,101 @@
#include <Arduino.h>
#define LED0 PORTD5 // D5
#define LED1 PORTD6 // D6
#define LED2 PORTB0 // D8
#define LED3 PORTB1 // D9
#define BUILTIN_LED PORTB5 // D13
#define BUTTON_SLOW PINB2 // D10
#define BUTTON_FAST PINB3 // D11
#define COMPARE_MIN 1000
#define COMPARE_MAX 50000
volatile unsigned char counter = 0;
volatile unsigned int compareValue = COMPARE_MAX;
void setupIO() {
// LEDs output
DDRD |= (1 << LED0) | (1 << LED1);
DDRB |= (1 << LED2) | (1 << LED3) | (1 << BUILTIN_LED);
// Buttons input with pull-up
DDRB &= ~((1 << BUTTON_SLOW) | (1 << BUTTON_FAST));
PORTB |= (1 << BUTTON_SLOW) | (1 << BUTTON_FAST);
// Pin change interrupt for D10/D11
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT2) | (1 << PCINT3);
}
// Setup Timer1 with COMPA (counter) and COMPB (1Hz LED)
void setupTimer1() {
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // CTC, prescaler 64
OCR1A = compareValue; // 4-bit counter update speed
OCR1B = 249999; // 1 Hz = (16e6 / 64) / 250000
TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B);
}
// Show counter value on 4 LEDs
void updateLEDs(unsigned char val) {
if (val & 0x01) PORTD |= (1 << LED0); else PORTD &= ~(1 << LED0);
if (val & 0x02) PORTD |= (1 << LED1); else PORTD &= ~(1 << LED1);
if (val & 0x04) PORTB |= (1 << LED2); else PORTB &= ~(1 << LED2);
if (val & 0x08) PORTB |= (1 << LED3); else PORTB &= ~(1 << LED3);
}
// Counter interrupt (frequency adjustable)
ISR(TIMER1_COMPA_vect) {
counter = (counter + 1) & 0x0F;
updateLEDs(counter);
}
ISR(TIMER1_COMPB_vect) {
PINB |= (1 << BUILTIN_LED); // Toggle built-in LED
}
// Button interrupt: handle speed control
ISR(PCINT0_vect) {
static unsigned char lastState = 0xFF;
unsigned char current = PINB;
// Falling edge: slow down
if (!(current & (1 << BUTTON_SLOW)) && (lastState & (1 << BUTTON_SLOW))) {
if (compareValue < COMPARE_MAX) {
compareValue <<= 1;
if (compareValue > COMPARE_MAX) compareValue = COMPARE_MAX;
}
}
// Falling edge: speed up
if (!(current & (1 << BUTTON_FAST)) && (lastState & (1 << BUTTON_FAST))) {
if (compareValue > COMPARE_MIN) {
compareValue >>= 1;
if (compareValue < COMPARE_MIN) compareValue = COMPARE_MIN;
}
}
lastState = current;
}
void setup() {
cli();
setupIO();
setupTimer1();
sei();
}
void loop() {
static unsigned int lastValue = 0;
cli();
if (compareValue != lastValue) {
TIMSK1 &= ~(1 << OCIE1A);
OCR1A = compareValue;
TIMSK1 |= (1 << OCIE1A);
lastValue = compareValue;
}
sei();
}

Binary file not shown.

Binary file not shown.