From abd5817df7a91672f4e534e9e8a984bd1699bcdd Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 13 Apr 2023 12:56:01 +0200 Subject: [PATCH 01/70] Support the complete ebus arbitration protocol 1) Only allowed to participate when the bus is in state "First Syn" 2) Participate in second arbitration round when priority class matches --- include/ebusstate.hpp | 79 ++++++++++++++++++++++++++++++++++++++++ include/main.hpp | 15 ++++++-- src/enhanced.cpp | 83 ++++++++++++++++++++++++++++++++----------- src/main.cpp | 43 ++++++++++++++++------ 4 files changed, 188 insertions(+), 32 deletions(-) create mode 100644 include/ebusstate.hpp diff --git a/include/ebusstate.hpp b/include/ebusstate.hpp new file mode 100644 index 0000000..1861616 --- /dev/null +++ b/include/ebusstate.hpp @@ -0,0 +1,79 @@ +#ifndef _EBUSSTATE_H_ +#define _EBUSSTATE_H_ +#include "main.hpp" + +enum symbols { + SYN = 0xAA +}; + +class EBusState { +public: + enum eState { + eStartup, // In startup mode to analyze bus state + eReceivedFirstSYN, // Received SYN + eReceivedAddressAfterFirstSYN, // Received SYN ADDRESS + eReceivedSecondSYN, // Received SYN ADDRESS SYN + eReceivedAddressAfterSecondSYN,// Received SYN ADDRESS SYN ADDRESS + eBusy // Bus is busy; _master is master that won, _byte is first symbol after the master address + }; + static const char* enumvalue(eState e) + { + const char* values[] = { + "eStartup", + "eReceivedFirstSYN", + "eReceivedAddressAfterFirstSYN", + "eReceivedSecondSYN", + "eReceivedAddressAfterSecondSYN", + "eBusy" + }; + return values[e]; + } + EBusState() + : _state(eStartup) + {} + + void data(uint8_t symbol) + { + switch (_state) + { + case eStartup: + _state = symbol == SYN ? eReceivedFirstSYN : eStartup; + break; + case eReceivedFirstSYN: + _state = symbol == SYN ? eReceivedFirstSYN : eReceivedAddressAfterFirstSYN; + _master = symbol; + break; + case eReceivedAddressAfterFirstSYN: + _state = symbol == SYN ? eReceivedSecondSYN : eBusy; + _byte = symbol; + break; + case eReceivedSecondSYN: + _state = symbol == SYN ? synerror(_state, eStartup) : eReceivedAddressAfterSecondSYN; + _master = symbol; + break; + case eReceivedAddressAfterSecondSYN: + _state = symbol == SYN ? synerror(_state, eStartup) : eBusy; + _byte = symbol; + break; + case eBusy: + _state = symbol == SYN ? eReceivedFirstSYN : eBusy; + break; + } + } + + eState synerror(eState currentstate, eState newstate) + { + // ("unexpected SYN on bus while state is %s, setting state to %s\n", enumvalue(currentstate), enumvalue(newstate)); + return newstate; + } + + void reset() + { + _state = eStartup; + } + + eState _state; + uint8_t _master; + uint8_t _byte; +}; +#endif diff --git a/include/main.hpp b/include/main.hpp index 6100349..c37b883 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -1,8 +1,12 @@ +#ifndef _MAIN_HPP_ +#define _MAIN_HPP_ + #include #include #define MAX_SRV_CLIENTS 4 #define RXBUFFERSIZE 1024 +#define ARBITRATION_BUFFER_SIZE 20 #define STACK_PROTECTOR 512 // bytes #define HOSTNAME "esp-eBus" #define RESET_PIN 5 @@ -19,9 +23,16 @@ #define AVAILABLE_THRESHOLD 1 #endif + + bool handleNewClient(WiFiServer &server, WiFiClient clients[]); int pushClient(WiFiClient* client, uint8_t B); void handleClient(WiFiClient* client); -int pushEnhClient(WiFiClient* client, uint8_t B); -void handleEnhClient(WiFiClient* client); +class EBusState; + +size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes); +int pushEnhClient(WiFiClient* client, uint8_t B); +void handleEnhClient(WiFiClient* client); + +#endif \ No newline at end of file diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 80a4826..d9fb94d 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -1,15 +1,12 @@ #include #include "main.hpp" +#include "ebusstate.hpp" #define M1 0b11000000 #define M2 0b10000000 #define ARBITRATION_TIMEOUT_MS 2000 -enum symbols { - SYN = 0xAA -}; - enum requests { CMD_INIT = 0, CMD_SEND, @@ -143,38 +140,84 @@ void handleEnhClient(WiFiClient* client){ } } -int pushEnhClient(WiFiClient* client, uint8_t B){ +size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes){ + int bytesread = 0; if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { - - if ((B == SYN) && (Serial.available() == 0)){ - if (arbitration_client == client) { - Serial.write(arbitration_address); - + // only allowed to start arbitration when the bus is in "eReceivedFirstSYN" state + if ( busstate._state == EBusState::eReceivedFirstSYN && + Serial.available() == 0 && + arbitration_client == client) + { + // start of arbitration + bool participateSecond = false; + bool won = false; + + Serial.write(arbitration_address); + while (busstate._state != EBusState::eBusy && !won ){ + if (bytesread == ARBITRATION_BUFFER_SIZE-1){ + send_res(client, FAILED, 0x3f); + arbitration_client = NULL; + return bytesread; + } while (Serial.available() == 0) { if (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS) { send_res(client, FAILED, 0x3f); arbitration_client = NULL; - return 1; + return bytesread; } } - - if (Serial.read() == arbitration_address){ // arbitration success - send_res(client, STARTED, arbitration_address); - arbitration_client = NULL; - } else { // arbitration fail - // do nothing, arbitration will retry on next SYN until timeout or cancel + uint8_t symbol = Serial.read(); + busstate.data(symbol); + bytes[bytesread++] = symbol; + + if (busstate._state == EBusState::eReceivedAddressAfterFirstSYN) { + if (symbol == arbitration_address) { + won = true; // we won; nobody else will write to the bus + } else if ((symbol & 0b11110000) == (arbitration_address & 0b11110000)) { + participateSecond = true; // participate in second round of arbitration if we have the same priority class + } + else { + // arbitration might be ongoing between other bus participants, so we cannot yet know what + // the winning master is. Need to wait for eBusy + } } - return 1; + if (busstate._state == EBusState::eReceivedSecondSYN && participateSecond) { + // participate in second round of arbitration + Serial.write(arbitration_address); + } + if (busstate._state == EBusState::eReceivedAddressAfterSecondSYN) { + if (symbol == arbitration_address) { + won = true; // we won; nobody else will write to the bus + } + else { + // we now know which address has won and we could exit here. + // but it is easier to wait for eBusy, so after the while loop, the + // "lost" state can be handled the same as when somebody lost in the first round + } + } + } + if (won) { + send_res(client, STARTED, busstate._master); } + else { + // Report FAILED arbitration. Include the extra byte. + send_res(client, FAILED, busstate._master); + send_received(client, busstate._byte); + } + arbitration_client = NULL; } - if (arbitration_client && (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS)){ send_res(arbitration_client, FAILED, 0x3f); arbitration_client = NULL; } + } + return bytesread; +} +int pushEnhClient(WiFiClient* client, uint8_t B){ + if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { send_received(client, B); return 1; } return 0; -} \ No newline at end of file +} diff --git a/src/main.cpp b/src/main.cpp index cc5cbdc..ce8b72f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include #include //https://github.com/tzapu/WiFiManager WiFi Configuration Magic #include "main.hpp" +#include "ebusstate.hpp" #ifdef ESP32 #include @@ -20,6 +21,7 @@ WiFiServer statusServer(5555); WiFiClient serverClients[MAX_SRV_CLIENTS]; WiFiClient serverClientsRO[MAX_SRV_CLIENTS]; WiFiClient enhClients[MAX_SRV_CLIENTS]; +EBusState busState; unsigned long last_comms = 0; int last_reset_code = -1; @@ -200,21 +202,42 @@ void loop() { //check UART for data if (size_t len = Serial.available()) { - byte B = Serial.read(); - - // push data to clients + uint8_t bytes[ARBITRATION_BUFFER_SIZE+1]; + bytes[0]= Serial.read(); + busState.data(bytes[0]); + + // handle enhanced client that is in arbitration mode + // as a side effect, additional data can be read from the bus, which needs to + // send to the other clients. this data will be returned in the bytes argument + size_t bytesread = 0; + int arbitrated_client = -1; for (int i = 0; i < MAX_SRV_CLIENTS; i++){ - if (pushClient(&serverClients[i], B)){ - last_comms = millis(); - } - if (pushClient(&serverClientsRO[i], B)){ + bytesread = arbitrateEnhClient(&enhClients[i], busState, &bytes[1]); + if (bytesread>0){ last_comms = millis(); + arbitrated_client = i; + break; } - if (pushEnhClient(&enhClients[i], B)){ - last_comms = millis(); + } + bytesread++; // for byte at position bytes[0] + + // push data to clients, including bytes[0] and all bytes + // returned by arbitrateEnhClient, that start at bytes[1] + for (int i = 0; i < bytesread; i++) { + for (int j = 0; j < MAX_SRV_CLIENTS; j++){ + if (pushClient(&serverClients[j], bytes[i])){ + last_comms = millis(); + } + if (pushClient(&serverClientsRO[j], bytes[i])){ + last_comms = millis(); + } + if (j != arbitrated_client) { + if (pushEnhClient(&enhClients[j], bytes[i])){ + last_comms = millis(); + } + } } } - } loop_duration(); From 230261edb30d3222d3c618a5795c3601003e304d Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 14 Apr 2023 12:50:14 +0200 Subject: [PATCH 02/70] ebusd expects the starting SYN --- src/enhanced.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index d9fb94d..c0ef4d3 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -148,6 +148,10 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte Serial.available() == 0 && arbitration_client == client) { + + // ebusd expects the starting SYN + send_res(client, RECEIVED, SYN); + // start of arbitration bool participateSecond = false; bool won = false; From e2493bbcaee0317edbc3af1bbed15f0e25a49c91 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 15 Apr 2023 13:55:39 +0200 Subject: [PATCH 03/70] Fix priority class checking Add DEBUG_LOG statements Make sure no WIFI communication during arbitration Avoid lockup during arbitration loop Handle unexpected state during arbitration --- include/ebusstate.hpp | 27 +++++++++---- include/main.hpp | 3 ++ src/enhanced.cpp | 93 +++++++++++++++++++++++++++++-------------- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/include/ebusstate.hpp b/include/ebusstate.hpp index 1861616..b1bfbb1 100644 --- a/include/ebusstate.hpp +++ b/include/ebusstate.hpp @@ -32,19 +32,19 @@ class EBusState { : _state(eStartup) {} - void data(uint8_t symbol) + inline void data(uint8_t symbol) { switch (_state) { case eStartup: - _state = symbol == SYN ? eReceivedFirstSYN : eStartup; + _state = symbol == SYN ? syn(eReceivedFirstSYN) : eStartup; break; case eReceivedFirstSYN: - _state = symbol == SYN ? eReceivedFirstSYN : eReceivedAddressAfterFirstSYN; + _state = symbol == SYN ? syn(eReceivedFirstSYN) : eReceivedAddressAfterFirstSYN; _master = symbol; break; case eReceivedAddressAfterFirstSYN: - _state = symbol == SYN ? eReceivedSecondSYN : eBusy; + _state = symbol == SYN ? syn(eReceivedSecondSYN ): eBusy; _byte = symbol; break; case eReceivedSecondSYN: @@ -56,14 +56,20 @@ class EBusState { _byte = symbol; break; case eBusy: - _state = symbol == SYN ? eReceivedFirstSYN : eBusy; + _state = symbol == SYN ? syn(eReceivedFirstSYN) : eBusy; break; } } - + inline eState syn(eState newstate) + { + _SYNtime = micros(); + return newstate; + } eState synerror(eState currentstate, eState newstate) { - // ("unexpected SYN on bus while state is %s, setting state to %s\n", enumvalue(currentstate), enumvalue(newstate)); + unsigned long lastsyn = passedsincesyn(); + _SYNtime = micros(); + DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _byte, lastsyn); return newstate; } @@ -72,8 +78,15 @@ class EBusState { _state = eStartup; } + unsigned long passedsincesyn() + { + return micros() - _SYNtime; + + } + eState _state; uint8_t _master; uint8_t _byte; + unsigned long _SYNtime; }; #endif diff --git a/include/main.hpp b/include/main.hpp index c37b883..3e808c6 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -23,7 +23,10 @@ #define AVAILABLE_THRESHOLD 1 #endif +#define DEBUG_LOG // +//int DEBUG_LOG_IMPL(const char *format, ...); +//#define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); int pushClient(WiFiClient* client, uint8_t B); diff --git a/src/enhanced.cpp b/src/enhanced.cpp index c0ef4d3..750cbe1 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -44,14 +44,6 @@ void send_res(WiFiClient* client, uint8_t c, uint8_t d){ client->write(data, 2); } -void send_received(WiFiClient* client, uint8_t byte){ - if (byte< 0x80){ - client->write(byte); - } else { - send_res(client, RECEIVED, byte); - } -} - void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ if (c == CMD_INIT){ send_res(client, RESETTED, 0x0); @@ -60,15 +52,24 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ if (c == CMD_START){ if (d == SYN){ arbitration_client = NULL; + DEBUG_LOG("CMD_START SYN\n"); send_res(client, FAILED, 0x3f); return; } else { // start arbitration - - if (arbitration_client) { - // only one client can be in arbitration - send_res(client, FAILED, 0x3f); - return; + if (arbitration_client ) { + if (arbitration_client!=client) { + // only one client can be in arbitration + DEBUG_LOG("CMD_START ONGOING 0x%02 0x%02x\n", arbitration_address, d); + send_res(client, FAILED, 0x3f); + return; + } + else { + DEBUG_LOG("CMD_START REPEAT 0x%02x\n", d); + } + } + else { + DEBUG_LOG("CMD_START 0x%02x\n", d); } arbitration_client = client; @@ -79,6 +80,7 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ } } if (c == CMD_SEND){ + DEBUG_LOG("SEND 0x%02x\n", d); Serial.write(d); return; } @@ -141,59 +143,85 @@ void handleEnhClient(WiFiClient* client){ } size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes){ - int bytesread = 0; + size_t bytesread = 0; + static int arb = 0; if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { // only allowed to start arbitration when the bus is in "eReceivedFirstSYN" state if ( busstate._state == EBusState::eReceivedFirstSYN && - Serial.available() == 0 && arbitration_client == client) { - - // ebusd expects the starting SYN - send_res(client, RECEIVED, SYN); + + // Arbitration is timing sensitive. Avoid communicating with WifiClient during arbitration + DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, arbitration_address, busstate.passedsincesyn()); + + if (Serial.available() != 0) + { + // ebusd expects the starting SYN + send_res(client, RECEIVED, SYN); + send_res(client, FAILED, 0x3f); + DEBUG_LOG("ARB LATE 0x%02x\n", Serial.peek()); + arbitration_client=NULL; + return 0; + } // start of arbitration bool participateSecond = false; bool won = false; + int loopcount = 0; + DEBUG_LOG("ARB MASTER1 0x%02x %ld us\n", arbitration_address, busstate.passedsincesyn()); Serial.write(arbitration_address); - while (busstate._state != EBusState::eBusy && !won ){ - if (bytesread == ARBITRATION_BUFFER_SIZE-1){ - send_res(client, FAILED, 0x3f); - arbitration_client = NULL; - return bytesread; - } + + while (busstate._state != EBusState::eBusy && !won && loopcount++ < ARBITRATION_BUFFER_SIZE){ while (Serial.available() == 0) { if (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS) { + DEBUG_LOG("ARB TIMEOUT 1 0x%02x 0x%02x\n", busstate._master, busstate._byte); + // ebusd expects the starting SYN + send_res(client, RECEIVED, SYN); send_res(client, FAILED, 0x3f); arbitration_client = NULL; return bytesread; } } uint8_t symbol = Serial.read(); + DEBUG_LOG("ARB SYMBOL 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); busstate.data(symbol); bytes[bytesread++] = symbol; + if (busstate._state == EBusState::eStartup) { + DEBUG_LOG("ARB STARTUP 0x%02x 0x%02x\n", busstate._master, busstate._byte); + // ebusd expects the starting SYN + send_res(client, RECEIVED, SYN); + send_res(client, FAILED, 0x3f); + arbitration_client = NULL; + return bytesread; + } if (busstate._state == EBusState::eReceivedAddressAfterFirstSYN) { if (symbol == arbitration_address) { + DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); won = true; // we won; nobody else will write to the bus - } else if ((symbol & 0b11110000) == (arbitration_address & 0b11110000)) { + } else if ((symbol & 0b00001111) == (arbitration_address & 0b00001111)) { + DEBUG_LOG("ARB PARTICIPATE SECOND 0x%02x 0x%02x\n", arbitration_address, symbol); participateSecond = true; // participate in second round of arbitration if we have the same priority class } else { + DEBUG_LOG("ARB LOST1 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); // arbitration might be ongoing between other bus participants, so we cannot yet know what // the winning master is. Need to wait for eBusy } } if (busstate._state == EBusState::eReceivedSecondSYN && participateSecond) { - // participate in second round of arbitration + // execute second round of arbitration + DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", arbitration_address, busstate.passedsincesyn()); Serial.write(arbitration_address); } if (busstate._state == EBusState::eReceivedAddressAfterSecondSYN) { if (symbol == arbitration_address) { + DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); won = true; // we won; nobody else will write to the bus } else { + DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); // we now know which address has won and we could exit here. // but it is easier to wait for eBusy, so after the while loop, the // "lost" state can be handled the same as when somebody lost in the first round @@ -201,16 +229,23 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } } if (won) { + // ebusd expects the starting SYN + DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.passedsincesyn()); + send_res(client, RECEIVED, SYN); send_res(client, STARTED, busstate._master); + } else { - // Report FAILED arbitration. Include the extra byte. + // ebusd expects the starting SYN + DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.passedsincesyn()); + send_res(client, RECEIVED, SYN); send_res(client, FAILED, busstate._master); - send_received(client, busstate._byte); + send_res(client, RECEIVED, busstate._byte); } arbitration_client = NULL; } if (arbitration_client && (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS)){ + DEBUG_LOG("ARB TIMEOUT 2 0x%02x 0x%02x\n", busstate._master, busstate._byte); send_res(arbitration_client, FAILED, 0x3f); arbitration_client = NULL; } @@ -220,7 +255,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte int pushEnhClient(WiFiClient* client, uint8_t B){ if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { - send_received(client, B); + send_res(client, RECEIVED, B); return 1; } return 0; From f3a17e15df13fbcafb12b772dd83660ce35e6a36 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 15 Apr 2023 15:29:52 +0200 Subject: [PATCH 04/70] Minor formatting of output string --- src/enhanced.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 750cbe1..fe41507 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -201,7 +201,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); won = true; // we won; nobody else will write to the bus } else if ((symbol & 0b00001111) == (arbitration_address & 0b00001111)) { - DEBUG_LOG("ARB PARTICIPATE SECOND 0x%02x 0x%02x\n", arbitration_address, symbol); + DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", arbitration_address, symbol); participateSecond = true; // participate in second round of arbitration if we have the same priority class } else { From 22fb1becfeeb180c567300021dfdf19ef969dda5 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 15 Apr 2023 16:06:57 +0200 Subject: [PATCH 05/70] Analyze bus before allowing arbitration --- include/ebusstate.hpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/ebusstate.hpp b/include/ebusstate.hpp index b1bfbb1..62c4fe9 100644 --- a/include/ebusstate.hpp +++ b/include/ebusstate.hpp @@ -10,6 +10,9 @@ class EBusState { public: enum eState { eStartup, // In startup mode to analyze bus state + eStartupFirstSyn, // Either the bus is busy, it is arbitrating, or it is free to start an arbitration + eStartupSymbolAfterFirstSyn, + eStartupSecondSyn, eReceivedFirstSYN, // Received SYN eReceivedAddressAfterFirstSYN, // Received SYN ADDRESS eReceivedSecondSYN, // Received SYN ADDRESS SYN @@ -20,6 +23,9 @@ class EBusState { { const char* values[] = { "eStartup", + "eStartupFirstSyn", + "eStartupSymbolAfterFirstSyn", + "eStartupSecondSyn", "eReceivedFirstSYN", "eReceivedAddressAfterFirstSYN", "eReceivedSecondSYN", @@ -37,8 +43,17 @@ class EBusState { switch (_state) { case eStartup: - _state = symbol == SYN ? syn(eReceivedFirstSYN) : eStartup; + _state = symbol == SYN ? syn(eStartupFirstSyn) : eStartup; break; + case eStartupFirstSyn: + _state = symbol == SYN ? syn(eReceivedFirstSYN) : eStartupSymbolAfterFirstSyn; + break; + case eStartupSymbolAfterFirstSyn: + _state = symbol == SYN ? syn(eStartupSecondSyn) : eBusy; + break; + case eStartupSecondSyn: + _state = symbol == SYN ? syn(eReceivedFirstSYN) : eBusy; + break; case eReceivedFirstSYN: _state = symbol == SYN ? syn(eReceivedFirstSYN) : eReceivedAddressAfterFirstSYN; _master = symbol; From e911d6b1504dc9753b90d9f0ffa8d3f76562aa9b Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 15 Apr 2023 20:46:18 +0200 Subject: [PATCH 06/70] Change multiple if's in a switch statement --- src/enhanced.cpp | 54 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index fe41507..80a02f1 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -24,9 +24,9 @@ enum responses { ERROR_HOST = 0xc }; -WiFiClient* arbitration_client; -unsigned long arbitration_start; -int arbitration_address; +WiFiClient* arbitration_client = NULL; +unsigned long arbitration_start = 0; +int arbitration_address = -1; void decode(int b1, int b2, uint8_t (&data)[2]){ data[0] = (b1 >> 2) & 0b1111; @@ -156,8 +156,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte if (Serial.available() != 0) { - // ebusd expects the starting SYN - send_res(client, RECEIVED, SYN); + send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, FAILED, 0x3f); DEBUG_LOG("ARB LATE 0x%02x\n", Serial.peek()); arbitration_client=NULL; @@ -176,8 +175,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte while (Serial.available() == 0) { if (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS) { DEBUG_LOG("ARB TIMEOUT 1 0x%02x 0x%02x\n", busstate._master, busstate._byte); - // ebusd expects the starting SYN - send_res(client, RECEIVED, SYN); + send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, FAILED, 0x3f); arbitration_client = NULL; return bytesread; @@ -188,15 +186,15 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte busstate.data(symbol); bytes[bytesread++] = symbol; - if (busstate._state == EBusState::eStartup) { - DEBUG_LOG("ARB STARTUP 0x%02x 0x%02x\n", busstate._master, busstate._byte); - // ebusd expects the starting SYN - send_res(client, RECEIVED, SYN); - send_res(client, FAILED, 0x3f); - arbitration_client = NULL; - return bytesread; - } - if (busstate._state == EBusState::eReceivedAddressAfterFirstSYN) { + switch (busstate._state) + { + case EBusState::eStartup: // error out + DEBUG_LOG("ARB STARTUP 0x%02x 0x%02x\n", busstate._master, busstate._byte); + send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN + send_res(client, FAILED, 0x3f); + arbitration_client = NULL; + return bytesread; + case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? if (symbol == arbitration_address) { DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); won = true; // we won; nobody else will write to the bus @@ -209,13 +207,15 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte // arbitration might be ongoing between other bus participants, so we cannot yet know what // the winning master is. Need to wait for eBusy } - } - if (busstate._state == EBusState::eReceivedSecondSYN && participateSecond) { - // execute second round of arbitration - DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", arbitration_address, busstate.passedsincesyn()); - Serial.write(arbitration_address); - } - if (busstate._state == EBusState::eReceivedAddressAfterSecondSYN) { + break; + case EBusState::eReceivedSecondSYN: // did we sign up for second round arbitration? + if (participateSecond) { + // execute second round of arbitration + DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", arbitration_address, busstate.passedsincesyn()); + Serial.write(arbitration_address); + } + break; + case EBusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? if (symbol == arbitration_address) { DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); won = true; // we won; nobody else will write to the bus @@ -226,19 +226,18 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte // but it is easier to wait for eBusy, so after the while loop, the // "lost" state can be handled the same as when somebody lost in the first round } + break; } } if (won) { - // ebusd expects the starting SYN DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.passedsincesyn()); - send_res(client, RECEIVED, SYN); + send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, STARTED, busstate._master); } else { - // ebusd expects the starting SYN DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.passedsincesyn()); - send_res(client, RECEIVED, SYN); + send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, FAILED, busstate._master); send_res(client, RECEIVED, busstate._byte); } @@ -246,6 +245,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } if (arbitration_client && (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS)){ DEBUG_LOG("ARB TIMEOUT 2 0x%02x 0x%02x\n", busstate._master, busstate._byte); + send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(arbitration_client, FAILED, 0x3f); arbitration_client = NULL; } From d4b9981dcfa43adf4322da6a407cf924fe6f0907 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 15 Apr 2023 21:02:32 +0200 Subject: [PATCH 07/70] Fix compile warnings --- include/main.hpp | 4 +--- src/main.cpp | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index 3e808c6..d514cf7 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -23,9 +23,7 @@ #define AVAILABLE_THRESHOLD 1 #endif -#define DEBUG_LOG // - -//int DEBUG_LOG_IMPL(const char *format, ...); +inline int DEBUG_LOG(const char *format, ...) { return 0;} //#define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); diff --git a/src/main.cpp b/src/main.cpp index ce8b72f..aa58466 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -201,7 +201,7 @@ void loop() { } //check UART for data - if (size_t len = Serial.available()) { + if (Serial.available()) { uint8_t bytes[ARBITRATION_BUFFER_SIZE+1]; bytes[0]= Serial.read(); busState.data(bytes[0]); @@ -223,7 +223,7 @@ void loop() { // push data to clients, including bytes[0] and all bytes // returned by arbitrateEnhClient, that start at bytes[1] - for (int i = 0; i < bytesread; i++) { + for (size_t i = 0; i < bytesread; i++) { for (int j = 0; j < MAX_SRV_CLIENTS; j++){ if (pushClient(&serverClients[j], bytes[i])){ last_comms = millis(); From 887d188811ef0fa78b623394a26e096bb7af97eb Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 16 Apr 2023 12:23:12 +0200 Subject: [PATCH 08/70] Check on timing requirements for start of arbitration Minor updates to make the code more readable Add all states when to abort to switch statement --- include/ebusstate.hpp | 10 +++++----- src/enhanced.cpp | 46 ++++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/include/ebusstate.hpp b/include/ebusstate.hpp index 62c4fe9..9b10a0b 100644 --- a/include/ebusstate.hpp +++ b/include/ebusstate.hpp @@ -63,11 +63,11 @@ class EBusState { _byte = symbol; break; case eReceivedSecondSYN: - _state = symbol == SYN ? synerror(_state, eStartup) : eReceivedAddressAfterSecondSYN; + _state = symbol == SYN ? error(_state, eReceivedFirstSYN) : eReceivedAddressAfterSecondSYN; _master = symbol; break; case eReceivedAddressAfterSecondSYN: - _state = symbol == SYN ? synerror(_state, eStartup) : eBusy; + _state = symbol == SYN ? error(_state, eReceivedFirstSYN) : eBusy; _byte = symbol; break; case eBusy: @@ -80,9 +80,9 @@ class EBusState { _SYNtime = micros(); return newstate; } - eState synerror(eState currentstate, eState newstate) + eState error(eState currentstate, eState newstate) { - unsigned long lastsyn = passedsincesyn(); + unsigned long lastsyn = microsSinceLastSyn(); _SYNtime = micros(); DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _byte, lastsyn); return newstate; @@ -93,7 +93,7 @@ class EBusState { _state = eStartup; } - unsigned long passedsincesyn() + unsigned long microsSinceLastSyn() { return micros() - _SYNtime; diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 80a02f1..7d459d8 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -143,24 +143,30 @@ void handleEnhClient(WiFiClient* client){ } size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes){ - size_t bytesread = 0; static int arb = 0; + size_t bytesread = 0; if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { // only allowed to start arbitration when the bus is in "eReceivedFirstSYN" state if ( busstate._state == EBusState::eReceivedFirstSYN && arbitration_client == client) { - // Arbitration is timing sensitive. Avoid communicating with WifiClient during arbitration - DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, arbitration_address, busstate.passedsincesyn()); + // arbitration is timing sensitive. avoid communicating with WifiClient during arbitration + // according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 + // "Calculated time distance between start bit of SYN byte and + // bus permission must be in the range of 4300 us - 4456,24 us ." + // rely on the uart to keep the timing + // just make sure the byte to send is available in time + DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, arbitration_address, busstate.microsSinceLastSyn()); - if (Serial.available() != 0) + // too late if we don't have enough time to send our symbol + // assume we need at least 20 us to send the symbol + unsigned long now = busstate.microsSinceLastSyn(); + if (Serial.available() != 0 || now>((4456-20)-4160)) { - send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN - send_res(client, FAILED, 0x3f); - DEBUG_LOG("ARB LATE 0x%02x\n", Serial.peek()); - arbitration_client=NULL; - return 0; + // if we are too late, don't try to participate and retry next round + DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), now); + return bytesread; } // start of arbitration @@ -168,7 +174,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte bool won = false; int loopcount = 0; - DEBUG_LOG("ARB MASTER1 0x%02x %ld us\n", arbitration_address, busstate.passedsincesyn()); + DEBUG_LOG("ARB MASTER1 0x%02x %ld us\n", arbitration_address, busstate.microsSinceLastSyn()); Serial.write(arbitration_address); while (busstate._state != EBusState::eBusy && !won && loopcount++ < ARBITRATION_BUFFER_SIZE){ @@ -182,13 +188,17 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } } uint8_t symbol = Serial.read(); - DEBUG_LOG("ARB SYMBOL 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); + DEBUG_LOG("ARB SYMBOL 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); busstate.data(symbol); bytes[bytesread++] = symbol; switch (busstate._state) { case EBusState::eStartup: // error out + case EBusState::eStartupFirstSyn: + case EBusState::eStartupSymbolAfterFirstSyn: + case EBusState::eStartupSecondSyn: + case EBusState::eReceivedFirstSYN: DEBUG_LOG("ARB STARTUP 0x%02x 0x%02x\n", busstate._master, busstate._byte); send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, FAILED, 0x3f); @@ -196,14 +206,14 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte return bytesread; case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? if (symbol == arbitration_address) { - DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); + DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); won = true; // we won; nobody else will write to the bus } else if ((symbol & 0b00001111) == (arbitration_address & 0b00001111)) { DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", arbitration_address, symbol); participateSecond = true; // participate in second round of arbitration if we have the same priority class } else { - DEBUG_LOG("ARB LOST1 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); + DEBUG_LOG("ARB LOST1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); // arbitration might be ongoing between other bus participants, so we cannot yet know what // the winning master is. Need to wait for eBusy } @@ -211,17 +221,17 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte case EBusState::eReceivedSecondSYN: // did we sign up for second round arbitration? if (participateSecond) { // execute second round of arbitration - DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", arbitration_address, busstate.passedsincesyn()); + DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", arbitration_address, busstate.microsSinceLastSyn()); Serial.write(arbitration_address); } break; case EBusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? if (symbol == arbitration_address) { - DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); + DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); won = true; // we won; nobody else will write to the bus } else { - DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.passedsincesyn()); + DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); // we now know which address has won and we could exit here. // but it is easier to wait for eBusy, so after the while loop, the // "lost" state can be handled the same as when somebody lost in the first round @@ -230,13 +240,13 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } } if (won) { - DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.passedsincesyn()); + DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.microsSinceLastSyn()); send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, STARTED, busstate._master); } else { - DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.passedsincesyn()); + DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.microsSinceLastSyn()); send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, FAILED, busstate._master); send_res(client, RECEIVED, busstate._byte); From e90b90f7b4018b3cd367e26a5b1a122e788da447 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 16 Apr 2023 17:21:13 +0200 Subject: [PATCH 09/70] Cleanup error handling --- src/enhanced.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 7d459d8..37784e2 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -24,6 +24,11 @@ enum responses { ERROR_HOST = 0xc }; +enum errors { + ERR_FRAMING = 0x00, + ERR_OVERRUN = 0x01 +}; + WiFiClient* arbitration_client = NULL; unsigned long arbitration_start = 0; int arbitration_address = -1; @@ -53,7 +58,6 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ if (d == SYN){ arbitration_client = NULL; DEBUG_LOG("CMD_START SYN\n"); - send_res(client, FAILED, 0x3f); return; } else { // start arbitration @@ -61,7 +65,7 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ if (arbitration_client!=client) { // only one client can be in arbitration DEBUG_LOG("CMD_START ONGOING 0x%02 0x%02x\n", arbitration_address, d); - send_res(client, FAILED, 0x3f); + send_res(client, ERROR_HOST, ERR_FRAMING); return; } else { @@ -181,8 +185,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte while (Serial.available() == 0) { if (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS) { DEBUG_LOG("ARB TIMEOUT 1 0x%02x 0x%02x\n", busstate._master, busstate._byte); - send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN - send_res(client, FAILED, 0x3f); + send_res(client, ERROR_EBUS, ERR_FRAMING); arbitration_client = NULL; return bytesread; } @@ -199,9 +202,8 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte case EBusState::eStartupSymbolAfterFirstSyn: case EBusState::eStartupSecondSyn: case EBusState::eReceivedFirstSYN: - DEBUG_LOG("ARB STARTUP 0x%02x 0x%02x\n", busstate._master, busstate._byte); - send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN - send_res(client, FAILED, 0x3f); + DEBUG_LOG("ARB ERROR 0x%02x 0x%02x\n", busstate._master, busstate._byte); + send_res(client, ERROR_EBUS, ERR_FRAMING); arbitration_client = NULL; return bytesread; case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? @@ -241,13 +243,11 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } if (won) { DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.microsSinceLastSyn()); - send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, STARTED, busstate._master); } else { DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.microsSinceLastSyn()); - send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN send_res(client, FAILED, busstate._master); send_res(client, RECEIVED, busstate._byte); } @@ -255,8 +255,7 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } if (arbitration_client && (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS)){ DEBUG_LOG("ARB TIMEOUT 2 0x%02x 0x%02x\n", busstate._master, busstate._byte); - send_res(client, RECEIVED, SYN); // ebusd expects the starting SYN - send_res(arbitration_client, FAILED, 0x3f); + send_res(client, ERROR_EBUS, ERR_FRAMING); arbitration_client = NULL; } } From 30b0d7e69bd68a6abb81411a505315588da2b1af Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Tue, 18 Apr 2023 18:25:01 +0200 Subject: [PATCH 10/70] start conversion to interrupt driven --- include/arbitration.hpp | 31 +++++++++++ src/arbitration.cpp | 99 ++++++++++++++++++++++++++++++++++ src/enhanced.cpp | 116 ++++++++++------------------------------ src/main.cpp | 36 ++++++++++++- 4 files changed, 191 insertions(+), 91 deletions(-) create mode 100644 include/arbitration.hpp create mode 100644 src/arbitration.cpp diff --git a/include/arbitration.hpp b/include/arbitration.hpp new file mode 100644 index 0000000..989dbf7 --- /dev/null +++ b/include/arbitration.hpp @@ -0,0 +1,31 @@ +#ifndef _ARBITRATION_H_ +#define _ARBITRATION_H_ + +#include "ebusstate.hpp" + +class Arbitration +{ + public: + enum state {none, // no arbitration ongoing/not yet completed + arbitrating, + won, // won + lost, // lost + error // error + }; + + Arbitration() + : _arbitrating(false) + , _participateSecond(false) + , _arbitration_address(0) + {} + + bool start (EBusState& busstate, uint8_t master); + Arbitration::state data (EBusState& busstate, uint8_t symbol); + + private: + bool _arbitrating; + bool _participateSecond; + uint8_t _arbitration_address; +}; + +#endif diff --git a/src/arbitration.cpp b/src/arbitration.cpp new file mode 100644 index 0000000..8391801 --- /dev/null +++ b/src/arbitration.cpp @@ -0,0 +1,99 @@ +#include "arbitration.hpp" +#include "ebusstate.hpp" + +bool Arbitration::start(EBusState& busstate, uint8_t master) +{ static int arb = 0; + if (_arbitrating) { + return false; + } + if (master == SYN) { + return false; + } + if (busstate._state != EBusState::eReceivedFirstSYN) { + return false; + } + DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, master, busstate.microsSinceLastSyn()); + + // too late if we don't have enough time to send our symbol + // assume we need at least 20 us to send the symbol + unsigned long now = busstate.microsSinceLastSyn(); + if (Serial.available() != 0 || now>((4456-20)-4160)) + { + // if we are too late, don't try to participate and retry next round + DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), now); + return false; + } + Serial.write(master); + + _arbitration_address = master; + _arbitrating = true; + _participateSecond = false; + return true; +} + +// arbitration is timing sensitive. avoid communicating with WifiClient during arbitration +// according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 +// "Calculated time distance between start bit of SYN byte and +// bus permission must be in the range of 4300 us - 4456,24 us ." +// rely on the uart to keep the timing +// just make sure the byte to send is available in time +Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { + static int arb = 0; + if (!_arbitrating){ + return none; + } + switch (busstate._state) + { + case EBusState::eStartup: // error out + case EBusState::eStartupFirstSyn: + case EBusState::eStartupSymbolAfterFirstSyn: + case EBusState::eStartupSecondSyn: + case EBusState::eReceivedFirstSYN: + DEBUG_LOG("ARB ERROR 0x%02x 0x%02x\n", busstate._master, busstate._byte); + //send_res(client, ERROR_EBUS, ERR_FRAMING); + _arbitrating = false; + return error; + case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? + if (symbol == _arbitration_address) { + DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + _arbitrating = false; + return won; // we won; nobody else will write to the bus + } else if ((symbol & 0b00001111) == (_arbitration_address & 0b00001111)) { + DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", _arbitration_address, symbol); + _participateSecond = true; // participate in second round of arbitration if we have the same priority class + } + else { + DEBUG_LOG("ARB LOST1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + // arbitration might be ongoing between other bus participants, so we cannot yet know what + // the winning master is. Need to wait for eBusy + } + return arbitrating; + case EBusState::eReceivedSecondSYN: // did we sign up for second round arbitration? + if (_participateSecond) { + // execute second round of arbitration + DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitration_address, busstate.microsSinceLastSyn()); + Serial.write(_arbitration_address); + } + else { + DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitration_address, busstate.microsSinceLastSyn()); + } + return arbitrating; + case EBusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? + if (symbol == _arbitration_address) { + DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + _arbitrating = false; + return won; // we won; nobody else will write to the bus + } + else { + DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + // we now know which address has won and we could exit here. + // but it is easier to wait for eBusy, so after the while loop, the + // "lost" state can be handled the same as when somebody lost in the first round + } + return arbitrating; + case EBusState::eBusy: + _arbitrating = false; + return lost; + } + return arbitrating; +} diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 37784e2..6b36af2 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -1,6 +1,7 @@ #include #include "main.hpp" #include "ebusstate.hpp" +#include "arbitration.hpp" #define M1 0b11000000 #define M2 0b10000000 @@ -147,41 +148,12 @@ void handleEnhClient(WiFiClient* client){ } size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes){ - static int arb = 0; size_t bytesread = 0; - if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { - // only allowed to start arbitration when the bus is in "eReceivedFirstSYN" state - if ( busstate._state == EBusState::eReceivedFirstSYN && - arbitration_client == client) - { - - // arbitration is timing sensitive. avoid communicating with WifiClient during arbitration - // according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 - // "Calculated time distance between start bit of SYN byte and - // bus permission must be in the range of 4300 us - 4456,24 us ." - // rely on the uart to keep the timing - // just make sure the byte to send is available in time - DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, arbitration_address, busstate.microsSinceLastSyn()); - - // too late if we don't have enough time to send our symbol - // assume we need at least 20 us to send the symbol - unsigned long now = busstate.microsSinceLastSyn(); - if (Serial.available() != 0 || now>((4456-20)-4160)) - { - // if we are too late, don't try to participate and retry next round - DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), now); - return bytesread; - } - - // start of arbitration - bool participateSecond = false; - bool won = false; - int loopcount = 0; - - DEBUG_LOG("ARB MASTER1 0x%02x %ld us\n", arbitration_address, busstate.microsSinceLastSyn()); - Serial.write(arbitration_address); - - while (busstate._state != EBusState::eBusy && !won && loopcount++ < ARBITRATION_BUFFER_SIZE){ + if (client->availableForWrite() >= AVAILABLE_THRESHOLD && arbitration_client == client) { + Arbitration arbitration; + if (arbitration.start(busstate, arbitration_address) ) { + int loopcount = 0; + while (loopcount++ < ARBITRATION_BUFFER_SIZE){ while (Serial.available() == 0) { if (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS) { DEBUG_LOG("ARB TIMEOUT 1 0x%02x 0x%02x\n", busstate._master, busstate._byte); @@ -190,67 +162,33 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte return bytesread; } } + uint8_t symbol = Serial.read(); DEBUG_LOG("ARB SYMBOL 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); busstate.data(symbol); bytes[bytesread++] = symbol; - - switch (busstate._state) - { - case EBusState::eStartup: // error out - case EBusState::eStartupFirstSyn: - case EBusState::eStartupSymbolAfterFirstSyn: - case EBusState::eStartupSecondSyn: - case EBusState::eReceivedFirstSYN: - DEBUG_LOG("ARB ERROR 0x%02x 0x%02x\n", busstate._master, busstate._byte); - send_res(client, ERROR_EBUS, ERR_FRAMING); - arbitration_client = NULL; - return bytesread; - case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? - if (symbol == arbitration_address) { - DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); - won = true; // we won; nobody else will write to the bus - } else if ((symbol & 0b00001111) == (arbitration_address & 0b00001111)) { - DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", arbitration_address, symbol); - participateSecond = true; // participate in second round of arbitration if we have the same priority class - } - else { - DEBUG_LOG("ARB LOST1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); - // arbitration might be ongoing between other bus participants, so we cannot yet know what - // the winning master is. Need to wait for eBusy - } - break; - case EBusState::eReceivedSecondSYN: // did we sign up for second round arbitration? - if (participateSecond) { - // execute second round of arbitration - DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", arbitration_address, busstate.microsSinceLastSyn()); - Serial.write(arbitration_address); - } - break; - case EBusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? - if (symbol == arbitration_address) { - DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); - won = true; // we won; nobody else will write to the bus - } - else { - DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); - // we now know which address has won and we could exit here. - // but it is easier to wait for eBusy, so after the while loop, the - // "lost" state can be handled the same as when somebody lost in the first round - } - break; + switch (arbitration.data(busstate, symbol)) { + case Arbitration::none: + arbitration_client = NULL; + return bytesread; + case Arbitration::arbitrating: + break; + case Arbitration::won: + DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.microsSinceLastSyn()); + send_res(client, STARTED, busstate._master); + arbitration_client = NULL; + return bytesread; + case Arbitration::lost: + DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.microsSinceLastSyn()); + send_res(client, FAILED, busstate._master); + send_res(client, RECEIVED, busstate._byte); + arbitration_client = NULL; + return bytesread; + case Arbitration::error: + arbitration_client = NULL; + return bytesread; } } - if (won) { - DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.microsSinceLastSyn()); - send_res(client, STARTED, busstate._master); - - } - else { - DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.microsSinceLastSyn()); - send_res(client, FAILED, busstate._master); - send_res(client, RECEIVED, busstate._byte); - } arbitration_client = NULL; } if (arbitration_client && (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS)){ diff --git a/src/main.cpp b/src/main.cpp index 02fc873..381b631 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include //https://github.com/tzapu/WiFiManager WiFi Configuration Magic #include "main.hpp" #include "ebusstate.hpp" +#include "arbitration.hpp" #ifdef ESP32 #include @@ -21,7 +22,7 @@ WiFiServer statusServer(5555); WiFiClient serverClients[MAX_SRV_CLIENTS]; WiFiClient serverClientsRO[MAX_SRV_CLIENTS]; WiFiClient enhClients[MAX_SRV_CLIENTS]; -EBusState busState; +EBusState busState; unsigned long last_comms = 0; int last_reset_code = -1; @@ -89,7 +90,35 @@ void check_reset() { } } } - + + +void OnReceiveCB() +{ + //static EBusState busState; + //static Arbitration arbitration; + // We received a byte. + // Handle arbitration in this CB; has to be fast enough to be done for the next character + // We can't send data to WifiClient + // - likely not thread safe + // - would take too long + // Instead regular wifi client communication needs to happen in the main loop, untill maybe WifiClient can be put in + // a task and we communicate to WifiClient with messages + // BusState should only be used from this thread, only relevant case + // HardwareSerial is thread safe, no problem to call write and read from different threads + + //uint_t byte = Serial.read(); + //busState.data(byte); + //arbitration.data(byte); + + // send results to mainloop + +} + +void OnReceiveErrorCB(hardwareSerial_error_t e) +{ + // todo +} + void setup() { check_reset(); @@ -98,6 +127,9 @@ void setup() { #ifdef ESP32 Serial1.begin(115200, SERIAL_8N1, 8, 10); Serial.begin(2400, SERIAL_8N1, 21, 20); + Serial.onReceive(OnReceiveCB, false); + Serial.onReceiveError(OnReceiveErrorCB); + // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit Serial.setRxFIFOFull(1); last_reset_code = rtc_get_reset_reason(0); From 3234d835ec949347836ac4cf14c9f605950ccded Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Tue, 18 Apr 2023 23:11:43 +0200 Subject: [PATCH 11/70] no good yet --- include/main.hpp | 5 ++- src/arbitration.cpp | 1 - src/enhanced.cpp | 80 ++++++++++++++++++++++++++++++++++ src/main.cpp | 102 +++++++++++++++++++++++++++++++++++++------- 4 files changed, 170 insertions(+), 18 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index 8b1f426..5522d7e 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -18,8 +18,9 @@ #define AVAILABLE_THRESHOLD 1 #endif -inline int DEBUG_LOG(const char *format, ...) { return 0;} -//#define DEBUG_LOG DEBUG_LOG_IMPL +//inline int DEBUG_LOG(const char *format, ...) { return 0;} +int DEBUG_LOG_IMPL(const char *format, ...); +#define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); int pushClient(WiFiClient* client, uint8_t B); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 8391801..57c9f44 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -38,7 +38,6 @@ bool Arbitration::start(EBusState& busstate, uint8_t master) // rely on the uart to keep the timing // just make sure the byte to send is available in time Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { - static int arb = 0; if (!_arbitrating){ return none; } diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 6b36af2..421bd27 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -200,10 +200,90 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte return bytesread; } +struct command{ + uint8_t c; + uint8_t d; +}; + +#define SENDCOMMANDQUEUELENGTH 10 + +inline QueueHandle_t getSendQueue() +{ + static QueueHandle_t sendqueue = 0; + if (sendqueue == 0) { + sendqueue=xQueueCreate(SENDCOMMANDQUEUELENGTH, sizeof(command)); + } + return sendqueue; +} +bool arbitration_ongoing = false; int pushEnhClient(WiFiClient* client, uint8_t B){ if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { + if (arbitration_ongoing) { + command c; + while (xQueueReceive(getSendQueue(), &c, 0) > 0) { + send_res(client, c.c, c.d); + } + } + else { send_res(client, RECEIVED, B); + + } return 1; } return 0; } + + +WiFiClient* startEnhArbitration(Arbitration& arbitration, EBusState& busState ){ + WiFiClient* client = 0; + static unsigned long lastTime = 0; + static int amount = 10; + unsigned long now = millis(); + unsigned long delta = now - lastTime; + bool a=false; + if (delta > 500) { + //a=true; + lastTime =now; + } + // lock + if ((arbitration_client || a) && !arbitration_ongoing) { + a=false; + //arbitration_address=0xf3; + if (arbitration.start(busState, arbitration_address)) { + client = arbitration_client; + arbitration_ongoing = true; + } + } + // unlock + return client; +} + +void queue_command(uint8_t c, uint8_t d) +{ + command com = {c, d}; + xQueueSendToBack(getSendQueue(), &com, 0); +} + +void enhArbitrationWon(WiFiClient* client, uint8_t master){ + // lock + arbitration_client = NULL; + arbitration_ongoing = false; + queue_command(STARTED, master); + // unlock +} + +void enhArbitrationLost(WiFiClient* client, uint8_t master, uint8_t nextsymbol){ + // lock + arbitration_client = NULL; + arbitration_ongoing = false; + queue_command(FAILED, master); + queue_command(RECEIVED, nextsymbol); + // unlock +} +void enhArbitrationError(WiFiClient* client){ + // lock + queue_command(ERROR_EBUS, ERR_FRAMING); + arbitration_client = NULL; + arbitration_ongoing = false; + // unlock +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 381b631..975da0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include "esp32c3/rom/rtc.h" + #include "driver/uart.h" #else #include #include @@ -90,8 +91,14 @@ void check_reset() { } } } +QueueHandle_t receivequeue; +WiFiClient* startEnhArbitration(Arbitration& arbitration, EBusState& busstate ); +void enhArbitrationWon(WiFiClient* client, uint8_t master); +void enhArbitrationLost(WiFiClient* client, uint8_t master, uint8_t nextsymbol); +void enhArbitrationError(WiFiClient* client); +Arbitration arbitration; void OnReceiveCB() { //static EBusState busState; @@ -106,17 +113,49 @@ void OnReceiveCB() // BusState should only be used from this thread, only relevant case // HardwareSerial is thread safe, no problem to call write and read from different threads - //uint_t byte = Serial.read(); - //busState.data(byte); - //arbitration.data(byte); - + static WiFiClient* client = 0; + uint8_t byte = Serial.read(); + busState.data(byte); + Arbitration::state state = arbitration.data(busState, byte); + switch (state) { + case Arbitration::none: + client=startEnhArbitration(arbitration, busState); + case Arbitration::arbitrating: + break; + case Arbitration::won: + DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); + enhArbitrationWon(client, busState._master); + client=0; + //send_res(client, STARTED, busstate._master); + //arbitration_client = NULL; + //return bytesread; + break; + case Arbitration::lost: + DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); + enhArbitrationLost(client, busState._master, busState._byte); + client=0; + //send_res(client, FAILED, busstate._master); + //send_res(client, RECEIVED, busstate._byte); + //arbitration_client = NULL; + //return bytesread; + break; + case Arbitration::error: + enhArbitrationError(client); + client=0; + break; + } + xQueueSendToBack(receivequeue, &byte, 0); // send results to mainloop + // -> each byte as it needs to be forwarded to other clients + // -> the state of the arbitration } void OnReceiveErrorCB(hardwareSerial_error_t e) { - // todo + if (e != UART_BREAK){ + DEBUG_LOG("OnReceiveErrorCB %i\n", e); + } } void setup() { @@ -127,12 +166,14 @@ void setup() { #ifdef ESP32 Serial1.begin(115200, SERIAL_8N1, 8, 10); Serial.begin(2400, SERIAL_8N1, 21, 20); + // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit + Serial.setRxFIFOFull(1); Serial.onReceive(OnReceiveCB, false); Serial.onReceiveError(OnReceiveErrorCB); - // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit - Serial.setRxFIFOFull(1); + last_reset_code = rtc_get_reset_reason(0); + receivequeue=xQueueCreate(RXBUFFERSIZE, sizeof(uint8_t)); #elif defined(ESP8266) Serial1.begin(115200); Serial.begin(2400); @@ -168,6 +209,27 @@ void setup() { last_comms = millis(); } +#define DEBUG_LOG_BUFFER_SIZE 4000 +static char message_buffer[DEBUG_LOG_BUFFER_SIZE]; +static size_t message_buffer_position; +int DEBUG_LOG_IMPL(const char *format, ...) +{ + int ret; + if (message_buffer_position>=DEBUG_LOG_BUFFER_SIZE) + return 0; + + va_list aptr; + va_start(aptr, format); + ret = vsnprintf(&message_buffer[message_buffer_position], DEBUG_LOG_BUFFER_SIZE-message_buffer_position, format, aptr); + va_end(aptr); + message_buffer_position+=ret; + if (message_buffer_position >= DEBUG_LOG_BUFFER_SIZE) { + message_buffer_position=DEBUG_LOG_BUFFER_SIZE; + } + message_buffer[DEBUG_LOG_BUFFER_SIZE-1]=0; + + return ret; +} bool handleStatusServerRequests() { if (!statusServer.hasClient()) @@ -176,6 +238,7 @@ bool handleStatusServerRequests() { WiFiClient client = statusServer.available(); if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { + client.printf("HTTP/1.1 200 OK\r\n\r\n"); client.printf("uptime: %ld ms\n", millis()); client.printf("rssi: %d dBm\n", WiFi.RSSI()); client.printf("free_heap: %d B\n", ESP.getFreeHeap()); @@ -183,6 +246,12 @@ bool handleStatusServerRequests() { client.printf("loop_duration: %ld us\r\n", loopDuration); client.printf("max_loop_duration: %ld us\r\n", maxLoopDuration); client.printf("version: %s\r\n", AUTO_VERSION); + if (message_buffer_position>0) + { + client.printf("lastmessages:\r\n%s\r\n", message_buffer); + message_buffer[0]=0; + message_buffer_position=0; + } client.flush(); client.stop(); } @@ -245,16 +314,19 @@ void loop() { } //check UART for data - if (Serial.available()) { - uint8_t bytes[ARBITRATION_BUFFER_SIZE+1]; - bytes[0]= Serial.read(); - busState.data(bytes[0]); + //if (Serial.available()) { + uint8_t bytes[ARBITRATION_BUFFER_SIZE+1]; + if (xQueueReceive(receivequeue, &bytes[0], 0) > 0) { + + //bytes[0]= Serial.read(); + + //busState.data(bytes[0]); // handle enhanced client that is in arbitration mode // as a side effect, additional data can be read from the bus, which needs to // send to the other clients. this data will be returned in the bytes argument size_t bytesread = 0; - int arbitrated_client = -1; + /*int arbitrated_client = -1; for (int i = 0; i < MAX_SRV_CLIENTS; i++){ bytesread = arbitrateEnhClient(&enhClients[i], busState, &bytes[1]); if (bytesread>0){ @@ -262,7 +334,7 @@ void loop() { arbitrated_client = i; break; } - } + }*/ bytesread++; // for byte at position bytes[0] // push data to clients, including bytes[0] and all bytes @@ -275,11 +347,11 @@ void loop() { if (pushClient(&serverClientsRO[j], bytes[i])){ last_comms = millis(); } - if (j != arbitrated_client) { + //if (j != arbitrated_client) { if (pushEnhClient(&enhClients[j], bytes[i])){ last_comms = millis(); } - } + //} } } } From 40bee27babb42b1e3bf8acf2615af3484f0a41a3 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 16:53:35 +0200 Subject: [PATCH 12/70] allow async reception og serial data --- include/bus.hpp | 38 ++++++++++ include/ebusstate.hpp | 5 +- include/enhanced.hpp | 37 ++++++++++ include/main.hpp | 7 +- src/bus.cpp | 160 ++++++++++++++++++++++++++++++++++++++++++ src/enhanced.cpp | 134 +++++++---------------------------- src/main.cpp | 121 ++++---------------------------- 7 files changed, 280 insertions(+), 222 deletions(-) create mode 100644 include/bus.hpp create mode 100644 include/enhanced.hpp create mode 100644 src/bus.cpp diff --git a/include/bus.hpp b/include/bus.hpp new file mode 100644 index 0000000..047cbde --- /dev/null +++ b/include/bus.hpp @@ -0,0 +1,38 @@ +#ifndef _BUS_H_ +#define _BUS_H_ +#include "main.hpp" +#include "queue" + +class BusType +{ + public: + // "receive" data should go to all clients that are not in arbitration mode + // "enhanced" data should go only to the arbitrating client + // a client is in arbitration mode if _client is not null + struct data { + bool _enhanced; // is this an enhanced command? + uint8_t _c; // command byte, only used when in "enhanced" mode + uint8_t _d; // data byte for both regular and enhanced command + WiFiClient* _client; // the client that is being arbitrated + }; + BusType(); + ~BusType(); + + bool read(data& d); + + private: + void push(data& d); + void receive (uint8_t byte); + +#ifdef USE_ASYNCHRONOUS + QueueHandle_t _queue; + static void OnReceiveCB(); + static void OnReceiveErrorCB(hardwareSerial_error_t e); +#else + std::queue _queue; +#endif +}; + +extern BusType Bus; + +#endif diff --git a/include/ebusstate.hpp b/include/ebusstate.hpp index 9b10a0b..ca089b4 100644 --- a/include/ebusstate.hpp +++ b/include/ebusstate.hpp @@ -1,10 +1,9 @@ #ifndef _EBUSSTATE_H_ #define _EBUSSTATE_H_ #include "main.hpp" +#include "enhanced.hpp" + -enum symbols { - SYN = 0xAA -}; class EBusState { public: diff --git a/include/enhanced.hpp b/include/enhanced.hpp new file mode 100644 index 0000000..0314d6d --- /dev/null +++ b/include/enhanced.hpp @@ -0,0 +1,37 @@ +#ifndef _ENHANCED_H_ +#define _ENHANCED_H_ +#include + +enum symbols { + SYN = 0xAA +}; + +enum requests { + CMD_INIT = 0, + CMD_SEND, + CMD_START, + CMD_INFO +}; + +enum responses { + RESETTED = 0x0, + RECEIVED = 0x1, + STARTED = 0x2, + INFO = 0x3, + FAILED = 0xa, + ERROR_EBUS = 0xb, + ERROR_HOST = 0xc +}; + +enum errors { + ERR_FRAMING = 0x00, + ERR_OVERRUN = 0x01 +}; + +void enhArbitrationDone(WiFiClient* client); +WiFiClient* enhArbitrationRequested(uint8_t& arbitration_client); + +int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d); +void handleEnhClient(WiFiClient* client); + +#endif diff --git a/include/main.hpp b/include/main.hpp index 5522d7e..2bc1c8e 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -11,6 +11,8 @@ #define HOSTNAME "esp-eBus" #define RESET_MS 1000 +#define USE_ASYNCHRONOUS + #ifdef ESP32 // https://esp32.com/viewtopic.php?t=19788 #define AVAILABLE_THRESHOLD 0 @@ -26,10 +28,5 @@ bool handleNewClient(WiFiServer &server, WiFiClient clients[]); int pushClient(WiFiClient* client, uint8_t B); void handleClient(WiFiClient* client); -class EBusState; - -size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes); -int pushEnhClient(WiFiClient* client, uint8_t B); -void handleEnhClient(WiFiClient* client); #endif \ No newline at end of file diff --git a/src/bus.cpp b/src/bus.cpp new file mode 100644 index 0000000..eebee7b --- /dev/null +++ b/src/bus.cpp @@ -0,0 +1,160 @@ +#include "main.hpp" +#include "bus.hpp" +#include "ebusstate.hpp" +#include "arbitration.hpp" +#include "enhanced.hpp" +#include "queue" + +BusType Bus; + +#ifdef USE_ASYNCHRONOUS +void BusType::OnReceiveCB() +{ + uint8_t byte = Serial.read(); + Bus.receive(byte); +} + +void BusType::OnReceiveErrorCB(hardwareSerial_error_t e) +{ + if (e != UART_BREAK_ERROR){ + DEBUG_LOG("OnReceiveErrorCB %i\n", e); + } +} +#endif + +BusType::BusType() +{ +#ifdef USE_ASYNCHRONOUS + Serial.onReceive(OnReceiveCB, false); + Serial.onReceiveError(OnReceiveErrorCB); + _queue = xQueueCreate(RXBUFFERSIZE, sizeof(uint8_t)); +#endif +} + +BusType::~BusType(){ +#ifdef USE_ASYNCHRONOUS + vQueueDelete(_queue); +#endif +} + +bool BusType::read(data& d) +{ +#ifdef USE_ASYNCHRONOUS + return xQueueReceive(_queue, &d, 0) > 0; +#else + if (Serial.available()){ + uint8_t byte = Serial.read(); + receive(byte); + } + if (_queue.size() > 0) { + d = _queue.front(); + _queue.pop(); + return true; + } + return false; +#endif +} + +void BusType::push(data& d) +{ +#ifdef USE_ASYNCHRONOUS + xQueueSendToBack(_queue, &d, 0); +#else + _queue.push(d); +#endif +} + +void BusType::receive(uint8_t byte) +{ + static EBusState busState; + static Arbitration arbitration; + static WiFiClient* client = 0; + + busState.data(byte); + + Arbitration::state state = arbitration.data(busState, byte); + switch (state) { + case Arbitration::none: + uint8_t arbitration_address; + client = enhArbitrationRequested(arbitration_address); + if (client && arbitration.start(busState, arbitration_address)) { + data d = {false, RECEIVED, byte, client}; // do not send to arbitration client + push(d); + } + else { + client = 0; + data d = {false, RECEIVED, byte, 0}; // send to everybody + push(d); + } + break; + case Arbitration::arbitrating: + { + data d = {false, RECEIVED, byte, client}; // do not send to arbitration client + push(d); + break; + } + case Arbitration::won: + { + DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); + data d1 = {false, RECEIVED, byte, client}; // do not send to arbitration client + push(d1); + data d2 = {true, STARTED, busState._master, client}; // send only to the arbitrating client + push(d2); + enhArbitrationDone(client); + client=0; + break; + } + case Arbitration::lost: + { + DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); + data data1 = {true, FAILED, busState._master, client}; // send only to the arbitrating client + push(data1); + data d2 = {false, RECEIVED, byte, 0}; // send to everybody + push(d2); + enhArbitrationDone(client); + client=0; + break; + } + case Arbitration::error: + { + data d1 = {true, ERROR_EBUS, ERR_FRAMING, client}; // send only to the arbitrating client + push(d1); + data d2 = {false, RECEIVED, byte, 0}; // send to everybody + push(d2); + enhArbitrationDone(client); + client=0; + break; + } + } +} + + + //static EBusState busState; + //static Arbitration arbitration; + // We received a byte. + // Handle arbitration in this CB; has to be fast enough to be done for the next character + // We can't send data to WifiClient + // - likely not thread safe + // - would take too long + // Instead regular wifi client communication needs to happen in the main loop, untill maybe WifiClient can be put in + // a task and we communicate to WifiClient with messages + // BusState should only be used from this thread, only relevant case + // HardwareSerial is thread safe, no problem to call write and read from different threads + // send results to mainloop + // -> each byte as it needs to be forwarded to other clients + // -> the state of the arbitration + + // What is needed in the main loop? + // - Each client should receive all chars that incoming on the bus + // - Need to maintain the correct order of bytes, both for + // receiving clients as for arbitrating clients + // - enhanced clients need to receive data in the enhanced format + // - If an arbitration is ongoing for client X + // + Client X should not receive the chars that are part of the arbitration + // + Client X should receive the result of the arbitration + // and then it should receive the bytes as usual + // How do we communicate to main loop? + // - for each byte, + // + the byte + // + is it a regular byte, or the result of arbitration + // + a client id in case an arbitration is ongoing \ No newline at end of file diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 421bd27..d39a358 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -2,33 +2,14 @@ #include "main.hpp" #include "ebusstate.hpp" #include "arbitration.hpp" +#include "enhanced.hpp" #define M1 0b11000000 #define M2 0b10000000 #define ARBITRATION_TIMEOUT_MS 2000 -enum requests { - CMD_INIT = 0, - CMD_SEND, - CMD_START, - CMD_INFO -}; -enum responses { - RESETTED = 0x0, - RECEIVED = 0x1, - STARTED = 0x2, - INFO = 0x3, - FAILED = 0xa, - ERROR_EBUS = 0xb, - ERROR_HOST = 0xc -}; - -enum errors { - ERR_FRAMING = 0x00, - ERR_OVERRUN = 0x01 -}; WiFiClient* arbitration_client = NULL; unsigned long arbitration_start = 0; @@ -147,6 +128,30 @@ void handleEnhClient(WiFiClient* client){ } } +int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d){ + if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { + send_res(client, c, d); + return 1; + } + return 0; +} + +void enhArbitrationDone(WiFiClient* client) { + // lock + arbitration_client = NULL; + // unlock +} + +WiFiClient* enhArbitrationRequested(uint8_t& aa) { + // lock + aa = arbitration_address; + return arbitration_client; + // unlock +} + + + +/* size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes){ size_t bytesread = 0; if (client->availableForWrite() >= AVAILABLE_THRESHOLD && arbitration_client == client) { @@ -199,91 +204,4 @@ size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* byte } return bytesread; } - -struct command{ - uint8_t c; - uint8_t d; -}; - -#define SENDCOMMANDQUEUELENGTH 10 - -inline QueueHandle_t getSendQueue() -{ - static QueueHandle_t sendqueue = 0; - if (sendqueue == 0) { - sendqueue=xQueueCreate(SENDCOMMANDQUEUELENGTH, sizeof(command)); - } - return sendqueue; -} -bool arbitration_ongoing = false; -int pushEnhClient(WiFiClient* client, uint8_t B){ - if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { - if (arbitration_ongoing) { - command c; - while (xQueueReceive(getSendQueue(), &c, 0) > 0) { - send_res(client, c.c, c.d); - } - } - else { - send_res(client, RECEIVED, B); - - } - return 1; - } - return 0; -} - - -WiFiClient* startEnhArbitration(Arbitration& arbitration, EBusState& busState ){ - WiFiClient* client = 0; - static unsigned long lastTime = 0; - static int amount = 10; - unsigned long now = millis(); - unsigned long delta = now - lastTime; - bool a=false; - if (delta > 500) { - //a=true; - lastTime =now; - } - // lock - if ((arbitration_client || a) && !arbitration_ongoing) { - a=false; - //arbitration_address=0xf3; - if (arbitration.start(busState, arbitration_address)) { - client = arbitration_client; - arbitration_ongoing = true; - } - } - // unlock - return client; -} - -void queue_command(uint8_t c, uint8_t d) -{ - command com = {c, d}; - xQueueSendToBack(getSendQueue(), &com, 0); -} - -void enhArbitrationWon(WiFiClient* client, uint8_t master){ - // lock - arbitration_client = NULL; - arbitration_ongoing = false; - queue_command(STARTED, master); - // unlock -} - -void enhArbitrationLost(WiFiClient* client, uint8_t master, uint8_t nextsymbol){ - // lock - arbitration_client = NULL; - arbitration_ongoing = false; - queue_command(FAILED, master); - queue_command(RECEIVED, nextsymbol); - // unlock -} -void enhArbitrationError(WiFiClient* client){ - // lock - queue_command(ERROR_EBUS, ERR_FRAMING); - arbitration_client = NULL; - arbitration_ongoing = false; - // unlock -} \ No newline at end of file +*/ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 975da0b..375e827 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,13 @@ #include #include //https://github.com/tzapu/WiFiManager WiFi Configuration Magic #include "main.hpp" -#include "ebusstate.hpp" -#include "arbitration.hpp" +#include "enhanced.hpp" +#include "bus.hpp" #ifdef ESP32 #include #include #include "esp32c3/rom/rtc.h" - #include "driver/uart.h" #else #include #include @@ -23,7 +22,6 @@ WiFiServer statusServer(5555); WiFiClient serverClients[MAX_SRV_CLIENTS]; WiFiClient serverClientsRO[MAX_SRV_CLIENTS]; WiFiClient enhClients[MAX_SRV_CLIENTS]; -EBusState busState; unsigned long last_comms = 0; int last_reset_code = -1; @@ -91,72 +89,6 @@ void check_reset() { } } } -QueueHandle_t receivequeue; -WiFiClient* startEnhArbitration(Arbitration& arbitration, EBusState& busstate ); - -void enhArbitrationWon(WiFiClient* client, uint8_t master); -void enhArbitrationLost(WiFiClient* client, uint8_t master, uint8_t nextsymbol); -void enhArbitrationError(WiFiClient* client); - -Arbitration arbitration; -void OnReceiveCB() -{ - //static EBusState busState; - //static Arbitration arbitration; - // We received a byte. - // Handle arbitration in this CB; has to be fast enough to be done for the next character - // We can't send data to WifiClient - // - likely not thread safe - // - would take too long - // Instead regular wifi client communication needs to happen in the main loop, untill maybe WifiClient can be put in - // a task and we communicate to WifiClient with messages - // BusState should only be used from this thread, only relevant case - // HardwareSerial is thread safe, no problem to call write and read from different threads - - static WiFiClient* client = 0; - uint8_t byte = Serial.read(); - busState.data(byte); - Arbitration::state state = arbitration.data(busState, byte); - switch (state) { - case Arbitration::none: - client=startEnhArbitration(arbitration, busState); - case Arbitration::arbitrating: - break; - case Arbitration::won: - DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); - enhArbitrationWon(client, busState._master); - client=0; - //send_res(client, STARTED, busstate._master); - //arbitration_client = NULL; - //return bytesread; - break; - case Arbitration::lost: - DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); - enhArbitrationLost(client, busState._master, busState._byte); - client=0; - //send_res(client, FAILED, busstate._master); - //send_res(client, RECEIVED, busstate._byte); - //arbitration_client = NULL; - //return bytesread; - break; - case Arbitration::error: - enhArbitrationError(client); - client=0; - break; - } - xQueueSendToBack(receivequeue, &byte, 0); - // send results to mainloop - // -> each byte as it needs to be forwarded to other clients - // -> the state of the arbitration - -} - -void OnReceiveErrorCB(hardwareSerial_error_t e) -{ - if (e != UART_BREAK){ - DEBUG_LOG("OnReceiveErrorCB %i\n", e); - } -} void setup() { check_reset(); @@ -168,12 +100,7 @@ void setup() { Serial.begin(2400, SERIAL_8N1, 21, 20); // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit Serial.setRxFIFOFull(1); - Serial.onReceive(OnReceiveCB, false); - Serial.onReceiveError(OnReceiveErrorCB); - - last_reset_code = rtc_get_reset_reason(0); - receivequeue=xQueueCreate(RXBUFFERSIZE, sizeof(uint8_t)); #elif defined(ESP8266) Serial1.begin(115200); Serial.begin(2400); @@ -313,45 +240,27 @@ void loop() { handleEnhClient(&enhClients[i]); } - //check UART for data - //if (Serial.available()) { - uint8_t bytes[ARBITRATION_BUFFER_SIZE+1]; - if (xQueueReceive(receivequeue, &bytes[0], 0) > 0) { - - //bytes[0]= Serial.read(); - - //busState.data(bytes[0]); - - // handle enhanced client that is in arbitration mode - // as a side effect, additional data can be read from the bus, which needs to - // send to the other clients. this data will be returned in the bytes argument - size_t bytesread = 0; - /*int arbitrated_client = -1; + //check queue for data + BusType::data d; + if (Bus.read(d) > 0) { for (int i = 0; i < MAX_SRV_CLIENTS; i++){ - bytesread = arbitrateEnhClient(&enhClients[i], busState, &bytes[1]); - if (bytesread>0){ - last_comms = millis(); - arbitrated_client = i; - break; + if (d._enhanced) { + if (d._client == &enhClients[i]) { + pushEnhClient(&enhClients[i], d._c, d._d); + } } - }*/ - bytesread++; // for byte at position bytes[0] - - // push data to clients, including bytes[0] and all bytes - // returned by arbitrateEnhClient, that start at bytes[1] - for (size_t i = 0; i < bytesread; i++) { - for (int j = 0; j < MAX_SRV_CLIENTS; j++){ - if (pushClient(&serverClients[j], bytes[i])){ + else { + if (pushClient(&serverClients[i], d._d)){ last_comms = millis(); } - if (pushClient(&serverClientsRO[j], bytes[i])){ + if (pushClient(&serverClientsRO[i], d._d)){ last_comms = millis(); } - //if (j != arbitrated_client) { - if (pushEnhClient(&enhClients[j], bytes[i])){ + if (d._client != &enhClients[i]) { + if (pushEnhClient(&enhClients[i], d._c, d._d)){ last_comms = millis(); } - //} + } } } } From fa9666f93b663888ad320a7c0a5b8607e8e72675 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 17:11:17 +0200 Subject: [PATCH 13/70] minor code cleanup --- include/bus.hpp | 2 +- src/bus.cpp | 29 ++++++++++------------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 047cbde..8bc99dc 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -21,7 +21,7 @@ class BusType bool read(data& d); private: - void push(data& d); + void push(const data& d); void receive (uint8_t byte); #ifdef USE_ASYNCHRONOUS diff --git a/src/bus.cpp b/src/bus.cpp index eebee7b..e77986a 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -55,7 +55,7 @@ bool BusType::read(data& d) #endif } -void BusType::push(data& d) +void BusType::push(const data& d) { #ifdef USE_ASYNCHRONOUS xQueueSendToBack(_queue, &d, 0); @@ -78,28 +78,23 @@ void BusType::receive(uint8_t byte) uint8_t arbitration_address; client = enhArbitrationRequested(arbitration_address); if (client && arbitration.start(busState, arbitration_address)) { - data d = {false, RECEIVED, byte, client}; // do not send to arbitration client - push(d); + push({false, RECEIVED, byte, client}); // do not send to arbitration client } else { client = 0; - data d = {false, RECEIVED, byte, 0}; // send to everybody - push(d); + push({false, RECEIVED, byte, 0}); // send to everybody } break; case Arbitration::arbitrating: { - data d = {false, RECEIVED, byte, client}; // do not send to arbitration client - push(d); + push({false, RECEIVED, byte, client}); // do not send to arbitration client break; } case Arbitration::won: { DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); - data d1 = {false, RECEIVED, byte, client}; // do not send to arbitration client - push(d1); - data d2 = {true, STARTED, busState._master, client}; // send only to the arbitrating client - push(d2); + push({false, RECEIVED, byte, client}); // do not send to arbitration client + push({true, STARTED, busState._master, client}); // send only to the arbitrating client enhArbitrationDone(client); client=0; break; @@ -107,20 +102,16 @@ void BusType::receive(uint8_t byte) case Arbitration::lost: { DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); - data data1 = {true, FAILED, busState._master, client}; // send only to the arbitrating client - push(data1); - data d2 = {false, RECEIVED, byte, 0}; // send to everybody - push(d2); + push({true, FAILED, busState._master, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); client=0; break; } case Arbitration::error: { - data d1 = {true, ERROR_EBUS, ERR_FRAMING, client}; // send only to the arbitrating client - push(d1); - data d2 = {false, RECEIVED, byte, 0}; // send to everybody - push(d2); + push( {true, ERROR_EBUS, ERR_FRAMING, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); client=0; break; From f4fdd338d1401dc0a8500705b599475935c7f025 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 17:43:03 +0200 Subject: [PATCH 14/70] fixes --- src/bus.cpp | 8 +++++--- src/main.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index e77986a..28025ec 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -27,7 +27,7 @@ BusType::BusType() #ifdef USE_ASYNCHRONOUS Serial.onReceive(OnReceiveCB, false); Serial.onReceiveError(OnReceiveErrorCB); - _queue = xQueueCreate(RXBUFFERSIZE, sizeof(uint8_t)); + _queue = xQueueCreate(RXBUFFERSIZE, sizeof(data)); #endif } @@ -40,7 +40,9 @@ BusType::~BusType(){ bool BusType::read(data& d) { #ifdef USE_ASYNCHRONOUS - return xQueueReceive(_queue, &d, 0) > 0; + bool result = xQueueReceive(_queue, &d, 0) == pdTRUE; + return result; + #else if (Serial.available()){ uint8_t byte = Serial.read(); @@ -110,7 +112,7 @@ void BusType::receive(uint8_t byte) } case Arbitration::error: { - push( {true, ERROR_EBUS, ERR_FRAMING, client}); // send only to the arbitrating client + push({true, ERROR_EBUS, ERR_FRAMING, client}); // send only to the arbitrating client push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); client=0; diff --git a/src/main.cpp b/src/main.cpp index 375e827..a7e919d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -242,7 +242,7 @@ void loop() { //check queue for data BusType::data d; - if (Bus.read(d) > 0) { + if (Bus.read(d)) { for (int i = 0; i < MAX_SRV_CLIENTS; i++){ if (d._enhanced) { if (d._client == &enhClients[i]) { From 1983ea25dbf8f58ed0cb2af9cdab92c7d62327ba Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 18:04:13 +0200 Subject: [PATCH 15/70] cosmetics --- src/bus.cpp | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index 28025ec..047e31c 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -73,7 +73,6 @@ void BusType::receive(uint8_t byte) static WiFiClient* client = 0; busState.data(byte); - Arbitration::state state = arbitration.data(busState, byte); switch (state) { case Arbitration::none: @@ -120,34 +119,3 @@ void BusType::receive(uint8_t byte) } } } - - - //static EBusState busState; - //static Arbitration arbitration; - // We received a byte. - // Handle arbitration in this CB; has to be fast enough to be done for the next character - // We can't send data to WifiClient - // - likely not thread safe - // - would take too long - // Instead regular wifi client communication needs to happen in the main loop, untill maybe WifiClient can be put in - // a task and we communicate to WifiClient with messages - // BusState should only be used from this thread, only relevant case - // HardwareSerial is thread safe, no problem to call write and read from different threads - // send results to mainloop - // -> each byte as it needs to be forwarded to other clients - // -> the state of the arbitration - - // What is needed in the main loop? - // - Each client should receive all chars that incoming on the bus - // - Need to maintain the correct order of bytes, both for - // receiving clients as for arbitrating clients - // - enhanced clients need to receive data in the enhanced format - // - If an arbitration is ongoing for client X - // + Client X should not receive the chars that are part of the arbitration - // + Client X should receive the result of the arbitration - // and then it should receive the bytes as usual - // How do we communicate to main loop? - // - for each byte, - // + the byte - // + is it a regular byte, or the result of arbitration - // + a client id in case an arbitration is ongoing \ No newline at end of file From ed54ca6a2481300b65182516a7d3277e9c87d79e Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 18:11:50 +0200 Subject: [PATCH 16/70] only support async on esp32 --- include/main.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/main.hpp b/include/main.hpp index 2bc1c8e..524ab28 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -11,10 +11,10 @@ #define HOSTNAME "esp-eBus" #define RESET_MS 1000 -#define USE_ASYNCHRONOUS #ifdef ESP32 // https://esp32.com/viewtopic.php?t=19788 +#define USE_ASYNCHRONOUS #define AVAILABLE_THRESHOLD 0 #else #define AVAILABLE_THRESHOLD 1 From fa5bcf591809e6b15d6fcf220100ce5c3716e706 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 18:15:22 +0200 Subject: [PATCH 17/70] status on async mode or not --- src/main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index a7e919d..c091a04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -158,6 +158,12 @@ int DEBUG_LOG_IMPL(const char *format, ...) return ret; } +#ifdef USE_ASYNCHRONOUS +#define ASYNC_MODE true +#else +#define ASYNC_MODE false +#endif + bool handleStatusServerRequests() { if (!statusServer.hasClient()) return false; @@ -166,6 +172,7 @@ bool handleStatusServerRequests() { if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { client.printf("HTTP/1.1 200 OK\r\n\r\n"); + client.printf("async mode: %s\n", ASYNC_MODE? "true" : "false"); client.printf("uptime: %ld ms\n", millis()); client.printf("rssi: %d dBm\n", WiFi.RSSI()); client.printf("free_heap: %d B\n", ESP.getFreeHeap()); From d673ba1538fb5f269e5982808b8bf133403915bf Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 18:17:23 +0200 Subject: [PATCH 18/70] comments --- include/arbitration.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 989dbf7..0486d8e 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -7,7 +7,7 @@ class Arbitration { public: enum state {none, // no arbitration ongoing/not yet completed - arbitrating, + arbitrating, // arbitration ongoing won, // won lost, // lost error // error From 07efbff159f36c8628113d516c64c09c688ed520 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 21:08:09 +0200 Subject: [PATCH 19/70] code cleanup --- include/main.hpp | 7 +++--- src/bus.cpp | 26 +++++++--------------- src/enhanced.cpp | 57 ------------------------------------------------ 3 files changed, 12 insertions(+), 78 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index 524ab28..c781892 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -6,6 +6,7 @@ #define MAX_SRV_CLIENTS 4 #define RXBUFFERSIZE 1024 +#define QUEUE_SIZE 480 #define ARBITRATION_BUFFER_SIZE 20 #define STACK_PROTECTOR 512 // bytes #define HOSTNAME "esp-eBus" @@ -13,8 +14,8 @@ #ifdef ESP32 -// https://esp32.com/viewtopic.php?t=19788 #define USE_ASYNCHRONOUS +// https://esp32.com/viewtopic.php?t=19788 #define AVAILABLE_THRESHOLD 0 #else #define AVAILABLE_THRESHOLD 1 @@ -25,8 +26,8 @@ int DEBUG_LOG_IMPL(const char *format, ...); #define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); -int pushClient(WiFiClient* client, uint8_t B); +int pushClient(WiFiClient* client, uint8_t B); void handleClient(WiFiClient* client); -#endif \ No newline at end of file +#endif diff --git a/src/bus.cpp b/src/bus.cpp index 047e31c..ea84bd3 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -27,7 +27,7 @@ BusType::BusType() #ifdef USE_ASYNCHRONOUS Serial.onReceive(OnReceiveCB, false); Serial.onReceiveError(OnReceiveErrorCB); - _queue = xQueueCreate(RXBUFFERSIZE, sizeof(data)); + _queue = xQueueCreate(QUEUE_SIZE, sizeof(data)); #endif } @@ -40,9 +40,7 @@ BusType::~BusType(){ bool BusType::read(data& d) { #ifdef USE_ASYNCHRONOUS - bool result = xQueueReceive(_queue, &d, 0) == pdTRUE; - return result; - + return xQueueReceive(_queue, &d, 0) == pdTRUE; #else if (Serial.available()){ uint8_t byte = Serial.read(); @@ -87,35 +85,27 @@ void BusType::receive(uint8_t byte) } break; case Arbitration::arbitrating: - { push({false, RECEIVED, byte, client}); // do not send to arbitration client break; - } case Arbitration::won: - { DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); - push({false, RECEIVED, byte, client}); // do not send to arbitration client - push({true, STARTED, busState._master, client}); // send only to the arbitrating client + push({true, STARTED, busState._master, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, client}); // do not send to arbitrating client enhArbitrationDone(client); client=0; break; - } case Arbitration::lost: - { DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); - push({true, FAILED, busState._master, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0}); // send to everybody + push({true, FAILED, busState._master, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); client=0; break; - } case Arbitration::error: - { - push({true, ERROR_EBUS, ERR_FRAMING, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0}); // send to everybody + push({true, ERROR_EBUS, ERR_FRAMING, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); client=0; break; - } } } diff --git a/src/enhanced.cpp b/src/enhanced.cpp index d39a358..e592933 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -148,60 +148,3 @@ WiFiClient* enhArbitrationRequested(uint8_t& aa) { return arbitration_client; // unlock } - - - -/* -size_t arbitrateEnhClient(WiFiClient* client, EBusState& busstate, uint8_t* bytes){ - size_t bytesread = 0; - if (client->availableForWrite() >= AVAILABLE_THRESHOLD && arbitration_client == client) { - Arbitration arbitration; - if (arbitration.start(busstate, arbitration_address) ) { - int loopcount = 0; - while (loopcount++ < ARBITRATION_BUFFER_SIZE){ - while (Serial.available() == 0) { - if (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS) { - DEBUG_LOG("ARB TIMEOUT 1 0x%02x 0x%02x\n", busstate._master, busstate._byte); - send_res(client, ERROR_EBUS, ERR_FRAMING); - arbitration_client = NULL; - return bytesread; - } - } - - uint8_t symbol = Serial.read(); - DEBUG_LOG("ARB SYMBOL 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); - busstate.data(symbol); - bytes[bytesread++] = symbol; - switch (arbitration.data(busstate, symbol)) { - case Arbitration::none: - arbitration_client = NULL; - return bytesread; - case Arbitration::arbitrating: - break; - case Arbitration::won: - DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busstate._master, busstate.microsSinceLastSyn()); - send_res(client, STARTED, busstate._master); - arbitration_client = NULL; - return bytesread; - case Arbitration::lost: - DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, busstate.microsSinceLastSyn()); - send_res(client, FAILED, busstate._master); - send_res(client, RECEIVED, busstate._byte); - arbitration_client = NULL; - return bytesread; - case Arbitration::error: - arbitration_client = NULL; - return bytesread; - } - } - arbitration_client = NULL; - } - if (arbitration_client && (millis() > arbitration_start + ARBITRATION_TIMEOUT_MS)){ - DEBUG_LOG("ARB TIMEOUT 2 0x%02x 0x%02x\n", busstate._master, busstate._byte); - send_res(client, ERROR_EBUS, ERR_FRAMING); - arbitration_client = NULL; - } - } - return bytesread; -} -*/ \ No newline at end of file From 7f3e9fbde1a1b1669587ee8e3c9e8c9bce0cdc25 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 19 Apr 2023 21:34:07 +0200 Subject: [PATCH 20/70] comments --- include/arbitration.hpp | 16 +++++++++++++++- include/bus.hpp | 6 ++++++ include/ebusstate.hpp | 8 ++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 0486d8e..11ebf2b 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -3,6 +3,11 @@ #include "ebusstate.hpp" +// Implements the arbitration algorithm. Uses the state of the bus to decide what to do. +// Typical usage: +// - try to start the arbitration with "start" method +// - pass each received value on the bus to the "data" method +// which will then tell you what the state of the arbitration is class Arbitration { public: @@ -18,8 +23,17 @@ class Arbitration , _participateSecond(false) , _arbitration_address(0) {} - + // Try to start arbitration for the specified master. + // Return values: + // - false : arbitration not started. Possible reasons: + // + the bus is not in a state that allows to start arbitration + // + another arbitration is already ongoing + // + the master address is SYN bool start (EBusState& busstate, uint8_t master); + + // A symbol was received on the bus, what does this do to the arbitration state? + // Return values: + // - see description of state enum value Arbitration::state data (EBusState& busstate, uint8_t symbol); private: diff --git a/include/bus.hpp b/include/bus.hpp index 8bc99dc..27d5284 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -3,6 +3,11 @@ #include "main.hpp" #include "queue" +// This object retrieves data from the Serial object and +// let's it flow through the arbitration process. +// The "read" method will return data with meta information that tells what should be done +// with the returned data. +// This object hides if the underlying implementation is synchronous or asynchronous class BusType { public: @@ -18,6 +23,7 @@ class BusType BusType(); ~BusType(); + // Is there a value available that should be send to a client? bool read(data& d); private: diff --git a/include/ebusstate.hpp b/include/ebusstate.hpp index ca089b4..3f046bd 100644 --- a/include/ebusstate.hpp +++ b/include/ebusstate.hpp @@ -4,7 +4,11 @@ #include "enhanced.hpp" - +// Implements the state of the bus +// The arbitration process can only start at well defined states of the bus +// To asses the state, all data received on the bus needs to be send to this object +// The object takes care of startup of the bus and recovery when an unexpected event +// happens class EBusState { public: enum eState { @@ -36,7 +40,7 @@ class EBusState { EBusState() : _state(eStartup) {} - + // Evaluate a symbol received on UART and determine what the new state of the bus is inline void data(uint8_t symbol) { switch (_state) From 97399fdc84075f5d954d5ba3f1b539d5ceb414da Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 13:24:47 +0200 Subject: [PATCH 21/70] add locking of variables shared between threads --- src/enhanced.cpp | 71 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index e592933..8a4bb57 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -9,11 +9,49 @@ #define ARBITRATION_TIMEOUT_MS 2000 +// Locking +#ifdef USE_ASYNCHRONOUS +SemaphoreHandle_t getMutex(){ + static SemaphoreHandle_t _lock = NULL; + if(_lock == NULL){ + _lock = xSemaphoreCreateMutex(); + if(_lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return NULL; + } + } + return _lock; +} +#define ENH_MUTEX_LOCK() do {} while (xSemaphoreTake(getMutex(), portMAX_DELAY) != pdPASS) +#define ENH_MUTEX_UNLOCK() xSemaphoreGive(getMutex()) +#else +#define ENH_MUTEX_LOCK() +#define ENH_MUTEX_UNLOCK() +#endif -WiFiClient* arbitration_client = NULL; +WiFiClient* _arbitration_client = NULL; +int _arbitration_address = -1; unsigned long arbitration_start = 0; -int arbitration_address = -1; + +void getEnhArbitrationClient(WiFiClient* &client, uint8_t &adress) { + ENH_MUTEX_LOCK(); + client = _arbitration_client; + adress=_arbitration_address; + ENH_MUTEX_UNLOCK(); +} + +bool setEnhArbitrationClient(WiFiClient* client, uint8_t address) { + bool result = false; + ENH_MUTEX_LOCK(); + if (!_arbitration_client) { + result = true; + _arbitration_client = client; + _arbitration_address = address; + } + ENH_MUTEX_UNLOCK(); + return result; +} void decode(int b1, int b2, uint8_t (&data)[2]){ data[0] = (b1 >> 2) & 0b1111; @@ -38,15 +76,19 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ } if (c == CMD_START){ if (d == SYN){ - arbitration_client = NULL; + //arbitration_client = NULL; + setEnhArbitrationClient(NULL, 0); DEBUG_LOG("CMD_START SYN\n"); return; } else { // start arbitration - if (arbitration_client ) { - if (arbitration_client!=client) { + WiFiClient* _client = NULL; + uint8_t _address= 0; + getEnhArbitrationClient(_client, _address); + if (_client ) { + if (_client!=client) { // only one client can be in arbitration - DEBUG_LOG("CMD_START ONGOING 0x%02 0x%02x\n", arbitration_address, d); + DEBUG_LOG("CMD_START ONGOING 0x%02 0x%02x\n", _address, d); send_res(client, ERROR_HOST, ERR_FRAMING); return; } @@ -57,11 +99,9 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ else { DEBUG_LOG("CMD_START 0x%02x\n", d); } - - arbitration_client = client; + //arbitration_client = client; + //arbitration_address = d; setEnhArbitrationClient(client, d); arbitration_start = millis(); - arbitration_address = d; - return; } } @@ -137,14 +177,11 @@ int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d){ } void enhArbitrationDone(WiFiClient* client) { - // lock - arbitration_client = NULL; - // unlock + setEnhArbitrationClient(NULL, 0); } WiFiClient* enhArbitrationRequested(uint8_t& aa) { - // lock - aa = arbitration_address; - return arbitration_client; - // unlock + WiFiClient* client = NULL; + getEnhArbitrationClient(client, aa); + return client; } From 17e81d98c239cf6bc020b7e6261d79c297430ae5 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 17:39:44 +0200 Subject: [PATCH 22/70] logging, locking, arbitration start improvements --- include/bus.hpp | 5 ++-- include/enhanced.hpp | 2 +- src/bus.cpp | 32 ++++++++++++++------------ src/enhanced.cpp | 54 +++++++++++++++++++++++++++++--------------- src/main.cpp | 4 ++-- 5 files changed, 59 insertions(+), 38 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 27d5284..f4bde0d 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -19,6 +19,7 @@ class BusType uint8_t _c; // command byte, only used when in "enhanced" mode uint8_t _d; // data byte for both regular and enhanced command WiFiClient* _client; // the client that is being arbitrated + bool _log; }; BusType(); ~BusType(); @@ -27,8 +28,8 @@ class BusType bool read(data& d); private: - void push(const data& d); - void receive (uint8_t byte); + inline void push (const data& d); + void receive (uint8_t byte); #ifdef USE_ASYNCHRONOUS QueueHandle_t _queue; diff --git a/include/enhanced.hpp b/include/enhanced.hpp index 0314d6d..6ec4247 100644 --- a/include/enhanced.hpp +++ b/include/enhanced.hpp @@ -31,7 +31,7 @@ enum errors { void enhArbitrationDone(WiFiClient* client); WiFiClient* enhArbitrationRequested(uint8_t& arbitration_client); -int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d); +int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d, bool log); void handleEnhClient(WiFiClient* client); #endif diff --git a/src/bus.cpp b/src/bus.cpp index ea84bd3..572b6fb 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -76,35 +76,37 @@ void BusType::receive(uint8_t byte) case Arbitration::none: uint8_t arbitration_address; client = enhArbitrationRequested(arbitration_address); - if (client && arbitration.start(busState, arbitration_address)) { - push({false, RECEIVED, byte, client}); // do not send to arbitration client - } - else { - client = 0; - push({false, RECEIVED, byte, 0}); // send to everybody + if (client) { + if (arbitration.start(busState, arbitration_address)) { + DEBUG_LOG("ARB START SUC 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); + } + else { + DEBUG_LOG("ARB START FAI 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); + } } + push({false, RECEIVED, byte, 0, client!=0}); // send to everybody break; case Arbitration::arbitrating: - push({false, RECEIVED, byte, client}); // do not send to arbitration client + push({false, RECEIVED, byte, client, client!=0}); // do not send to arbitration client break; case Arbitration::won: - DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); - push({true, STARTED, busState._master, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, client}); // do not send to arbitrating client enhArbitrationDone(client); + DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); + push({true, STARTED, busState._master, client, client!=0}); // send only to the arbitrating client + push({false, RECEIVED, byte, client, client!=0}); // do not send to arbitrating client client=0; break; case Arbitration::lost: - DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); - push({true, FAILED, busState._master, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); + DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); + push({true, FAILED, busState._master, client, client!=0}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0, client!=0}); // send to everybody client=0; break; case Arbitration::error: - push({true, ERROR_EBUS, ERR_FRAMING, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0}); // send to everybody enhArbitrationDone(client); + push({true, ERROR_EBUS, ERR_FRAMING, client, client!=0}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0, client!=0}); // send to everybody client=0; break; } diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 8a4bb57..4e3e767 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -16,7 +16,7 @@ SemaphoreHandle_t getMutex(){ if(_lock == NULL){ _lock = xSemaphoreCreateMutex(); if(_lock == NULL){ - log_e("xSemaphoreCreateMutex failed"); + DEBUG_LOG("xSemaphoreCreateMutex failed"); return NULL; } } @@ -34,21 +34,32 @@ WiFiClient* _arbitration_client = NULL; int _arbitration_address = -1; unsigned long arbitration_start = 0; -void getEnhArbitrationClient(WiFiClient* &client, uint8_t &adress) { +void getEnhArbitrationClient(WiFiClient* &client, uint8_t &address) { ENH_MUTEX_LOCK(); client = _arbitration_client; - adress=_arbitration_address; + address= _arbitration_address; ENH_MUTEX_UNLOCK(); } -bool setEnhArbitrationClient(WiFiClient* client, uint8_t address) { - bool result = false; +void clearEnhArbitrationClient() { ENH_MUTEX_LOCK(); - if (!_arbitration_client) { - result = true; + _arbitration_client = 0; + _arbitration_address= -1; + ENH_MUTEX_UNLOCK(); +} + +bool setEnhArbitrationClient(WiFiClient* &client, uint8_t &address) { + bool result = true; + ENH_MUTEX_LOCK(); + if (_arbitration_client == NULL) { _arbitration_client = client; _arbitration_address = address; } + else { + result = false; + client = _arbitration_client; + address = _arbitration_address; + } ENH_MUTEX_UNLOCK(); return result; } @@ -77,18 +88,17 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ if (c == CMD_START){ if (d == SYN){ //arbitration_client = NULL; - setEnhArbitrationClient(NULL, 0); + clearEnhArbitrationClient(); DEBUG_LOG("CMD_START SYN\n"); return; } else { // start arbitration - WiFiClient* _client = NULL; - uint8_t _address= 0; - getEnhArbitrationClient(_client, _address); - if (_client ) { - if (_client!=client) { + WiFiClient* cl = client; + uint8_t ad = d; + if (!setEnhArbitrationClient(client, d) ) { + if (cl!=client) { // only one client can be in arbitration - DEBUG_LOG("CMD_START ONGOING 0x%02 0x%02x\n", _address, d); + DEBUG_LOG("CMD_START ONGOING 0x%02 0x%02x\n", ad, d); send_res(client, ERROR_HOST, ERR_FRAMING); return; } @@ -100,7 +110,8 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ DEBUG_LOG("CMD_START 0x%02x\n", d); } //arbitration_client = client; - //arbitration_address = d; setEnhArbitrationClient(client, d); + //arbitration_address = d; + setEnhArbitrationClient(client, d); arbitration_start = millis(); return; } @@ -133,7 +144,8 @@ bool read_cmd(WiFiClient* client, uint8_t (&data)[2]){ } if (b<0b11000000){ - client->write("first command signature error"); + DEBUG_LOG("first command missing\n"); + client->write("first command missing"); // first command signature error client->stop(); return false; @@ -143,6 +155,7 @@ bool read_cmd(WiFiClient* client, uint8_t (&data)[2]){ if (b2<0) { // second command missing + DEBUG_LOG("second command missing\n"); client->write("second command missing"); client->stop(); return false; @@ -150,6 +163,7 @@ bool read_cmd(WiFiClient* client, uint8_t (&data)[2]){ if ((b2 & 0b11000000) != 0b10000000){ // second command signature error + DEBUG_LOG("second command signature error\n"); client->write("second command signature error"); client->stop(); return false; @@ -168,7 +182,11 @@ void handleEnhClient(WiFiClient* client){ } } -int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d){ +int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d, bool log){ + + if (log) { + DEBUG_LOG("DATA 0x%02x 0x%02x\n", c, d); + } if (client->availableForWrite() >= AVAILABLE_THRESHOLD) { send_res(client, c, d); return 1; @@ -177,7 +195,7 @@ int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d){ } void enhArbitrationDone(WiFiClient* client) { - setEnhArbitrationClient(NULL, 0); + clearEnhArbitrationClient(); } WiFiClient* enhArbitrationRequested(uint8_t& aa) { diff --git a/src/main.cpp b/src/main.cpp index c091a04..ed10990 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,7 +253,7 @@ void loop() { for (int i = 0; i < MAX_SRV_CLIENTS; i++){ if (d._enhanced) { if (d._client == &enhClients[i]) { - pushEnhClient(&enhClients[i], d._c, d._d); + pushEnhClient(&enhClients[i], d._c, d._d, d._log); } } else { @@ -264,7 +264,7 @@ void loop() { last_comms = millis(); } if (d._client != &enhClients[i]) { - if (pushEnhClient(&enhClients[i], d._c, d._d)){ + if (pushEnhClient(&enhClients[i], d._c, d._d, d._log)){ last_comms = millis(); } } From 6879ab6ed630946dd06069cc07450f9aee58e4ee Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 17:49:37 +0200 Subject: [PATCH 23/70] logging --- include/bus.hpp | 2 +- src/bus.cpp | 16 ++++++++-------- src/main.cpp | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index f4bde0d..4b84336 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -19,7 +19,7 @@ class BusType uint8_t _c; // command byte, only used when in "enhanced" mode uint8_t _d; // data byte for both regular and enhanced command WiFiClient* _client; // the client that is being arbitrated - bool _log; + WiFiClient* _logtoclient; // the client that needs to log }; BusType(); ~BusType(); diff --git a/src/bus.cpp b/src/bus.cpp index 572b6fb..9af1cfc 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -84,29 +84,29 @@ void BusType::receive(uint8_t byte) DEBUG_LOG("ARB START FAI 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); } } - push({false, RECEIVED, byte, 0, client!=0}); // send to everybody + push({false, RECEIVED, byte, 0, client}); // send to everybody break; case Arbitration::arbitrating: - push({false, RECEIVED, byte, client, client!=0}); // do not send to arbitration client + push({false, RECEIVED, byte, client, client}); // do not send to arbitration client break; case Arbitration::won: enhArbitrationDone(client); DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); - push({true, STARTED, busState._master, client, client!=0}); // send only to the arbitrating client - push({false, RECEIVED, byte, client, client!=0}); // do not send to arbitrating client + push({true, STARTED, busState._master, client, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, client, client}); // do not send to arbitrating client client=0; break; case Arbitration::lost: enhArbitrationDone(client); DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); - push({true, FAILED, busState._master, client, client!=0}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0, client!=0}); // send to everybody + push({true, FAILED, busState._master, client, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0, client}); // send to everybody client=0; break; case Arbitration::error: enhArbitrationDone(client); - push({true, ERROR_EBUS, ERR_FRAMING, client, client!=0}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0, client!=0}); // send to everybody + push({true, ERROR_EBUS, ERR_FRAMING, client, client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0, client}); // send to everybody client=0; break; } diff --git a/src/main.cpp b/src/main.cpp index ed10990..7e589da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,7 +253,7 @@ void loop() { for (int i = 0; i < MAX_SRV_CLIENTS; i++){ if (d._enhanced) { if (d._client == &enhClients[i]) { - pushEnhClient(&enhClients[i], d._c, d._d, d._log); + pushEnhClient(&enhClients[i], d._c, d._d, true); } } else { @@ -264,7 +264,7 @@ void loop() { last_comms = millis(); } if (d._client != &enhClients[i]) { - if (pushEnhClient(&enhClients[i], d._c, d._d, d._log)){ + if (pushEnhClient(&enhClients[i], d._c, d._d, d._logtoclient == &enhClients[i])){ last_comms = millis(); } } From a6e0313d6b97383834298194ff5b4637bc1050f1 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 18:12:07 +0200 Subject: [PATCH 24/70] logging --- src/bus.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index 9af1cfc..b04d5d1 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -78,27 +78,28 @@ void BusType::receive(uint8_t byte) client = enhArbitrationRequested(arbitration_address); if (client) { if (arbitration.start(busState, arbitration_address)) { - DEBUG_LOG("ARB START SUC 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START SUC 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); } else { - DEBUG_LOG("ARB START FAI 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START FAI 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); } } push({false, RECEIVED, byte, 0, client}); // send to everybody break; case Arbitration::arbitrating: + DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); push({false, RECEIVED, byte, client, client}); // do not send to arbitration client break; case Arbitration::won: enhArbitrationDone(client); - DEBUG_LOG("ARB SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); + DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); push({true, STARTED, busState._master, client, client}); // send only to the arbitrating client push({false, RECEIVED, byte, client, client}); // do not send to arbitrating client client=0; break; case Arbitration::lost: enhArbitrationDone(client); - DEBUG_LOG("ARB SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); + DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); push({true, FAILED, busState._master, client, client}); // send only to the arbitrating client push({false, RECEIVED, byte, 0, client}); // send to everybody client=0; From cc171aedd03d7fc4de361960c7c2220097b63f0a Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 18:43:15 +0200 Subject: [PATCH 25/70] disable logging --- include/main.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index c781892..b1d0c25 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -21,9 +21,9 @@ #define AVAILABLE_THRESHOLD 1 #endif -//inline int DEBUG_LOG(const char *format, ...) { return 0;} +inline int DEBUG_LOG(const char *format, ...) { return 0;} int DEBUG_LOG_IMPL(const char *format, ...); -#define DEBUG_LOG DEBUG_LOG_IMPL +//#define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); int pushClient(WiFiClient* client, uint8_t B); From 13f545ce0b26145108b8f258e55b78eb7e44eed3 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 18:46:10 +0200 Subject: [PATCH 26/70] code cleanup --- include/enhanced.hpp | 2 +- src/bus.cpp | 6 +++--- src/enhanced.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/enhanced.hpp b/include/enhanced.hpp index 6ec4247..0dbc2fd 100644 --- a/include/enhanced.hpp +++ b/include/enhanced.hpp @@ -28,7 +28,7 @@ enum errors { ERR_OVERRUN = 0x01 }; -void enhArbitrationDone(WiFiClient* client); +void enhArbitrationDone(); WiFiClient* enhArbitrationRequested(uint8_t& arbitration_client); int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d, bool log); diff --git a/src/bus.cpp b/src/bus.cpp index b04d5d1..5628727 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -91,21 +91,21 @@ void BusType::receive(uint8_t byte) push({false, RECEIVED, byte, client, client}); // do not send to arbitration client break; case Arbitration::won: - enhArbitrationDone(client); + enhArbitrationDone(); DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); push({true, STARTED, busState._master, client, client}); // send only to the arbitrating client push({false, RECEIVED, byte, client, client}); // do not send to arbitrating client client=0; break; case Arbitration::lost: - enhArbitrationDone(client); + enhArbitrationDone(); DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); push({true, FAILED, busState._master, client, client}); // send only to the arbitrating client push({false, RECEIVED, byte, 0, client}); // send to everybody client=0; break; case Arbitration::error: - enhArbitrationDone(client); + enhArbitrationDone(); push({true, ERROR_EBUS, ERR_FRAMING, client, client}); // send only to the arbitrating client push({false, RECEIVED, byte, 0, client}); // send to everybody client=0; diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 4e3e767..07feb87 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -194,7 +194,7 @@ int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d, bool log){ return 0; } -void enhArbitrationDone(WiFiClient* client) { +void enhArbitrationDone() { clearEnhArbitrationClient(); } From 31a4cd76fbd9709d60b933fcef9499ef4d7cef95 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 19:18:41 +0200 Subject: [PATCH 27/70] comments --- src/arbitration.cpp | 1 - src/bus.cpp | 2 +- src/enhanced.cpp | 9 +++------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 57c9f44..a4e0371 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -49,7 +49,6 @@ Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { case EBusState::eStartupSecondSyn: case EBusState::eReceivedFirstSYN: DEBUG_LOG("ARB ERROR 0x%02x 0x%02x\n", busstate._master, busstate._byte); - //send_res(client, ERROR_EBUS, ERR_FRAMING); _arbitrating = false; return error; case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? diff --git a/src/bus.cpp b/src/bus.cpp index 5628727..47b6984 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -84,7 +84,7 @@ void BusType::receive(uint8_t byte) DEBUG_LOG("BUS START FAI 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); } } - push({false, RECEIVED, byte, 0, client}); // send to everybody + push({false, RECEIVED, byte, 0, client}); // send to everybody. ebusd needs the SYN to get in the right mood break; case Arbitration::arbitrating: DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 07feb87..2be2cbf 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -87,7 +87,6 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ } if (c == CMD_START){ if (d == SYN){ - //arbitration_client = NULL; clearEnhArbitrationClient(); DEBUG_LOG("CMD_START SYN\n"); return; @@ -108,9 +107,7 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ } else { DEBUG_LOG("CMD_START 0x%02x\n", d); - } - //arbitration_client = client; - //arbitration_address = d; + } setEnhArbitrationClient(client, d); arbitration_start = millis(); return; @@ -144,8 +141,8 @@ bool read_cmd(WiFiClient* client, uint8_t (&data)[2]){ } if (b<0b11000000){ - DEBUG_LOG("first command missing\n"); - client->write("first command missing"); + DEBUG_LOG("first command signature error\n"); + client->write("first command signature error"); // first command signature error client->stop(); return false; From a22d92e8b515243550e9b98d6c246c26b9a1edf7 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 20 Apr 2023 22:54:04 +0200 Subject: [PATCH 28/70] comments --- include/arbitration.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 11ebf2b..a9e593f 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -29,6 +29,7 @@ class Arbitration // + the bus is not in a state that allows to start arbitration // + another arbitration is already ongoing // + the master address is SYN + // = true : arbitration started. Make sure to pass all bus data to this object through the "data" method bool start (EBusState& busstate, uint8_t master); // A symbol was received on the bus, what does this do to the arbitration state? From e658cf684ca89e3c1828f2b1ff27a7ba0e5abf34 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 11:13:30 +0200 Subject: [PATCH 29/70] code feedback, rename ebusstate to busstate --- include/arbitration.hpp | 6 +-- include/bus.hpp | 18 +++++--- include/{ebusstate.hpp => busstate.hpp} | 18 ++++---- include/main.hpp | 6 +-- src/arbitration.cpp | 26 +++++------ src/bus.cpp | 59 +++++++++++-------------- src/enhanced.cpp | 8 +--- src/main.cpp | 38 +--------------- 8 files changed, 70 insertions(+), 109 deletions(-) rename include/{ebusstate.hpp => busstate.hpp} (90%) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index a9e593f..60f1bf4 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -1,7 +1,7 @@ #ifndef _ARBITRATION_H_ #define _ARBITRATION_H_ -#include "ebusstate.hpp" +#include "busstate.hpp" // Implements the arbitration algorithm. Uses the state of the bus to decide what to do. // Typical usage: @@ -30,12 +30,12 @@ class Arbitration // + another arbitration is already ongoing // + the master address is SYN // = true : arbitration started. Make sure to pass all bus data to this object through the "data" method - bool start (EBusState& busstate, uint8_t master); + bool start (BusState& busstate, uint8_t master); // A symbol was received on the bus, what does this do to the arbitration state? // Return values: // - see description of state enum value - Arbitration::state data (EBusState& busstate, uint8_t symbol); + Arbitration::state data (BusState& busstate, uint8_t symbol); private: bool _arbitrating; diff --git a/include/bus.hpp b/include/bus.hpp index 4b84336..a3e592f 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -1,13 +1,15 @@ #ifndef _BUS_H_ #define _BUS_H_ #include "main.hpp" +#include "busstate.hpp" +#include "arbitration.hpp" #include "queue" -// This object retrieves data from the Serial object and -// let's it flow through the arbitration process. -// The "read" method will return data with meta information that tells what should be done -// with the returned data. -// This object hides if the underlying implementation is synchronous or asynchronous +// This object retrieves data from the Serial object and let's +// it flow through the arbitration process. The "read" method +// will return data with meta information that tells what should +// be done with the returned data. This object hides if the +// underlying implementation is synchronous or asynchronous class BusType { public: @@ -30,8 +32,10 @@ class BusType private: inline void push (const data& d); void receive (uint8_t byte); - -#ifdef USE_ASYNCHRONOUS + BusState _busState; + Arbitration _arbitration; + WiFiClient* _client; +#if USE_ASYNCHRONOUS QueueHandle_t _queue; static void OnReceiveCB(); static void OnReceiveErrorCB(hardwareSerial_error_t e); diff --git a/include/ebusstate.hpp b/include/busstate.hpp similarity index 90% rename from include/ebusstate.hpp rename to include/busstate.hpp index 3f046bd..bceee50 100644 --- a/include/ebusstate.hpp +++ b/include/busstate.hpp @@ -1,15 +1,15 @@ -#ifndef _EBUSSTATE_H_ -#define _EBUSSTATE_H_ +#ifndef _BUSSTATE_H_ +#define _BUSSTATE_H_ #include "main.hpp" #include "enhanced.hpp" -// Implements the state of the bus -// The arbitration process can only start at well defined states of the bus -// To asses the state, all data received on the bus needs to be send to this object -// The object takes care of startup of the bus and recovery when an unexpected event -// happens -class EBusState { +// Implements the state of the bus. The arbitration process can +// only start at well defined states of the bus. To asses the +// state, all data received on the bus needs to be send to this +// object. The object takes care of startup of the bus and +// recovery when an unexpected event happens. +class BusState { public: enum eState { eStartup, // In startup mode to analyze bus state @@ -37,7 +37,7 @@ class EBusState { }; return values[e]; } - EBusState() + BusState() : _state(eStartup) {} // Evaluate a symbol received on UART and determine what the new state of the bus is diff --git a/include/main.hpp b/include/main.hpp index b1d0c25..a165545 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -7,22 +7,22 @@ #define MAX_SRV_CLIENTS 4 #define RXBUFFERSIZE 1024 #define QUEUE_SIZE 480 -#define ARBITRATION_BUFFER_SIZE 20 #define STACK_PROTECTOR 512 // bytes #define HOSTNAME "esp-eBus" #define RESET_MS 1000 #ifdef ESP32 -#define USE_ASYNCHRONOUS +#define USE_ASYNCHRONOUS 1 // https://esp32.com/viewtopic.php?t=19788 #define AVAILABLE_THRESHOLD 0 #else +#define USE_ASYNCHRONOUS 0 #define AVAILABLE_THRESHOLD 1 #endif inline int DEBUG_LOG(const char *format, ...) { return 0;} -int DEBUG_LOG_IMPL(const char *format, ...); +//int DEBUG_LOG_IMPL(const char *format, ...); //#define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index a4e0371..da1e34e 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -1,7 +1,7 @@ #include "arbitration.hpp" -#include "ebusstate.hpp" +#include "busstate.hpp" -bool Arbitration::start(EBusState& busstate, uint8_t master) +bool Arbitration::start(BusState& busstate, uint8_t master) { static int arb = 0; if (_arbitrating) { return false; @@ -9,7 +9,7 @@ bool Arbitration::start(EBusState& busstate, uint8_t master) if (master == SYN) { return false; } - if (busstate._state != EBusState::eReceivedFirstSYN) { + if (busstate._state != BusState::eReceivedFirstSYN) { return false; } DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, master, busstate.microsSinceLastSyn()); @@ -37,21 +37,21 @@ bool Arbitration::start(EBusState& busstate, uint8_t master) // bus permission must be in the range of 4300 us - 4456,24 us ." // rely on the uart to keep the timing // just make sure the byte to send is available in time -Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { +Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { if (!_arbitrating){ return none; } switch (busstate._state) { - case EBusState::eStartup: // error out - case EBusState::eStartupFirstSyn: - case EBusState::eStartupSymbolAfterFirstSyn: - case EBusState::eStartupSecondSyn: - case EBusState::eReceivedFirstSYN: + case BusState::eStartup: // error out + case BusState::eStartupFirstSyn: + case BusState::eStartupSymbolAfterFirstSyn: + case BusState::eStartupSecondSyn: + case BusState::eReceivedFirstSYN: DEBUG_LOG("ARB ERROR 0x%02x 0x%02x\n", busstate._master, busstate._byte); _arbitrating = false; return error; - case EBusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? + case BusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? if (symbol == _arbitration_address) { DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; @@ -66,7 +66,7 @@ Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { // the winning master is. Need to wait for eBusy } return arbitrating; - case EBusState::eReceivedSecondSYN: // did we sign up for second round arbitration? + case BusState::eReceivedSecondSYN: // did we sign up for second round arbitration? if (_participateSecond) { // execute second round of arbitration DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitration_address, busstate.microsSinceLastSyn()); @@ -76,7 +76,7 @@ Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitration_address, busstate.microsSinceLastSyn()); } return arbitrating; - case EBusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? + case BusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? if (symbol == _arbitration_address) { DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; @@ -89,7 +89,7 @@ Arbitration::state Arbitration::data(EBusState& busstate, uint8_t symbol) { // "lost" state can be handled the same as when somebody lost in the first round } return arbitrating; - case EBusState::eBusy: + case BusState::eBusy: _arbitrating = false; return lost; } diff --git a/src/bus.cpp b/src/bus.cpp index 47b6984..8ad98a4 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -1,13 +1,11 @@ #include "main.hpp" #include "bus.hpp" -#include "ebusstate.hpp" -#include "arbitration.hpp" #include "enhanced.hpp" #include "queue" BusType Bus; -#ifdef USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS void BusType::OnReceiveCB() { uint8_t byte = Serial.read(); @@ -23,8 +21,9 @@ void BusType::OnReceiveErrorCB(hardwareSerial_error_t e) #endif BusType::BusType() +: _client(0) { -#ifdef USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS Serial.onReceive(OnReceiveCB, false); Serial.onReceiveError(OnReceiveErrorCB); _queue = xQueueCreate(QUEUE_SIZE, sizeof(data)); @@ -32,14 +31,14 @@ BusType::BusType() } BusType::~BusType(){ -#ifdef USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS vQueueDelete(_queue); #endif } bool BusType::read(data& d) { -#ifdef USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS return xQueueReceive(_queue, &d, 0) == pdTRUE; #else if (Serial.available()){ @@ -57,7 +56,7 @@ bool BusType::read(data& d) void BusType::push(const data& d) { -#ifdef USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS xQueueSendToBack(_queue, &d, 0); #else _queue.push(d); @@ -66,49 +65,45 @@ void BusType::push(const data& d) void BusType::receive(uint8_t byte) { - static EBusState busState; - static Arbitration arbitration; - static WiFiClient* client = 0; - - busState.data(byte); - Arbitration::state state = arbitration.data(busState, byte); + _busState.data(byte); + Arbitration::state state = _arbitration.data(_busState, byte); switch (state) { case Arbitration::none: uint8_t arbitration_address; - client = enhArbitrationRequested(arbitration_address); - if (client) { - if (arbitration.start(busState, arbitration_address)) { - DEBUG_LOG("BUS START SUC 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); + _client = enhArbitrationRequested(arbitration_address); + if (_client) { + if (_arbitration.start(_busState, arbitration_address)) { + DEBUG_LOG("BUS START SUC 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); } else { - DEBUG_LOG("BUS START FAI 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START FAI 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); } } - push({false, RECEIVED, byte, 0, client}); // send to everybody. ebusd needs the SYN to get in the right mood + push({false, RECEIVED, byte, 0, _client}); // send to everybody. ebusd needs the SYN to get in the right mood break; case Arbitration::arbitrating: - DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", byte, busState.microsSinceLastSyn()); - push({false, RECEIVED, byte, client, client}); // do not send to arbitration client + DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); + push({false, RECEIVED, byte, _client, _client}); // do not send to arbitration client break; case Arbitration::won: enhArbitrationDone(); - DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", busState._master, busState.microsSinceLastSyn()); - push({true, STARTED, busState._master, client, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, client, client}); // do not send to arbitrating client - client=0; + DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", _busState._master, _busState.microsSinceLastSyn()); + push({true, STARTED, _busState._master, _client, _client}); // send only to the arbitrating client + push({false, RECEIVED, byte, _client, _client}); // do not send to arbitrating client + _client=0; break; case Arbitration::lost: enhArbitrationDone(); - DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", busState._master, busState._byte, busState.microsSinceLastSyn()); - push({true, FAILED, busState._master, client, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0, client}); // send to everybody - client=0; + DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", _busState._master, _busState._byte, _busState.microsSinceLastSyn()); + push({true, FAILED, _busState._master, _client, _client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0, _client}); // send to everybody + _client=0; break; case Arbitration::error: enhArbitrationDone(); - push({true, ERROR_EBUS, ERR_FRAMING, client, client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0, client}); // send to everybody - client=0; + push({true, ERROR_EBUS, ERR_FRAMING, _client, _client}); // send only to the arbitrating client + push({false, RECEIVED, byte, 0, _client}); // send to everybody + _client=0; break; } } diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 2be2cbf..628d9e5 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -1,14 +1,10 @@ #include #include "main.hpp" -#include "ebusstate.hpp" -#include "arbitration.hpp" #include "enhanced.hpp" #define M1 0b11000000 #define M2 0b10000000 -#define ARBITRATION_TIMEOUT_MS 2000 - // Locking #ifdef USE_ASYNCHRONOUS SemaphoreHandle_t getMutex(){ @@ -32,7 +28,7 @@ SemaphoreHandle_t getMutex(){ WiFiClient* _arbitration_client = NULL; int _arbitration_address = -1; -unsigned long arbitration_start = 0; +unsigned long _arbitration_start = 0; void getEnhArbitrationClient(WiFiClient* &client, uint8_t &address) { ENH_MUTEX_LOCK(); @@ -109,7 +105,7 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ DEBUG_LOG("CMD_START 0x%02x\n", d); } setEnhArbitrationClient(client, d); - arbitration_start = millis(); + _arbitration_start = millis(); return; } } diff --git a/src/main.cpp b/src/main.cpp index 7e589da..e227fa0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -136,34 +136,6 @@ void setup() { last_comms = millis(); } -#define DEBUG_LOG_BUFFER_SIZE 4000 -static char message_buffer[DEBUG_LOG_BUFFER_SIZE]; -static size_t message_buffer_position; -int DEBUG_LOG_IMPL(const char *format, ...) -{ - int ret; - if (message_buffer_position>=DEBUG_LOG_BUFFER_SIZE) - return 0; - - va_list aptr; - va_start(aptr, format); - ret = vsnprintf(&message_buffer[message_buffer_position], DEBUG_LOG_BUFFER_SIZE-message_buffer_position, format, aptr); - va_end(aptr); - message_buffer_position+=ret; - if (message_buffer_position >= DEBUG_LOG_BUFFER_SIZE) { - message_buffer_position=DEBUG_LOG_BUFFER_SIZE; - } - message_buffer[DEBUG_LOG_BUFFER_SIZE-1]=0; - - return ret; -} - -#ifdef USE_ASYNCHRONOUS -#define ASYNC_MODE true -#else -#define ASYNC_MODE false -#endif - bool handleStatusServerRequests() { if (!statusServer.hasClient()) return false; @@ -171,8 +143,7 @@ bool handleStatusServerRequests() { WiFiClient client = statusServer.available(); if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { - client.printf("HTTP/1.1 200 OK\r\n\r\n"); - client.printf("async mode: %s\n", ASYNC_MODE? "true" : "false"); + client.printf("async mode: %s\n", USE_ASYNCHRONOUS ? "true" : "false"); client.printf("uptime: %ld ms\n", millis()); client.printf("rssi: %d dBm\n", WiFi.RSSI()); client.printf("free_heap: %d B\n", ESP.getFreeHeap()); @@ -180,12 +151,7 @@ bool handleStatusServerRequests() { client.printf("loop_duration: %ld us\r\n", loopDuration); client.printf("max_loop_duration: %ld us\r\n", maxLoopDuration); client.printf("version: %s\r\n", AUTO_VERSION); - if (message_buffer_position>0) - { - client.printf("lastmessages:\r\n%s\r\n", message_buffer); - message_buffer[0]=0; - message_buffer_position=0; - } + client.flush(); client.stop(); } From 815d0306022094ff28ca776bdddad682f18d8853 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 11:20:50 +0200 Subject: [PATCH 30/70] missed changing and #ifdef to #if --- src/enhanced.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 628d9e5..7134713 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -6,7 +6,7 @@ #define M2 0b10000000 // Locking -#ifdef USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS SemaphoreHandle_t getMutex(){ static SemaphoreHandle_t _lock = NULL; if(_lock == NULL){ From d44c0c74f5922e7647d1369d4ca9364377fd94a7 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 11:41:50 +0200 Subject: [PATCH 31/70] added last_comms = milis() --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index e227fa0..c356a82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -219,7 +219,9 @@ void loop() { for (int i = 0; i < MAX_SRV_CLIENTS; i++){ if (d._enhanced) { if (d._client == &enhClients[i]) { - pushEnhClient(&enhClients[i], d._c, d._d, true); + if (pushEnhClient(&enhClients[i], d._c, d._d, true)) { + last_comms = millis(); + } } } else { From 0ae7a24400109dd3babf2d978c607730d5df89e6 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 13:24:08 +0200 Subject: [PATCH 32/70] retry arbitration when SYN received --- include/arbitration.hpp | 9 ++++++--- src/arbitration.cpp | 26 +++++++++++++++++--------- src/bus.cpp | 1 + 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 60f1bf4..25bec58 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -15,13 +15,15 @@ class Arbitration arbitrating, // arbitration ongoing won, // won lost, // lost - error // error + error, // error + restart // restart }; Arbitration() : _arbitrating(false) , _participateSecond(false) - , _arbitration_address(0) + , _arbitrationAddress(0) + , _restartCount(0) {} // Try to start arbitration for the specified master. // Return values: @@ -40,7 +42,8 @@ class Arbitration private: bool _arbitrating; bool _participateSecond; - uint8_t _arbitration_address; + uint8_t _arbitrationAddress; + int _restartCount; }; #endif diff --git a/src/arbitration.cpp b/src/arbitration.cpp index da1e34e..fc684a4 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -25,7 +25,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master) } Serial.write(master); - _arbitration_address = master; + _arbitrationAddress = master; _arbitrating = true; _participateSecond = false; return true; @@ -48,16 +48,22 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eStartupSymbolAfterFirstSyn: case BusState::eStartupSecondSyn: case BusState::eReceivedFirstSYN: - DEBUG_LOG("ARB ERROR 0x%02x 0x%02x\n", busstate._master, busstate._byte); + DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, symbol, busstate.microsSinceLastSyn()); _arbitrating = false; + // Sometimes a second SYN is received instead of an address + // Could be another bus participant won, but is not starting transmission + // Try to restart arbitration maximum 2 times + if (_restartCount++ < 3) + return restart; return error; case BusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? - if (symbol == _arbitration_address) { + if (symbol == _arbitrationAddress) { DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; + _restartCount = 0; return won; // we won; nobody else will write to the bus - } else if ((symbol & 0b00001111) == (_arbitration_address & 0b00001111)) { - DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", _arbitration_address, symbol); + } else if ((symbol & 0b00001111) == (_arbitrationAddress & 0b00001111)) { + DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", _arbitrationAddress, symbol); _participateSecond = true; // participate in second round of arbitration if we have the same priority class } else { @@ -69,17 +75,18 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eReceivedSecondSYN: // did we sign up for second round arbitration? if (_participateSecond) { // execute second round of arbitration - DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitration_address, busstate.microsSinceLastSyn()); - Serial.write(_arbitration_address); + DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); + Serial.write(_arbitrationAddress); } else { - DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitration_address, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); } return arbitrating; case BusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? - if (symbol == _arbitration_address) { + if (symbol == _arbitrationAddress) { DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; + _restartCount = 0; return won; // we won; nobody else will write to the bus } else { @@ -91,6 +98,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { return arbitrating; case BusState::eBusy: _arbitrating = false; + _restartCount = 0; return lost; } return arbitrating; diff --git a/src/bus.cpp b/src/bus.cpp index 8ad98a4..1b52be3 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -69,6 +69,7 @@ void BusType::receive(uint8_t byte) Arbitration::state state = _arbitration.data(_busState, byte); switch (state) { case Arbitration::none: + case Arbitration::restart: uint8_t arbitration_address; _client = enhArbitrationRequested(arbitration_address); if (_client) { From b8c34caf0748021d63d77cf61675aea135c142ae Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 14:42:00 +0200 Subject: [PATCH 33/70] logging, more precise restart --- include/busstate.hpp | 23 ++++++++++++++++++++--- src/arbitration.cpp | 5 +++-- src/bus.cpp | 4 ++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/include/busstate.hpp b/include/busstate.hpp index bceee50..59b26ff 100644 --- a/include/busstate.hpp +++ b/include/busstate.hpp @@ -39,6 +39,7 @@ class BusState { } BusState() : _state(eStartup) + , _previousState(eStartup) {} // Evaluate a symbol received on UART and determine what the new state of the bus is inline void data(uint8_t symbol) @@ -46,48 +47,58 @@ class BusState { switch (_state) { case eStartup: + _previousState = _state; _state = symbol == SYN ? syn(eStartupFirstSyn) : eStartup; break; case eStartupFirstSyn: + _previousState = _state; _state = symbol == SYN ? syn(eReceivedFirstSYN) : eStartupSymbolAfterFirstSyn; break; case eStartupSymbolAfterFirstSyn: + _previousState = _state; _state = symbol == SYN ? syn(eStartupSecondSyn) : eBusy; break; case eStartupSecondSyn: + _previousState = _state; _state = symbol == SYN ? syn(eReceivedFirstSYN) : eBusy; break; case eReceivedFirstSYN: + _previousState = _state; _state = symbol == SYN ? syn(eReceivedFirstSYN) : eReceivedAddressAfterFirstSYN; _master = symbol; break; case eReceivedAddressAfterFirstSYN: + _previousState = _state; _state = symbol == SYN ? syn(eReceivedSecondSYN ): eBusy; _byte = symbol; break; case eReceivedSecondSYN: + _previousState = _state; _state = symbol == SYN ? error(_state, eReceivedFirstSYN) : eReceivedAddressAfterSecondSYN; _master = symbol; break; case eReceivedAddressAfterSecondSYN: + _previousState = _state; _state = symbol == SYN ? error(_state, eReceivedFirstSYN) : eBusy; _byte = symbol; break; case eBusy: + _previousState = _state; _state = symbol == SYN ? syn(eReceivedFirstSYN) : eBusy; break; } } inline eState syn(eState newstate) { + _previousSYNtime = _SYNtime; _SYNtime = micros(); return newstate; } eState error(eState currentstate, eState newstate) { - unsigned long lastsyn = microsSinceLastSyn(); + _previousSYNtime = _SYNtime; _SYNtime = micros(); - DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _byte, lastsyn); + DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _byte, microsSincePreviousSyn()); return newstate; } @@ -99,12 +110,18 @@ class BusState { unsigned long microsSinceLastSyn() { return micros() - _SYNtime; - } + + unsigned long microsSincePreviousSyn() + { + return micros() - _previousSYNtime; + } eState _state; + eState _previousState; uint8_t _master; uint8_t _byte; unsigned long _SYNtime; + unsigned long _previousSYNtime; }; #endif diff --git a/src/arbitration.cpp b/src/arbitration.cpp index fc684a4..ff2d68e 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -48,13 +48,14 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eStartupSymbolAfterFirstSyn: case BusState::eStartupSecondSyn: case BusState::eReceivedFirstSYN: - DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us\n", busstate._master, busstate._byte, symbol, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us %ld us\n", busstate._master, busstate._byte, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); _arbitrating = false; // Sometimes a second SYN is received instead of an address // Could be another bus participant won, but is not starting transmission // Try to restart arbitration maximum 2 times - if (_restartCount++ < 3) + if (_restartCount++ < 3 && busstate._previousState == BusState::eReceivedFirstSYN) return restart; + _restartCount = 0; return error; case BusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? if (symbol == _arbitrationAddress) { diff --git a/src/bus.cpp b/src/bus.cpp index 1b52be3..a6ce746 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -74,10 +74,10 @@ void BusType::receive(uint8_t byte) _client = enhArbitrationRequested(arbitration_address); if (_client) { if (_arbitration.start(_busState, arbitration_address)) { - DEBUG_LOG("BUS START SUC 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); } else { - DEBUG_LOG("BUS START FAI 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START WAIT 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); } } push({false, RECEIVED, byte, 0, _client}); // send to everybody. ebusd needs the SYN to get in the right mood From 8577db4f20cae52adc479a30eb441aa5635c2450 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 15:46:14 +0200 Subject: [PATCH 34/70] comment on restart conditions --- src/arbitration.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/arbitration.cpp b/src/arbitration.cpp index ff2d68e..4884ca7 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -51,7 +51,8 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us %ld us\n", busstate._master, busstate._byte, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); _arbitrating = false; // Sometimes a second SYN is received instead of an address - // Could be another bus participant won, but is not starting transmission + // This means the address we put on the bus in the "start" method + // got lost. Could be an electrical issue? Interference? // Try to restart arbitration maximum 2 times if (_restartCount++ < 3 && busstate._previousState == BusState::eReceivedFirstSYN) return restart; From 85f113232dc4149cb2ce9e6e186ac97f653e0b80 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 17:34:40 +0200 Subject: [PATCH 35/70] replace Serial with Bus --- include/bus.hpp | 9 +++++-- src/arbitration.cpp | 5 ++-- src/bus.cpp | 58 +++++++++++++++++++++++++++++---------------- src/client.cpp | 5 ++-- src/enhanced.cpp | 3 ++- src/main.cpp | 7 +----- 6 files changed, 54 insertions(+), 33 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index a3e592f..6a036e1 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -13,6 +13,9 @@ class BusType { public: + void begin(); + void end(); + // "receive" data should go to all clients that are not in arbitration mode // "enhanced" data should go only to the arbitrating client // a client is in arbitration mode if _client is not null @@ -27,8 +30,10 @@ class BusType ~BusType(); // Is there a value available that should be send to a client? - bool read(data& d); - + bool read(data& d); + size_t write(uint8_t c); + int availableForWrite(); + private: inline void push (const data& d); void receive (uint8_t byte); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 4884ca7..629a8ba 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -1,5 +1,6 @@ #include "arbitration.hpp" #include "busstate.hpp" +#include "bus.hpp" bool Arbitration::start(BusState& busstate, uint8_t master) { static int arb = 0; @@ -23,7 +24,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master) DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), now); return false; } - Serial.write(master); + Bus.write(master); _arbitrationAddress = master; _arbitrating = true; @@ -78,7 +79,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { if (_participateSecond) { // execute second round of arbitration DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); - Serial.write(_arbitrationAddress); + Bus.write(_arbitrationAddress); } else { DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); diff --git a/src/bus.cpp b/src/bus.cpp index a6ce746..4673541 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -5,24 +5,24 @@ BusType Bus; -#if USE_ASYNCHRONOUS -void BusType::OnReceiveCB() -{ - uint8_t byte = Serial.read(); - Bus.receive(byte); +BusType::BusType() +: _client(0) { } -void BusType::OnReceiveErrorCB(hardwareSerial_error_t e) -{ - if (e != UART_BREAK_ERROR){ - DEBUG_LOG("OnReceiveErrorCB %i\n", e); - } +BusType::~BusType() { + end(); } + +void BusType::begin() { + Serial.setRxBufferSize(RXBUFFERSIZE); + +#ifdef ESP32 + Serial.begin(2400, SERIAL_8N1, 21, 20); + Serial.setRxFIFOFull(1); // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit +#elif defined(ESP8266) + Serial.begin(); #endif -BusType::BusType() -: _client(0) -{ #if USE_ASYNCHRONOUS Serial.onReceive(OnReceiveCB, false); Serial.onReceiveError(OnReceiveErrorCB); @@ -30,14 +30,34 @@ BusType::BusType() #endif } -BusType::~BusType(){ +void BusType::end() { #if USE_ASYNCHRONOUS vQueueDelete(_queue); #endif } -bool BusType::read(data& d) -{ +#if USE_ASYNCHRONOUS +void BusType::OnReceiveCB() { + uint8_t byte = Serial.read(); + Bus.receive(byte); +} + +void BusType::OnReceiveErrorCB(hardwareSerial_error_t e) { + if (e != UART_BREAK_ERROR){ + DEBUG_LOG("OnReceiveErrorCB %i\n", e); + } +} +#endif + +int BusType::availableForWrite() { + return Serial.availableForWrite(); +} + +size_t BusType::write(uint8_t c) { + return Serial.write(c); +} + +bool BusType::read(data& d) { #if USE_ASYNCHRONOUS return xQueueReceive(_queue, &d, 0) == pdTRUE; #else @@ -54,8 +74,7 @@ bool BusType::read(data& d) #endif } -void BusType::push(const data& d) -{ +void BusType::push(const data& d){ #if USE_ASYNCHRONOUS xQueueSendToBack(_queue, &d, 0); #else @@ -63,8 +82,7 @@ void BusType::push(const data& d) #endif } -void BusType::receive(uint8_t byte) -{ +void BusType::receive(uint8_t byte) { _busState.data(byte); Arbitration::state state = _arbitration.data(_busState, byte); switch (state) { diff --git a/src/client.cpp b/src/client.cpp index a61fb63..b4cc8af 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,4 +1,5 @@ #include "main.hpp" +#include "bus.hpp" bool handleNewClient(WiFiServer &server, WiFiClient clients[]) { if (!server.hasClient()) @@ -35,8 +36,8 @@ int pushClient(WiFiClient* client, uint8_t B){ } void handleClient(WiFiClient* client){ - while (client->available() && Serial.availableForWrite() > 0) { + while (client->available() && Bus.availableForWrite() > 0) { // working char by char is not very efficient - Serial.write(client->read()); + Bus.write(client->read()); } } diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 7134713..8eb3479 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -1,6 +1,7 @@ #include #include "main.hpp" #include "enhanced.hpp" +#include "bus.hpp" #define M1 0b11000000 #define M2 0b10000000 @@ -111,7 +112,7 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ } if (c == CMD_SEND){ DEBUG_LOG("SEND 0x%02x\n", d); - Serial.write(d); + Bus.write(d); return; } if (c == CMD_INFO){ diff --git a/src/main.cpp b/src/main.cpp index c356a82..f68901f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,19 +93,14 @@ void check_reset() { void setup() { check_reset(); - Serial.setRxBufferSize(RXBUFFERSIZE); - #ifdef ESP32 Serial1.begin(115200, SERIAL_8N1, 8, 10); - Serial.begin(2400, SERIAL_8N1, 21, 20); - // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit - Serial.setRxFIFOFull(1); last_reset_code = rtc_get_reset_reason(0); #elif defined(ESP8266) Serial1.begin(115200); - Serial.begin(2400); last_reset_code = (int) ESP.getResetInfoPtr(); #endif + Bus.begin(); Serial1.setDebugOutput(true); From 8e45ceac042874c1cef44a9bcfb428e86fd25266 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 17:40:06 +0200 Subject: [PATCH 36/70] missed baudrate --- src/bus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bus.cpp b/src/bus.cpp index 4673541..d18fe95 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -20,7 +20,7 @@ void BusType::begin() { Serial.begin(2400, SERIAL_8N1, 21, 20); Serial.setRxFIFOFull(1); // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit #elif defined(ESP8266) - Serial.begin(); + Serial.begin(2400); #endif #if USE_ASYNCHRONOUS From c6056fbda7a30cd82cf181a1241fc7d5dcbbaa87 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 18:52:08 +0200 Subject: [PATCH 37/70] cosmetics --- include/bus.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 6a036e1..805eca9 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -13,9 +13,6 @@ class BusType { public: - void begin(); - void end(); - // "receive" data should go to all clients that are not in arbitration mode // "enhanced" data should go only to the arbitrating client // a client is in arbitration mode if _client is not null @@ -29,11 +26,15 @@ class BusType BusType(); ~BusType(); + // begin and end, like with Serial + void begin(); + void end(); + // Is there a value available that should be send to a client? bool read(data& d); size_t write(uint8_t c); int availableForWrite(); - + private: inline void push (const data& d); void receive (uint8_t byte); From 933cfb4a2ff835118b50518d034e1469de4658dd Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 18:53:35 +0200 Subject: [PATCH 38/70] cosmetics --- include/bus.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 805eca9..5671d61 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -38,13 +38,13 @@ class BusType private: inline void push (const data& d); void receive (uint8_t byte); - BusState _busState; - Arbitration _arbitration; - WiFiClient* _client; + BusState _busState; + Arbitration _arbitration; + WiFiClient* _client; #if USE_ASYNCHRONOUS - QueueHandle_t _queue; - static void OnReceiveCB(); - static void OnReceiveErrorCB(hardwareSerial_error_t e); + QueueHandle_t _queue; + static void OnReceiveCB(); + static void OnReceiveErrorCB(hardwareSerial_error_t e); #else std::queue _queue; #endif From bbafa9f2c5b9fdc49189a8a3c21dd55064ff1ddb Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 19:00:08 +0200 Subject: [PATCH 39/70] cosmetics --- include/bus.hpp | 4 ++-- include/busstate.hpp | 8 ++++---- src/arbitration.cpp | 2 +- src/bus.cpp | 36 ++++++++++++++++++------------------ 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 5671d61..d672fcc 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -32,12 +32,12 @@ class BusType // Is there a value available that should be send to a client? bool read(data& d); - size_t write(uint8_t c); + size_t write(uint8_t symbol); int availableForWrite(); private: inline void push (const data& d); - void receive (uint8_t byte); + void receive (uint8_t symbol); BusState _busState; Arbitration _arbitration; WiFiClient* _client; diff --git a/include/busstate.hpp b/include/busstate.hpp index 59b26ff..ecded46 100644 --- a/include/busstate.hpp +++ b/include/busstate.hpp @@ -70,7 +70,7 @@ class BusState { case eReceivedAddressAfterFirstSYN: _previousState = _state; _state = symbol == SYN ? syn(eReceivedSecondSYN ): eBusy; - _byte = symbol; + _symbol = symbol; break; case eReceivedSecondSYN: _previousState = _state; @@ -80,7 +80,7 @@ class BusState { case eReceivedAddressAfterSecondSYN: _previousState = _state; _state = symbol == SYN ? error(_state, eReceivedFirstSYN) : eBusy; - _byte = symbol; + _symbol = symbol; break; case eBusy: _previousState = _state; @@ -98,7 +98,7 @@ class BusState { { _previousSYNtime = _SYNtime; _SYNtime = micros(); - DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _byte, microsSincePreviousSyn()); + DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _symbol, microsSincePreviousSyn()); return newstate; } @@ -120,7 +120,7 @@ class BusState { eState _state; eState _previousState; uint8_t _master; - uint8_t _byte; + uint8_t _symbol; unsigned long _SYNtime; unsigned long _previousSYNtime; }; diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 629a8ba..261cdd0 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -49,7 +49,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eStartupSymbolAfterFirstSyn: case BusState::eStartupSecondSyn: case BusState::eReceivedFirstSYN: - DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us %ld us\n", busstate._master, busstate._byte, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); + DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us %ld us\n", busstate._master, busstate._symbol, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); _arbitrating = false; // Sometimes a second SYN is received instead of an address // This means the address we put on the bus in the "start" method diff --git a/src/bus.cpp b/src/bus.cpp index d18fe95..0c808a7 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -38,8 +38,8 @@ void BusType::end() { #if USE_ASYNCHRONOUS void BusType::OnReceiveCB() { - uint8_t byte = Serial.read(); - Bus.receive(byte); + uint8_t symbol = Serial.read(); + Bus.receive(symbol); } void BusType::OnReceiveErrorCB(hardwareSerial_error_t e) { @@ -53,8 +53,8 @@ int BusType::availableForWrite() { return Serial.availableForWrite(); } -size_t BusType::write(uint8_t c) { - return Serial.write(c); +size_t BusType::write(uint8_t symbol) { + return Serial.write(symbol); } bool BusType::read(data& d) { @@ -62,8 +62,8 @@ bool BusType::read(data& d) { return xQueueReceive(_queue, &d, 0) == pdTRUE; #else if (Serial.available()){ - uint8_t byte = Serial.read(); - receive(byte); + uint8_t symbol = Serial.read(); + receive(symbol); } if (_queue.size() > 0) { d = _queue.front(); @@ -82,9 +82,9 @@ void BusType::push(const data& d){ #endif } -void BusType::receive(uint8_t byte) { - _busState.data(byte); - Arbitration::state state = _arbitration.data(_busState, byte); +void BusType::receive(uint8_t symbol) { + _busState.data(symbol); + Arbitration::state state = _arbitration.data(_busState, symbol); switch (state) { case Arbitration::none: case Arbitration::restart: @@ -92,36 +92,36 @@ void BusType::receive(uint8_t byte) { _client = enhArbitrationRequested(arbitration_address); if (_client) { if (_arbitration.start(_busState, arbitration_address)) { - DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); } else { - DEBUG_LOG("BUS START WAIT 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START WAIT 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); } } - push({false, RECEIVED, byte, 0, _client}); // send to everybody. ebusd needs the SYN to get in the right mood + push({false, RECEIVED, symbol, 0, _client}); // send to everybody. ebusd needs the SYN to get in the right mood break; case Arbitration::arbitrating: - DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", byte, _busState.microsSinceLastSyn()); - push({false, RECEIVED, byte, _client, _client}); // do not send to arbitration client + DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); + push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitration client break; case Arbitration::won: enhArbitrationDone(); DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", _busState._master, _busState.microsSinceLastSyn()); push({true, STARTED, _busState._master, _client, _client}); // send only to the arbitrating client - push({false, RECEIVED, byte, _client, _client}); // do not send to arbitrating client + push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitrating client _client=0; break; case Arbitration::lost: enhArbitrationDone(); - DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", _busState._master, _busState._byte, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", _busState._master, _busState._symbol, _busState.microsSinceLastSyn()); push({true, FAILED, _busState._master, _client, _client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0, _client}); // send to everybody + push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; break; case Arbitration::error: enhArbitrationDone(); push({true, ERROR_EBUS, ERR_FRAMING, _client, _client}); // send only to the arbitrating client - push({false, RECEIVED, byte, 0, _client}); // send to everybody + push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; break; } From 804510e6af2254353290b80848fbf931befa4f5e Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 21 Apr 2023 19:43:53 +0200 Subject: [PATCH 40/70] arbitration start no longer needed, no while loop --- src/enhanced.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/enhanced.cpp b/src/enhanced.cpp index 8eb3479..4ccbece 100644 --- a/src/enhanced.cpp +++ b/src/enhanced.cpp @@ -29,7 +29,6 @@ SemaphoreHandle_t getMutex(){ WiFiClient* _arbitration_client = NULL; int _arbitration_address = -1; -unsigned long _arbitration_start = 0; void getEnhArbitrationClient(WiFiClient* &client, uint8_t &address) { ENH_MUTEX_LOCK(); @@ -106,7 +105,6 @@ void process_cmd(WiFiClient* client, uint8_t c, uint8_t d){ DEBUG_LOG("CMD_START 0x%02x\n", d); } setEnhArbitrationClient(client, d); - _arbitration_start = millis(); return; } } From 743abfa61dd305cb3993dcff94efb8c31dc7be2a Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 22 Apr 2023 08:12:10 +0200 Subject: [PATCH 41/70] report statistics on the arbitration process --- include/bus.hpp | 3 +++ src/bus.cpp | 10 +++++++--- src/main.cpp | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index d672fcc..1509127 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -35,12 +35,15 @@ class BusType size_t write(uint8_t symbol); int availableForWrite(); + int _nbrRestarts; + int _nbrArbitrations; private: inline void push (const data& d); void receive (uint8_t symbol); BusState _busState; Arbitration _arbitration; WiFiClient* _client; + #if USE_ASYNCHRONOUS QueueHandle_t _queue; static void OnReceiveCB(); diff --git a/src/bus.cpp b/src/bus.cpp index 0c808a7..16eeeed 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -6,7 +6,9 @@ BusType Bus; BusType::BusType() -: _client(0) { + : _client(0) + , _nbrRestarts(0) + , _nbrArbitrations(0) { } BusType::~BusType() { @@ -86,12 +88,14 @@ void BusType::receive(uint8_t symbol) { _busState.data(symbol); Arbitration::state state = _arbitration.data(_busState, symbol); switch (state) { - case Arbitration::none: case Arbitration::restart: + _nbrRestarts++; + case Arbitration::none: uint8_t arbitration_address; _client = enhArbitrationRequested(arbitration_address); if (_client) { - if (_arbitration.start(_busState, arbitration_address)) { + if (_arbitration.start(_busState, arbitration_address)) { + _nbrArbitrations++; DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); } else { diff --git a/src/main.cpp b/src/main.cpp index f68901f..e4586a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -146,6 +146,8 @@ bool handleStatusServerRequests() { client.printf("loop_duration: %ld us\r\n", loopDuration); client.printf("max_loop_duration: %ld us\r\n", maxLoopDuration); client.printf("version: %s\r\n", AUTO_VERSION); + client.printf("nbr arbitrations: %i\r\n", Bus._nbrArbitrations); + client.printf("nbr restarts: %i\r\n", Bus._nbrRestarts); client.flush(); client.stop(); From d8fb70fe0a5603d4bafe6beaf7738d17e3b1b7d9 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 22 Apr 2023 10:45:09 +0200 Subject: [PATCH 42/70] improve arbitration timing --- src/arbitration.cpp | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 261cdd0..9e87464 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -2,6 +2,14 @@ #include "busstate.hpp" #include "bus.hpp" +// arbitration is timing sensitive. avoid communicating with WifiClient during arbitration +// according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 +// "Calculated time distance between start bit of SYN byte and +// bus permission must be in the range of 4300 us - 4456,24 us ." +// SYN symbol is 4167 us. If we would receive the symbol immediately, +// we need to wait (4300 - 4167)=133 us after we received the SYN. +// rely on the uart to keep the timing +// just make sure the byte to send is available in time bool Arbitration::start(BusState& busstate, uint8_t master) { static int arb = 0; if (_arbitrating) { @@ -13,31 +21,40 @@ bool Arbitration::start(BusState& busstate, uint8_t master) if (busstate._state != BusState::eReceivedFirstSYN) { return false; } - DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, master, busstate.microsSinceLastSyn()); // too late if we don't have enough time to send our symbol // assume we need at least 20 us to send the symbol - unsigned long now = busstate.microsSinceLastSyn(); - if (Serial.available() != 0 || now>((4456-20)-4160)) + unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); + if (Serial.available() != 0 || microsSinceLastSyn>((4456-20)-4160)) { // if we are too late, don't try to participate and retry next round - DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), now); + DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), microsSinceLastSyn); return false; } +#if USE_ASYNCHRONOUS + // When in async mode, we get immediately interrupted when a symbol is received on the bus + // The earliest allowed to send is 4300 measured from the start bit of the SYN command + // We don't have the exact timing of the start bit. Assume the symbol arrives with 15 us delay + // And that the SYN takes 4167 us. + int delay = (4300-4167)-busstate.microsSinceLastSyn()-15; + if (delay > 0) { + delayMicroseconds(delay); + } +#endif Bus.write(master); - + // Do logging of the ARB START message after writing the symbol, so enabled or disabled + // logging does not affect timing calculations. +#if USE_ASYNCHRONOUS + DEBUG_LOG("ARB START %04i 0x%02x %ld us %i us\n", arb++, master, microsSinceLastSyn, delay); +#else + DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, master, microsSinceLastSyn); +#endif _arbitrationAddress = master; _arbitrating = true; _participateSecond = false; return true; } -// arbitration is timing sensitive. avoid communicating with WifiClient during arbitration -// according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 -// "Calculated time distance between start bit of SYN byte and -// bus permission must be in the range of 4300 us - 4456,24 us ." -// rely on the uart to keep the timing -// just make sure the byte to send is available in time Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { if (!_arbitrating){ return none; @@ -53,7 +70,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { _arbitrating = false; // Sometimes a second SYN is received instead of an address // This means the address we put on the bus in the "start" method - // got lost. Could be an electrical issue? Interference? + // got lost. Could be an electrical issue? Interference? Wrong timing of sending he symbol? // Try to restart arbitration maximum 2 times if (_restartCount++ < 3 && busstate._previousState == BusState::eReceivedFirstSYN) return restart; From 36c089675b154a112d07326dc482fe3e2d4aa4ab Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 22 Apr 2023 11:53:19 +0200 Subject: [PATCH 43/70] more statistics, more timing tweaking --- include/bus.hpp | 3 +++ src/arbitration.cpp | 17 ++++++++++++++--- src/bus.cpp | 8 +++++++- src/main.cpp | 5 ++++- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 1509127..b3a9d91 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -37,6 +37,9 @@ class BusType int _nbrRestarts; int _nbrArbitrations; + int _nbrLost; + int _nbrWon; + int _nbrErrors; private: inline void push (const data& d); void receive (uint8_t symbol); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 9e87464..7f0fb21 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -34,8 +34,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master) #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command - // We don't have the exact timing of the start bit. Assume the symbol arrives with 15 us delay - // And that the SYN takes 4167 us. + // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. int delay = (4300-4167)-busstate.microsSinceLastSyn()-15; if (delay > 0) { delayMicroseconds(delay); @@ -95,8 +94,20 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eReceivedSecondSYN: // did we sign up for second round arbitration? if (_participateSecond) { // execute second round of arbitration - DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); + unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); +#if USE_ASYNCHRONOUS + // When in async mode, we get immediately interrupted when a symbol is received on the bus + // The earliest allowed to send is 4300 measured from the start bit of the SYN command + // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. + int delay = (4300-4167)-busstate.microsSinceLastSyn()-15; + if (delay > 0) { + delayMicroseconds(delay); + } +#endif + // Do logging of the ARB START message after writing the symbol, so enabled or disabled + // logging does not affect timing calculations. Bus.write(_arbitrationAddress); + DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitrationAddress, microsSinceLastSyn); } else { DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); diff --git a/src/bus.cpp b/src/bus.cpp index 16eeeed..ea5adeb 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -8,7 +8,10 @@ BusType Bus; BusType::BusType() : _client(0) , _nbrRestarts(0) - , _nbrArbitrations(0) { + , _nbrArbitrations(0) + , _nbrLost(0) + , _nbrWon(0) + , _nbrErrors(0) { } BusType::~BusType() { @@ -114,6 +117,7 @@ void BusType::receive(uint8_t symbol) { push({true, STARTED, _busState._master, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitrating client _client=0; + _nbrWon++; break; case Arbitration::lost: enhArbitrationDone(); @@ -121,12 +125,14 @@ void BusType::receive(uint8_t symbol) { push({true, FAILED, _busState._master, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; + _nbrLost++; break; case Arbitration::error: enhArbitrationDone(); push({true, ERROR_EBUS, ERR_FRAMING, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; + _nbrErrors++; break; } } diff --git a/src/main.cpp b/src/main.cpp index e4586a6..3743dbb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -148,7 +148,10 @@ bool handleStatusServerRequests() { client.printf("version: %s\r\n", AUTO_VERSION); client.printf("nbr arbitrations: %i\r\n", Bus._nbrArbitrations); client.printf("nbr restarts: %i\r\n", Bus._nbrRestarts); - + client.printf("nbr lost: %i\r\n", Bus._nbrLost); + client.printf("nbr won: %i\r\n", Bus._nbrWon); + client.printf("nbr errors: %i\r\n", Bus._nbrErrors); + client.flush(); client.stop(); } From 2f4c052d7deda7370518231de6b3333db463bccd Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 22 Apr 2023 14:48:49 +0200 Subject: [PATCH 44/70] reduce amount of errors in favour of restarts --- src/arbitration.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 7f0fb21..6e65c55 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -31,7 +31,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master) DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), microsSinceLastSyn); return false; } -#if USE_ASYNCHRONOUS +/*#if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. @@ -39,7 +39,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master) if (delay > 0) { delayMicroseconds(delay); } -#endif +#endif*/ Bus.write(master); // Do logging of the ARB START message after writing the symbol, so enabled or disabled // logging does not affect timing calculations. @@ -67,11 +67,14 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eReceivedFirstSYN: DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us %ld us\n", busstate._master, busstate._symbol, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); _arbitrating = false; - // Sometimes a second SYN is received instead of an address - // This means the address we put on the bus in the "start" method - // got lost. Could be an electrical issue? Interference? Wrong timing of sending he symbol? + // Sometimes a second SYN is received instead of an address, either + // after having started the arbitration, or after participating in + // the second round of arbitration. This means the address we put on + // the bus got lost. Most likely this is caused by not perfect timing + // of the arbitration on our side, but could also be electrical interference + // or wrong implementation in another bus participant. // Try to restart arbitration maximum 2 times - if (_restartCount++ < 3 && busstate._previousState == BusState::eReceivedFirstSYN) + if (_restartCount++ < 3 && (busstate._previousState == BusState::eReceivedFirstSYN || busstate._previousState == BusState::eReceivedSecondSYN) ) return restart; _restartCount = 0; return error; @@ -95,7 +98,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { if (_participateSecond) { // execute second round of arbitration unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); -#if USE_ASYNCHRONOUS +/*#if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. @@ -103,7 +106,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { if (delay > 0) { delayMicroseconds(delay); } -#endif +#endif*/ // Do logging of the ARB START message after writing the symbol, so enabled or disabled // logging does not affect timing calculations. Bus.write(_arbitrationAddress); From d62c976298ac43b386d93c50acdc8d8de1bec13d Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 22 Apr 2023 19:53:56 +0200 Subject: [PATCH 45/70] improve timing, 1% restarts instead of 5% --- include/arbitration.hpp | 9 ++++++--- include/bus.hpp | 9 ++++++--- src/arbitration.cpp | 26 +++++++++++++++----------- src/bus.cpp | 36 ++++++++++++++++++++++++++---------- src/main.cpp | 13 ++++++++----- 5 files changed, 61 insertions(+), 32 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 25bec58..7bbb144 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -13,10 +13,13 @@ class Arbitration public: enum state {none, // no arbitration ongoing/not yet completed arbitrating, // arbitration ongoing - won, // won - lost, // lost + won1, // won + won2, // won + lost1, // lost + lost2, // lost error, // error - restart // restart + restart1, // restart + restart2, // restart }; Arbitration() diff --git a/include/bus.hpp b/include/bus.hpp index b3a9d91..9cbbdae 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -35,10 +35,13 @@ class BusType size_t write(uint8_t symbol); int availableForWrite(); - int _nbrRestarts; + int _nbrRestarts1; + int _nbrRestarts2; int _nbrArbitrations; - int _nbrLost; - int _nbrWon; + int _nbrLost1; + int _nbrLost2; + int _nbrWon1; + int _nbrWon2; int _nbrErrors; private: inline void push (const data& d); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 6e65c55..d076d20 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -1,6 +1,8 @@ #include "arbitration.hpp" #include "busstate.hpp" #include "bus.hpp" +#define ARB_FIRST_DELAY_US -27 +#define ARB_SECOND_DELAY_US -27 // arbitration is timing sensitive. avoid communicating with WifiClient during arbitration // according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 @@ -31,15 +33,15 @@ bool Arbitration::start(BusState& busstate, uint8_t master) DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), microsSinceLastSyn); return false; } -/*#if USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. - int delay = (4300-4167)-busstate.microsSinceLastSyn()-15; + int delay = (4300-4167)-busstate.microsSinceLastSyn()-ARB_FIRST_DELAY_US; if (delay > 0) { delayMicroseconds(delay); } -#endif*/ +#endif Bus.write(master); // Do logging of the ARB START message after writing the symbol, so enabled or disabled // logging does not affect timing calculations. @@ -74,8 +76,10 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { // of the arbitration on our side, but could also be electrical interference // or wrong implementation in another bus participant. // Try to restart arbitration maximum 2 times - if (_restartCount++ < 3 && (busstate._previousState == BusState::eReceivedFirstSYN || busstate._previousState == BusState::eReceivedSecondSYN) ) - return restart; + if (_restartCount++ < 3 && busstate._previousState == BusState::eReceivedFirstSYN ) + return restart1; + if (_restartCount++ < 3 && busstate._previousState == BusState::eReceivedSecondSYN ) + return restart2; _restartCount = 0; return error; case BusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? @@ -83,7 +87,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; _restartCount = 0; - return won; // we won; nobody else will write to the bus + return won1; // we won; nobody else will write to the bus } else if ((symbol & 0b00001111) == (_arbitrationAddress & 0b00001111)) { DEBUG_LOG("ARB PART SECND 0x%02x 0x%02x\n", _arbitrationAddress, symbol); _participateSecond = true; // participate in second round of arbitration if we have the same priority class @@ -98,15 +102,15 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { if (_participateSecond) { // execute second round of arbitration unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); -/*#if USE_ASYNCHRONOUS +#if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. - int delay = (4300-4167)-busstate.microsSinceLastSyn()-15; + int delay = (4300-4167)-busstate.microsSinceLastSyn()-ARB_SECOND_DELAY_US; if (delay > 0) { delayMicroseconds(delay); } -#endif*/ +#endif // Do logging of the ARB START message after writing the symbol, so enabled or disabled // logging does not affect timing calculations. Bus.write(_arbitrationAddress); @@ -121,7 +125,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; _restartCount = 0; - return won; // we won; nobody else will write to the bus + return won2; // we won; nobody else will write to the bus } else { DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); @@ -133,7 +137,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { case BusState::eBusy: _arbitrating = false; _restartCount = 0; - return lost; + return _participateSecond ? lost2 : lost1; } return arbitrating; } diff --git a/src/bus.cpp b/src/bus.cpp index ea5adeb..6d411a8 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -7,10 +7,13 @@ BusType Bus; BusType::BusType() : _client(0) - , _nbrRestarts(0) + , _nbrRestarts1(0) + , _nbrRestarts2(0) , _nbrArbitrations(0) - , _nbrLost(0) - , _nbrWon(0) + , _nbrLost1(0) + , _nbrLost2(0) + , _nbrWon1(0) + , _nbrWon2(0) , _nbrErrors(0) { } @@ -91,9 +94,14 @@ void BusType::receive(uint8_t symbol) { _busState.data(symbol); Arbitration::state state = _arbitration.data(_busState, symbol); switch (state) { - case Arbitration::restart: - _nbrRestarts++; + case Arbitration::restart1: + _nbrRestarts1++; + goto NONE; + case Arbitration::restart2: + _nbrRestarts2++; + goto NONE; case Arbitration::none: + NONE: uint8_t arbitration_address; _client = enhArbitrationRequested(arbitration_address); if (_client) { @@ -111,28 +119,36 @@ void BusType::receive(uint8_t symbol) { DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitration client break; - case Arbitration::won: + case Arbitration::won1: + _nbrWon1++; + goto WON; + case Arbitration::won2: + _nbrWon2++; + WON: enhArbitrationDone(); DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", _busState._master, _busState.microsSinceLastSyn()); push({true, STARTED, _busState._master, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitrating client _client=0; - _nbrWon++; break; - case Arbitration::lost: + case Arbitration::lost1: + _nbrLost1++; + goto LOST; + case Arbitration::lost2: + _nbrLost2++; + LOST: enhArbitrationDone(); DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", _busState._master, _busState._symbol, _busState.microsSinceLastSyn()); push({true, FAILED, _busState._master, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; - _nbrLost++; break; case Arbitration::error: + _nbrErrors++; enhArbitrationDone(); push({true, ERROR_EBUS, ERR_FRAMING, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; - _nbrErrors++; break; } } diff --git a/src/main.cpp b/src/main.cpp index 3743dbb..18b35f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,11 +147,14 @@ bool handleStatusServerRequests() { client.printf("max_loop_duration: %ld us\r\n", maxLoopDuration); client.printf("version: %s\r\n", AUTO_VERSION); client.printf("nbr arbitrations: %i\r\n", Bus._nbrArbitrations); - client.printf("nbr restarts: %i\r\n", Bus._nbrRestarts); - client.printf("nbr lost: %i\r\n", Bus._nbrLost); - client.printf("nbr won: %i\r\n", Bus._nbrWon); - client.printf("nbr errors: %i\r\n", Bus._nbrErrors); - + client.printf("nbr restarts1: %i\r\n", Bus._nbrRestarts1); + client.printf("nbr restarts2: %i\r\n", Bus._nbrRestarts2); + client.printf("nbr lost1: %i\r\n", Bus._nbrLost1); + client.printf("nbr lost2: %i\r\n", Bus._nbrLost2); + client.printf("nbr won1: %i\r\n", Bus._nbrWon1); + client.printf("nbr won2: %i\r\n", Bus._nbrWon2); + client.printf("nbr errors: %i\r\n", Bus._nbrErrors); + client.flush(); client.stop(); } From 01e18b8e3beb210d4b484d2c2743b343178daa1e Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sat, 22 Apr 2023 20:28:22 +0200 Subject: [PATCH 46/70] reduced restarts/errors to 0.5% to 1.0 % --- include/main.hpp | 2 +- src/arbitration.cpp | 4 ++-- src/main.cpp | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index a165545..6073d1d 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -22,7 +22,7 @@ #endif inline int DEBUG_LOG(const char *format, ...) { return 0;} -//int DEBUG_LOG_IMPL(const char *format, ...); +int DEBUG_LOG_IMPL(const char *format, ...); //#define DEBUG_LOG DEBUG_LOG_IMPL bool handleNewClient(WiFiServer &server, WiFiClient clients[]); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index d076d20..33554c2 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -1,8 +1,8 @@ #include "arbitration.hpp" #include "busstate.hpp" #include "bus.hpp" -#define ARB_FIRST_DELAY_US -27 -#define ARB_SECOND_DELAY_US -27 +#define ARB_FIRST_DELAY_US -36 +#define ARB_SECOND_DELAY_US -36 // arbitration is timing sensitive. avoid communicating with WifiClient during arbitration // according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 diff --git a/src/main.cpp b/src/main.cpp index 18b35f3..7d6848a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -131,6 +131,28 @@ void setup() { last_comms = millis(); } +#define DEBUG_LOG_BUFFER_SIZE 4000 +static char message_buffer[DEBUG_LOG_BUFFER_SIZE]; +static size_t message_buffer_position; +int DEBUG_LOG_IMPL(const char *format, ...) +{ + int ret; + if (message_buffer_position>=DEBUG_LOG_BUFFER_SIZE) + return 0; + + va_list aptr; + va_start(aptr, format); + ret = vsnprintf(&message_buffer[message_buffer_position], DEBUG_LOG_BUFFER_SIZE-message_buffer_position, format, aptr); + va_end(aptr); + message_buffer_position+=ret; + if (message_buffer_position >= DEBUG_LOG_BUFFER_SIZE) { + message_buffer_position=DEBUG_LOG_BUFFER_SIZE; + } + message_buffer[DEBUG_LOG_BUFFER_SIZE-1]=0; + + return ret; +} + bool handleStatusServerRequests() { if (!statusServer.hasClient()) return false; @@ -138,6 +160,7 @@ bool handleStatusServerRequests() { WiFiClient client = statusServer.available(); if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { + client.printf("HTTP/1.1 200 OK\r\n\r\n"); client.printf("async mode: %s\n", USE_ASYNCHRONOUS ? "true" : "false"); client.printf("uptime: %ld ms\n", millis()); client.printf("rssi: %d dBm\n", WiFi.RSSI()); @@ -153,8 +176,13 @@ bool handleStatusServerRequests() { client.printf("nbr lost2: %i\r\n", Bus._nbrLost2); client.printf("nbr won1: %i\r\n", Bus._nbrWon1); client.printf("nbr won2: %i\r\n", Bus._nbrWon2); - client.printf("nbr errors: %i\r\n", Bus._nbrErrors); - + client.printf("nbr errors: %i\r\n", Bus._nbrErrors); + if (message_buffer_position>0) + { + client.printf("lastmessages:\r\n%s\r\n", message_buffer); + message_buffer[0]=0; + message_buffer_position=0; + } client.flush(); client.stop(); } From 7d48b4a02de584d328e7b98f1d96c5d97a06a4a2 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Sun, 23 Apr 2023 20:04:24 +0200 Subject: [PATCH 47/70] SoftwareSerial for accurate arbitration timing --- include/arbitration.hpp | 4 +- include/bus.hpp | 12 ++++-- platformio.ini | 5 ++- src/arbitration.cpp | 24 ++++++------ src/bus.cpp | 87 +++++++++++++++++++++++++++++------------ src/main.cpp | 30 +------------- 6 files changed, 92 insertions(+), 70 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 7bbb144..de7aea1 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -35,12 +35,12 @@ class Arbitration // + another arbitration is already ongoing // + the master address is SYN // = true : arbitration started. Make sure to pass all bus data to this object through the "data" method - bool start (BusState& busstate, uint8_t master); + bool start (BusState& busstate, uint8_t master, unsigned int startBitTime); // A symbol was received on the bus, what does this do to the arbitration state? // Return values: // - see description of state enum value - Arbitration::state data (BusState& busstate, uint8_t symbol); + Arbitration::state data (BusState& busstate, uint8_t symbol, unsigned int startBitTime); private: bool _arbitrating; diff --git a/include/bus.hpp b/include/bus.hpp index 9cbbdae..094521d 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -45,15 +45,21 @@ class BusType int _nbrErrors; private: inline void push (const data& d); - void receive (uint8_t symbol); + void receive (uint8_t symbol, unsigned int startBitTime); BusState _busState; Arbitration _arbitration; WiFiClient* _client; #if USE_ASYNCHRONOUS + friend void IRAM_ATTR _receiveHandler(); + // queue from Bus to read method QueueHandle_t _queue; - static void OnReceiveCB(); - static void OnReceiveErrorCB(hardwareSerial_error_t e); + + // queue from receiveHandler to bus + QueueHandle_t _serialEventQueue; + TaskHandle_t _serialEventTask; + + static void readDataFromSoftwareSerial(void *args); #else std::queue _queue; #endif diff --git a/platformio.ini b/platformio.ini index a5be7d7..64771b4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -48,7 +48,10 @@ platform = espressif32 board = esp32-c3-devkitm-1 build_flags = -DRESET_PIN=20 - +lib_deps = + https://github.com/tzapu/WiFiManager + https://github.com/plerup/espsoftwareserial + [env:esp32-c3-ota] extends = env:esp32-c3 upload_port = esp-ebus.local diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 33554c2..3b79cb4 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -1,8 +1,9 @@ #include "arbitration.hpp" #include "busstate.hpp" #include "bus.hpp" -#define ARB_FIRST_DELAY_US -36 -#define ARB_SECOND_DELAY_US -36 +#define ARB_FIRST_DELAY_US -50 +#define ARB_SECOND_DELAY_US -50 +extern unsigned long lastStartBit; // arbitration is timing sensitive. avoid communicating with WifiClient during arbitration // according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 @@ -12,7 +13,7 @@ // we need to wait (4300 - 4167)=133 us after we received the SYN. // rely on the uart to keep the timing // just make sure the byte to send is available in time -bool Arbitration::start(BusState& busstate, uint8_t master) +bool Arbitration::start(BusState& busstate, uint8_t master, unsigned int startBitTime) { static int arb = 0; if (_arbitrating) { return false; @@ -26,8 +27,9 @@ bool Arbitration::start(BusState& busstate, uint8_t master) // too late if we don't have enough time to send our symbol // assume we need at least 20 us to send the symbol + unsigned long now = micros(); unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); - if (Serial.available() != 0 || microsSinceLastSyn>((4456-20)-4160)) + if (now - startBitTime > 4456) { // if we are too late, don't try to participate and retry next round DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), microsSinceLastSyn); @@ -35,9 +37,9 @@ bool Arbitration::start(BusState& busstate, uint8_t master) } #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus - // The earliest allowed to send is 4300 measured from the start bit of the SYN command - // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. - int delay = (4300-4167)-busstate.microsSinceLastSyn()-ARB_FIRST_DELAY_US; + // The earliest allowed to send is 4300 measured from the start bit of the SYN command. + // This timing is provided by the Bus. + int delay = 4300-(now-startBitTime); if (delay > 0) { delayMicroseconds(delay); } @@ -56,7 +58,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master) return true; } -Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { +Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigned int startBitTime) { if (!_arbitrating){ return none; } @@ -104,9 +106,9 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol) { unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus - // The earliest allowed to send is 4300 measured from the start bit of the SYN command - // We don't have the exact timing of the start bit. Assume SYN takes 4167 us. - int delay = (4300-4167)-busstate.microsSinceLastSyn()-ARB_SECOND_DELAY_US; + // The earliest allowed to send is 4300 measured from the start bit of the SYN command. + // This timing is provided by the Bus. + int delay = 4300-(micros()-startBitTime); if (delay > 0) { delayMicroseconds(delay); } diff --git a/src/bus.cpp b/src/bus.cpp index 6d411a8..f741eb8 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -3,59 +3,98 @@ #include "enhanced.hpp" #include "queue" +// Using SoftwareSerial we get notified through an interrupt +// exactly when the start bit is received. We can use this +// for the timing of the arbitration. Because SoftwareSerial +// cannot write and read at the same, we use SoftwareSerial only +// for reading and for timing we still use HardwareSerial +#if USE_ASYNCHRONOUS +#include +SoftwareSerial mySerial; +#endif + BusType Bus; +#define BAUD_RATE 2400 +#define MAX_FRAMEBITS (1 + 8 + 1) +#define SERIAL_EVENT_TASK_STACK_SIZE 2048 +#define SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES-1) +#define SERIAL_EVENT_TASK_RUNNING_CORE -1 + BusType::BusType() - : _client(0) - , _nbrRestarts1(0) + : _nbrRestarts1(0) , _nbrRestarts2(0) , _nbrArbitrations(0) , _nbrLost1(0) , _nbrLost2(0) , _nbrWon1(0) , _nbrWon2(0) - , _nbrErrors(0) { + , _nbrErrors(0) + , _client(0) { } BusType::~BusType() { end(); } + +#if USE_ASYNCHRONOUS +void IRAM_ATTR _receiveHandler() { + unsigned long lastStartBit= micros(); + xQueueSendToBackFromISR(Bus._serialEventQueue, &lastStartBit, 0); + vPortYieldFromISR(); +} + +void BusType::readDataFromSoftwareSerial(void *args) +{ + for(;;) { + //Waiting for UART event. + unsigned int startBitTime = 0; + if(xQueueReceive(Bus._serialEventQueue, (void * )&startBitTime, (portTickType)portMAX_DELAY)) { + auto avail = mySerial.available(); + if ( !avail) { + // event fired on start bit, wait until first stop bit of longest frame + delayMicroseconds(1+ MAX_FRAMEBITS * 1000000 / BAUD_RATE); + avail = mySerial.available(); + } + if (avail){ + uint8_t symbol = mySerial.read(); + Bus.receive(symbol, startBitTime); + } + } + } + vTaskDelete(NULL); +} +#endif + void BusType::begin() { - Serial.setRxBufferSize(RXBUFFERSIZE); #ifdef ESP32 - Serial.begin(2400, SERIAL_8N1, 21, 20); - Serial.setRxFIFOFull(1); // ESP32 in Arduino uses heuristics to sometimes set RxFIFOFull to 1, better to be explicit + Serial.begin(2400, SERIAL_8N1, -1, 20); // used for writing + mySerial.begin(2400, SWSERIAL_8N1, 21, -1); // used for reading + mySerial.enableTx(false); + mySerial.enableIntTx(false); #elif defined(ESP8266) Serial.begin(2400); #endif #if USE_ASYNCHRONOUS - Serial.onReceive(OnReceiveCB, false); - Serial.onReceiveError(OnReceiveErrorCB); _queue = xQueueCreate(QUEUE_SIZE, sizeof(data)); + _serialEventQueue = xQueueCreate(QUEUE_SIZE, sizeof(unsigned int)); + xTaskCreateUniversal(BusType::readDataFromSoftwareSerial, "_serialEventQueue", SERIAL_EVENT_TASK_STACK_SIZE, this, SERIAL_EVENT_TASK_PRIORITY, &_serialEventTask, SERIAL_EVENT_TASK_RUNNING_CORE); + mySerial.onReceive(_receiveHandler); +#else + Serial.setRxBufferSize(RXBUFFERSIZE); #endif } void BusType::end() { #if USE_ASYNCHRONOUS vQueueDelete(_queue); + vQueueDelete(_serialEventQueue); #endif } -#if USE_ASYNCHRONOUS -void BusType::OnReceiveCB() { - uint8_t symbol = Serial.read(); - Bus.receive(symbol); -} - -void BusType::OnReceiveErrorCB(hardwareSerial_error_t e) { - if (e != UART_BREAK_ERROR){ - DEBUG_LOG("OnReceiveErrorCB %i\n", e); - } -} -#endif int BusType::availableForWrite() { return Serial.availableForWrite(); @@ -71,7 +110,7 @@ bool BusType::read(data& d) { #else if (Serial.available()){ uint8_t symbol = Serial.read(); - receive(symbol); + receive(symbol, micros()); } if (_queue.size() > 0) { d = _queue.front(); @@ -90,9 +129,9 @@ void BusType::push(const data& d){ #endif } -void BusType::receive(uint8_t symbol) { +void BusType::receive(uint8_t symbol, unsigned int startBitTime) { _busState.data(symbol); - Arbitration::state state = _arbitration.data(_busState, symbol); + Arbitration::state state = _arbitration.data(_busState, symbol, startBitTime); switch (state) { case Arbitration::restart1: _nbrRestarts1++; @@ -105,7 +144,7 @@ void BusType::receive(uint8_t symbol) { uint8_t arbitration_address; _client = enhArbitrationRequested(arbitration_address); if (_client) { - if (_arbitration.start(_busState, arbitration_address)) { + if (_arbitration.start(_busState, arbitration_address, startBitTime)) { _nbrArbitrations++; DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); } diff --git a/src/main.cpp b/src/main.cpp index 7d6848a..32716bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -131,28 +131,6 @@ void setup() { last_comms = millis(); } -#define DEBUG_LOG_BUFFER_SIZE 4000 -static char message_buffer[DEBUG_LOG_BUFFER_SIZE]; -static size_t message_buffer_position; -int DEBUG_LOG_IMPL(const char *format, ...) -{ - int ret; - if (message_buffer_position>=DEBUG_LOG_BUFFER_SIZE) - return 0; - - va_list aptr; - va_start(aptr, format); - ret = vsnprintf(&message_buffer[message_buffer_position], DEBUG_LOG_BUFFER_SIZE-message_buffer_position, format, aptr); - va_end(aptr); - message_buffer_position+=ret; - if (message_buffer_position >= DEBUG_LOG_BUFFER_SIZE) { - message_buffer_position=DEBUG_LOG_BUFFER_SIZE; - } - message_buffer[DEBUG_LOG_BUFFER_SIZE-1]=0; - - return ret; -} - bool handleStatusServerRequests() { if (!statusServer.hasClient()) return false; @@ -160,7 +138,6 @@ bool handleStatusServerRequests() { WiFiClient client = statusServer.available(); if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { - client.printf("HTTP/1.1 200 OK\r\n\r\n"); client.printf("async mode: %s\n", USE_ASYNCHRONOUS ? "true" : "false"); client.printf("uptime: %ld ms\n", millis()); client.printf("rssi: %d dBm\n", WiFi.RSSI()); @@ -177,12 +154,7 @@ bool handleStatusServerRequests() { client.printf("nbr won1: %i\r\n", Bus._nbrWon1); client.printf("nbr won2: %i\r\n", Bus._nbrWon2); client.printf("nbr errors: %i\r\n", Bus._nbrErrors); - if (message_buffer_position>0) - { - client.printf("lastmessages:\r\n%s\r\n", message_buffer); - message_buffer[0]=0; - message_buffer_position=0; - } + client.flush(); client.stop(); } From c76ff74dee4a7433212625cb1a04290328d74b67 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Mon, 24 Apr 2023 08:40:05 +0200 Subject: [PATCH 48/70] use unsigned long consistently --- include/arbitration.hpp | 4 ++-- include/bus.hpp | 2 +- src/arbitration.cpp | 12 +++++++----- src/bus.cpp | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index de7aea1..6581b40 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -35,12 +35,12 @@ class Arbitration // + another arbitration is already ongoing // + the master address is SYN // = true : arbitration started. Make sure to pass all bus data to this object through the "data" method - bool start (BusState& busstate, uint8_t master, unsigned int startBitTime); + bool start (BusState& busstate, uint8_t master, unsigned long startBitTime); // A symbol was received on the bus, what does this do to the arbitration state? // Return values: // - see description of state enum value - Arbitration::state data (BusState& busstate, uint8_t symbol, unsigned int startBitTime); + Arbitration::state data (BusState& busstate, uint8_t symbol, unsigned long startBitTime); private: bool _arbitrating; diff --git a/include/bus.hpp b/include/bus.hpp index 094521d..b1aa89e 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -45,7 +45,7 @@ class BusType int _nbrErrors; private: inline void push (const data& d); - void receive (uint8_t symbol, unsigned int startBitTime); + void receive (uint8_t symbol, unsigned long startBitTime); BusState _busState; Arbitration _arbitration; WiFiClient* _client; diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 3b79cb4..d1b1ca5 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -13,7 +13,7 @@ extern unsigned long lastStartBit; // we need to wait (4300 - 4167)=133 us after we received the SYN. // rely on the uart to keep the timing // just make sure the byte to send is available in time -bool Arbitration::start(BusState& busstate, uint8_t master, unsigned int startBitTime) +bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startBitTime) { static int arb = 0; if (_arbitrating) { return false; @@ -29,7 +29,8 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned int startBi // assume we need at least 20 us to send the symbol unsigned long now = micros(); unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); - if (now - startBitTime > 4456) + unsigned long timeSinceStartBit = now-startBitTime; + if (timeSinceStartBit > 4456) { // if we are too late, don't try to participate and retry next round DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), microsSinceLastSyn); @@ -39,7 +40,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned int startBi // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command. // This timing is provided by the Bus. - int delay = 4300-(now-startBitTime); + int delay = 4300-timeSinceStartBit; if (delay > 0) { delayMicroseconds(delay); } @@ -58,7 +59,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned int startBi return true; } -Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigned int startBitTime) { +Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigned long startBitTime) { if (!_arbitrating){ return none; } @@ -108,7 +109,8 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command. // This timing is provided by the Bus. - int delay = 4300-(micros()-startBitTime); + unsigned long timeSinceStartBit = micros()-startBitTime; + int delay = 4300-timeSinceStartBit; if (delay > 0) { delayMicroseconds(delay); } diff --git a/src/bus.cpp b/src/bus.cpp index f741eb8..535b3fc 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -49,7 +49,7 @@ void BusType::readDataFromSoftwareSerial(void *args) { for(;;) { //Waiting for UART event. - unsigned int startBitTime = 0; + unsigned long startBitTime = 0; if(xQueueReceive(Bus._serialEventQueue, (void * )&startBitTime, (portTickType)portMAX_DELAY)) { auto avail = mySerial.available(); if ( !avail) { @@ -80,7 +80,7 @@ void BusType::begin() { #if USE_ASYNCHRONOUS _queue = xQueueCreate(QUEUE_SIZE, sizeof(data)); - _serialEventQueue = xQueueCreate(QUEUE_SIZE, sizeof(unsigned int)); + _serialEventQueue = xQueueCreate(QUEUE_SIZE, sizeof(unsigned long)); xTaskCreateUniversal(BusType::readDataFromSoftwareSerial, "_serialEventQueue", SERIAL_EVENT_TASK_STACK_SIZE, this, SERIAL_EVENT_TASK_PRIORITY, &_serialEventTask, SERIAL_EVENT_TASK_RUNNING_CORE); mySerial.onReceive(_receiveHandler); #else @@ -129,7 +129,7 @@ void BusType::push(const data& d){ #endif } -void BusType::receive(uint8_t symbol, unsigned int startBitTime) { +void BusType::receive(uint8_t symbol, unsigned long startBitTime) { _busState.data(symbol); Arbitration::state state = _arbitration.data(_busState, symbol, startBitTime); switch (state) { From d73c9ed0ad632e8fc633f60173db634dbe238431 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Mon, 24 Apr 2023 10:17:32 +0200 Subject: [PATCH 49/70] cleanup comments --- include/arbitration.hpp | 18 +++++++++--------- src/arbitration.cpp | 6 ------ src/bus.cpp | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 6581b40..2f698f9 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -11,15 +11,15 @@ class Arbitration { public: - enum state {none, // no arbitration ongoing/not yet completed + enum state {none, // no arbitration ongoing arbitrating, // arbitration ongoing - won1, // won - won2, // won - lost1, // lost - lost2, // lost - error, // error - restart1, // restart - restart2, // restart + won1, // won + won2, // won + lost1, // lost + lost2, // lost + error, // error + restart1, // restart the arbitration + restart2, // restart the arbitration }; Arbitration() @@ -34,7 +34,7 @@ class Arbitration // + the bus is not in a state that allows to start arbitration // + another arbitration is already ongoing // + the master address is SYN - // = true : arbitration started. Make sure to pass all bus data to this object through the "data" method + // - true : arbitration started. Make sure to pass all bus data to this object through the "data" method bool start (BusState& busstate, uint8_t master, unsigned long startBitTime); // A symbol was received on the bus, what does this do to the arbitration state? diff --git a/src/arbitration.cpp b/src/arbitration.cpp index d1b1ca5..04e8a3a 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -1,9 +1,6 @@ #include "arbitration.hpp" #include "busstate.hpp" #include "bus.hpp" -#define ARB_FIRST_DELAY_US -50 -#define ARB_SECOND_DELAY_US -50 -extern unsigned long lastStartBit; // arbitration is timing sensitive. avoid communicating with WifiClient during arbitration // according https://ebus-wiki.org/lib/exe/fetch.php/ebus/spec_test_1_v1_1_1.pdf section 3.2 @@ -11,8 +8,6 @@ extern unsigned long lastStartBit; // bus permission must be in the range of 4300 us - 4456,24 us ." // SYN symbol is 4167 us. If we would receive the symbol immediately, // we need to wait (4300 - 4167)=133 us after we received the SYN. -// rely on the uart to keep the timing -// just make sure the byte to send is available in time bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startBitTime) { static int arb = 0; if (_arbitrating) { @@ -26,7 +21,6 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startB } // too late if we don't have enough time to send our symbol - // assume we need at least 20 us to send the symbol unsigned long now = micros(); unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); unsigned long timeSinceStartBit = now-startBitTime; diff --git a/src/bus.cpp b/src/bus.cpp index 535b3fc..9e694f9 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -7,7 +7,7 @@ // exactly when the start bit is received. We can use this // for the timing of the arbitration. Because SoftwareSerial // cannot write and read at the same, we use SoftwareSerial only -// for reading and for timing we still use HardwareSerial +// for reading. For writing we still use HardwareSerial #if USE_ASYNCHRONOUS #include SoftwareSerial mySerial; From 019f80151479764b1f6ad7d517526b4a539ab428 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Mon, 24 Apr 2023 18:12:39 +0200 Subject: [PATCH 50/70] atomic counters --- .vscode/settings.json | 18 ++++++++++++++++-- platformio.ini | 35 +++++++++++++++++------------------ src/bus.cpp | 6 +++--- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a63ed69..e2ed55e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,20 @@ "*.tcc": "cpp", "fstream": "cpp", "istream": "cpp", - "ostream": "cpp" - } + "ostream": "cpp", + "array": "cpp", + "string": "cpp", + "string_view": "cpp", + "ranges": "cpp", + "functional": "cpp", + "cstddef": "cpp", + "chrono": "cpp" + }, + "cSpell.words": [ + "ARBITRATIN", + "busstate", + "currentstate", + "newstate", + "synerror" + ] } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 64771b4..11e6392 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,24 @@ lib_deps = https://github.com/tzapu/WiFiManager extra_scripts = pre:auto_firmware_version.py +[env:esp32-c3] +platform = espressif32 +board = esp32-c3-devkitm-1 +build_flags = + -DRESET_PIN=20 +lib_deps = + https://github.com/tzapu/WiFiManager + https://github.com/plerup/espsoftwareserial + +[env:esp32-c3-ota] +extends = env:esp32-c3 +upload_port = esp-ebus.local +upload_protocol = espota +[env:esp32-c3-ota-vpn] +extends = env:esp32-c3-ota +upload_protocol = custom +upload_command = scp $SOURCE root@10.9.0.6:firmware.bin && ssh root@10.9.0.6 espota.py -i esp-ebus.local -p 3232 -f firmware.bin -d -r [env:esp12e] platform = espressif8266 board = esp12e @@ -43,21 +60,3 @@ extends = env:esp12e-v3.0 upload_port = esp-ebus.local upload_protocol = espota -[env:esp32-c3] -platform = espressif32 -board = esp32-c3-devkitm-1 -build_flags = - -DRESET_PIN=20 -lib_deps = - https://github.com/tzapu/WiFiManager - https://github.com/plerup/espsoftwareserial - -[env:esp32-c3-ota] -extends = env:esp32-c3 -upload_port = esp-ebus.local -upload_protocol = espota - -[env:esp32-c3-ota-vpn] -extends = env:esp32-c3-ota -upload_protocol = custom -upload_command = scp $SOURCE root@10.9.0.6:firmware.bin && ssh root@10.9.0.6 espota.py -i esp-ebus.local -p 3232 -f firmware.bin -d -r \ No newline at end of file diff --git a/src/bus.cpp b/src/bus.cpp index 9e694f9..a6881ef 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -40,9 +40,9 @@ BusType::~BusType() { #if USE_ASYNCHRONOUS void IRAM_ATTR _receiveHandler() { - unsigned long lastStartBit= micros(); - xQueueSendToBackFromISR(Bus._serialEventQueue, &lastStartBit, 0); - vPortYieldFromISR(); + unsigned long startBitTime= micros(); + xQueueSendToBackFromISR(Bus._serialEventQueue, &startBitTime, 0); + portYIELD_FROM_ISR(); } void BusType::readDataFromSoftwareSerial(void *args) From 70baf60052b4d853c32a4edba36c6197bf42de80 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Mon, 24 Apr 2023 18:20:59 +0200 Subject: [PATCH 51/70] atomic on shared variables --- include/bus.hpp | 17 +++++++++-------- src/main.cpp | 16 ++++++++-------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index b1aa89e..bed4463 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -4,6 +4,7 @@ #include "busstate.hpp" #include "arbitration.hpp" #include "queue" +#include "atomic" // This object retrieves data from the Serial object and let's // it flow through the arbitration process. The "read" method @@ -35,14 +36,14 @@ class BusType size_t write(uint8_t symbol); int availableForWrite(); - int _nbrRestarts1; - int _nbrRestarts2; - int _nbrArbitrations; - int _nbrLost1; - int _nbrLost2; - int _nbrWon1; - int _nbrWon2; - int _nbrErrors; + std::atomic _nbrRestarts1; + std::atomic _nbrRestarts2; + std::atomic _nbrArbitrations; + std::atomic _nbrLost1; + std::atomic _nbrLost2; + std::atomic _nbrWon1; + std::atomic _nbrWon2; + std::atomic _nbrErrors; private: inline void push (const data& d); void receive (uint8_t symbol, unsigned long startBitTime); diff --git a/src/main.cpp b/src/main.cpp index 32716bf..af1f6b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -146,14 +146,14 @@ bool handleStatusServerRequests() { client.printf("loop_duration: %ld us\r\n", loopDuration); client.printf("max_loop_duration: %ld us\r\n", maxLoopDuration); client.printf("version: %s\r\n", AUTO_VERSION); - client.printf("nbr arbitrations: %i\r\n", Bus._nbrArbitrations); - client.printf("nbr restarts1: %i\r\n", Bus._nbrRestarts1); - client.printf("nbr restarts2: %i\r\n", Bus._nbrRestarts2); - client.printf("nbr lost1: %i\r\n", Bus._nbrLost1); - client.printf("nbr lost2: %i\r\n", Bus._nbrLost2); - client.printf("nbr won1: %i\r\n", Bus._nbrWon1); - client.printf("nbr won2: %i\r\n", Bus._nbrWon2); - client.printf("nbr errors: %i\r\n", Bus._nbrErrors); + client.printf("nbr arbitrations: %i\r\n", (int)Bus._nbrArbitrations); + client.printf("nbr restarts1: %i\r\n", (int)Bus._nbrRestarts1); + client.printf("nbr restarts2: %i\r\n", (int)Bus._nbrRestarts2); + client.printf("nbr lost1: %i\r\n", (int)Bus._nbrLost1); + client.printf("nbr lost2: %i\r\n", (int)Bus._nbrLost2); + client.printf("nbr won1: %i\r\n", (int)Bus._nbrWon1); + client.printf("nbr won2: %i\r\n", (int)Bus._nbrWon2); + client.printf("nbr errors: %i\r\n", (int)Bus._nbrErrors); client.flush(); client.stop(); From 5e46429dd7648084580b07f7131e9a5327858e07 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Tue, 25 Apr 2023 23:42:42 +0200 Subject: [PATCH 52/70] improve timing of start bit using SoftwareSerial --- include/bus.hpp | 7 ++++--- platformio.ini | 35 ++++++++++++++++++----------------- src/arbitration.cpp | 2 +- src/bus.cpp | 44 +++++++++++++++++++++++++++----------------- 4 files changed, 50 insertions(+), 38 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index bed4463..48d1de1 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -52,12 +52,13 @@ class BusType WiFiClient* _client; #if USE_ASYNCHRONOUS - friend void IRAM_ATTR _receiveHandler(); + // handler to be notified when there is signal change on the serial input + static void IRAM_ATTR receiveHandler(); + // queue from Bus to read method QueueHandle_t _queue; - // queue from receiveHandler to bus - QueueHandle_t _serialEventQueue; + // task to read bytes form the serial object and process them with receive methods TaskHandle_t _serialEventTask; static void readDataFromSoftwareSerial(void *args); diff --git a/platformio.ini b/platformio.ini index 11e6392..97f63fa 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,24 +14,7 @@ lib_deps = https://github.com/tzapu/WiFiManager extra_scripts = pre:auto_firmware_version.py -[env:esp32-c3] -platform = espressif32 -board = esp32-c3-devkitm-1 -build_flags = - -DRESET_PIN=20 -lib_deps = - https://github.com/tzapu/WiFiManager - https://github.com/plerup/espsoftwareserial - -[env:esp32-c3-ota] -extends = env:esp32-c3 -upload_port = esp-ebus.local -upload_protocol = espota -[env:esp32-c3-ota-vpn] -extends = env:esp32-c3-ota -upload_protocol = custom -upload_command = scp $SOURCE root@10.9.0.6:firmware.bin && ssh root@10.9.0.6 espota.py -i esp-ebus.local -p 3232 -f firmware.bin -d -r [env:esp12e] platform = espressif8266 board = esp12e @@ -60,3 +43,21 @@ extends = env:esp12e-v3.0 upload_port = esp-ebus.local upload_protocol = espota +[env:esp32-c3] +platform = espressif32 +board = esp32-c3-devkitm-1 +build_flags = + -DRESET_PIN=20 +lib_deps = + https://github.com/tzapu/WiFiManager + https://github.com/guido4096/espsoftwareserial.git#add-startbit-timestamp + +[env:esp32-c3-ota] +extends = env:esp32-c3 +upload_port = esp-ebus.local +upload_protocol = espota + +[env:esp32-c3-ota-vpn] +extends = env:esp32-c3-ota +upload_protocol = custom +upload_command = scp $SOURCE root@10.9.0.6:firmware.bin && ssh root@10.9.0.6 espota.py -i esp-ebus.local -p 3232 -f firmware.bin -d -r diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 04e8a3a..08b6b38 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -27,7 +27,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startB if (timeSinceStartBit > 4456) { // if we are too late, don't try to participate and retry next round - DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), microsSinceLastSyn); + DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), timeSinceStartBit); return false; } #if USE_ASYNCHRONOUS diff --git a/src/bus.cpp b/src/bus.cpp index a6881ef..06f337f 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -37,31 +37,41 @@ BusType::~BusType() { end(); } - #if USE_ASYNCHRONOUS -void IRAM_ATTR _receiveHandler() { - unsigned long startBitTime= micros(); - xQueueSendToBackFromISR(Bus._serialEventQueue, &startBitTime, 0); - portYIELD_FROM_ISR(); +void IRAM_ATTR BusType::receiveHandler() { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xTaskNotifyFromISR( Bus._serialEventTask, 0, eNoAction, &xHigherPriorityTaskWoken ); + portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); } void BusType::readDataFromSoftwareSerial(void *args) { for(;;) { - //Waiting for UART event. - unsigned long startBitTime = 0; - if(xQueueReceive(Bus._serialEventQueue, (void * )&startBitTime, (portTickType)portMAX_DELAY)) { - auto avail = mySerial.available(); + BaseType_t r=xTaskNotifyWait(0, 0, 0, portMAX_DELAY); + { + int avail = mySerial.available(); if ( !avail) { - // event fired on start bit, wait until first stop bit of longest frame - delayMicroseconds(1+ MAX_FRAMEBITS * 1000000 / BAUD_RATE); + // this would be a busy wait: delayMicroseconds(1+ MAX_FRAMEBITS * 1000000 / BAUD_RATE); + // Need to wait for 1000000 / BAUD_RATE, rounded to the next upper digit + // delayMicroseconds is a busy wait, which blocks the CPU to do other things + // Instead do the majority of the waiting with vTaskDelay. Because vTaskDelay is switching + // at Tick cycle, doing vTaskDelay(1) can wait anywhere between 1 Tick and 2 Ticks. + // Typically 1 Tick is 1 MS, although it depends on configuration. Here the code assumes 1 Tick is one MiliSecond. + // So we can do maximum 3 MS vTaskDelay and do the rest with a busy wait through delayMicroseconds() + // which is validated with the next compile time assert + static_assert (pdMS_TO_TICKS(1) == 1); + + unsigned int begin = micros(); + vTaskDelay(pdMS_TO_TICKS(3)); + unsigned int end = micros(); + delayMicroseconds(4167-(end - begin)); avail = mySerial.available(); } if (avail){ uint8_t symbol = mySerial.read(); - Bus.receive(symbol, startBitTime); - } - } + Bus.receive(symbol, mySerial.readStartBitTimeStamp()); + } + } } vTaskDelete(NULL); } @@ -71,6 +81,7 @@ void BusType::begin() { #ifdef ESP32 Serial.begin(2400, SERIAL_8N1, -1, 20); // used for writing + mySerial.enableStartBitTimeStampRecording(true); mySerial.begin(2400, SWSERIAL_8N1, 21, -1); // used for reading mySerial.enableTx(false); mySerial.enableIntTx(false); @@ -80,9 +91,9 @@ void BusType::begin() { #if USE_ASYNCHRONOUS _queue = xQueueCreate(QUEUE_SIZE, sizeof(data)); - _serialEventQueue = xQueueCreate(QUEUE_SIZE, sizeof(unsigned long)); xTaskCreateUniversal(BusType::readDataFromSoftwareSerial, "_serialEventQueue", SERIAL_EVENT_TASK_STACK_SIZE, this, SERIAL_EVENT_TASK_PRIORITY, &_serialEventTask, SERIAL_EVENT_TASK_RUNNING_CORE); - mySerial.onReceive(_receiveHandler); + + mySerial.onReceive(BusType::receiveHandler); #else Serial.setRxBufferSize(RXBUFFERSIZE); #endif @@ -91,7 +102,6 @@ void BusType::begin() { void BusType::end() { #if USE_ASYNCHRONOUS vQueueDelete(_queue); - vQueueDelete(_serialEventQueue); #endif } From d95cec1caf14d340530f0276c789784706d40f70 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Tue, 25 Apr 2023 23:57:55 +0200 Subject: [PATCH 53/70] fix atomic link error on esp12e --- include/bus.hpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 48d1de1..b742d22 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -4,8 +4,13 @@ #include "busstate.hpp" #include "arbitration.hpp" #include "queue" -#include "atomic" +#ifdef ESP32 +#include "atomic" +#define ATOMIC_INT std::atomic +#else +#define ATOMIC_INT int +#endif // This object retrieves data from the Serial object and let's // it flow through the arbitration process. The "read" method // will return data with meta information that tells what should @@ -36,14 +41,15 @@ class BusType size_t write(uint8_t symbol); int availableForWrite(); - std::atomic _nbrRestarts1; - std::atomic _nbrRestarts2; - std::atomic _nbrArbitrations; - std::atomic _nbrLost1; - std::atomic _nbrLost2; - std::atomic _nbrWon1; - std::atomic _nbrWon2; - std::atomic _nbrErrors; + // std::atomic seems not well supported on esp12e, besides it is also not needed there + ATOMIC_INT _nbrRestarts1; + ATOMIC_INT _nbrRestarts2; + ATOMIC_INT _nbrArbitrations; + ATOMIC_INT _nbrLost1; + ATOMIC_INT _nbrLost2; + ATOMIC_INT _nbrWon1; + ATOMIC_INT _nbrWon2; + ATOMIC_INT _nbrErrors; private: inline void push (const data& d); void receive (uint8_t symbol, unsigned long startBitTime); From ac20515e59f100f0a487f5297a5fe498d2b93af3 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 26 Apr 2023 00:10:45 +0200 Subject: [PATCH 54/70] improve comments --- src/bus.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index 06f337f..163683d 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -3,11 +3,13 @@ #include "enhanced.hpp" #include "queue" -// Using SoftwareSerial we get notified through an interrupt -// exactly when the start bit is received. We can use this -// for the timing of the arbitration. Because SoftwareSerial -// cannot write and read at the same, we use SoftwareSerial only -// for reading. For writing we still use HardwareSerial +// For ESP's based on FreeRTOS we can optimize the arbitration timing. +// With SoftwareSerial we get notified with an callback that the +// signal has changed. SoftwareSerial itself can and does know the +// exact timing of the start bit. Use this for the timing of the +// arbitration. SoftwareSerial seems to have trouble with writing +// and reading at the same time. Hence use SoftwareSerial only for +// reading. For writing use HardwareSerial. #if USE_ASYNCHRONOUS #include SoftwareSerial mySerial; @@ -51,14 +53,18 @@ void BusType::readDataFromSoftwareSerial(void *args) { int avail = mySerial.available(); if ( !avail) { - // this would be a busy wait: delayMicroseconds(1+ MAX_FRAMEBITS * 1000000 / BAUD_RATE); - // Need to wait for 1000000 / BAUD_RATE, rounded to the next upper digit - // delayMicroseconds is a busy wait, which blocks the CPU to do other things - // Instead do the majority of the waiting with vTaskDelay. Because vTaskDelay is switching - // at Tick cycle, doing vTaskDelay(1) can wait anywhere between 1 Tick and 2 Ticks. - // Typically 1 Tick is 1 MS, although it depends on configuration. Here the code assumes 1 Tick is one MiliSecond. - // So we can do maximum 3 MS vTaskDelay and do the rest with a busy wait through delayMicroseconds() - // which is validated with the next compile time assert + // avoid this busy wait: delayMicroseconds(1+ MAX_FRAMEBITS * 1000000 / BAUD_RATE); + + // Need to wait for 1000000 / BAUD_RATE, rounded to the next upper digit. + // delayMicroseconds is a busy wait, which blocks the CPU to do other things and + // could be the reason that the Wifi connection is blocked. + // Instead of a busy wait, do the majority of the waiting with vTaskDelay. + // Because vTaskDelay is switching at Tick cycle, doing vTaskDelay(1) can wait + // anywhere between 1 Tick and 2 Ticks. Typically 1 Tick is 1 MiliSecond, although it + // depends on configuration. Do maximum 3 MiliSeconds (Ticks) with vTaskDelay and do + // the rest with a busy wait through delayMicroseconds() + + // Validate 1 Tick is 1 MiliSecond with a compile time assert static_assert (pdMS_TO_TICKS(1) == 1); unsigned int begin = micros(); From d25603c6236e4df69450639e99ff5709c98c259f Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 26 Apr 2023 07:16:54 +0200 Subject: [PATCH 55/70] react faster if a byte is available while checking --- src/bus.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index 163683d..efa898d 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -68,10 +68,17 @@ void BusType::readDataFromSoftwareSerial(void *args) static_assert (pdMS_TO_TICKS(1) == 1); unsigned int begin = micros(); - vTaskDelay(pdMS_TO_TICKS(3)); - unsigned int end = micros(); - delayMicroseconds(4167-(end - begin)); - avail = mySerial.available(); + int count = 3; + while (!avail && count > 0) { + vTaskDelay(pdMS_TO_TICKS(1)); + avail = mySerial.available(); + count --; + } + if (!avail) { + unsigned int end = micros(); + delayMicroseconds(4167-(end - begin)); + avail = mySerial.available(); + } } if (avail){ uint8_t symbol = mySerial.read(); From 49707fd26145359de1043ca087f807afe4291ca8 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 26 Apr 2023 08:34:00 +0200 Subject: [PATCH 56/70] comments on vTaskDelay duration --- src/bus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bus.cpp b/src/bus.cpp index efa898d..065e2e1 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -60,7 +60,7 @@ void BusType::readDataFromSoftwareSerial(void *args) // could be the reason that the Wifi connection is blocked. // Instead of a busy wait, do the majority of the waiting with vTaskDelay. // Because vTaskDelay is switching at Tick cycle, doing vTaskDelay(1) can wait - // anywhere between 1 Tick and 2 Ticks. Typically 1 Tick is 1 MiliSecond, although it + // anywhere between 0 Tick and 1 Ticks. Typically 1 Tick is 1 MiliSecond, although it // depends on configuration. Do maximum 3 MiliSeconds (Ticks) with vTaskDelay and do // the rest with a busy wait through delayMicroseconds() From 97b34b9e3834e2d108e2b5dd24fbb3dcbe7c1df5 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 26 Apr 2023 08:43:07 +0200 Subject: [PATCH 57/70] instable when checking available() too much not clear why. --- src/bus.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index 065e2e1..6651006 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -68,17 +68,10 @@ void BusType::readDataFromSoftwareSerial(void *args) static_assert (pdMS_TO_TICKS(1) == 1); unsigned int begin = micros(); - int count = 3; - while (!avail && count > 0) { - vTaskDelay(pdMS_TO_TICKS(1)); - avail = mySerial.available(); - count --; - } - if (!avail) { - unsigned int end = micros(); - delayMicroseconds(4167-(end - begin)); - avail = mySerial.available(); - } + vTaskDelay(3); + unsigned int end = micros(); + delayMicroseconds(4167-(end - begin)); + avail = mySerial.available(); } if (avail){ uint8_t symbol = mySerial.read(); From c33571a726f0aa13694d2ecb47e864b0abe9f44a Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 27 Apr 2023 08:18:22 +0200 Subject: [PATCH 58/70] Fix OTA update fails with running background tasks --- src/bus.cpp | 9 +++++++++ src/main.cpp | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/bus.cpp b/src/bus.cpp index 6651006..beb5b9f 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -106,8 +106,17 @@ void BusType::begin() { } void BusType::end() { + Serial.end(); +#ifdef ESP32 + mySerial.end(); +#endif + #if USE_ASYNCHRONOUS vQueueDelete(_queue); + _queue=0; + + vTaskDelete(_serialEventTask); + _serialEventTask=0; #endif } diff --git a/src/main.cpp b/src/main.cpp index af1f6b5..1e51e9f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,6 +121,10 @@ void setup() { wifiServerEnh.begin(); statusServer.begin(); + // Stop the Bus when the OTA update starts, because it interferes with the OTA process + ArduinoOTA.onStart([]() { + Bus.end(); + }); ArduinoOTA.begin(); MDNS.end(); @@ -153,7 +157,7 @@ bool handleStatusServerRequests() { client.printf("nbr lost2: %i\r\n", (int)Bus._nbrLost2); client.printf("nbr won1: %i\r\n", (int)Bus._nbrWon1); client.printf("nbr won2: %i\r\n", (int)Bus._nbrWon2); - client.printf("nbr errors: %i\r\n", (int)Bus._nbrErrors); + client.printf("nbr errors: %i\r\n", (int)Bus._nbrErrors); client.flush(); client.stop(); From 049aba9eac7866164bf749dcb88a6396bebc8bab Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 27 Apr 2023 08:19:04 +0200 Subject: [PATCH 59/70] avoid confusion int / unsigned long --- src/bus.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/bus.cpp b/src/bus.cpp index beb5b9f..854c891 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -60,17 +60,19 @@ void BusType::readDataFromSoftwareSerial(void *args) // could be the reason that the Wifi connection is blocked. // Instead of a busy wait, do the majority of the waiting with vTaskDelay. // Because vTaskDelay is switching at Tick cycle, doing vTaskDelay(1) can wait - // anywhere between 0 Tick and 1 Ticks. Typically 1 Tick is 1 MiliSecond, although it - // depends on configuration. Do maximum 3 MiliSeconds (Ticks) with vTaskDelay and do + // anywhere between 0 Tick and 1 Ticks. Typically 1 Tick is 1 MilliSecond, although it + // depends on configuration. Do 4 MilliSeconds (Ticks) with vTaskDelay and do // the rest with a busy wait through delayMicroseconds() - // Validate 1 Tick is 1 MiliSecond with a compile time assert + // Validate 1 Tick is 1 MilliSecond with a compile time assert static_assert (pdMS_TO_TICKS(1) == 1); - unsigned int begin = micros(); - vTaskDelay(3); - unsigned int end = micros(); - delayMicroseconds(4167-(end - begin)); + unsigned long begin = micros(); + vTaskDelay(4); + unsigned long end = micros(); + unsigned long delay = end - begin; + if (delay < 4167) + delayMicroseconds(4167-delay); avail = mySerial.available(); } if (avail){ From 53deacb8a51b7160cef308c4366268b97bc3cc99 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 27 Apr 2023 16:03:21 +0200 Subject: [PATCH 60/70] more statistics, minor code cleanup --- include/arbitration.hpp | 14 ++++++++------ include/bus.hpp | 1 + src/arbitration.cpp | 12 ++++++------ src/bus.cpp | 43 ++++++++++++++++++++++++----------------- src/main.cpp | 1 + 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/include/arbitration.hpp b/include/arbitration.hpp index 2f698f9..080ce80 100644 --- a/include/arbitration.hpp +++ b/include/arbitration.hpp @@ -30,12 +30,14 @@ class Arbitration {} // Try to start arbitration for the specified master. // Return values: - // - false : arbitration not started. Possible reasons: - // + the bus is not in a state that allows to start arbitration - // + another arbitration is already ongoing - // + the master address is SYN - // - true : arbitration started. Make sure to pass all bus data to this object through the "data" method - bool start (BusState& busstate, uint8_t master, unsigned long startBitTime); + // - started : arbitration started. Make sure to pass all bus data to this object through the "data" method + // - not_started : arbitration not started. Possible reasons: + // + the bus is not in a state that allows to start arbitration + // + another arbitration is already ongoing + // + the master address is SYN + // - late : arbitration not started because the start is too late compared to the SYN symbol received + enum result {started, not_started, late}; + result start(BusState& busstate, uint8_t master, unsigned long startBitTime); // A symbol was received on the bus, what does this do to the arbitration state? // Return values: diff --git a/include/bus.hpp b/include/bus.hpp index b742d22..4948ed4 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -50,6 +50,7 @@ class BusType ATOMIC_INT _nbrWon1; ATOMIC_INT _nbrWon2; ATOMIC_INT _nbrErrors; + ATOMIC_INT _nbrLate; private: inline void push (const data& d); void receive (uint8_t symbol, unsigned long startBitTime); diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 08b6b38..c35886d 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -8,16 +8,16 @@ // bus permission must be in the range of 4300 us - 4456,24 us ." // SYN symbol is 4167 us. If we would receive the symbol immediately, // we need to wait (4300 - 4167)=133 us after we received the SYN. -bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startBitTime) +Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsigned long startBitTime) { static int arb = 0; if (_arbitrating) { - return false; + return not_started; } if (master == SYN) { - return false; + return not_started; } if (busstate._state != BusState::eReceivedFirstSYN) { - return false; + return not_started; } // too late if we don't have enough time to send our symbol @@ -28,7 +28,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startB { // if we are too late, don't try to participate and retry next round DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), timeSinceStartBit); - return false; + return late; } #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus @@ -50,7 +50,7 @@ bool Arbitration::start(BusState& busstate, uint8_t master, unsigned long startB _arbitrationAddress = master; _arbitrating = true; _participateSecond = false; - return true; + return started; } Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigned long startBitTime) { diff --git a/src/bus.cpp b/src/bus.cpp index 854c891..e2a2a66 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -42,17 +42,20 @@ BusType::~BusType() { #if USE_ASYNCHRONOUS void IRAM_ATTR BusType::receiveHandler() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xTaskNotifyFromISR( Bus._serialEventTask, 0, eNoAction, &xHigherPriorityTaskWoken ); + vTaskNotifyGiveFromISR( Bus._serialEventTask, &xHigherPriorityTaskWoken ); portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); } void BusType::readDataFromSoftwareSerial(void *args) { for(;;) { - BaseType_t r=xTaskNotifyWait(0, 0, 0, portMAX_DELAY); + BaseType_t r=ulTaskNotifyTake(pdFALSE, portMAX_DELAY); { + // For SoftwareSerial; + // The method "available" always evaluates all the interrupts received + // The method "read" only evaluates the interrupts received if there is no byte available int avail = mySerial.available(); - if ( !avail) { + if ( !avail && r==1) { // avoid this busy wait: delayMicroseconds(1+ MAX_FRAMEBITS * 1000000 / BAUD_RATE); // Need to wait for 1000000 / BAUD_RATE, rounded to the next upper digit. @@ -60,23 +63,24 @@ void BusType::readDataFromSoftwareSerial(void *args) // could be the reason that the Wifi connection is blocked. // Instead of a busy wait, do the majority of the waiting with vTaskDelay. // Because vTaskDelay is switching at Tick cycle, doing vTaskDelay(1) can wait - // anywhere between 0 Tick and 1 Ticks. Typically 1 Tick is 1 MilliSecond, although it - // depends on configuration. Do 4 MilliSeconds (Ticks) with vTaskDelay and do - // the rest with a busy wait through delayMicroseconds() + // anywhere between 0 Tick and 1 Ticks. On esp32 Arduino 1 Tick is 1 MilliSecond, + // although it depends on configuration. Do 4 MilliSeconds (Ticks) with vTaskDelay + // and do the rest with a busy wait through delayMicroseconds() // Validate 1 Tick is 1 MilliSecond with a compile time assert static_assert (pdMS_TO_TICKS(1) == 1); unsigned long begin = micros(); - vTaskDelay(4); - unsigned long end = micros(); - unsigned long delay = end - begin; - if (delay < 4167) - delayMicroseconds(4167-delay); + vTaskDelay(pdMS_TO_TICKS(4)); + unsigned long delayed = micros() - begin; + + if ( delayed < 4167) { + delayMicroseconds(4167-delayed); + } avail = mySerial.available(); } if (avail){ - uint8_t symbol = mySerial.read(); + int symbol = mySerial.read(); Bus.receive(symbol, mySerial.readStartBitTimeStamp()); } } @@ -171,12 +175,15 @@ void BusType::receive(uint8_t symbol, unsigned long startBitTime) { uint8_t arbitration_address; _client = enhArbitrationRequested(arbitration_address); if (_client) { - if (_arbitration.start(_busState, arbitration_address, startBitTime)) { - _nbrArbitrations++; - DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); - } - else { - DEBUG_LOG("BUS START WAIT 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); + switch (_arbitration.start(_busState, arbitration_address, startBitTime)) { + case Arbitration::started: + _nbrArbitrations++; + DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); + break; + case Arbitration::late: + _nbrLate++; + case Arbitration::not_started: + DEBUG_LOG("BUS START WAIT 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); } } push({false, RECEIVED, symbol, 0, _client}); // send to everybody. ebusd needs the SYN to get in the right mood diff --git a/src/main.cpp b/src/main.cpp index 1e51e9f..f317158 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -157,6 +157,7 @@ bool handleStatusServerRequests() { client.printf("nbr lost2: %i\r\n", (int)Bus._nbrLost2); client.printf("nbr won1: %i\r\n", (int)Bus._nbrWon1); client.printf("nbr won2: %i\r\n", (int)Bus._nbrWon2); + client.printf("nbr late: %i\r\n", (int)Bus._nbrLate); client.printf("nbr errors: %i\r\n", (int)Bus._nbrErrors); client.flush(); From 371aa4d5651b99bdb4271cac45ad1de3d2ba3696 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Thu, 27 Apr 2023 16:50:48 +0200 Subject: [PATCH 61/70] Support SoftwareSerial for esp8266 --- include/main.hpp | 9 +++++---- platformio.ini | 3 ++- src/bus.cpp | 27 +++++++++++++++++++++------ src/main.cpp | 1 + 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index 6073d1d..7415b25 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -13,11 +13,12 @@ #ifdef ESP32 -#define USE_ASYNCHRONOUS 1 -// https://esp32.com/viewtopic.php?t=19788 -#define AVAILABLE_THRESHOLD 0 +#define USE_SOFTWARE_SERIAL 1 +#define USE_ASYNCHRONOUS 1 // requires USE_SOFTWARE_SERIAL +#define AVAILABLE_THRESHOLD 0 // https://esp32.com/viewtopic.php?t=19788 #else -#define USE_ASYNCHRONOUS 0 +#define USE_SOFTWARE_SERIAL 1 +#define USE_ASYNCHRONOUS 0 // requires USE_SOFTWARE_SERIAL #define AVAILABLE_THRESHOLD 1 #endif diff --git a/platformio.ini b/platformio.ini index 97f63fa..99e118a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,6 +26,7 @@ build_flags = lib_deps = https://github.com/tzapu/WiFiManager https://github.com/marvinroger/ESP8266TrueRandom + https://github.com/guido4096/espsoftwareserial.git#add-startbit-timestamp [env:esp12e-ota] extends = env:esp12e @@ -51,7 +52,7 @@ build_flags = lib_deps = https://github.com/tzapu/WiFiManager https://github.com/guido4096/espsoftwareserial.git#add-startbit-timestamp - + [env:esp32-c3-ota] extends = env:esp32-c3 upload_port = esp-ebus.local diff --git a/src/bus.cpp b/src/bus.cpp index e2a2a66..1e605c4 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -10,7 +10,7 @@ // arbitration. SoftwareSerial seems to have trouble with writing // and reading at the same time. Hence use SoftwareSerial only for // reading. For writing use HardwareSerial. -#if USE_ASYNCHRONOUS +#if USE_SOFTWARE_SERIAL #include SoftwareSerial mySerial; #endif @@ -91,14 +91,24 @@ void BusType::readDataFromSoftwareSerial(void *args) void BusType::begin() { -#ifdef ESP32 +#if USE_SOFTWARE_SERIAL + #if defined(ESP32) Serial.begin(2400, SERIAL_8N1, -1, 20); // used for writing + #elif defined(ESP8266) + Serial.begin(2400, SERIAL_8N1, SERIAL_TX_ONLY); + #endif mySerial.enableStartBitTimeStampRecording(true); mySerial.begin(2400, SWSERIAL_8N1, 21, -1); // used for reading mySerial.enableTx(false); mySerial.enableIntTx(false); -#elif defined(ESP8266) +#else + #if defined(ESP32) + Serial.begin(2400, SERIAL_8N1, 21, 20); // used for writing + Serial.setRxFIFOFull(1); + #elif defined(ESP8266) Serial.begin(2400); + #endif + Serial.setRxBufferSize(RXBUFFERSIZE); #endif #if USE_ASYNCHRONOUS @@ -106,14 +116,12 @@ void BusType::begin() { xTaskCreateUniversal(BusType::readDataFromSoftwareSerial, "_serialEventQueue", SERIAL_EVENT_TASK_STACK_SIZE, this, SERIAL_EVENT_TASK_PRIORITY, &_serialEventTask, SERIAL_EVENT_TASK_RUNNING_CORE); mySerial.onReceive(BusType::receiveHandler); -#else - Serial.setRxBufferSize(RXBUFFERSIZE); #endif } void BusType::end() { Serial.end(); -#ifdef ESP32 +#if USE_SOFTWARE_SERIAL mySerial.end(); #endif @@ -138,11 +146,18 @@ size_t BusType::write(uint8_t symbol) { bool BusType::read(data& d) { #if USE_ASYNCHRONOUS return xQueueReceive(_queue, &d, 0) == pdTRUE; +#else +#if USE_SOFTWARE_SERIAL + if (mySerial.available()){ + uint8_t symbol = mySerial.read(); + receive(symbol, mySerial.readStartBitTimeStamp()); + } #else if (Serial.available()){ uint8_t symbol = Serial.read(); receive(symbol, micros()); } +#endif if (_queue.size() > 0) { d = _queue.front(); _queue.pop(); diff --git a/src/main.cpp b/src/main.cpp index f317158..8306658 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,6 +143,7 @@ bool handleStatusServerRequests() { if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { client.printf("async mode: %s\n", USE_ASYNCHRONOUS ? "true" : "false"); + client.printf("software serial mode: %s\n", USE_SOFTWARE_SERIAL ? "true" : "false"); client.printf("uptime: %ld ms\n", millis()); client.printf("rssi: %d dBm\n", WiFi.RSSI()); client.printf("free_heap: %d B\n", ESP.getFreeHeap()); From 7dac398259f0669afc3c0de788d2356da81ee5ce Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 28 Apr 2023 13:34:22 +0200 Subject: [PATCH 62/70] finetune timing and waiting algorithm --- src/arbitration.cpp | 10 ++++++---- src/bus.cpp | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/arbitration.cpp b/src/arbitration.cpp index c35886d..7013a0b 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -33,8 +33,9 @@ Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsig #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command. - // This timing is provided by the Bus. - int delay = 4300-timeSinceStartBit; + // We receive the exact flange of the startbit, wait for that time. Still we need to + // give time to the uart to put the byte on the bus and testing has shown this requires 700 micros + int delay = 4300-timeSinceStartBit-700; if (delay > 0) { delayMicroseconds(delay); } @@ -102,9 +103,10 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command. - // This timing is provided by the Bus. + // We receive the exact flange of the startbit, wait for that time. Still we need to + // give time to the uart to put the byte on the bus and testing has shown this requires 700 micros unsigned long timeSinceStartBit = micros()-startBitTime; - int delay = 4300-timeSinceStartBit; + int delay = 4300-timeSinceStartBit-700; if (delay > 0) { delayMicroseconds(delay); } diff --git a/src/bus.cpp b/src/bus.cpp index 1e605c4..2987986 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -64,20 +64,44 @@ void BusType::readDataFromSoftwareSerial(void *args) // Instead of a busy wait, do the majority of the waiting with vTaskDelay. // Because vTaskDelay is switching at Tick cycle, doing vTaskDelay(1) can wait // anywhere between 0 Tick and 1 Ticks. On esp32 Arduino 1 Tick is 1 MilliSecond, - // although it depends on configuration. Do 4 MilliSeconds (Ticks) with vTaskDelay - // and do the rest with a busy wait through delayMicroseconds() + // although it depends on configuration. // Validate 1 Tick is 1 MilliSecond with a compile time assert static_assert (pdMS_TO_TICKS(1) == 1); + // We need to poll mySerial for availability of a byte. Testing has shown that from 1 millisecond + // onward we need to check for incoming data every 500 micros. We have to wait using vTaskDelay + // to allow the processor to do other things, however that only allows millisecond resolution. + // To work around, split the polling in two sections: + // 1) Wait for 500 micros using busy wait with delayMicroseconds + // 2) Wait the rest of the timeslice, which will be about 500 micros, using vTaskDelay unsigned long begin = micros(); - vTaskDelay(pdMS_TO_TICKS(4)); + vTaskDelay(pdMS_TO_TICKS(1)); + avail = mySerial.available(); + + // How was the delay until now? unsigned long delayed = micros() - begin; - if ( delayed < 4167) { - delayMicroseconds(4167-delayed); + // Loop till the maximum duration of 1 byte (4167 micros from begin) + // and check every 500 micros, using combination of delayMicroseconds(500); and + // vTaskDelay(pdMS_TO_TICKS(1)) . This vTaskDelay will wait till for the next timeslice, + // which is typically about 500 micros away. The vTaskDelay's make sure we are in sync + // on each tick. + while (delayed < 4167 && !avail) { + if (4167 - delayed > 1000) { // Need to wait more than 1000 micros? + delayMicroseconds(500); + avail = mySerial.available(); + if (!avail) { + vTaskDelay(pdMS_TO_TICKS(1)); + } + } + else { // Otherwise spend the remaining wait with delayMicroseconds + unsigned long delay = 4167-delayed<500?4167-delayed:500; + delayMicroseconds(delay); + } + avail = mySerial.available(); + delayed = micros() - begin; } - avail = mySerial.available(); } if (avail){ int symbol = mySerial.read(); From 7dde1164e7a5558d0c0e1c4a5e01a141b4423adc Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 28 Apr 2023 13:51:13 +0200 Subject: [PATCH 63/70] improve comments --- src/arbitration.cpp | 10 ++++++---- src/bus.cpp | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 7013a0b..ed8dffa 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -33,8 +33,9 @@ Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsig #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command. - // We receive the exact flange of the startbit, wait for that time. Still we need to - // give time to the uart to put the byte on the bus and testing has shown this requires 700 micros + // We receive the exact flange of the startbit, use that to calculate the exact time to wait. + // Then subtract time from the wait to allow the uart to put the byte on the bus. Testing + // has shown this requires about 700 micros on the esp32-c3. int delay = 4300-timeSinceStartBit-700; if (delay > 0) { delayMicroseconds(delay); @@ -103,8 +104,9 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne #if USE_ASYNCHRONOUS // When in async mode, we get immediately interrupted when a symbol is received on the bus // The earliest allowed to send is 4300 measured from the start bit of the SYN command. - // We receive the exact flange of the startbit, wait for that time. Still we need to - // give time to the uart to put the byte on the bus and testing has shown this requires 700 micros + // We receive the exact flange of the startbit, use that to calculate the exact time to wait. + // Then subtract time from the wait to allow the uart to put the byte on the bus. Testing + // has shown this requires about 700 micros on the esp32-c3. unsigned long timeSinceStartBit = micros()-startBitTime; int delay = 4300-timeSinceStartBit-700; if (delay > 0) { diff --git a/src/bus.cpp b/src/bus.cpp index 2987986..ae5547d 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -83,10 +83,11 @@ void BusType::readDataFromSoftwareSerial(void *args) unsigned long delayed = micros() - begin; // Loop till the maximum duration of 1 byte (4167 micros from begin) - // and check every 500 micros, using combination of delayMicroseconds(500); and - // vTaskDelay(pdMS_TO_TICKS(1)) . This vTaskDelay will wait till for the next timeslice, - // which is typically about 500 micros away. The vTaskDelay's make sure we are in sync - // on each tick. + // and check every 500 micros, using combination of delayMicroseconds(500) and + // vTaskDelay(pdMS_TO_TICKS(1)) . The vTaskDelay will wait till the end of the + // current timeslice, which is typically about 500 micros away, because the + // previous vTaskDelay makes sure the code is already synced to this tick + // Assumption: time needed for mySerial.available() is less than 500 micros. while (delayed < 4167 && !avail) { if (4167 - delayed > 1000) { // Need to wait more than 1000 micros? delayMicroseconds(500); From af08afc98e93ef8c3dc524a0992a279a358a601f Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 28 Apr 2023 15:21:58 +0200 Subject: [PATCH 64/70] avoid read buffer overflow --- src/bus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bus.cpp b/src/bus.cpp index ae5547d..de31630 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -123,7 +123,7 @@ void BusType::begin() { Serial.begin(2400, SERIAL_8N1, SERIAL_TX_ONLY); #endif mySerial.enableStartBitTimeStampRecording(true); - mySerial.begin(2400, SWSERIAL_8N1, 21, -1); // used for reading + mySerial.begin(2400, SWSERIAL_8N1, 21, -1, false, RXBUFFERSIZE); // used for reading mySerial.enableTx(false); mySerial.enableIntTx(false); #else From aedf087b04ae5f2afb592dfe5f347e6417111a7c Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Fri, 28 Apr 2023 19:55:09 +0200 Subject: [PATCH 65/70] use %lu for unsigned long --- include/busstate.hpp | 2 +- src/arbitration.cpp | 20 ++++++++++---------- src/bus.cpp | 11 ++++++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/busstate.hpp b/include/busstate.hpp index ecded46..ef0ca7d 100644 --- a/include/busstate.hpp +++ b/include/busstate.hpp @@ -98,7 +98,7 @@ class BusState { { _previousSYNtime = _SYNtime; _SYNtime = micros(); - DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %ld us\n", enumvalue(currentstate), enumvalue(newstate), _master, _symbol, microsSincePreviousSyn()); + DEBUG_LOG ("unexpected SYN on bus while state is %s, setting state to %s m=0x%02x, b=0x%02x %lu us\n", enumvalue(currentstate), enumvalue(newstate), _master, _symbol, microsSincePreviousSyn()); return newstate; } diff --git a/src/arbitration.cpp b/src/arbitration.cpp index ed8dffa..1ee11d9 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -27,7 +27,7 @@ Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsig if (timeSinceStartBit > 4456) { // if we are too late, don't try to participate and retry next round - DEBUG_LOG("ARB LATE 0x%02x %ld us\n", Serial.peek(), timeSinceStartBit); + DEBUG_LOG("ARB LATE 0x%02x %lu us\n", Serial.peek(), timeSinceStartBit); return late; } #if USE_ASYNCHRONOUS @@ -45,9 +45,9 @@ Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsig // Do logging of the ARB START message after writing the symbol, so enabled or disabled // logging does not affect timing calculations. #if USE_ASYNCHRONOUS - DEBUG_LOG("ARB START %04i 0x%02x %ld us %i us\n", arb++, master, microsSinceLastSyn, delay); + DEBUG_LOG("ARB START %04i 0x%02x %lu us %i us\n", arb++, master, microsSinceLastSyn, delay); #else - DEBUG_LOG("ARB START %04i 0x%02x %ld us\n", arb++, master, microsSinceLastSyn); + DEBUG_LOG("ARB START %04i 0x%02x %lu us\n", arb++, master, microsSinceLastSyn); #endif _arbitrationAddress = master; _arbitrating = true; @@ -66,7 +66,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne case BusState::eStartupSymbolAfterFirstSyn: case BusState::eStartupSecondSyn: case BusState::eReceivedFirstSYN: - DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %ld us %ld us\n", busstate._master, busstate._symbol, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); + DEBUG_LOG("ARB ERROR 0x%02x 0x%02x 0x%02x %lu us %lu us\n", busstate._master, busstate._symbol, symbol, busstate.microsSinceLastSyn(), busstate.microsSincePreviousSyn()); _arbitrating = false; // Sometimes a second SYN is received instead of an address, either // after having started the arbitration, or after participating in @@ -83,7 +83,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne return error; case BusState::eReceivedAddressAfterFirstSYN: // did we win 1st round of abitration? if (symbol == _arbitrationAddress) { - DEBUG_LOG("ARB WON1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB WON1 0x%02x %lu us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; _restartCount = 0; return won1; // we won; nobody else will write to the bus @@ -92,7 +92,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne _participateSecond = true; // participate in second round of arbitration if we have the same priority class } else { - DEBUG_LOG("ARB LOST1 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB LOST1 0x%02x %lu us\n", symbol, busstate.microsSinceLastSyn()); // arbitration might be ongoing between other bus participants, so we cannot yet know what // the winning master is. Need to wait for eBusy } @@ -116,21 +116,21 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne // Do logging of the ARB START message after writing the symbol, so enabled or disabled // logging does not affect timing calculations. Bus.write(_arbitrationAddress); - DEBUG_LOG("ARB MASTER2 0x%02x %ld us\n", _arbitrationAddress, microsSinceLastSyn); + DEBUG_LOG("ARB MASTER2 0x%02x %lu us\n", _arbitrationAddress, microsSinceLastSyn); } else { - DEBUG_LOG("ARB SKIP 0x%02x %ld us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB SKIP 0x%02x %lu us\n", _arbitrationAddress, busstate.microsSinceLastSyn()); } return arbitrating; case BusState::eReceivedAddressAfterSecondSYN: // did we win 2nd round of arbitration? if (symbol == _arbitrationAddress) { - DEBUG_LOG("ARB WON2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB WON2 0x%02x %lu us\n", symbol, busstate.microsSinceLastSyn()); _arbitrating = false; _restartCount = 0; return won2; // we won; nobody else will write to the bus } else { - DEBUG_LOG("ARB LOST2 0x%02x %ld us\n", symbol, busstate.microsSinceLastSyn()); + DEBUG_LOG("ARB LOST2 0x%02x %lu us\n", symbol, busstate.microsSinceLastSyn()); // we now know which address has won and we could exit here. // but it is easier to wait for eBusy, so after the while loop, the // "lost" state can be handled the same as when somebody lost in the first round diff --git a/src/bus.cpp b/src/bus.cpp index de31630..c581efb 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -68,6 +68,7 @@ void BusType::readDataFromSoftwareSerial(void *args) // Validate 1 Tick is 1 MilliSecond with a compile time assert static_assert (pdMS_TO_TICKS(1) == 1); + static_assert (sizeof(uint32_t) == sizeof(unsigned long)); // We need to poll mySerial for availability of a byte. Testing has shown that from 1 millisecond // onward we need to check for incoming data every 500 micros. We have to wait using vTaskDelay @@ -218,18 +219,18 @@ void BusType::receive(uint8_t symbol, unsigned long startBitTime) { switch (_arbitration.start(_busState, arbitration_address, startBitTime)) { case Arbitration::started: _nbrArbitrations++; - DEBUG_LOG("BUS START SUCC 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START SUCC 0x%02x %lu us\n", symbol, _busState.microsSinceLastSyn()); break; case Arbitration::late: _nbrLate++; case Arbitration::not_started: - DEBUG_LOG("BUS START WAIT 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS START WAIT 0x%02x %lu us\n", symbol, _busState.microsSinceLastSyn()); } } push({false, RECEIVED, symbol, 0, _client}); // send to everybody. ebusd needs the SYN to get in the right mood break; case Arbitration::arbitrating: - DEBUG_LOG("BUS ARBITRATIN 0x%02x %ld us\n", symbol, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS ARBITRATIN 0x%02x %lu us\n", symbol, _busState.microsSinceLastSyn()); push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitration client break; case Arbitration::won1: @@ -239,7 +240,7 @@ void BusType::receive(uint8_t symbol, unsigned long startBitTime) { _nbrWon2++; WON: enhArbitrationDone(); - DEBUG_LOG("BUS SEND WON 0x%02x %ld us\n", _busState._master, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS SEND WON 0x%02x %lu us\n", _busState._master, _busState.microsSinceLastSyn()); push({true, STARTED, _busState._master, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, _client, _client}); // do not send to arbitrating client _client=0; @@ -251,7 +252,7 @@ void BusType::receive(uint8_t symbol, unsigned long startBitTime) { _nbrLost2++; LOST: enhArbitrationDone(); - DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %ld us\n", _busState._master, _busState._symbol, _busState.microsSinceLastSyn()); + DEBUG_LOG("BUS SEND LOST 0x%02x 0x%02x %lu us\n", _busState._master, _busState._symbol, _busState.microsSinceLastSyn()); push({true, FAILED, _busState._master, _client, _client}); // send only to the arbitrating client push({false, RECEIVED, symbol, 0, _client}); // send to everybody _client=0; From de7bde0b42cc8361222ceff514ab6e75db622f39 Mon Sep 17 00:00:00 2001 From: Daniel Kucera Date: Sat, 15 Apr 2023 08:33:16 +0200 Subject: [PATCH 66/70] fix disable and reset pins --- include/main.hpp | 1 - platformio.ini | 1 - 2 files changed, 2 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index 7415b25..0763ed1 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -11,7 +11,6 @@ #define HOSTNAME "esp-eBus" #define RESET_MS 1000 - #ifdef ESP32 #define USE_SOFTWARE_SERIAL 1 #define USE_ASYNCHRONOUS 1 // requires USE_SOFTWARE_SERIAL diff --git a/platformio.ini b/platformio.ini index 99e118a..b2c7c0b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,7 +22,6 @@ monitor_speed = 2400 upload_speed = 921600 build_flags = -DRESET_PIN=5 - -DTX_DISABLE_PIN=5 lib_deps = https://github.com/tzapu/WiFiManager https://github.com/marvinroger/ESP8266TrueRandom From 79102a4decda9f0339ef38a63c499734c3ba9894 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 10 May 2023 20:14:36 +0200 Subject: [PATCH 67/70] RX and TX pin as defines --- include/main.hpp | 4 ++++ src/bus.cpp | 17 ++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/main.hpp b/include/main.hpp index 0763ed1..8c0c76e 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -12,10 +12,14 @@ #define RESET_MS 1000 #ifdef ESP32 +#define UART_TX 20 +#define UART_RX 21 #define USE_SOFTWARE_SERIAL 1 #define USE_ASYNCHRONOUS 1 // requires USE_SOFTWARE_SERIAL #define AVAILABLE_THRESHOLD 0 // https://esp32.com/viewtopic.php?t=19788 #else +#define UART_TX 1 +#define UART_RX 3 #define USE_SOFTWARE_SERIAL 1 #define USE_ASYNCHRONOUS 0 // requires USE_SOFTWARE_SERIAL #define AVAILABLE_THRESHOLD 1 diff --git a/src/bus.cpp b/src/bus.cpp index c581efb..7f1ae93 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -119,28 +119,27 @@ void BusType::begin() { #if USE_SOFTWARE_SERIAL #if defined(ESP32) - Serial.begin(2400, SERIAL_8N1, -1, 20); // used for writing + Serial.begin(2400, SERIAL_8N1, -1, UART_TX); // used for writing #elif defined(ESP8266) - Serial.begin(2400, SERIAL_8N1, SERIAL_TX_ONLY); + Serial.begin(2400, SERIAL_8N1, SERIAL_TX_ONLY, UART_TX); #endif mySerial.enableStartBitTimeStampRecording(true); - mySerial.begin(2400, SWSERIAL_8N1, 21, -1, false, RXBUFFERSIZE); // used for reading mySerial.enableTx(false); mySerial.enableIntTx(false); + mySerial.begin(2400, SWSERIAL_8N1, UART_RX, -1, false, RXBUFFERSIZE); // used for reading #else + Serial.setRxBufferSize(RXBUFFERSIZE); #if defined(ESP32) - Serial.begin(2400, SERIAL_8N1, 21, 20); // used for writing + Serial.begin(2400, SERIAL_8N1, UART_RX, UART_TX); // used for writing Serial.setRxFIFOFull(1); #elif defined(ESP8266) Serial.begin(2400); #endif - Serial.setRxBufferSize(RXBUFFERSIZE); #endif #if USE_ASYNCHRONOUS _queue = xQueueCreate(QUEUE_SIZE, sizeof(data)); xTaskCreateUniversal(BusType::readDataFromSoftwareSerial, "_serialEventQueue", SERIAL_EVENT_TASK_STACK_SIZE, this, SERIAL_EVENT_TASK_PRIORITY, &_serialEventTask, SERIAL_EVENT_TASK_RUNNING_CORE); - mySerial.onReceive(BusType::receiveHandler); #endif } @@ -173,17 +172,17 @@ bool BusType::read(data& d) { #if USE_ASYNCHRONOUS return xQueueReceive(_queue, &d, 0) == pdTRUE; #else -#if USE_SOFTWARE_SERIAL + #if USE_SOFTWARE_SERIAL if (mySerial.available()){ uint8_t symbol = mySerial.read(); receive(symbol, mySerial.readStartBitTimeStamp()); } -#else + #else if (Serial.available()){ uint8_t symbol = Serial.read(); receive(symbol, micros()); } -#endif + #endif if (_queue.size() > 0) { d = _queue.front(); _queue.pop(); From 117e467086f2f10d40466c97502d479026541f3b Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 10 May 2023 20:14:43 +0200 Subject: [PATCH 68/70] avoid running out of heap on ESP8266 --- include/main.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/main.hpp b/include/main.hpp index 8c0c76e..9d1c3f7 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -5,7 +5,7 @@ #include #define MAX_SRV_CLIENTS 4 -#define RXBUFFERSIZE 1024 +#define RXBUFFERSIZE 512 // On ESP8266, maximum 512 icw SoftwareSerial, otherwise you run out of heap #define QUEUE_SIZE 480 #define STACK_PROTECTOR 512 // bytes #define HOSTNAME "esp-eBus" From 8ce3fe780f3318944436f6967c41d1bbb22dc5c3 Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 10 May 2023 20:33:37 +0200 Subject: [PATCH 69/70] fixup merge error with DTX_DISABLE_PIN --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index b2c7c0b..99e118a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,6 +22,7 @@ monitor_speed = 2400 upload_speed = 921600 build_flags = -DRESET_PIN=5 + -DTX_DISABLE_PIN=5 lib_deps = https://github.com/tzapu/WiFiManager https://github.com/marvinroger/ESP8266TrueRandom From 6d29ef5abb5350ae4487c22177193b6af225dcfa Mon Sep 17 00:00:00 2001 From: Guido Jansen Date: Wed, 16 Aug 2023 16:20:01 +0200 Subject: [PATCH 70/70] Add method "available" to BusType --- include/bus.hpp | 1 + include/main.hpp | 2 +- src/arbitration.cpp | 4 ++-- src/bus.cpp | 8 ++++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/bus.hpp b/include/bus.hpp index 4948ed4..283b3e9 100644 --- a/include/bus.hpp +++ b/include/bus.hpp @@ -40,6 +40,7 @@ class BusType bool read(data& d); size_t write(uint8_t symbol); int availableForWrite(); + int available(); // std::atomic seems not well supported on esp12e, besides it is also not needed there ATOMIC_INT _nbrRestarts1; diff --git a/include/main.hpp b/include/main.hpp index 9d1c3f7..ba1a1cd 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -20,7 +20,7 @@ #else #define UART_TX 1 #define UART_RX 3 -#define USE_SOFTWARE_SERIAL 1 +#define USE_SOFTWARE_SERIAL 0 #define USE_ASYNCHRONOUS 0 // requires USE_SOFTWARE_SERIAL #define AVAILABLE_THRESHOLD 1 #endif diff --git a/src/arbitration.cpp b/src/arbitration.cpp index 1ee11d9..a37b9e5 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -24,7 +24,7 @@ Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsig unsigned long now = micros(); unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); unsigned long timeSinceStartBit = now-startBitTime; - if (timeSinceStartBit > 4456) + if (timeSinceStartBit > 4456 || Bus.available()) { // if we are too late, don't try to participate and retry next round DEBUG_LOG("ARB LATE 0x%02x %lu us\n", Serial.peek(), timeSinceStartBit); @@ -98,7 +98,7 @@ Arbitration::state Arbitration::data(BusState& busstate, uint8_t symbol, unsigne } return arbitrating; case BusState::eReceivedSecondSYN: // did we sign up for second round arbitration? - if (_participateSecond) { + if (_participateSecond && Bus.available() == 0) { // execute second round of arbitration unsigned long microsSinceLastSyn = busstate.microsSinceLastSyn(); #if USE_ASYNCHRONOUS diff --git a/src/bus.cpp b/src/bus.cpp index 7f1ae93..0fc4b0e 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -192,6 +192,14 @@ bool BusType::read(data& d) { #endif } +int BusType::available() { +#if USE_SOFTWARE_SERIAL + return mySerial.available(); +#else + return Serial.available(); +#endif +} + void BusType::push(const data& d){ #if USE_ASYNCHRONOUS xQueueSendToBack(_queue, &d, 0);