Some more improvements

This commit is contained in:
2026-03-01 08:26:07 +01:00
parent c6394650dc
commit d13c1ef8c1
10 changed files with 952 additions and 90 deletions

View File

@@ -45,6 +45,7 @@ bool ap_enabled = true;
unsigned long firstStart = 0;
unsigned long lastSensor = 0;
unsigned long lastPrint = 0;
unsigned long lastRefresh = 0;
unsigned long env_interval = 2000;
@@ -74,13 +75,79 @@ float hum = 0.0;
uint8_t nodeid; // NMEA2000 id on bus
Nmea2kTwai &NMEA2000=*(new Nmea2kTwai(CAN_TX, CAN_RX, CAN_RECOVERY_PERIOD));
tN2kDeviceList *pN2kDeviceList;
uint8_t n2k_id[3] = { 254, 254, 254 }; // aktuelle IDs für dest A, B, C
uint8_t n2k_id[3] = { 254, 254, 254 }; // destination ids; 254 = undef.
uint64_t n2k_name[3] = { 0, 0, 0 }; // destination names
uint8_t switchbank[3] = { 0, 0, 0 }; // switch bank of destionation
String processor(const String& var) {
// dummy for now
return "";
}
uint64_t config_to_n2kname(const String &cfgval) {
if (cfgval == "all") {
LOGD(TAG, "n2kname: Broadcast");
return NAME_BROADCAST;
}
if (cfgval == "none") {
LOGD(TAG, "n2kname: None");
return NAME_NONE;
}
// parse as hex string
LOGD(TAG, "n2kname: %s", cfgval.c_str());
return strtoull(cfgval.c_str(), nullptr, 16);
}
void refresh_n2k_ids() {
// Debug
for (uint8_t i = 0; i < 3; i++) {
char hex_name[17];
snprintf(hex_name, sizeof(hex_name), "%08X%08X",
(uint32_t)(n2k_name[i] >> 32), (uint32_t)(n2k_name[i] & 0xFFFFFFFF));
LOGD(TAG, "Dest %d: name=%s", i, hex_name);
}
if (!pN2kDeviceList->ReadResetIsListUpdated()) {
// no changes, nothing to do
return;
}
LOGD(TAG, "refreshing NMEA2000 device ids");
for (int i = 0; i < 3; i++) {
if (n2k_name[i] == NAME_BROADCAST) {
n2k_id[i] = 255;
} else {
n2k_id[i] = 254;
}
}
uint8_t hits = 0;
for (int i = 0; i <= 252; i++) {
const tNMEA2000::tDevice *d = pN2kDeviceList->FindDeviceBySource(i);
if (d == nullptr) {
continue;
}
for (int j = 0; j < 3; j++) {
if (d->GetName() == n2k_name[j] && n2k_id[j] == 254) {
n2k_id[j] = i;
hits++;
}
}
if (hits == 3) {
// all found no need to look further
break;
}
}
// Debug
for (uint8_t i = 0; i < 3; i++) {
char hex_name[17];
snprintf(hex_name, sizeof(hex_name), "%08X%08X",
(uint32_t)(n2k_name[i] >> 32), (uint32_t)(n2k_name[i] & 0xFFFFFFFF));
LOGD(TAG, "Dest %d: name=%s, id=%d", i, hex_name, n2k_id[i]);
}
}
TaskHandle_t ledTaskHandle = NULL;
TaskHandle_t sensorTaskHandle = NULL;
TaskHandle_t keyTaskHandle = NULL;
@@ -178,8 +245,10 @@ void cpuFreqTimerCallback(TimerHandle_t xTimer) {
void setup() {
Serial.begin(115200);
while (!Serial) delay(10); // verhindert Booten ohne USB-Verbindung
delay(500);
unsigned long start = millis();
while (!Serial && millis() - start < 3000) {
delay(10);
}
// Configure I/O pins
@@ -213,6 +282,9 @@ void setup() {
pinMode(KEY_6, INPUT_PULLUP);
pinMode(KEY_DST, INPUT_PULLUP);
// Light sensor input
pinMode(LDR, INPUT);
// Early signal system activity, red while booting
digitalWrite(RGBLED_R, HIGH);
digitalWrite(RGBLED_G, LOW);
@@ -247,6 +319,10 @@ void setup() {
longcode[4] = config.getByte("key5long");
longcode[5] = config.getByte("key6long");
switchbank[0] = config.getByte("switchBankA");
switchbank[1] = config.getByte("switchBankB");
switchbank[2] = config.getByte("switchBankC");
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_EXT0) {
LOGI(TAG, "Wake up by key");
@@ -300,13 +376,9 @@ void setup() {
// NMEA2000 configuration
// Destinations setup, refresh in loop later
if (config.getString("n2kDestA") == "all") {
// broadcast
} else if (config.getString("n2kDestA") == "none") {
// disabled
} else {
// NAME
}
n2k_name[0] = config_to_n2kname(config.getString("n2kDestA"));
n2k_name[1] = config_to_n2kname(config.getString("n2kDestB"));
n2k_name[2] = config_to_n2kname(config.getString("n2kDestC"));
NMEA2000.SetN2kCANMsgBufSize(8);
NMEA2000.SetN2kCANReceiveFrameBufSize(250);
@@ -330,14 +402,15 @@ void setup() {
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(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.SetDeviceInformation(uid, // Unique number. Use e.g. Serial number.
N2K_DEVFUNCT,
N2K_DEVCLASS,
N2K_MANUFACTURERCODE,
N2K_INDUSTRYGROUP
);
uint8_t devinst = config.getByte("n2kDevInst");
// devinst lower, devinst upper, sysinst
NMEA2000.SetDeviceInformationInstances(devinst & 0x07, devinst & 0xf8 >> 3, config.getByte("n2kSysInst"));
// Debug Start
// NMEA2000.SetForwardStream(&Serial);
@@ -349,7 +422,7 @@ void setup() {
//TODO: N2km_NodeOnly N2km_ListenAndNode?
NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, nodeid);
NMEA2000.SetForwardOwnMessages(false);
NMEA2000.SetHeartbeatIntervalAndOffset(NMEA2000_HEARTBEAT_INTERVAL);
NMEA2000.SetHeartbeatIntervalAndOffset(N2K_HEARTBEAT_INTERVAL);
// Features?
// 130311 environment
@@ -454,22 +527,29 @@ void setup() {
xTimerStart(stopApTimer, 0);
}
refresh_n2k_ids(); // hopefully some devices already detected
}
void shortBeep() {
ledcWriteTone(LEDC_BUZZER, 2500);
delay(15);
ledcWriteTone(LEDC_BUZZER, 0);
if (audiomode == 'D') {
return;
}
ledcWriteTone(LEDC_BUZZER, 2500);
delay(30);
ledcWriteTone(LEDC_BUZZER, 0);
}
void atariKeyclick() {
ledcWriteTone(LEDC_BUZZER, 3200);
delayMicroseconds(3000);
ledcWriteTone(LEDC_BUZZER, 0);
delayMicroseconds(800);
ledcWriteTone(LEDC_BUZZER, 3200);
delayMicroseconds(2000);
ledcWriteTone(LEDC_BUZZER, 0);
if (audiomode == 'D') {
return;
}
ledcWriteTone(LEDC_BUZZER, 3200);
delayMicroseconds(3000);
ledcWriteTone(LEDC_BUZZER, 0);
delayMicroseconds(800);
ledcWriteTone(LEDC_BUZZER, 3200);
delayMicroseconds(2000);
ledcWriteTone(LEDC_BUZZER, 0);
}
void print_n2k_devicelist() {
@@ -506,22 +586,23 @@ void print_n2k_devicelist() {
Serial.println("------------------------------");
}
// rename to: void sendSwitchBank()
// later: void send_switchbank(uint8_t keycode, uint8_t dest_id) {
void send_switchbank(uint8_t keycode) {
void send_switchbank(uint8_t keycode, uint8_t bank_id, uint8_t dest_id) {
// Seems there is no destination possible for 127502?
//
if (dest_id == 254) {
LOGW(TAG, "nothing sent: destination undefined (254)");
return;
}
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);
SetN2kPGN127502(N2kMsg, bank_id, bankstatus);
if (dest_id != 255) {
N2kMsg.Destination = dest_id;
}
NMEA2000.SendMsg(N2kMsg);
LOGI(TAG, "PGN127502 sent: Switch=%d", keycode);
LOGI(TAG, "PGN127502 sent: switch #%d to device #%d", keycode, dest_id);
}
void send_sensor_temphum(float temp_k, float hum_perc) {
@@ -538,6 +619,25 @@ void send_sensor_temphum(float temp_k, float hum_perc) {
SID = (SID + 1) % 256;
}
void send_sensor_brightness(uint16_t value) {
// value range 0..4095 from LDR
// proprietary PGN 65280
// device instance 8bits
// brightness 0-100%, resolution 0.1% 16bits
// 3 bytes reserved
tN2kMsg N2kMsg;
N2kMsg.SetPGN(65280); // proprietary PGN
N2kMsg.Priority = 6;
// 11bits manuf.-code, 2bits reserved (1), 3bits industry group
N2kMsg.Add2ByteUInt((N2K_MANUFACTURERCODE & 0x7FF) | (0x03 << 11) | ((N2K_INDUSTRYGROUP & 0x7) << 13));
N2kMsg.AddByte(0); // instance not yet used now
N2kMsg.Add2ByteUInt(value * 1000UL / 4095); // resolution 0.1
N2kMsg.AddByte(0xFF); //reserved bytes
N2kMsg.AddByte(0xFF);
N2kMsg.AddByte(0xFF);
NMEA2000.SendMsg(N2kMsg);
}
void loop() {
ButtonEvent event;
@@ -545,6 +645,7 @@ void loop() {
if (event.buttonId == BUTTON_DST) {
// destination / mode button
if (event.pressType == ButtonPressType::SHORT) {
atariKeyclick();
if (mode == 'N') {
// switch destination only in normal mode
if (destination == 'A') {
@@ -581,10 +682,12 @@ void loop() {
if (mode == 'N') {
// Send key code to destination
atariKeyclick();
uint8_t index = destination - 'A';
if ((event.pressType == ButtonPressType::SHORT)) {
send_switchbank(keycode[event.buttonId]);
LOGI(TAG, "Send key %d: dst index = %d", keycode[event.buttonId], index);
send_switchbank(keycode[event.buttonId], switchbank[index], n2k_id[index]);
} else if ((event.pressType == ButtonPressType::MEDIUM)) {
send_switchbank(longcode[event.buttonId]);
send_switchbank(longcode[event.buttonId], switchbank[index], n2k_id[index]);
}
// Debug:
if ((event.buttonId == BUTTON_1)
@@ -654,6 +757,12 @@ void loop() {
// NMEA2000.loop(); // not implemented yet
NMEA2000.ParseMessages();
if (millis() - lastRefresh >= 30000) {
// look every 30 seconds for changed devices
lastRefresh = millis();
refresh_n2k_ids();
}
if ((millis() - lastSensor >= env_interval) and sht_available) {
lastSensor = millis();
sht.read();
@@ -662,6 +771,14 @@ void loop() {
// Send environment data to NMEA2000
send_sensor_temphum(temp + 273.15, hum);
#ifdef HARDWARE_V2
int ldrval = analogRead(LDR);
LOGI(TAG, "LDR value =%d", ldrval);
// TODO send brightness to NMEA2000
//send_sensor_brightness(ldrval);
#endif
}
delay(1); // 1ms for FreeRTOS

View File

@@ -13,6 +13,8 @@
static const char* TAG = "WEB";
AsyncWebServer server(80);
bool updateSuccess = false;
String updateError = "";
class EmbeddedFile;
static std::map<String, EmbeddedFile*> embeddedFiles;
@@ -96,7 +98,9 @@ void webserver_init() {
server.on("/api/checkpass", HTTP_GET, [](AsyncWebServerRequest *request) {
LOGD(TAG, "checkpass called");
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
// TODO _hash überprüfen!
// LOGD(TAG, "check hash: %s", hash);
doc["status"] = "OK";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
@@ -112,7 +116,6 @@ void webserver_init() {
doc["instDesc2"] = config.getString("instDesc2");
doc["logLevel"] = loglevel;
doc["version"] = VERSION;
doc["fwtype"] = "unknown"; // TODO ?
doc["salt"] = "secret";
doc["AdminPassword"] = "********";
doc["useAdminPass"] = config.getBool("useAdminPass") ? "true" : "false";
@@ -128,7 +131,6 @@ void webserver_init() {
doc["rgbBrightness"] = config.getShort("rgbBrightness");
doc["buzEnable"] = config.getBool("buzEnable") ? "true" : "false";
doc["buzPower"] = config.getByte("buzPower");
doc["switchBank"] = config.getByte("switchBank");
doc["key1"] = keycode[BUTTON_1];
doc["key2"] = keycode[BUTTON_2];
doc["key3"] = keycode[BUTTON_3];
@@ -144,8 +146,11 @@ void webserver_init() {
doc["n2kSysInst"] = config.getByte("n2kSysInst");
doc["n2kDevInst"] = config.getByte("n2kDevInst");
doc["n2kDestA"] = config.getString("n2kDestA");
doc["switchBankA"] = config.getByte("switchBankA");
doc["n2kDestB"] = config.getString("n2kDestB");
doc["switchBankB"] = config.getByte("switchBankB");
doc["n2kDestC"] = config.getString("n2kDestC");
doc["switchBankC"] = config.getByte("switchBankC");
doc["envInterval"] = config.getShort("envInterval");
String out;
serializeJson(doc, out);
@@ -180,7 +185,9 @@ void webserver_init() {
doc["cpuspeed"] = String(cpu_freq) + "MHz";
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
doc["chipid"] = String(ssid);
doc["ssid"] = ssid;
doc["fwtype"] = FIRMWARE_TYPE; // TODO ?
doc["chipid"] = CONFIG_IDF_FIRMWARE_CHIP_ID; // IDF chipid NOT device!
doc["uptime"] = uptime_with_unit();
doc["heap"]=(long)xPortGetFreeHeapSize();
doc["temp"] = String(temp, 1);
@@ -260,10 +267,20 @@ void webserver_init() {
// create the response, add header, and send response
LOGD(TAG, "update called");
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
if (updateSuccess) {
doc["status"] = "OK";
doc["message"] = "Update sucessfull, rebooting...";
} else {
doc["status"] = "FAILED";
doc["message"] = updateError;
}
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
if (updateSuccess) {
delay(100); // Ensure response is sent
ESP.restart();
}
}, [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
// this is the new image upload part
Serial.print("Retrieving firmware image named: ");
@@ -272,14 +289,19 @@ void webserver_init() {
if (index == 0) {
if (! Update.begin(UPDATE_SIZE_UNKNOWN)) {
Update.printError(Serial);
updateError = "Update.begin() failed";
}
}
if (Update.write(data, len) != len) {
Update.printError(Serial);
updateError = "Update.write() failed";
}
if (final) {
if (!Update.end(true)) {
if (Update.end(true)) {
updateSuccess = true;
} else {
Update.printError(Serial);
updateError = "Update.end() failed";
}
}