running version of Runner Game Controller

This commit is contained in:
Marcel Walter 2025-10-04 21:11:15 +02:00
parent 27de5f1840
commit 0b40474c22
7 changed files with 407 additions and 0 deletions

5
.gitignore vendored Normal file
View File

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

10
.vscode/extensions.json vendored Normal file
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"
]
}

37
include/README Normal file
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

46
lib/README Normal file
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

19
platformio.ini Normal file
View File

@ -0,0 +1,19 @@
; 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 =
;build_flags=-DTELEGRAM_DEBUG
upload_port = COM7
monitor_port = COM7

279
src/main.cpp Normal file
View File

@ -0,0 +1,279 @@
#include <Arduino.h>
#include <esp_now.h>
#include <WiFi.h>
// ===== Konfiguration =====
#define BUTTON_PIN_1 GPIO_NUM_33
#define BUTTON_PIN_2 GPIO_NUM_32
#define BUTTON_PIN_3 GPIO_NUM_19
#define BUTTON_PIN_4 GPIO_NUM_18
// MAC-Adresse des Empfängers (Game-ESP)
// WICHTIG: Durch die echte MAC-Adresse ersetzen!
uint8_t receiverMAC[] = {0x3C, 0x61, 0x05, 0x3E, 0xC7, 0x74};
esp_now_peer_info_t peerInfo = {};
int16_t tryChannel = 0;
int16_t ChannelNr = -1;
// ===== Nachrichtenstruktur =====
#define MAX_EVENTS_PER_PACKET 20
struct ButtonPacket
{
uint8_t eventCount; // Anzahl Events in diesem Paket
uint8_t playerIds[MAX_EVENTS_PER_PACKET]; // Welcher Spieler (1-4)
uint32_t timestamps[MAX_EVENTS_PER_PACKET]; // Zeitstempel (optional für Debugging)
};
// ===== Globale Variablen =====
ButtonPacket currentPacket;
unsigned long lastSendTime = 0;
unsigned long lastPingTime = 0;
const unsigned long SEND_INTERVAL_MS = 100; // Sende alle 10ms wenn Events vorhanden
// Debounce
struct ButtonState
{
bool currentState;
bool debounceState;
bool lastDebouncedState;
unsigned long lastDebounceTime;
unsigned long debounceDelay;
};
#define DEBOUNCE_TIME 15
ButtonState buttons[4] = {
{true, true, true, 0, DEBOUNCE_TIME},
{true, true, true, 0, DEBOUNCE_TIME},
{true, true, true, 0, DEBOUNCE_TIME},
{true, true, true, 0, DEBOUNCE_TIME}};
// ===== ESP-NOW Callback =====
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
if (status == ESP_NOW_SEND_SUCCESS)
{
Serial.println("Send OK");
}
else
{
Serial.println("Send FAIL");
}
}
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len)
{
if ((len == 4) && (incomingData[0] == 'P') && (incomingData[0] == 'O') && (incomingData[0] == 'N') && (incomingData[0] == 'G'))
{
ChannelNr = tryChannel;
Serial.print("PONG on Channel ");
Serial.print(tryChannel);
}
}
// ===== Event zum Paket hinzufügen =====
void sendPacket();
void addEventToPacket(uint8_t playerId)
{
if (currentPacket.eventCount < MAX_EVENTS_PER_PACKET)
{
currentPacket.playerIds[currentPacket.eventCount] = playerId;
currentPacket.timestamps[currentPacket.eventCount] = millis();
currentPacket.eventCount++;
Serial.print("Added P");
Serial.print(playerId);
Serial.print(" (");
Serial.print(currentPacket.eventCount);
Serial.println(" events buffered)");
}
else
{
// Paket voll - sofort senden
sendPacket();
addEventToPacket(playerId); // Rekursiv für das neue Event
}
}
// void changeChannel()
// {
// esp_now_deinit();
// WiFi.disconnect();
// peerInfo.channel += 1;
// if (peerInfo.channel > 14) peerInfo.channel = 1;
// WiFi.begin("GEHEIM","GEHEIM", peerInfo.channel);
// if (esp_now_init() != ESP_OK)
// {
// Serial.println("ESP-NOW init failed");
// return;
// }
// if (esp_now_add_peer(&peerInfo) != ESP_OK)
// {
// Serial.println("Failed to add peer");
// return;
// }
// }
// ===== Paket senden =====
void sendPacket()
{
// if (ChannelNr < 0)
// {
// if ((millis() - lastPingTime) > 500)
// {
// changeChannel();
// esp_now_send(receiverMAC, (uint8_t *)"PING", 4);
// Serial.print("PING on channel ");
// Serial.print(peerInfo.channel);
// Serial.println(" events");
// }
// return;
// }
if (currentPacket.eventCount == 0)
return;
esp_err_t result = esp_now_send(receiverMAC, (uint8_t *)&currentPacket, sizeof(ButtonPacket));
if (result == ESP_OK)
{
Serial.print("Sent ");
Serial.print(currentPacket.eventCount);
Serial.println(" events");
}
else
{
Serial.println("Send error");
}
// Paket zurücksetzen
currentPacket.eventCount = 0;
lastSendTime = millis();
}
// ===== Button-Polling mit Debounce =====
void checkButtons()
{
const uint8_t buttonPins[] = {BUTTON_PIN_1, BUTTON_PIN_2, BUTTON_PIN_3, BUTTON_PIN_4};
static uint32_t timestamp = 0;
timestamp = millis();
for (int i = 0; i < 4; i++)
{
buttons[i].currentState = digitalRead(buttonPins[i]);
}
for (int i = 0; i < 4; i++)
{
// Serial.print(i + 1);
// Serial.print(" = ");
// Serial.println(reading);
// Bei State-Änderung Debounce-Timer starten
if (buttons[i].currentState != buttons[i].debounceState)
{
buttons[i].lastDebounceTime = timestamp;
buttons[i].debounceState = buttons[i].currentState;
// Serial.print("Reading");
// Serial.print(i + 1);
// Serial.print(" = ");
// Serial.print(buttons[i].currentState);
// Serial.print(" ==> ");
// Serial.println(buttons[i].lastDebounceTime);
}
// Nach Debounce-Zeit prüfen
if ((buttons[i].lastDebouncedState != buttons[i].debounceState) && ((timestamp - buttons[i].lastDebounceTime) > buttons[i].debounceDelay))
{
// Serial.print("Button");
// Serial.print(i + 1);
// Serial.print(" debounced ");
// Serial.println(buttons[i].debounceState);
// Fallende Flanke = Button gedrückt (Pull-up aktiv)
if (buttons[i].debounceState == LOW && buttons[i].lastDebouncedState == HIGH)
{
addEventToPacket(i + 1); // Spieler 1-4
// Serial.println(i + 1);
}
buttons[i].lastDebouncedState = buttons[i].debounceState;
}
}
}
// ===== Setup =====
void setup()
{
Serial.begin(115200);
Serial.println("ESP-NOW Button Sender");
// Button-Pins konfigurieren (mit internem Pull-up)
pinMode(BUTTON_PIN_1, INPUT_PULLUP);
pinMode(BUTTON_PIN_2, INPUT_PULLUP);
pinMode(BUTTON_PIN_3, INPUT_PULLUP);
pinMode(BUTTON_PIN_4, INPUT_PULLUP);
// WiFi im Station-Mode
WiFi.mode(WIFI_STA);
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
Serial.print("Channel: ");
Serial.println(WiFi.channel());
// ESP-NOW initialisieren
if (esp_now_init() != ESP_OK)
{
Serial.println("ESP-NOW init failed");
return;
}
// Callback registrieren
esp_now_register_send_cb(OnDataSent);
// Peer hinzufügen
memcpy(peerInfo.peer_addr, receiverMAC, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK)
{
Serial.println("Failed to add peer");
return;
}
Serial.println("Setup complete");
// Paket initialisieren
currentPacket.eventCount = 0;
}
// ===== Main Loop =====
void loop()
{
unsigned long currentTime = millis();
// Buttons überprüfen
checkButtons();
// Paket senden wenn:
// 1. Events vorhanden sind UND
// 2. Sendeintervall abgelaufen ist
if ((currentTime - lastSendTime) >= SEND_INTERVAL_MS)
{
sendPacket();
}
delay(1); // Kurze Pause
}

11
test/README Normal file
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