Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the complete ebus arbitration protocol #40

Merged
Merged
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
abd5817
Support the complete ebus arbitration protocol
guido4096 Apr 13, 2023
230261e
ebusd expects the starting SYN
guido4096 Apr 14, 2023
e2493bb
Fix priority class checking
guido4096 Apr 15, 2023
f3a17e1
Minor formatting of output string
guido4096 Apr 15, 2023
22fb1be
Analyze bus before allowing arbitration
guido4096 Apr 15, 2023
e911d6b
Change multiple if's in a switch statement
guido4096 Apr 15, 2023
d4b9981
Fix compile warnings
guido4096 Apr 15, 2023
887d188
Check on timing requirements for start of arbitration
guido4096 Apr 16, 2023
d72f355
Merge branch 'setRxFIFOFull' into support-complete-arbitration
guido4096 Apr 16, 2023
c8a29eb
Merge branch 'improve-max-loop-duration' into support-complete-arbitr…
guido4096 Apr 16, 2023
e90b90f
Cleanup error handling
guido4096 Apr 16, 2023
b7cf2a8
Merge branch 'danielkucera:master' into support-complete-arbitration
guido4096 Apr 17, 2023
30b0d7e
start conversion to interrupt driven
guido4096 Apr 18, 2023
3234d83
no good yet
guido4096 Apr 18, 2023
40bee27
allow async reception og serial data
guido4096 Apr 19, 2023
fa9666f
minor code cleanup
guido4096 Apr 19, 2023
f4fdd33
fixes
guido4096 Apr 19, 2023
1983ea2
cosmetics
guido4096 Apr 19, 2023
ed54ca6
only support async on esp32
guido4096 Apr 19, 2023
fa5bcf5
status on async mode or not
guido4096 Apr 19, 2023
d673ba1
comments
guido4096 Apr 19, 2023
07efbff
code cleanup
guido4096 Apr 19, 2023
7f3e9fb
comments
guido4096 Apr 19, 2023
97399fd
add locking of variables shared between threads
guido4096 Apr 20, 2023
17e81d9
logging, locking, arbitration start improvements
guido4096 Apr 20, 2023
6879ab6
logging
guido4096 Apr 20, 2023
a6e0313
logging
guido4096 Apr 20, 2023
cc171ae
disable logging
guido4096 Apr 20, 2023
13f545c
code cleanup
guido4096 Apr 20, 2023
31a4cd7
comments
guido4096 Apr 20, 2023
a22d92e
comments
guido4096 Apr 20, 2023
e658cf6
code feedback, rename ebusstate to busstate
guido4096 Apr 21, 2023
815d030
missed changing and #ifdef to #if
guido4096 Apr 21, 2023
d44c0c7
added last_comms = milis()
guido4096 Apr 21, 2023
0ae7a24
retry arbitration when SYN received
guido4096 Apr 21, 2023
b8c34ca
logging, more precise restart
guido4096 Apr 21, 2023
8577db4
comment on restart conditions
guido4096 Apr 21, 2023
85f1132
replace Serial with Bus
guido4096 Apr 21, 2023
8e45cea
missed baudrate
guido4096 Apr 21, 2023
c6056fb
cosmetics
guido4096 Apr 21, 2023
933cfb4
cosmetics
guido4096 Apr 21, 2023
bbafa9f
cosmetics
guido4096 Apr 21, 2023
804510e
arbitration start no longer needed, no while loop
guido4096 Apr 21, 2023
743abfa
report statistics on the arbitration process
guido4096 Apr 22, 2023
d8fb70f
improve arbitration timing
guido4096 Apr 22, 2023
36c0896
more statistics, more timing tweaking
guido4096 Apr 22, 2023
2f4c052
reduce amount of errors in favour of restarts
guido4096 Apr 22, 2023
d62c976
improve timing, 1% restarts instead of 5%
guido4096 Apr 22, 2023
01e18b8
reduced restarts/errors to 0.5% to 1.0 %
guido4096 Apr 22, 2023
7d48b4a
SoftwareSerial for accurate arbitration timing
guido4096 Apr 23, 2023
c76ff74
use unsigned long consistently
guido4096 Apr 24, 2023
d73c9ed
cleanup comments
guido4096 Apr 24, 2023
019f801
atomic counters
guido4096 Apr 24, 2023
70baf60
atomic on shared variables
guido4096 Apr 24, 2023
5e46429
improve timing of start bit using SoftwareSerial
guido4096 Apr 25, 2023
d95cec1
fix atomic link error on esp12e
guido4096 Apr 25, 2023
ac20515
improve comments
guido4096 Apr 25, 2023
d25603c
react faster if a byte is available while checking
guido4096 Apr 26, 2023
49707fd
comments on vTaskDelay duration
guido4096 Apr 26, 2023
97b34b9
instable when checking available() too much
guido4096 Apr 26, 2023
c33571a
Fix OTA update fails with running background tasks
guido4096 Apr 27, 2023
049aba9
avoid confusion int / unsigned long
guido4096 Apr 27, 2023
53deacb
more statistics, minor code cleanup
guido4096 Apr 27, 2023
371aa4d
Support SoftwareSerial for esp8266
guido4096 Apr 27, 2023
7dac398
finetune timing and waiting algorithm
guido4096 Apr 28, 2023
7dde116
improve comments
guido4096 Apr 28, 2023
af08afc
avoid read buffer overflow
guido4096 Apr 28, 2023
aedf087
use %lu for unsigned long
guido4096 Apr 28, 2023
de7bde0
fix disable and reset pins
danielkucera Apr 15, 2023
79102a4
RX and TX pin as defines
guido4096 May 10, 2023
117e467
avoid running out of heap on ESP8266
guido4096 May 10, 2023
8ce3fe7
fixup merge error with DTX_DISABLE_PIN
guido4096 May 10, 2023
f75ab12
Merge branch 'danielkucera:master' into support-complete-arbitration
guido4096 May 10, 2023
6d29ef5
Add method "available" to BusType
guido4096 Aug 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
54 changes: 54 additions & 0 deletions include/arbitration.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef _ARBITRATION_H_
#define _ARBITRATION_H_

