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
|
- Stromversorgung über M12-Anschluß über NMEA2000
|
||||||
Eingangsbereich 6~21V
|
- Eingangsbereich 6~21V
|
||||||
- Ein- und Ausschalten durch langen Tastendruck auf DST/ ONOFF
|
- Konfigurationsmodus durch langen Tastendruck auf DST
|
||||||
|
- Tiefschlaf und Reset aus Konfigmodus heraus auswählbar
|
||||||
- Konfiguration über Web-Interface
|
- 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
|
- Buzzer für Tastendruck-Feedback
|
||||||
passiv, damit können die Töne mit PWM programmiert werden
|
passiv, damit können die Töne mit PWM programmiert werden
|
||||||
- I²C Temp/Hum-Sensor SHT31
|
- I²C Temp/Hum-Sensor SHT31
|
||||||
@@ -25,14 +24,15 @@ Optionen für später
|
|||||||
bedeutet auch: viel komplizierteres Gehäuse
|
bedeutet auch: viel komplizierteres Gehäuse
|
||||||
|
|
||||||
Damit die LEDs nicht stören, kann umgeschaltet werden zwischen
|
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
|
Einschaltvorgang
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Es erfolgt ein "Lampentest": Alle LEDs werden einmal durchgeschaltet.
|
- Status leuchtet kurz rot auf
|
||||||
|
- Es erfolgt ein "Lampentest": Alle LEDs werden einmal durchgeschaltet.
|
||||||
Sind alle Tests erfolgreich ertönt ein Buzzer-Signal.
|
- Sind alle Tests erfolgreich ertönt ein Buzzer-Signal.
|
||||||
|
|
||||||
Beschreibung
|
Beschreibung
|
||||||
------------
|
------------
|
||||||
@@ -44,31 +44,29 @@ Die Destination-LEDs leuchten nur, wenn ein entsprechendes
|
|||||||
N2K-Zielgerät konfiguriert und erkannt wurde.
|
N2K-Zielgerät konfiguriert und erkannt wurde.
|
||||||
|
|
||||||
|
|
||||||
|
Gehäuse
|
||||||
|
-------
|
||||||
|
|
||||||
Bohrung Taster: 12mm
|
Bohrung Taster: 12mm
|
||||||
Taster Außenmaß: 17.5mm
|
Taster Außenmaß: 17.5mm
|
||||||
|
|
||||||
Verbindungskabel CPU-Platine
|
Verbindungskabel CPU-Platine
|
||||||
JST 2.54 XH 6 Pin Steckverbinder -> LED
|
JST 2.54 XH 7 Pin Steckverbinder -> LEDs + GND
|
||||||
|
|
||||||
Anschlußmöglichkeiten
|
Anschlußmöglichkeiten
|
||||||
|
---------------------
|
||||||
|
|
||||||
für Stromversorgung +12V und NMEA2000
|
für Stromversorgung +12V und NMEA2000
|
||||||
4pin Terminalblock steck-/schraubbar +12V, DNG, CAN-L, CAN-H
|
2pin Terminalblock
|
||||||
|
|
||||||
für I²C-Module
|
für I²C-Module
|
||||||
2x 4pin Buchsenleiste weibl.
|
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
|
für mechanische Taster
|
||||||
1x JST 2.54 XH 7 Pin Steckverbinder -> Tasten
|
1x Terminalblock 8fach, 7 Tasten und GND
|
||||||
Masseverbindung über einzelnes getrenntes Kabel
|
|
||||||
in eine 8-fach WAGO-Klemme
|
|
||||||
|
|
||||||
für LEDs
|
|
||||||
TBD
|
|
||||||
|
|
||||||
|
|
||||||
Bemerkungen
|
Bemerkungen
|
||||||
@@ -77,6 +75,10 @@ Bemerkungen
|
|||||||
Bei den aktuell verwendeten Tasten sind die Anschlußdrähte extrem
|
Bei den aktuell verwendeten Tasten sind die Anschlußdrähte extrem
|
||||||
filigran. Leichtes Brechen und schlechte Verarbeitung.
|
filigran. Leichtes Brechen und schlechte Verarbeitung.
|
||||||
|
|
||||||
|
Es gibt verschiedene Varianten mit unterschiedlicher Federkraft.
|
||||||
|
Auswahl muß noch erfolgen
|
||||||
|
|
||||||
|
|
||||||
Beschaltung MCU Nano
|
Beschaltung MCU Nano
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@@ -94,7 +96,7 @@ werden!
|
|||||||
|
|
||||||
Die Pins für SPI (D11, D12, D13) sind absichtlich
|
Die Pins für SPI (D11, D12, D13) sind absichtlich
|
||||||
nicht belegt um frei für Erweiterungen zu sein. An SPI kann
|
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
|
Key Color Pin Remarks
|
||||||
@@ -124,27 +126,35 @@ ggf. ein Epaper angeschlossen werden.
|
|||||||
BUZZER
|
BUZZER
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
Bauteilliste
|
Bauteilliste (WIP)
|
||||||
------------
|
------------
|
||||||
|
|
||||||
1x ESP32-S3 Nano (Waveshare)
|
1x ESP32-S3 Nano (Waveshare)
|
||||||
berrybase.de
|
berrybase.de
|
||||||
eckstein-shop.de
|
eckstein-shop.de
|
||||||
5x Taster schwarz
|
6x Taster schwarz (1-6)
|
||||||
2x Taster gelb
|
1x Taster farbig (DST)
|
||||||
1x M12 Einbaubuchse
|
1x M12 Micro-C Einbaubuchse
|
||||||
1x RGB LED (gemeinsame Anode), diffus
|
1x RGB LED (gemeinsame Kathode), diffus
|
||||||
3x LED grün, diffus
|
3x LED grün, diffus
|
||||||
6x Widerstand 330 Ohm
|
6x Widerstand 330 Ohm
|
||||||
1x SN65HVD230 CAN Transceiver
|
1x SN65HVD230 CAN Transceiver
|
||||||
1x Buzzer, passiv
|
1x Buzzer 12V, passiv
|
||||||
1x Kabelsatz
|
1x MOSFET 2N7000
|
||||||
1x Terminalblock 4pol. 2,54mm schraubbar
|
1x Widerstand 150 Ohm
|
||||||
1x Gehäuse 150x60x40 bestehend auf Front- und Rückseite
|
1x Kabelsatz für Tasten, 0,25 bis 0,5 mm²
|
||||||
4x Befestigungsschraube M4
|
1x Terminalblock 2pol. 2,54mm schraubbar
|
||||||
4x Gehäuseschraube M2,5
|
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 Silikondichtschnur 2mm
|
||||||
|
1x SHT31 I²C-Modul
|
||||||
|
Buchsenleiste 2,54 mm
|
||||||
|
Stiftleiste 2,54mm
|
||||||
|
2x Jumper
|
||||||
|
1x Polyfuse
|
||||||
|
|
||||||
Konfiguration
|
Konfiguration
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
// NMEA2000 defaults
|
// NMEA2000 defaults
|
||||||
#define N2K_DEFAULT_NODEID 124
|
#define N2K_DEFAULT_NODEID 124
|
||||||
|
#define NMEA2000_HEARTBEAT_INTERVAL 5000 // milliseconds
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
#define KEY_1 GPIO_NUM_5 // D2
|
#define KEY_1 GPIO_NUM_5 // D2
|
||||||
@@ -54,3 +55,24 @@
|
|||||||
#define SPI_MISO GPIO_NUM_47 // D12
|
#define SPI_MISO GPIO_NUM_47 // D12
|
||||||
#define SPI_SCK GPIO_NUM_48 // D13
|
#define SPI_SCK GPIO_NUM_48 // D13
|
||||||
#define SPI_CS GPIO_NUM_46 // B0
|
#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]
|
[env:esp32-s3-nano]
|
||||||
build_type = release # debug | release
|
build_type = release # debug | release
|
||||||
# board = esp32-s3-devkitc-1
|
|
||||||
board = esp32_s3_nano
|
board = esp32_s3_nano
|
||||||
#board = arduino_nano_esp32 # ATTENTION! Pin numbering scheme changes
|
#board = arduino_nano_esp32 # ATTENTION! Pin numbering scheme changes
|
||||||
board_upload.flash_size = 16MB
|
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_port = /dev/ttyACM0
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
|
|||||||
507
src/main.cpp
507
src/main.cpp
@@ -13,9 +13,9 @@
|
|||||||
#include "Nmea2kTwai.h"
|
#include "Nmea2kTwai.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
#include "mbedtls/md.h" // for SHA256
|
#include "mbedtls/md.h" // for SHA256
|
||||||
#include <esp32/clk.h> // for cpu frequency
|
#include <esp32/clk.h> // for cpu frequency
|
||||||
|
#include "driver/rtc_io.h" // for wakeup from deep sleep
|
||||||
|
|
||||||
String get_sha256(String payload) {
|
String get_sha256(String payload) {
|
||||||
byte shaResult[32];
|
byte shaResult[32];
|
||||||
@@ -90,8 +90,10 @@ uint64_t chipid = ESP.getEfuseMac();
|
|||||||
|
|
||||||
const char* wifi_ssid = "OBPKP61";
|
const char* wifi_ssid = "OBPKP61";
|
||||||
const char* wifi_pass = "keypad61";
|
const char* wifi_pass = "keypad61";
|
||||||
|
bool apEnabled = false;
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
unsigned long firstStart = 0;
|
||||||
unsigned long lastSensor = 0;
|
unsigned long lastSensor = 0;
|
||||||
unsigned long lastPrint = 0;
|
unsigned long lastPrint = 0;
|
||||||
unsigned long counter = 0;
|
unsigned long counter = 0;
|
||||||
@@ -100,12 +102,17 @@ bool rgb_r = false;
|
|||||||
bool rgb_g = false;
|
bool rgb_g = false;
|
||||||
bool rgb_b = 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 led_brightness = 16; // analog pin: 0 .. 255
|
||||||
uint8_t rgb_brightness = 64;
|
uint8_t rgb_brightness = 64;
|
||||||
|
|
||||||
uint buzzerpower = 50; // TBD make use of this
|
uint buzzerpower = 50; // TBD make use of this
|
||||||
|
|
||||||
|
uint8_t keycode[6]; // configurable keycodes
|
||||||
|
|
||||||
SHT31 sht(SHT31_ADDRESS);
|
SHT31 sht(SHT31_ADDRESS);
|
||||||
bool sht_available = false;
|
bool sht_available = false;
|
||||||
float temp = 0.0;
|
float temp = 0.0;
|
||||||
@@ -143,7 +150,7 @@ String uptime_with_unit() {
|
|||||||
void ledtest() {
|
void ledtest() {
|
||||||
// all led one after another to test functionality
|
// all led one after another to test functionality
|
||||||
|
|
||||||
Serial.print("LED test started");
|
Serial.println("LED test started");
|
||||||
|
|
||||||
// Onbard RGB LED, inverted mode
|
// Onbard RGB LED, inverted mode
|
||||||
digitalWrite(LED_IR, LOW);
|
digitalWrite(LED_IR, LOW);
|
||||||
@@ -173,9 +180,6 @@ void ledtest() {
|
|||||||
analogWrite(LED_C, led_brightness);
|
analogWrite(LED_C, led_brightness);
|
||||||
delay(500);
|
delay(500);
|
||||||
analogWrite(LED_C, 0);
|
analogWrite(LED_C, 0);
|
||||||
// select dst A finally
|
|
||||||
analogWrite(LED_A, led_brightness);
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
// enclosure rgb led (common cathode, so low is off)
|
// enclosure rgb led (common cathode, so low is off)
|
||||||
analogWrite(RGBLED_R, rgb_brightness);
|
analogWrite(RGBLED_R, rgb_brightness);
|
||||||
@@ -190,25 +194,126 @@ void ledtest() {
|
|||||||
delay(700);
|
delay(700);
|
||||||
analogWrite(RGBLED_B, 0);
|
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() {
|
void setup() {
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
// while (!Serial) delay(10);
|
// while (!Serial) delay(10); verhindert Booten ohne USB-Verbindung
|
||||||
delay(500);
|
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
|
// Configure I/O pins
|
||||||
|
|
||||||
// internal user led (red)
|
// internal user led (red)
|
||||||
pinMode(LED_USER, OUTPUT);
|
pinMode(LED_USER, OUTPUT);
|
||||||
|
|
||||||
// Init onbard RGB led
|
// Init onbard RGB led and switch off
|
||||||
pinMode(LED_IR, OUTPUT);
|
pinMode(LED_IR, OUTPUT);
|
||||||
pinMode(LED_IG, OUTPUT);
|
pinMode(LED_IG, OUTPUT);
|
||||||
pinMode(LED_IB, OUTPUT);
|
pinMode(LED_IB, OUTPUT);
|
||||||
|
digitalWrite(LED_IR, HIGH);
|
||||||
|
digitalWrite(LED_IG, HIGH);
|
||||||
|
digitalWrite(LED_IB, HIGH);
|
||||||
|
|
||||||
// destination leds
|
// destination leds
|
||||||
pinMode(LED_A, OUTPUT);
|
pinMode(LED_A, OUTPUT);
|
||||||
@@ -244,6 +349,13 @@ void setup() {
|
|||||||
|
|
||||||
ESP_LOGI(TAG, "Starting ...");
|
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
|
// N2K basics
|
||||||
nodeid = N2K_DEFAULT_NODEID;
|
nodeid = N2K_DEFAULT_NODEID;
|
||||||
ESP_LOGI(TAG, "N2K default node is %d", 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_subnet(255, 255, 255, 0);
|
||||||
IPAddress ap_gateway(ap_addr);
|
IPAddress ap_gateway(ap_addr);
|
||||||
WiFi.softAPConfig(ap_addr, ap_gateway, ap_subnet);
|
WiFi.softAPConfig(ap_addr, ap_gateway, ap_subnet);
|
||||||
|
apEnabled = true;
|
||||||
|
|
||||||
// Route for root / web page
|
// Route for root / web page
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
@@ -292,11 +405,29 @@ void setup() {
|
|||||||
request->send(200, "application/json", out);
|
request->send(200, "application/json", out);
|
||||||
});
|
});
|
||||||
server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
StaticJsonDocument<100> doc;
|
StaticJsonDocument<512> doc;
|
||||||
doc["systemName"] = "Keypad1";
|
doc["systemName"] = "Keypad1";
|
||||||
|
doc["logLevel"] = 0;
|
||||||
doc["version"] = "0.0";
|
doc["version"] = "0.0";
|
||||||
doc["fwtype"] = "unknown";
|
doc["fwtype"] = "unknown";
|
||||||
doc["salt"] = "secret";
|
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;
|
String out;
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
request->send(200, "application/json", out);
|
request->send(200, "application/json", out);
|
||||||
@@ -334,6 +465,13 @@ void setup() {
|
|||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
request->send(200, "application/json", 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
|
// TODO POST vom Client entgegennehmen
|
||||||
|
|
||||||
@@ -348,17 +486,37 @@ void setup() {
|
|||||||
NMEA2000.SetProductInformation("00000001", // Manufacturer's Model serial code
|
NMEA2000.SetProductInformation("00000001", // Manufacturer's Model serial code
|
||||||
74, // Manufacturer's product code
|
74, // Manufacturer's product code
|
||||||
"OBPkeypad6/1", // Manufacturer's Model ID
|
"OBPkeypad6/1", // Manufacturer's Model ID
|
||||||
"1.0.0 (2025-11-28)", // Manufacturer's Software version code
|
"0.1.0 (2026-01-04)", // Manufacturer's Software version code
|
||||||
"0.1" // Manufacturer's Model version
|
"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
|
// TODO Device unique id stored in preferences
|
||||||
NMEA2000.SetDeviceInformation(1, // Unique number. Use e.g. Serial number.
|
NMEA2000.SetDeviceInformation(uid, // Unique number. Use e.g. Serial number.
|
||||||
130, // Device function=Atmospheric
|
130, // Device function=Button Interface (or 190?)
|
||||||
85, // Device class=External Environment
|
110, // Device class=Human Interface? (or 140?)
|
||||||
2046
|
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)
|
// internal user led (red)
|
||||||
digitalWrite(LED_USER, HIGH);
|
digitalWrite(LED_USER, HIGH);
|
||||||
@@ -369,10 +527,11 @@ void setup() {
|
|||||||
ledcSetup(LEDC_CHANNEL, LEDC_BASE_FREQ, LEDC_TIMER_8_BIT);
|
ledcSetup(LEDC_CHANNEL, LEDC_BASE_FREQ, LEDC_TIMER_8_BIT);
|
||||||
ledcAttachPin(BUZZER, LEDC_CHANNEL);
|
ledcAttachPin(BUZZER, LEDC_CHANNEL);
|
||||||
// Test tone while booting
|
// Test tone while booting
|
||||||
|
// Buzzer 12V, 2500Hz +/- 200Hz, 30mA, ca. 90dB
|
||||||
if (ledcWriteTone(LEDC_CHANNEL, 2300) == 0) {
|
if (ledcWriteTone(LEDC_CHANNEL, 2300) == 0) {
|
||||||
Serial.println("Error setting buzzer tone");
|
Serial.println("Error setting buzzer tone");
|
||||||
} else {
|
} else {
|
||||||
delay(750);
|
delay(50);
|
||||||
ledcWriteTone(LEDC_CHANNEL, 0); // Buzzer off
|
ledcWriteTone(LEDC_CHANNEL, 0); // Buzzer off
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,6 +539,19 @@ void setup() {
|
|||||||
analogWrite(RGBLED_R, 0); // boot status off
|
analogWrite(RGBLED_R, 0); // boot status off
|
||||||
delay(500);
|
delay(500);
|
||||||
ledtest();
|
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
|
// I²C
|
||||||
Serial.print("SHT31_LIB_VERSION: ");
|
Serial.print("SHT31_LIB_VERSION: ");
|
||||||
@@ -396,18 +568,47 @@ void setup() {
|
|||||||
// Additional tests
|
// Additional tests
|
||||||
String passhash = get_sha256("secretTEST");
|
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() {
|
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);
|
ledcWriteTone(LEDC_CHANNEL, 3200);
|
||||||
delayMicroseconds(3000);
|
delayMicroseconds(3000);
|
||||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
ledcWriteTone(LEDC_CHANNEL, 0);
|
||||||
@@ -417,118 +618,161 @@ void atariKeyclick() {
|
|||||||
ledcWriteTone(LEDC_CHANNEL, 0);
|
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() {
|
void loop() {
|
||||||
|
|
||||||
// Button pressed? (active low)
|
ButtonEvent event;
|
||||||
uint8_t button = 0;
|
if (xQueueReceive(keyQueue, &event, 0) == pdPASS) {
|
||||||
if (digitalRead(KEY_1) == LOW) {
|
Serial.print("Button ");
|
||||||
Serial.println("Button detected: 1");
|
Serial.print(event.buttonId);
|
||||||
button = 1;
|
Serial.print(" -> ");
|
||||||
if (rgb_r) {
|
switch (event.pressType) {
|
||||||
rgb_r = false;
|
case ButtonPressType::SHORT:
|
||||||
analogWrite(RGBLED_R, 255);
|
Serial.println("SHORT");
|
||||||
} else {
|
break;
|
||||||
rgb_r = true;
|
case ButtonPressType::MEDIUM:
|
||||||
analogWrite(RGBLED_R, 255 - rgb_brightness);
|
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 (event.buttonId == BUTTON_DST) {
|
||||||
if (digitalRead(KEY_2) == LOW) {
|
if (event.pressType == ButtonPressType::SHORT) {
|
||||||
Serial.println("Button detected: 2");
|
if (mode == 'N') {
|
||||||
button += 2;
|
// switch destination only in normal mode
|
||||||
if (rgb_g) {
|
if (destination == 'A') {
|
||||||
rgb_g = false;
|
destination = 'B';
|
||||||
analogWrite(RGBLED_G, 255);
|
analogWrite(LED_A, 0);
|
||||||
|
analogWrite(LED_B, led_brightness);
|
||||||
|
} else if (destination == 'B') {
|
||||||
|
destination = 'C';
|
||||||
|
analogWrite(LED_B, 0);
|
||||||
|
analogWrite(LED_C, led_brightness);
|
||||||
|
} else {
|
||||||
|
destination = 'A';
|
||||||
|
analogWrite(LED_C, 0);
|
||||||
|
analogWrite(LED_A, led_brightness);
|
||||||
|
}
|
||||||
|
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 {
|
} else {
|
||||||
rgb_g = true;
|
// normal button
|
||||||
analogWrite(RGBLED_G, 255 - rgb_brightness);
|
if (mode == 'N') {
|
||||||
}
|
// TODO send key code to destination
|
||||||
}
|
atariKeyclick();
|
||||||
if (digitalRead(KEY_3) == LOW) {
|
SendToN2K(keycode[event.buttonId]);
|
||||||
Serial.println("Button detected: 3");
|
} else {
|
||||||
button += 4;
|
// online config mode
|
||||||
if (rgb_b) {
|
switch (event.buttonId) {
|
||||||
rgb_b = false;
|
case BUTTON_1: // switch day/night mode
|
||||||
analogWrite(RGBLED_B, 255);
|
if (ledmode == 'D') {
|
||||||
} else {
|
Serial.println("Night mode enabled");
|
||||||
rgb_b = true;
|
ledmode = 'N';
|
||||||
analogWrite(RGBLED_B, 255 - rgb_brightness);
|
} 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);
|
||||||
|
|
||||||
|
rtc_gpio_pullup_en(KEY_DST);
|
||||||
|
rtc_gpio_pulldown_dis(KEY_DST);
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (digitalRead(KEY_4) == LOW) {
|
NMEA2000.loop();
|
||||||
Serial.println("Button detected: 4");
|
NMEA2000.ParseMessages();
|
||||||
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 (destination == 'A') {
|
|
||||||
destination = 'B';
|
|
||||||
analogWrite(LED_A, 0);
|
|
||||||
analogWrite(LED_B, led_brightness);
|
|
||||||
} else if (destination == 'B') {
|
|
||||||
destination = 'C';
|
|
||||||
analogWrite(LED_B, 0);
|
|
||||||
analogWrite(LED_C, led_brightness);
|
|
||||||
} else {
|
|
||||||
destination = 'A';
|
|
||||||
analogWrite(LED_C, 0);
|
|
||||||
analogWrite(LED_A, led_brightness);
|
|
||||||
}
|
|
||||||
Serial.print("Destination=");
|
|
||||||
Serial.println(destination);
|
|
||||||
|
|
||||||
/*
|
|
||||||
digitalWrite(LED_USER, HIGH); // Turn LED on
|
|
||||||
delay(500); // Keep it on 0.5s
|
|
||||||
digitalWrite(LED_USER, LOW); // Turn LED off
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (millis() - lastSensor >= 5000) {
|
if (millis() - lastSensor >= 5000) {
|
||||||
lastSensor = millis();
|
lastSensor = millis();
|
||||||
@@ -544,4 +788,5 @@ void loop() {
|
|||||||
Serial.printf("Loop counter: %lu\n", counter);
|
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].",
|
"description": "Temperature format: Kelvin, Celsius or Fahrenheit [K|C|F].",
|
||||||
"category": "Units"
|
"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