Keyboard functional and first sending of PGN 127502
This commit is contained in:
74
README
74
README
@@ -2,11 +2,10 @@ OBP Keypad
|
||||
==========
|
||||
|
||||
- Stromversorgung über M12-Anschluß über NMEA2000
|
||||
Eingangsbereich 6~21V
|
||||
- Ein- und Ausschalten durch langen Tastendruck auf DST/ ONOFF
|
||||
- Eingangsbereich 6~21V
|
||||
- Konfigurationsmodus durch langen Tastendruck auf DST
|
||||
- Tiefschlaf und Reset aus Konfigmodus heraus auswählbar
|
||||
- Konfiguration über Web-Interface
|
||||
- PWR leuchtet grün wen NMEA2000-Verbindung etabliert
|
||||
- PWR leuchtet rot wenn nur Stromversorgung aktiv ist
|
||||
- Buzzer für Tastendruck-Feedback
|
||||
passiv, damit können die Töne mit PWM programmiert werden
|
||||
- I²C Temp/Hum-Sensor SHT31
|
||||
@@ -25,14 +24,15 @@ Optionen für später
|
||||
bedeutet auch: viel komplizierteres Gehäuse
|
||||
|
||||
Damit die LEDs nicht stören, kann umgeschaltet werden zwischen
|
||||
permanentem Leuchten und nur kurzem Aufblinken bei Betätigung.
|
||||
permanentem Leuchten und nur kurzem Aufblinken bei Betätigung:
|
||||
Tag- und Nachtmodus
|
||||
|
||||
Einschaltvorgang
|
||||
----------------
|
||||
|
||||
Es erfolgt ein "Lampentest": Alle LEDs werden einmal durchgeschaltet.
|
||||
|
||||
Sind alle Tests erfolgreich ertönt ein Buzzer-Signal.
|
||||
- Status leuchtet kurz rot auf
|
||||
- Es erfolgt ein "Lampentest": Alle LEDs werden einmal durchgeschaltet.
|
||||
- Sind alle Tests erfolgreich ertönt ein Buzzer-Signal.
|
||||
|
||||
Beschreibung
|
||||
------------
|
||||
@@ -44,31 +44,29 @@ Die Destination-LEDs leuchten nur, wenn ein entsprechendes
|
||||
N2K-Zielgerät konfiguriert und erkannt wurde.
|
||||
|
||||
|
||||
|
||||
|
||||
Gehäuse
|
||||
-------
|
||||
|
||||
Bohrung Taster: 12mm
|
||||
Taster Außenmaß: 17.5mm
|
||||
|
||||
Verbindungskabel CPU-Platine
|
||||
JST 2.54 XH 6 Pin Steckverbinder -> LED
|
||||
JST 2.54 XH 7 Pin Steckverbinder -> LEDs + GND
|
||||
|
||||
Anschlußmöglichkeiten
|
||||
---------------------
|
||||
|
||||
für Stromversorgung +12V und NMEA2000
|
||||
4pin Terminalblock steck-/schraubbar +12V, DNG, CAN-L, CAN-H
|
||||
2pin Terminalblock
|
||||
|
||||
für I²C-Module
|
||||
2x 4pin Buchsenleiste weibl.
|
||||
1x QWIIC-Buchse (JST_SH_BM04B-SRSS-TB_04x1.00mm)
|
||||
1x qwiic-Buchse (JST_SH_BM04B-SRSS-TB_04x1.00mm)
|
||||
|
||||
für CAN-Transceiver-Modul: 4fach buchse liegend
|
||||
|
||||
für mechanische Taster
|
||||
1x JST 2.54 XH 7 Pin Steckverbinder -> Tasten
|
||||
Masseverbindung über einzelnes getrenntes Kabel
|
||||
in eine 8-fach WAGO-Klemme
|
||||
|
||||
für LEDs
|
||||
TBD
|
||||
1x Terminalblock 8fach, 7 Tasten und GND
|
||||
|
||||
|
||||
Bemerkungen
|
||||
@@ -77,6 +75,10 @@ Bemerkungen
|
||||
Bei den aktuell verwendeten Tasten sind die Anschlußdrähte extrem
|
||||
filigran. Leichtes Brechen und schlechte Verarbeitung.
|
||||
|
||||
Es gibt verschiedene Varianten mit unterschiedlicher Federkraft.
|
||||
Auswahl muß noch erfolgen
|
||||
|
||||
|
||||
Beschaltung MCU Nano
|
||||
--------------------
|
||||
|
||||
@@ -94,7 +96,7 @@ werden!
|
||||
|
||||
Die Pins für SPI (D11, D12, D13) sind absichtlich
|
||||
nicht belegt um frei für Erweiterungen zu sein. An SPI kann
|
||||
ggf. ein Epaper angeschlossen werden.
|
||||
ggf. ein E-Paper angeschlossen werden.
|
||||
|
||||
|
||||
Key Color Pin Remarks
|
||||
@@ -124,27 +126,35 @@ ggf. ein Epaper angeschlossen werden.
|
||||
BUZZER
|
||||
TBD
|
||||
|
||||
Bauteilliste
|
||||
Bauteilliste (WIP)
|
||||
------------
|
||||
|
||||
1x ESP32-S3 Nano (Waveshare)
|
||||
berrybase.de
|
||||
eckstein-shop.de
|
||||
5x Taster schwarz
|
||||
2x Taster gelb
|
||||
1x M12 Einbaubuchse
|
||||
1x RGB LED (gemeinsame Anode), diffus
|
||||
6x Taster schwarz (1-6)
|
||||
1x Taster farbig (DST)
|
||||
1x M12 Micro-C Einbaubuchse
|
||||
1x RGB LED (gemeinsame Kathode), diffus
|
||||
3x LED grün, diffus
|
||||
6x Widerstand 330 Ohm
|
||||
1x SN65HVD230 CAN Transceiver
|
||||
1x Buzzer, passiv
|
||||
1x Kabelsatz
|
||||
1x Terminalblock 4pol. 2,54mm schraubbar
|
||||
1x Gehäuse 150x60x40 bestehend auf Front- und Rückseite
|
||||
4x Befestigungsschraube M4
|
||||
4x Gehäuseschraube M2,5
|
||||
1x Buzzer 12V, passiv
|
||||
1x MOSFET 2N7000
|
||||
1x Widerstand 150 Ohm
|
||||
1x Kabelsatz für Tasten, 0,25 bis 0,5 mm²
|
||||
1x Terminalblock 2pol. 2,54mm schraubbar
|
||||
1x 3D-Gehäuse bestehend auf Front- und Rückseite
|
||||
1x Mutternwerkzeug 3D-Druck
|
||||
4x Befestigungsschraube M4 Senkkopf
|
||||
4x Gehäuseschraube M2,5, lang
|
||||
8x Platinenschraube M2,5, kurz
|
||||
1x Silikondichtschnur 2mm
|
||||
|
||||
1x SHT31 I²C-Modul
|
||||
Buchsenleiste 2,54 mm
|
||||
Stiftleiste 2,54mm
|
||||
2x Jumper
|
||||
1x Polyfuse
|
||||
|
||||
Konfiguration
|
||||
-------------
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
// NMEA2000 defaults
|
||||
#define N2K_DEFAULT_NODEID 124
|
||||
#define NMEA2000_HEARTBEAT_INTERVAL 5000 // milliseconds
|
||||
|
||||
// Keys
|
||||
#define KEY_1 GPIO_NUM_5 // D2
|
||||
@@ -54,3 +55,24 @@
|
||||
#define SPI_MISO GPIO_NUM_47 // D12
|
||||
#define SPI_SCK GPIO_NUM_48 // D13
|
||||
#define SPI_CS GPIO_NUM_46 // B0
|
||||
|
||||
// Button indices
|
||||
#define BUTTON_1 0
|
||||
#define BUTTON_2 1
|
||||
#define BUTTON_3 2
|
||||
#define BUTTON_4 3
|
||||
#define BUTTON_5 4
|
||||
#define BUTTON_6 5
|
||||
#define BUTTON_DST 6
|
||||
|
||||
enum class ButtonPressType : uint8_t
|
||||
{
|
||||
SHORT, // < 1 second
|
||||
MEDIUM, // >= 1 second and < 3 seconds
|
||||
LONG // >= 3 seconds
|
||||
};
|
||||
struct ButtonEvent
|
||||
{
|
||||
uint8_t buttonId;
|
||||
ButtonPressType pressType;
|
||||
};
|
||||
|
||||
@@ -41,10 +41,9 @@ build_unflags =
|
||||
|
||||
[env:esp32-s3-nano]
|
||||
build_type = release # debug | release
|
||||
# board = esp32-s3-devkitc-1
|
||||
board = esp32_s3_nano
|
||||
#board = arduino_nano_esp32 # ATTENTION! Pin numbering scheme changes
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.partitions = default.csv
|
||||
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
|
||||
upload_port = /dev/ttyACM0
|
||||
upload_protocol = esptool
|
||||
|
||||
479
src/main.cpp
479
src/main.cpp
@@ -13,9 +13,9 @@
|
||||
#include "Nmea2kTwai.h"
|
||||
#include <map>
|
||||
|
||||
|
||||
#include "mbedtls/md.h" // for SHA256
|
||||
#include <esp32/clk.h> // for cpu frequency
|
||||
#include "driver/rtc_io.h" // for wakeup from deep sleep
|
||||
|
||||
String get_sha256(String payload) {
|
||||
byte shaResult[32];
|
||||
@@ -90,8 +90,10 @@ uint64_t chipid = ESP.getEfuseMac();
|
||||
|
||||
const char* wifi_ssid = "OBPKP61";
|
||||
const char* wifi_pass = "keypad61";
|
||||
bool apEnabled = false;
|
||||
AsyncWebServer server(80);
|
||||
|
||||
unsigned long firstStart = 0;
|
||||
unsigned long lastSensor = 0;
|
||||
unsigned long lastPrint = 0;
|
||||
unsigned long counter = 0;
|
||||
@@ -100,12 +102,17 @@ bool rgb_r = false;
|
||||
bool rgb_g = false;
|
||||
bool rgb_b = false;
|
||||
|
||||
char destination = 'A'; // A | B | C
|
||||
char mode = 'N'; // (N)ormal | (C)onfig
|
||||
char ledmode = 'D'; // (D)ay | (N)ight
|
||||
char audiomode = 'E'; // (E)nabled | (D)isabled
|
||||
RTC_DATA_ATTR char destination = 'A'; // A | B | C im RTC-Speicher überlebt deep sleep
|
||||
uint8_t led_brightness = 16; // analog pin: 0 .. 255
|
||||
uint8_t rgb_brightness = 64;
|
||||
|
||||
uint buzzerpower = 50; // TBD make use of this
|
||||
|
||||
uint8_t keycode[6]; // configurable keycodes
|
||||
|
||||
SHT31 sht(SHT31_ADDRESS);
|
||||
bool sht_available = false;
|
||||
float temp = 0.0;
|
||||
@@ -143,7 +150,7 @@ String uptime_with_unit() {
|
||||
void ledtest() {
|
||||
// all led one after another to test functionality
|
||||
|
||||
Serial.print("LED test started");
|
||||
Serial.println("LED test started");
|
||||
|
||||
// Onbard RGB LED, inverted mode
|
||||
digitalWrite(LED_IR, LOW);
|
||||
@@ -173,9 +180,6 @@ void ledtest() {
|
||||
analogWrite(LED_C, led_brightness);
|
||||
delay(500);
|
||||
analogWrite(LED_C, 0);
|
||||
// select dst A finally
|
||||
analogWrite(LED_A, led_brightness);
|
||||
delay(500);
|
||||
|
||||
// enclosure rgb led (common cathode, so low is off)
|
||||
analogWrite(RGBLED_R, rgb_brightness);
|
||||
@@ -190,25 +194,126 @@ void ledtest() {
|
||||
delay(700);
|
||||
analogWrite(RGBLED_B, 0);
|
||||
|
||||
Serial.print("LED test finished");
|
||||
Serial.println("LED test finished");
|
||||
|
||||
}
|
||||
|
||||
TaskHandle_t ledTaskHandle = NULL;
|
||||
TaskHandle_t sensorTaskHandle = NULL;
|
||||
TaskHandle_t keyTaskHandle = NULL;
|
||||
|
||||
QueueHandle_t ledQueue = NULL;
|
||||
QueueHandle_t keyQueue = NULL;
|
||||
|
||||
void ledTask(void *parameter) {
|
||||
Serial.println("Starting LED task");
|
||||
for (;;) {
|
||||
vTaskDelay(5000);
|
||||
analogWrite(RGBLED_G, 10); // a short activity flash
|
||||
vTaskDelay(20);
|
||||
analogWrite(RGBLED_G, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void sensorTask(void *parameter) {
|
||||
Serial.println("Starting sensor task");
|
||||
for (;;) {
|
||||
vTaskDelay(10000); // nothing yet
|
||||
}
|
||||
}
|
||||
|
||||
void keyTask(void *parameter) {
|
||||
// short key press <= 1s
|
||||
// medium key press >1s and < 3s
|
||||
// long key press >= 3s
|
||||
Serial.println("Starting keyboard task");
|
||||
|
||||
constexpr uint8_t NUM_BUTTONS = 7;
|
||||
constexpr gpio_num_t buttonPins[NUM_BUTTONS] = {
|
||||
KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_DST
|
||||
};
|
||||
constexpr TickType_t DEBOUNCE_TIME = pdMS_TO_TICKS(30);
|
||||
constexpr TickType_t POLL_INTERVAL = pdMS_TO_TICKS(10); // 100Hz update rate (10ms)
|
||||
|
||||
bool lastRead[NUM_BUTTONS];
|
||||
bool lastStable[NUM_BUTTONS];
|
||||
TickType_t lastDebounce[NUM_BUTTONS];
|
||||
TickType_t pressStart[NUM_BUTTONS];
|
||||
// init
|
||||
for (int i = 0; i < NUM_BUTTONS; i++) {
|
||||
lastRead[i]= HIGH;
|
||||
lastStable[i] = HIGH;
|
||||
lastDebounce[i]= 0;
|
||||
pressStart[i] = 0;
|
||||
}
|
||||
for (;;) {
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
for (int i = 0; i < NUM_BUTTONS; i++) {
|
||||
bool reading = digitalRead(buttonPins[i]);
|
||||
if (reading != lastRead[i]) {
|
||||
lastDebounce[i] = now;
|
||||
lastRead[i] = reading;
|
||||
}
|
||||
if ((now - lastDebounce[i]) > DEBOUNCE_TIME) {
|
||||
if (reading != lastStable[i]) {
|
||||
lastStable[i] = reading;
|
||||
if (reading == LOW) {
|
||||
// button pressed
|
||||
pressStart[i] = now;
|
||||
} else {
|
||||
// button released: send to queue
|
||||
TickType_t duration = now - pressStart[i];
|
||||
ButtonEvent event;
|
||||
event.buttonId = i;
|
||||
if (duration < 1000) {
|
||||
event.pressType = ButtonPressType::SHORT;
|
||||
} else if (duration < 3000) {
|
||||
event.pressType = ButtonPressType::MEDIUM;
|
||||
} else {
|
||||
event.pressType = ButtonPressType::LONG;
|
||||
}
|
||||
xQueueSend(keyQueue, &event, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelay(POLL_INTERVAL);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void cpuFreqTimerCallback(TimerHandle_t xTimer) {
|
||||
Serial.println("after 3 minutes: set CPU frequency to 160MHz");
|
||||
setCpuFrequencyMhz(160);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// while (!Serial) delay(10);
|
||||
// while (!Serial) delay(10); verhindert Booten ohne USB-Verbindung
|
||||
delay(500);
|
||||
|
||||
preferences.begin("nvs", false);
|
||||
keycode[0] = preferences.getInt("key1", 1);
|
||||
keycode[1] = preferences.getInt("key2", 2);
|
||||
keycode[2] = preferences.getInt("key3", 3);
|
||||
keycode[3] = preferences.getInt("key4", 4);
|
||||
keycode[4] = preferences.getInt("key5", 5);
|
||||
keycode[5] = preferences.getInt("key6", 6);
|
||||
preferences.end();
|
||||
|
||||
// Configure I/O pins
|
||||
|
||||
// internal user led (red)
|
||||
pinMode(LED_USER, OUTPUT);
|
||||
|
||||
// Init onbard RGB led
|
||||
// Init onbard RGB led and switch off
|
||||
pinMode(LED_IR, OUTPUT);
|
||||
pinMode(LED_IG, OUTPUT);
|
||||
pinMode(LED_IB, OUTPUT);
|
||||
digitalWrite(LED_IR, HIGH);
|
||||
digitalWrite(LED_IG, HIGH);
|
||||
digitalWrite(LED_IB, HIGH);
|
||||
|
||||
// destination leds
|
||||
pinMode(LED_A, OUTPUT);
|
||||
@@ -244,6 +349,13 @@ void setup() {
|
||||
|
||||
ESP_LOGI(TAG, "Starting ...");
|
||||
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause == ESP_SLEEP_WAKEUP_EXT0) {
|
||||
ESP_LOGI(TAG, " Wake up by key");
|
||||
} else {
|
||||
destination = 'A';
|
||||
}
|
||||
|
||||
// N2K basics
|
||||
nodeid = N2K_DEFAULT_NODEID;
|
||||
ESP_LOGI(TAG, "N2K default node is %d", nodeid);
|
||||
@@ -264,6 +376,7 @@ void setup() {
|
||||
IPAddress ap_subnet(255, 255, 255, 0);
|
||||
IPAddress ap_gateway(ap_addr);
|
||||
WiFi.softAPConfig(ap_addr, ap_gateway, ap_subnet);
|
||||
apEnabled = true;
|
||||
|
||||
// Route for root / web page
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
@@ -292,11 +405,29 @@ void setup() {
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
StaticJsonDocument<512> doc;
|
||||
doc["systemName"] = "Keypad1";
|
||||
doc["logLevel"] = 0;
|
||||
doc["version"] = "0.0";
|
||||
doc["fwtype"] = "unknown";
|
||||
doc["salt"] = "secret";
|
||||
doc["AdminPassword"] = "********";
|
||||
doc["useAdminPass"] = false;
|
||||
doc["apIp"] = "192.168.15.1";
|
||||
doc["apMask"] = "255.255.255.0";
|
||||
doc["apPassword"] = "********";
|
||||
doc["stopApTime"] = 0;
|
||||
doc["cpuSpeed"] = 160;
|
||||
doc["tempFormat"] = "C";
|
||||
doc["ledBrightness"] = led_brightness;
|
||||
doc["ledRgbBrightness"] = rgb_brightness;
|
||||
doc["tempFormat"] = "C";
|
||||
doc["key1"] = keycode[BUTTON_1];
|
||||
doc["key2"] = keycode[BUTTON_2];
|
||||
doc["key3"] = keycode[BUTTON_3];
|
||||
doc["key4"] = keycode[BUTTON_4];
|
||||
doc["key5"] = keycode[BUTTON_5];
|
||||
doc["key6"] = keycode[BUTTON_6];
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
@@ -334,6 +465,13 @@ void setup() {
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
server.on("/api/update", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<100> doc;
|
||||
doc["status"] = "FAILED";
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
request->send(200, "application/json", out);
|
||||
});
|
||||
|
||||
// TODO POST vom Client entgegennehmen
|
||||
|
||||
@@ -348,17 +486,37 @@ void setup() {
|
||||
NMEA2000.SetProductInformation("00000001", // Manufacturer's Model serial code
|
||||
74, // Manufacturer's product code
|
||||
"OBPkeypad6/1", // Manufacturer's Model ID
|
||||
"1.0.0 (2025-11-28)", // Manufacturer's Software version code
|
||||
"0.1" // Manufacturer's Model version
|
||||
"0.1.0 (2026-01-04)", // Manufacturer's Software version code
|
||||
"0.1", // Manufacturer's Model version
|
||||
1 // LoadEquivalency (LEN)
|
||||
);
|
||||
|
||||
uint32_t uid; // ISO 21bit identity number devived from chipid (MAC)
|
||||
uid = ((chipid >> 32) ^ (chipid & 0xFFFFFFFF)) & 0x1FFFFF;
|
||||
// TODO Device unique id stored in preferences
|
||||
NMEA2000.SetDeviceInformation(1, // Unique number. Use e.g. Serial number.
|
||||
130, // Device function=Atmospheric
|
||||
85, // Device class=External Environment
|
||||
2046
|
||||
NMEA2000.SetDeviceInformation(uid, // Unique number. Use e.g. Serial number.
|
||||
130, // Device function=Button Interface (or 190?)
|
||||
110, // Device class=Human Interface? (or 140?)
|
||||
2046, // Manufacturer code (custom OBP)
|
||||
4 // Industrygoup=Marine
|
||||
);
|
||||
|
||||
NMEA2000.SetForwardType(tNMEA2000::fwdt_Text);
|
||||
|
||||
// Debug
|
||||
// NMEA2000.SetForwardStream(&Serial);
|
||||
// NMEA2000.SetDebugMode(tNMEA2000::dm_2);
|
||||
|
||||
NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, nodeid);
|
||||
NMEA2000.SetForwardOwnMessages(false);
|
||||
NMEA2000.SetHeartbeatIntervalAndOffset(NMEA2000_HEARTBEAT_INTERVAL);
|
||||
|
||||
const unsigned long TransmitPGNs[] = {
|
||||
127502UL,
|
||||
0
|
||||
};
|
||||
NMEA2000.ExtendTransmitMessages(TransmitPGNs);
|
||||
NMEA2000.Open();
|
||||
|
||||
// internal user led (red)
|
||||
digitalWrite(LED_USER, HIGH);
|
||||
@@ -369,10 +527,11 @@ void setup() {
|
||||
ledcSetup(LEDC_CHANNEL, LEDC_BASE_FREQ, LEDC_TIMER_8_BIT);
|
||||
ledcAttachPin(BUZZER, LEDC_CHANNEL);
|
||||
// Test tone while booting
|
||||
// Buzzer 12V, 2500Hz +/- 200Hz, 30mA, ca. 90dB
|
||||
if (ledcWriteTone(LEDC_CHANNEL, 2300) == 0) {
|
||||
Serial.println("Error setting buzzer tone");
|
||||
} else {
|
||||
delay(750);
|
||||
delay(50);
|
||||
ledcWriteTone(LEDC_CHANNEL, 0); // Buzzer off
|
||||
}
|
||||
|
||||
@@ -381,6 +540,19 @@ void setup() {
|
||||
delay(500);
|
||||
ledtest();
|
||||
|
||||
// select current destination
|
||||
switch (destination) {
|
||||
case 'A':
|
||||
analogWrite(LED_A, led_brightness);
|
||||
break;
|
||||
case 'B':
|
||||
analogWrite(LED_B, led_brightness);
|
||||
break;
|
||||
case 'C':
|
||||
analogWrite(LED_C, led_brightness);
|
||||
break;
|
||||
}
|
||||
|
||||
// I²C
|
||||
Serial.print("SHT31_LIB_VERSION: ");
|
||||
Serial.println(SHT31_LIB_VERSION);
|
||||
@@ -396,18 +568,47 @@ void setup() {
|
||||
// Additional tests
|
||||
String passhash = get_sha256("secretTEST");
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
ledTask, // Task function
|
||||
"LEDTask", // Task name
|
||||
10000, // Stack size (bytes)
|
||||
NULL, // Parameters
|
||||
1, // Priority
|
||||
&ledTaskHandle, // Task handle
|
||||
1 // Core 1
|
||||
);
|
||||
xTaskCreatePinnedToCore(sensorTask,"SensorTask", 10000, NULL, 1, &sensorTaskHandle, 1);
|
||||
xTaskCreatePinnedToCore(keyTask,"KeyboardTask", 10000, NULL, 1, &keyTaskHandle, 1);
|
||||
|
||||
// Create queues (5 items, each uint16_t)
|
||||
keyQueue = xQueueCreate(5, sizeof(ButtonEvent));
|
||||
ledQueue = xQueueCreate(5, sizeof(uint8_t));
|
||||
|
||||
if (esp_sleep_is_valid_wakeup_gpio(KEY_DST)) {
|
||||
Serial.println("DST-Taste ist als Wakeup-Pin konfiguriert");
|
||||
esp_sleep_enable_ext0_wakeup(KEY_DST, 0);
|
||||
} else {
|
||||
Serial.println("Keine Wakeup-Funktion vorhanden! Deep-sleep deaktiviert.");
|
||||
}
|
||||
|
||||
TimerHandle_t cpuFreqTimer = xTimerCreate(
|
||||
"cpuFreqTimer",
|
||||
pdMS_TO_TICKS(2 * 60 * 1000),
|
||||
pdFALSE, // one shot only
|
||||
nullptr,
|
||||
cpuFreqTimerCallback
|
||||
);
|
||||
xTimerStart(cpuFreqTimer, 0);
|
||||
|
||||
}
|
||||
|
||||
void shortBeep() {
|
||||
ledcWriteTone(LEDC_CHANNEL, 2500);
|
||||
delay(15);
|
||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
||||
}
|
||||
|
||||
void atariKeyclick() {
|
||||
/*
|
||||
* Anderer 12V-fähiger Buzzer!
|
||||
*
|
||||
Buzzer + → 12V
|
||||
Buzzer – → IRLZ44N Drain
|
||||
IRLZ44N Source → GND
|
||||
ESP32 GPIO43 → 1kΩ resistor → Gate
|
||||
ESP32 GND must be connected to the same GND as the 12V supply.
|
||||
* */
|
||||
ledcWriteTone(LEDC_CHANNEL, 3200);
|
||||
delayMicroseconds(3000);
|
||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
||||
@@ -417,83 +618,50 @@ void atariKeyclick() {
|
||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
||||
}
|
||||
|
||||
void SendToN2K(uint8_t keycode) {
|
||||
tN2kMsg N2kMsg;
|
||||
tN2kBinaryStatus bankstatus;
|
||||
N2kResetBinaryStatus(bankstatus);
|
||||
N2kSetStatusBinaryOnStatus(bankstatus, N2kOnOff_On, keycode);
|
||||
|
||||
// TODO
|
||||
// Needs filled N2K device list to map NAME to current address
|
||||
// N2kMsg.Destination = deviceAddress;
|
||||
//
|
||||
SetN2kPGN127502(N2kMsg, 0, bankstatus);
|
||||
NMEA2000.SendMsg(N2kMsg);
|
||||
|
||||
Serial.print("PGN127502 sent: Switch=");
|
||||
Serial.print(keycode);
|
||||
Serial.println(" Action=On");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// Button pressed? (active low)
|
||||
uint8_t button = 0;
|
||||
if (digitalRead(KEY_1) == LOW) {
|
||||
Serial.println("Button detected: 1");
|
||||
button = 1;
|
||||
if (rgb_r) {
|
||||
rgb_r = false;
|
||||
analogWrite(RGBLED_R, 255);
|
||||
} else {
|
||||
rgb_r = true;
|
||||
analogWrite(RGBLED_R, 255 - rgb_brightness);
|
||||
ButtonEvent event;
|
||||
if (xQueueReceive(keyQueue, &event, 0) == pdPASS) {
|
||||
Serial.print("Button ");
|
||||
Serial.print(event.buttonId);
|
||||
Serial.print(" -> ");
|
||||
switch (event.pressType) {
|
||||
case ButtonPressType::SHORT:
|
||||
Serial.println("SHORT");
|
||||
break;
|
||||
case ButtonPressType::MEDIUM:
|
||||
Serial.println("MEDIUM");
|
||||
break;
|
||||
case ButtonPressType::LONG:
|
||||
Serial.println("LONG");
|
||||
break;
|
||||
default:
|
||||
Serial.print("UNBEKANNT: ");
|
||||
Serial.println(static_cast<uint8_t>(event.pressType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (digitalRead(KEY_2) == LOW) {
|
||||
Serial.println("Button detected: 2");
|
||||
button += 2;
|
||||
if (rgb_g) {
|
||||
rgb_g = false;
|
||||
analogWrite(RGBLED_G, 255);
|
||||
} else {
|
||||
rgb_g = true;
|
||||
analogWrite(RGBLED_G, 255 - rgb_brightness);
|
||||
}
|
||||
}
|
||||
if (digitalRead(KEY_3) == LOW) {
|
||||
Serial.println("Button detected: 3");
|
||||
button += 4;
|
||||
if (rgb_b) {
|
||||
rgb_b = false;
|
||||
analogWrite(RGBLED_B, 255);
|
||||
} else {
|
||||
rgb_b = true;
|
||||
analogWrite(RGBLED_B, 255 - rgb_brightness);
|
||||
}
|
||||
}
|
||||
|
||||
if (digitalRead(KEY_4) == LOW) {
|
||||
Serial.println("Button detected: 4");
|
||||
button += 8;
|
||||
|
||||
atariKeyclick();
|
||||
|
||||
digitalWrite(LED_USER, HIGH); // Turn LED on
|
||||
delay(500); // Keep it on 0.5s
|
||||
digitalWrite(LED_USER, LOW); // Turn LED off
|
||||
}
|
||||
if (digitalRead(KEY_5) == LOW) {
|
||||
Serial.println("Button detected: 5");
|
||||
button += 16;
|
||||
|
||||
ledcWriteTone(LEDC_CHANNEL, 3200);
|
||||
delay(8);
|
||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
||||
|
||||
digitalWrite(LED_USER, HIGH); // Turn LED on
|
||||
delay(500); // Keep it on 0.5s
|
||||
digitalWrite(LED_USER, LOW); // Turn LED off
|
||||
}
|
||||
if (digitalRead(KEY_6) == LOW) {
|
||||
Serial.println("Button detected: 6");
|
||||
button += 32;
|
||||
|
||||
ledcWriteTone(LEDC_CHANNEL, 3200);
|
||||
delay(8);
|
||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
||||
|
||||
digitalWrite(LED_USER, HIGH); // Turn LED on
|
||||
delay(500); // Keep it on 0.5s
|
||||
digitalWrite(LED_USER, LOW); // Turn LED off
|
||||
}
|
||||
|
||||
if (digitalRead(KEY_DST) == LOW) {
|
||||
Serial.println("Button detected: DST");
|
||||
button += 64;
|
||||
|
||||
if (event.buttonId == BUTTON_DST) {
|
||||
if (event.pressType == ButtonPressType::SHORT) {
|
||||
if (mode == 'N') {
|
||||
// switch destination only in normal mode
|
||||
if (destination == 'A') {
|
||||
destination = 'B';
|
||||
analogWrite(LED_A, 0);
|
||||
@@ -507,28 +675,104 @@ void loop() {
|
||||
analogWrite(LED_C, 0);
|
||||
analogWrite(LED_A, led_brightness);
|
||||
}
|
||||
Serial.print("Destination=");
|
||||
Serial.print("New destination=");
|
||||
Serial.println(destination);
|
||||
}
|
||||
} else if (event.pressType == ButtonPressType::LONG) {
|
||||
shortBeep();
|
||||
// switch config mode
|
||||
if (mode == 'N') {
|
||||
mode = 'C';
|
||||
analogWrite(RGBLED_B, rgb_brightness); // blue status indicator
|
||||
} else {
|
||||
mode = 'N';
|
||||
analogWrite(RGBLED_B, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal button
|
||||
if (mode == 'N') {
|
||||
// TODO send key code to destination
|
||||
atariKeyclick();
|
||||
SendToN2K(keycode[event.buttonId]);
|
||||
} else {
|
||||
// online config mode
|
||||
switch (event.buttonId) {
|
||||
case BUTTON_1: // switch day/night mode
|
||||
if (ledmode == 'D') {
|
||||
Serial.println("Night mode enabled");
|
||||
ledmode = 'N';
|
||||
} else {
|
||||
Serial.println("Day mode enabled");
|
||||
ledmode = 'D';
|
||||
}
|
||||
break;
|
||||
case BUTTON_2: // switch audio on/off
|
||||
if (audiomode == 'E') {
|
||||
Serial.println("Disabled audio");
|
||||
audiomode = 'D';
|
||||
} else {
|
||||
Serial.println("Enabled audio");
|
||||
audiomode = 'E';
|
||||
}
|
||||
break;
|
||||
case BUTTON_3: // switch accesspoint on/off
|
||||
if (apEnabled) {
|
||||
Serial.println("Disable Accesspoint");
|
||||
WiFi.softAPdisconnect(true);
|
||||
apEnabled = false;
|
||||
} else {
|
||||
Serial.println("Enable Accesspoint");
|
||||
WiFi.softAP(wifi_ssid, wifi_pass);
|
||||
apEnabled = true;
|
||||
}
|
||||
break;
|
||||
case BUTTON_4: // reserved
|
||||
break;
|
||||
case BUTTON_5: // reset
|
||||
Serial.println("Device reset");
|
||||
analogWrite(RGBLED_B, 0);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_G, 255);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_G, 0);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_G, 255);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_G, 0);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_G, 255);
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
break;
|
||||
case BUTTON_6: // deep sleep
|
||||
Serial.println("Going into deep sleep");
|
||||
Serial.flush();
|
||||
analogWrite(RGBLED_B, 0);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_B, 255);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_B, 0);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_B, 255);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_B, 0);
|
||||
delay(500);
|
||||
analogWrite(RGBLED_B, 255);
|
||||
delay(500);
|
||||
|
||||
/*
|
||||
digitalWrite(LED_USER, HIGH); // Turn LED on
|
||||
delay(500); // Keep it on 0.5s
|
||||
digitalWrite(LED_USER, LOW); // Turn LED off
|
||||
*/
|
||||
rtc_gpio_pullup_en(KEY_DST);
|
||||
rtc_gpio_pulldown_dis(KEY_DST);
|
||||
esp_deep_sleep_start();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (button > 0) {
|
||||
Serial.print(temp, 1);
|
||||
Serial.print("\t");
|
||||
Serial.println(hum, 1);
|
||||
// Debounce delay to avoid multiple triggers
|
||||
delay(200);
|
||||
}
|
||||
|
||||
|
||||
// NMEA2000.loop();
|
||||
// NMEA2000.ParseMessages();
|
||||
NMEA2000.loop();
|
||||
NMEA2000.ParseMessages();
|
||||
|
||||
if (millis() - lastSensor >= 5000) {
|
||||
lastSensor = millis();
|
||||
@@ -544,4 +788,5 @@ void loop() {
|
||||
Serial.printf("Loop counter: %lu\n", counter);
|
||||
}
|
||||
|
||||
delay(1); // 1ms für freertos
|
||||
}
|
||||
|
||||
@@ -125,5 +125,65 @@
|
||||
],
|
||||
"description": "Temperature format: Kelvin, Celsius or Fahrenheit [K|C|F].",
|
||||
"category": "Units"
|
||||
},
|
||||
{
|
||||
"name": "key1",
|
||||
"label": "Key 1 code",
|
||||
"type": "number",
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 28,
|
||||
"description": "The keycode to send for key 1 (leftmost) [1 .. 28]",
|
||||
"category": "Keys"
|
||||
},
|
||||
{
|
||||
"name": "key2",
|
||||
"label": "Key 2 code",
|
||||
"type": "number",
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"max": 28,
|
||||
"description": "The keycode to send for key 2 (second from left) [1 .. 28]",
|
||||
"category": "Keys"
|
||||
},
|
||||
{
|
||||
"name": "key3",
|
||||
"label": "Key 3 code",
|
||||
"type": "number",
|
||||
"default": 3,
|
||||
"min": 1,
|
||||
"max": 28,
|
||||
"description": "The keycode to send for key 3 (third from left) [1 .. 28]",
|
||||
"category": "Keys"
|
||||
},
|
||||
{
|
||||
"name": "key4",
|
||||
"label": "Key 4 code",
|
||||
"type": "number",
|
||||
"default": 4,
|
||||
"min": 1,
|
||||
"max": 28,
|
||||
"description": "The keycode to send for key 4 (fourth from left) [1 .. 28]",
|
||||
"category": "Keys"
|
||||
},
|
||||
{
|
||||
"name": "key5",
|
||||
"label": "Key 5 code",
|
||||
"type": "number",
|
||||
"default": 5,
|
||||
"min": 1,
|
||||
"max": 28,
|
||||
"description": "The keycode to send for key 5 (fifth from left) [1 .. 28]",
|
||||
"category": "Keys"
|
||||
},
|
||||
{
|
||||
"name": "key6",
|
||||
"label": "Key 6 code",
|
||||
"type": "number",
|
||||
"default": 6,
|
||||
"min": 1,
|
||||
"max": 28,
|
||||
"description": "The keycode to send for key 6 (rightmost) [1 .. 28]",
|
||||
"category": "Keys"
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user