Skip to content

Commit

Permalink
Introduced a serial port manager.
Browse files Browse the repository at this point in the history
In order to prevent the battery and the Victron MPPT to use the same hw serial ports, this class keeps track of the used ports and their owners.
  • Loading branch information
Gumbagubanga committed Mar 14, 2024
1 parent 18e3b12 commit 0cbdb44
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 12 deletions.
1 change: 0 additions & 1 deletion include/Battery.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <stdint.h>
#include <memory>
#include <mutex>
#include <TaskSchedulerDeclarations.h>
Expand Down
27 changes: 27 additions & 0 deletions include/SerialPortManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <map>

class SerialPortManager {
public:
bool allocateMpptPort(int port);
bool allocateBatteryPort(int port);
void invalidateBatteryPort();
void invalidateMpptPorts();

private:
enum Owner {
BATTERY,
MPPT
};

std::map<uint8_t, Owner> allocatedPorts;

bool allocatePort(uint8_t port, Owner owner);
void invalidate(Owner owner);

static const char* print(Owner owner);
};

extern SerialPortManager PortManager;
21 changes: 16 additions & 5 deletions src/Battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "JkBmsController.h"
#include "VictronSmartShunt.h"
#include "MqttBattery.h"
#include "SerialPortManager.h"

BatteryClass Battery;

Expand Down Expand Up @@ -38,6 +39,7 @@ void BatteryClass::updateSettings()
_upProvider->deinit();
_upProvider = nullptr;
}
PortManager.invalidateBatteryPort();

CONFIG_T& config = Configuration.get();
if (!config.Battery.Enabled) { return; }
Expand All @@ -47,23 +49,32 @@ void BatteryClass::updateSettings()
switch (config.Battery.Provider) {
case 0:
_upProvider = std::make_unique<PylontechCanReceiver>();
if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; }
break;
case 1:
_upProvider = std::make_unique<JkBms::Controller>();
if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; }
break;
case 2:
_upProvider = std::make_unique<MqttBattery>();
if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; }
break;
case 3:
_upProvider = std::make_unique<VictronSmartShunt>();
if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; }
break;
default:
MessageOutput.printf("Unknown battery provider: %d\r\n", config.Battery.Provider);
break;
return;
}

if(_upProvider->usesHwPort2()) {
if (!PortManager.allocateBatteryPort(2)) {
MessageOutput.printf("[Battery] Serial port %d already in use. Initialization aborted!\r\n", 2);
_upProvider = nullptr;
return;
}
}

if (!_upProvider->init(verboseLogging)) {
PortManager.invalidateBatteryPort();
_upProvider = nullptr;
}
}

Expand Down
59 changes: 59 additions & 0 deletions src/SerialPortManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "SerialPortManager.h"
#include "MessageOutput.h"

#define MAX_CONTROLLERS 3

SerialPortManager PortManager;

bool SerialPortManager::allocateBatteryPort(int port)
{
return allocatePort(port, Owner::BATTERY);
}

bool SerialPortManager::allocateMpptPort(int port)
{
return allocatePort(port, Owner::MPPT);
}

bool SerialPortManager::allocatePort(uint8_t port, Owner owner)
{
if (port > MAX_CONTROLLERS) {
MessageOutput.printf("[SerialPortManager] Invalid serial port = %d \r\n", port);
return false;
}

return allocatedPorts.insert({port, owner}).second;
}

void SerialPortManager::invalidateBatteryPort()
{
invalidate(Owner::BATTERY);
}

void SerialPortManager::invalidateMpptPorts()
{
invalidate(Owner::MPPT);
}

void SerialPortManager::invalidate(Owner owner)
{
for (auto it = allocatedPorts.begin(); it != allocatedPorts.end();) {
if (it->second == owner) {
MessageOutput.printf("[SerialPortManager] Removing port = %d, owner = %s \r\n", it->first, print(owner));
it = allocatedPorts.erase(it);
} else {
++it;
}
}
}

const char* SerialPortManager::print(Owner owner)
{
switch (owner) {
case BATTERY:
return "BATTERY";
case MPPT:
return "MPPT";
}
}
22 changes: 16 additions & 6 deletions src/VictronMppt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "Configuration.h"
#include "PinMapping.h"
#include "MessageOutput.h"
#include "SerialPortManager.h"

VictronMpptClass VictronMppt;

Expand All @@ -21,6 +22,7 @@ void VictronMpptClass::updateSettings()
std::lock_guard<std::mutex> lock(_mutex);

_controllers.clear();
PortManager.invalidateMpptPorts();

CONFIG_T& config = Configuration.get();
if (!config.Vedirect.Enabled) { return; }
Expand All @@ -36,11 +38,18 @@ void VictronMpptClass::updateSettings()
initController(pin.victron_rx2, pin.victron_tx2, config.Vedirect.VerboseLogging, hwSerialPort);
}

bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging, int hwSerialPort) {
MessageOutput.printf("[VictronMppt] rx = %d, tx = %d\r\n", rx, tx);
bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging, int hwSerialPort)
{
MessageOutput.printf("[VictronMppt] rx = %d, tx = %d, hwSerialPort = %d\r\n", rx, tx, hwSerialPort);

if (rx < 0) {
MessageOutput.println("[VictronMppt] invalid pin config");
MessageOutput.printf("[VictronMppt] invalid pin config rx = %d, tx = %d\r\n", rx, tx);
return false;
}

if (!PortManager.allocateMpptPort(hwSerialPort)) {
MessageOutput.printf("[VictronMppt] Serial port %d already in use. Initialization aborted!\r\n",
hwSerialPort);
return false;
}

Expand All @@ -63,7 +72,7 @@ bool VictronMpptClass::isDataValid() const
{
std::lock_guard<std::mutex> lock(_mutex);

for (auto const& upController : _controllers) {
for (auto const& upController: _controllers) {
if (!upController->isDataValid()) { return false; }
}

Expand Down Expand Up @@ -101,12 +110,13 @@ uint32_t VictronMpptClass::getDataAgeMillis() const
return age;
}

std::optional<VeDirectMpptController::spData_t> VictronMpptClass::getData(size_t idx) const {
std::optional<VeDirectMpptController::spData_t> VictronMpptClass::getData(size_t idx) const
{
std::lock_guard<std::mutex> lock(_mutex);

if (_controllers.empty() || idx >= _controllers.size()) {
MessageOutput.printf("ERROR: MPPT controller index %d is out of bounds (%d controllers)\r\n",
idx, _controllers.size());
idx, _controllers.size());
return std::nullopt;
}

Expand Down

0 comments on commit 0cbdb44

Please sign in to comment.