#include "busstate.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:
enum state {none, // no arbitration ongoing
arbitrating, // arbitration ongoing
won1, // won
won2, // won
lost1, // lost
lost2, // lost
error, // error
restart1, // restart the arbitration
restart2, // restart the arbitration
};

Arbitration()
: _arbitrating(false)
, _participateSecond(false)
, _arbitrationAddress(0)
, _restartCount(0)
{}
// Try to start arbitration for the specified master.
// Return values:
// - 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:
// - see description of state enum value
Arbitration::state data (BusState& busstate, uint8_t symbol, unsigned long startBitTime);

private:
bool _arbitrating;
bool _participateSecond;
uint8_t _arbitrationAddress;
int _restartCount;
};

#endif
79 changes: 79 additions & 0 deletions include/bus.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#ifndef _BUS_H_
#define _BUS_H_
#include "main.hpp"
#include "busstate.hpp"
#include "arbitration.hpp"
#include "queue"

#ifdef ESP32
#include "atomic"
#define ATOMIC_INT std::atomic<int>
#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
// be done with the returned data. This object hides if the
// underlying implementation is synchronous or asynchronous
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
WiFiClient* _logtoclient; // the client that needs to log
};
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 symbol);
int availableForWrite();

// 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;
ATOMIC_INT _nbrLate;
private:
inline void push (const data& d);
void receive (uint8_t symbol, unsigned long startBitTime);
BusState _busState;
Arbitration _arbitration;
WiFiClient* _client;

#if USE_ASYNCHRONOUS
// 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;

// task to read bytes form the serial object and process them with receive methods
TaskHandle_t _serialEventTask;

static void readDataFromSoftwareSerial(void *args);
#else
std::queue<data> _queue;
#endif
};

extern BusType Bus;

#endif
127 changes: 127 additions & 0 deletions include/busstate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#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 BusState {
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
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",
"eStartupFirstSyn",
"eStartupSymbolAfterFirstSyn",
"eStartupSecondSyn",
"eReceivedFirstSYN",
"eReceivedAddressAfterFirstSYN",
"eReceivedSecondSYN",
"eReceivedAddressAfterSecondSYN",
"eBusy"
};
return values[e];
}
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)
{
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;
_symbol = 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;
_symbol = 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)
{
_previousSYNtime = _SYNtime;
_SYNtime = micros();
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;
}

void reset()
{
_state = eStartup;
}

unsigned long microsSinceLastSyn()
{
return micros() - _SYNtime;
}

unsigned long microsSincePreviousSyn()
{
return micros() - _previousSYNtime;
}

eState _state;
eState _previousState;
uint8_t _master;
uint8_t _symbol;
unsigned long _SYNtime;
unsigned long _previousSYNtime;
};
#endif
37 changes: 37 additions & 0 deletions include/enhanced.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef _ENHANCED_H_
#define _ENHANCED_H_
#include <WiFiClient.h>

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* enhArbitrationRequested(uint8_t& arbitration_client);

int pushEnhClient(WiFiClient* client, uint8_t c, uint8_t d, bool log);
void handleEnhClient(WiFiClient* client);

#endif
27 changes: 21 additions & 6 deletions include/main.hpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
#ifndef _MAIN_HPP_
#define _MAIN_HPP_

#include <WiFiClient.h>
#include <WiFiServer.h>

#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"
#define RESET_MS 1000

#ifdef ESP32
// https://esp32.com/viewtopic.php?t=19788
#define AVAILABLE_THRESHOLD 0
#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
#endif

inline int DEBUG_LOG(const char *format, ...) { return 0;}
int DEBUG_LOG_IMPL(const char *format, ...);
//#define DEBUG_LOG DEBUG_LOG_IMPL
guido4096 marked this conversation as resolved.
Show resolved Hide resolved

bool handleNewClient(WiFiServer &server, WiFiClient clients[]);
int pushClient(WiFiClient* client, uint8_t B);
int pushClient(WiFiClient* client, uint8_t B);
void handleClient(WiFiClient* client);

int pushEnhClient(WiFiClient* client, uint8_t B);
void handleEnhClient(WiFiClient* client);

#endif
guido4096 marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 5 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,6 +49,9 @@ 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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@guido4096 , did you consider creating pull request to the original library?


[env:esp32-c3-ota]
extends = env:esp32-c3
Expand All @@ -57,4 +61,4 @@ upload_protocol = espota
[env:esp32-c3-ota-vpn]
extends = env:esp32-c3-ota
upload_protocol = custom
upload_command = scp $SOURCE [email protected]:firmware.bin && ssh [email protected] espota.py -i esp-ebus.local -p 3232 -f firmware.bin -d -r
upload_command = scp $SOURCE [email protected]:firmware.bin && ssh [email protected] espota.py -i esp-ebus.local -p 3232 -f firmware.bin -d -r
Loading