Skip to content

Commit be41e6b

Browse files
committed
refactor serial port manager: hand out UARTs FCFS
get rid of particular compile-time designations by UART index. just hand out the next free index of hardware UARTs, or indicate that none is available any more. use names as keys to register and free UARTs.
1 parent 5a1c3af commit be41e6b

10 files changed

+97
-125
lines changed

include/Battery.h

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class BatteryProvider {
1414
virtual void deinit() = 0;
1515
virtual void loop() = 0;
1616
virtual std::shared_ptr<BatteryStats> getStats() const = 0;
17-
virtual int usedHwUart() const { return -1; } // -1 => no HW UART used
1817
};
1918

2019
class BatteryClass {

include/JkBmsController.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
#include "Battery.h"
88
#include "JkBmsSerialMessage.h"
99

10+
//#define JKBMS_DUMMY_SERIAL
11+
1012
class DataPointContainer;
1113

1214
namespace JkBms {
1315

14-
uint8_t constexpr HwSerialPort = ((ARDUINO_USB_CDC_ON_BOOT != 1)?2:0);
15-
1616
class Controller : public BatteryProvider {
1717
public:
1818
Controller() = default;
@@ -21,9 +21,16 @@ class Controller : public BatteryProvider {
2121
void deinit() final;
2222
void loop() final;
2323
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
24-
int usedHwUart() const final { return HwSerialPort; }
2524

2625
private:
26+
static char constexpr _serialPortOwner[] = "JK BMS";
27+
28+
#ifdef JKBMS_DUMMY_SERIAL
29+
std::unique_ptr<DummySerial> _upSerial;
30+
#else
31+
std::unique_ptr<HardwareSerial> _upSerial;
32+
#endif
33+
2734
enum class Status : unsigned {
2835
Initializing,
2936
Timeout,

include/SerialPortManager.h

+9-17
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
#pragma once
33

4-
#include <map>
4+
#include <array>
5+
#include <optional>
6+
#include <string>
57

68
class SerialPortManagerClass {
79
public:
810
void init();
9-
bool allocateMpptPort(uint8_t port);
10-
bool allocateBatteryPort(uint8_t port);
11-
void invalidateBatteryPort();
12-
void invalidateMpptPorts();
1311

14-
private:
15-
enum class Owner {
16-
Console,
17-
Battery,
18-
MPPT
19-
};
20-
21-
std::map<uint8_t, Owner> allocatedPorts;
12+
std::optional<uint8_t> allocatePort(std::string const& owner);
13+
void freePort(std::string const& owner);
2214

23-
bool allocatePort(uint8_t port, Owner owner);
24-
void invalidate(Owner owner);
25-
26-
static const char* print(Owner owner);
15+
private:
16+
// the amount of hardare UARTs available on supported ESP32 chips
17+
static size_t constexpr _num_controllers = 3;
18+
std::array<std::string, _num_controllers> _ports = { "" };
2719
};
2820

2921
extern SerialPortManagerClass SerialPortManager;

include/VictronMppt.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ class VictronMpptClass {
5555
using controller_t = std::unique_ptr<VeDirectMpptController>;
5656
std::vector<controller_t> _controllers;
5757

58+
std::vector<String> _serialPortOwners;
5859
bool initController(int8_t rx, int8_t tx, bool logging,
59-
uint8_t instance, uint8_t hwSerialPort);
60+
uint8_t instance);
6061
};
6162

6263
extern VictronMpptClass VictronMppt;

include/VictronSmartShunt.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
class VictronSmartShunt : public BatteryProvider {
77
public:
88
bool init(bool verboseLogging) final;
9-
void deinit() final { }
9+
void deinit() final;
1010
void loop() final;
1111
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
12-
int usedHwUart() const final { return _hwSerialPort; }
1312

1413
private:
15-
static uint8_t constexpr _hwSerialPort = ((ARDUINO_USB_CDC_ON_BOOT != 1)?2:0);
14+
static char constexpr _serialPortOwner[] = "SmartShunt";
15+
1616
uint32_t _lastUpdate = 0;
1717
std::shared_ptr<VictronSmartShuntStats> _stats =
1818
std::make_shared<VictronSmartShuntStats>();

src/Battery.cpp

+1-14
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include "JkBmsController.h"
66
#include "VictronSmartShunt.h"
77
#include "MqttBattery.h"
8-
#include "SerialPortManager.h"
98

109
BatteryClass Battery;
1110

@@ -39,7 +38,6 @@ void BatteryClass::updateSettings()
3938
_upProvider->deinit();
4039
_upProvider = nullptr;
4140
}
42-
SerialPortManager.invalidateBatteryPort();
4341

4442
CONFIG_T& config = Configuration.get();
4543
if (!config.Battery.Enabled) { return; }
@@ -64,18 +62,7 @@ void BatteryClass::updateSettings()
6462
return;
6563
}
6664

67-
// port is -1 if provider is neither JK BMS nor SmartShunt. otherwise, port
68-
// is 2, unless running on ESP32-S3 with USB CDC, then port is 0.
69-
int port = _upProvider->usedHwUart();
70-
if (port >= 0 && !SerialPortManager.allocateBatteryPort(port)) {
71-
_upProvider = nullptr;
72-
return;
73-
}
74-
75-
if (!_upProvider->init(verboseLogging)) {
76-
SerialPortManager.invalidateBatteryPort();
77-
_upProvider = nullptr;
78-
}
65+
if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; }
7966
}
8067

8168
void BatteryClass::loop()

src/JkBmsController.cpp

+21-14
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
#include "MessageOutput.h"
66
#include "JkBmsDataPoints.h"
77
#include "JkBmsController.h"
8+
#include "SerialPortManager.h"
89
#include <frozen/map.h>
910

1011
namespace JkBms {
1112

12-
//#define JKBMS_DUMMY_SERIAL
13-
1413
#ifdef JKBMS_DUMMY_SERIAL
1514
class DummySerial {
1615
public:
@@ -198,9 +197,6 @@ class DummySerial {
198197
size_t _msg_idx = 0;
199198
size_t _byte_idx = 0;
200199
};
201-
DummySerial HwSerial;
202-
#else
203-
HardwareSerial HwSerial(HwSerialPort);
204200
#endif
205201

206202
bool Controller::init(bool verboseLogging)
@@ -220,9 +216,18 @@ bool Controller::init(bool verboseLogging)
220216
return false;
221217
}
222218

223-
HwSerial.end(); // make sure the UART will be re-initialized
224-
HwSerial.begin(115200, SERIAL_8N1, pin.battery_rx, pin.battery_tx);
225-
HwSerial.flush();
219+
#ifdef JKBMS_DUMMY_SERIAL
220+
_upSerial = std::make_unique<DummySerial>();
221+
#else
222+
auto oHwSerialPort = SerialPortManager.allocatePort(_serialPortOwner);
223+
if (!oHwSerialPort) { return false; }
224+
225+
_upSerial = std::make_unique<HardwareSerial>(*oHwSerialPort);
226+
#endif
227+
228+
_upSerial->end(); // make sure the UART will be re-initialized
229+
_upSerial->begin(115200, SERIAL_8N1, pin.battery_rx, pin.battery_tx);
230+
_upSerial->flush();
226231

227232
if (Interface::Transceiver != getInterface()) { return true; }
228233

@@ -242,10 +247,12 @@ bool Controller::init(bool verboseLogging)
242247

243248
void Controller::deinit()
244249
{
245-
HwSerial.end();
250+
_upSerial->end();
246251

247252
if (_rxEnablePin > 0) { pinMode(_rxEnablePin, INPUT); }
248253
if (_txEnablePin > 0) { pinMode(_txEnablePin, INPUT); }
254+
255+
SerialPortManager.freePort(_serialPortOwner);
249256
}
250257

251258
Controller::Interface Controller::getInterface() const
@@ -296,7 +303,7 @@ void Controller::sendRequest(uint8_t pollInterval)
296303
return announceStatus(Status::WaitingForPollInterval);
297304
}
298305

299-
if (!HwSerial.availableForWrite()) {
306+
if (!_upSerial->availableForWrite()) {
300307
return announceStatus(Status::HwSerialNotAvailableForWrite);
301308
}
302309

@@ -307,10 +314,10 @@ void Controller::sendRequest(uint8_t pollInterval)
307314
digitalWrite(_txEnablePin, HIGH); // enable transmission
308315
}
309316

310-
HwSerial.write(readAll.data(), readAll.size());
317+
_upSerial->write(readAll.data(), readAll.size());
311318

312319
if (Interface::Transceiver == getInterface()) {
313-
HwSerial.flush();
320+
_upSerial->flush();
314321
digitalWrite(_rxEnablePin, LOW); // enable reception
315322
digitalWrite(_txEnablePin, LOW); // disable transmission (free the bus)
316323
}
@@ -326,8 +333,8 @@ void Controller::loop()
326333
CONFIG_T& config = Configuration.get();
327334
uint8_t pollInterval = config.Battery.JkBmsPollingInterval;
328335

329-
while (HwSerial.available()) {
330-
rxData(HwSerial.read());
336+
while (_upSerial->available()) {
337+
rxData(_upSerial->read());
331338
}
332339

333340
sendRequest(pollInterval);

src/SerialPortManager.cpp

+23-56
Original file line numberDiff line numberDiff line change
@@ -2,79 +2,46 @@
22
#include "SerialPortManager.h"
33
#include "MessageOutput.h"
44

5-
#define MAX_CONTROLLERS 3
6-
75
SerialPortManagerClass SerialPortManager;
86

97
void SerialPortManagerClass::init()
108
{
119
if (ARDUINO_USB_CDC_ON_BOOT != 1) {
12-
allocatePort(0, Owner::Console);
10+
_ports[0] = "Serial Console";
11+
MessageOutput.println("[SerialPortManager] HW UART port 0 now in use "
12+
"by 'Serial Console'");
1313
}
1414
}
1515

16-
bool SerialPortManagerClass::allocateBatteryPort(uint8_t port)
16+
std::optional<uint8_t> SerialPortManagerClass::allocatePort(std::string const& owner)
1717
{
18-
return allocatePort(port, Owner::Battery);
19-
}
20-
21-
bool SerialPortManagerClass::allocateMpptPort(uint8_t port)
22-
{
23-
return allocatePort(port, Owner::MPPT);
24-
}
18+
for (size_t i = 0; i < _ports.size(); ++i) {
19+
if (_ports[i] != "") {
20+
MessageOutput.printf("[SerialPortManager] HW UART %d already "
21+
"in use by '%s'\r\n", i, _ports[i].c_str());
22+
continue;
23+
}
2524

26-
bool SerialPortManagerClass::allocatePort(uint8_t port, Owner owner)
27-
{
28-
if (port >= MAX_CONTROLLERS) {
29-
MessageOutput.printf("[SerialPortManager] Invalid serial port: %d\r\n", port);
30-
return false;
31-
}
25+
_ports[i] = owner;
3226

33-
auto res = allocatedPorts.insert({port, owner});
27+
MessageOutput.printf("[SerialPortManager] HW UART %d now in use "
28+
"by '%s'\r\n", i, owner.c_str());
3429

35-
if (!res.second) {
36-
MessageOutput.printf("[SerialPortManager] Cannot assign HW UART "
37-
"port %d to %s: already in use by %s\r\n",
38-
port, print(owner), print(res.first->second));
39-
return false;
30+
return i;
4031
}
4132

42-
MessageOutput.printf("[SerialPortManager] HW UART port %d now in use "
43-
"by %s\r\n", port, print(owner));
44-
return true;
45-
}
46-
47-
void SerialPortManagerClass::invalidateBatteryPort()
48-
{
49-
invalidate(Owner::Battery);
33+
MessageOutput.printf("[SerialPortManager] Cannot assign another HW "
34+
"UART port to '%s'\r\n", owner.c_str());
35+
return std::nullopt;
5036
}
5137

52-
void SerialPortManagerClass::invalidateMpptPorts()
38+
void SerialPortManagerClass::freePort(std::string const& owner)
5339
{
54-
invalidate(Owner::MPPT);
55-
}
40+
for (size_t i = 0; i < _ports.size(); ++i) {
41+
if (_ports[i] != owner) { continue; }
5642

57-
void SerialPortManagerClass::invalidate(Owner owner)
58-
{
59-
for (auto it = allocatedPorts.begin(); it != allocatedPorts.end();) {
60-
if (it->second == owner) {
61-
MessageOutput.printf("[SerialPortManager] Removing port = %d, owner = %s \r\n", it->first, print(owner));
62-
it = allocatedPorts.erase(it);
63-
} else {
64-
++it;
65-
}
66-
}
67-
}
68-
69-
const char* SerialPortManagerClass::print(Owner owner)
70-
{
71-
switch (owner) {
72-
case Owner::Console:
73-
return "Serial Console";
74-
case Owner::Battery:
75-
return "Battery Interface";
76-
case Owner::MPPT:
77-
return "Victron MPPT";
43+
MessageOutput.printf("[SerialPortManager] Freeing HW UART %d, owner "
44+
"was '%s'\r\n", i, owner.c_str());
45+
_ports[i] = "";
7846
}
79-
return "unknown";
8047
}

0 commit comments

Comments
 (0)