mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 02:39:57 +02:00
Merge branch 'main' into feature/event-CAN-timing
This commit is contained in:
commit
d17e677a90
18 changed files with 171 additions and 66 deletions
|
@ -374,11 +374,19 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
|
|
||||||
system_remaining_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
|
system_remaining_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
|
||||||
|
|
||||||
system_max_charge_power_W = (battery_max_charge_amperage * system_battery_voltage_dV);
|
if ((battery_max_charge_amperage * system_battery_voltage_dV) > 65000) {
|
||||||
|
system_max_charge_power_W = 65000;
|
||||||
|
} else {
|
||||||
|
system_max_charge_power_W = (battery_max_charge_amperage * system_battery_voltage_dV);
|
||||||
|
}
|
||||||
|
|
||||||
system_max_discharge_power_W = (battery_max_discharge_amperage * system_battery_voltage_dV);
|
if ((battery_max_discharge_amperage * system_battery_voltage_dV) > 65000) {
|
||||||
|
system_max_discharge_power_W = 65000;
|
||||||
|
} else {
|
||||||
|
system_max_discharge_power_W = (battery_max_discharge_amperage * system_battery_voltage_dV);
|
||||||
|
}
|
||||||
|
|
||||||
battery_power = (system_battery_current_dA * (system_battery_voltage_dV / 10));
|
battery_power = (system_battery_current_dA * (system_battery_voltage_dV / 100));
|
||||||
|
|
||||||
system_active_power_W = battery_power;
|
system_active_power_W = battery_power;
|
||||||
|
|
||||||
|
@ -429,8 +437,8 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
||||||
battery_awake = true;
|
battery_awake = true;
|
||||||
CANstillAlive = 12; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
|
CANstillAlive = 12; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
|
||||||
battery_current = ((rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) / 10) - 819; //Amps
|
battery_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
|
||||||
battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
|
battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
|
||||||
battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
|
battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
|
||||||
battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
|
battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
|
||||||
battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
|
battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
|
||||||
|
@ -463,7 +471,7 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
|
case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
|
||||||
battery_awake = true;
|
battery_awake = true;
|
||||||
if (calculateCRC(rx_frame, 3, 0x15) != rx_frame.data.u8[0]) {
|
if (calculateCRC(rx_frame, rx_frame.FIR.B.DLC, 0x15) != rx_frame.data.u8[0]) {
|
||||||
//If calculated CRC does not match transmitted CRC, increase CANerror counter
|
//If calculated CRC does not match transmitted CRC, increase CANerror counter
|
||||||
CANerror++;
|
CANerror++;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
This is commit 9ab44bd from https://github.com/ayushsharma82/ElegantOTA
|
|
||||||
|
|
||||||
<p><br/></p>
|
<p><br/></p>
|
||||||
<p align="center"><img src="/docs/feature.png?sanitize=true&raw=true" width="700"></p>
|
<p align="center"><img src="/docs/feature.png?sanitize=true&raw=true" width="700"></p>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"maintainer": true
|
"maintainer": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": ["espressif8266", "espressif32", "raspberrypi"]
|
"platforms": ["espressif8266", "espressif32", "raspberrypi"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name=ElegantOTA
|
name=ElegantOTA
|
||||||
version=3.1.0
|
version=3.1.1
|
||||||
author=Ayush Sharma
|
author=Ayush Sharma
|
||||||
category=Communication
|
category=Communication
|
||||||
maintainer=Ayush Sharma <asrocks5@gmail.com>
|
maintainer=Ayush Sharma <asrocks5@gmail.com>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import requests
|
||||||
import hashlib
|
import hashlib
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
import time
|
import time
|
||||||
|
from requests.auth import HTTPDigestAuth
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -29,7 +30,9 @@ except ImportError:
|
||||||
|
|
||||||
def on_upload(source, target, env):
|
def on_upload(source, target, env):
|
||||||
firmware_path = str(source[0])
|
firmware_path = str(source[0])
|
||||||
upload_url_compatibility = env.GetProjectOption('upload_url')
|
|
||||||
|
auth = None
|
||||||
|
upload_url_compatibility = env.GetProjectOption('custom_upload_url')
|
||||||
upload_url = upload_url_compatibility.replace("/update", "")
|
upload_url = upload_url_compatibility.replace("/update", "")
|
||||||
|
|
||||||
with open(firmware_path, 'rb') as firmware:
|
with open(firmware_path, 'rb') as firmware:
|
||||||
|
@ -50,12 +53,37 @@ def on_upload(source, target, env):
|
||||||
'Referer': f'{upload_url}/update',
|
'Referer': f'{upload_url}/update',
|
||||||
'Connection': 'keep-alive'
|
'Connection': 'keep-alive'
|
||||||
}
|
}
|
||||||
|
|
||||||
start_response = requests.get(start_url, headers=start_headers)
|
|
||||||
|
|
||||||
if start_response.status_code != 200:
|
checkAuthResponse = requests.get(f"{upload_url_compatibility}/update")
|
||||||
print("start-request faild " + str(start_response.status_code))
|
|
||||||
return
|
if checkAuthResponse.status_code == 401:
|
||||||
|
try:
|
||||||
|
username = env.GetProjectOption('custom_username')
|
||||||
|
password = env.GetProjectOption('custom_password')
|
||||||
|
except:
|
||||||
|
username = None
|
||||||
|
password = None
|
||||||
|
print("No authentication values specified.")
|
||||||
|
print('Please, add some Options in your .ini file like: \n\ncustom_username=username\ncustom_password=password\n')
|
||||||
|
if username is None or password is None:
|
||||||
|
print("Authentication required, but no credentials provided.")
|
||||||
|
return
|
||||||
|
print("Serverconfiguration: authentication needed.")
|
||||||
|
auth = HTTPDigestAuth(username, password)
|
||||||
|
doUpdateAuth = requests.get(start_url, headers=start_headers, auth=auth)
|
||||||
|
|
||||||
|
if doUpdateAuth.status_code != 200:
|
||||||
|
print("authentication faild " + str(doUpdateAuth.status_code))
|
||||||
|
return
|
||||||
|
print("Authentication successfull")
|
||||||
|
else:
|
||||||
|
auth = None
|
||||||
|
print("Serverconfiguration: autentication not needed.")
|
||||||
|
doUpdate = requests.get(start_url, headers=start_headers)
|
||||||
|
|
||||||
|
if doUpdate.status_code != 200:
|
||||||
|
print("start-request faild " + str(doUpdate.status_code))
|
||||||
|
return
|
||||||
|
|
||||||
firmware.seek(0)
|
firmware.seek(0)
|
||||||
encoder = MultipartEncoder(fields={
|
encoder = MultipartEncoder(fields={
|
||||||
|
@ -87,7 +115,7 @@ def on_upload(source, target, env):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
response = requests.post(f"{upload_url}/ota/upload", data=monitor, headers=post_headers)
|
response = requests.post(f"{upload_url}/ota/upload", data=monitor, headers=post_headers, auth=auth)
|
||||||
|
|
||||||
bar.close()
|
bar.close()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
@ -100,4 +128,4 @@ def on_upload(source, target, env):
|
||||||
tqdm.write(message)
|
tqdm.write(message)
|
||||||
|
|
||||||
|
|
||||||
env.Replace(UPLOADCMD=on_upload)
|
env.Replace(UPLOADCMD=on_upload)
|
|
@ -226,11 +226,11 @@
|
||||||
#define ARDUINOJSON_BIN2ALPHA_1111() P
|
#define ARDUINOJSON_BIN2ALPHA_1111() P
|
||||||
#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D()
|
#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D()
|
||||||
#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D)
|
#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D)
|
||||||
#define ARDUINOJSON_VERSION "7.0.3"
|
#define ARDUINOJSON_VERSION "7.0.4"
|
||||||
#define ARDUINOJSON_VERSION_MAJOR 7
|
#define ARDUINOJSON_VERSION_MAJOR 7
|
||||||
#define ARDUINOJSON_VERSION_MINOR 0
|
#define ARDUINOJSON_VERSION_MINOR 0
|
||||||
#define ARDUINOJSON_VERSION_REVISION 3
|
#define ARDUINOJSON_VERSION_REVISION 4
|
||||||
#define ARDUINOJSON_VERSION_MACRO V703
|
#define ARDUINOJSON_VERSION_MACRO V704
|
||||||
#ifndef ARDUINOJSON_VERSION_NAMESPACE
|
#ifndef ARDUINOJSON_VERSION_NAMESPACE
|
||||||
# define ARDUINOJSON_VERSION_NAMESPACE \
|
# define ARDUINOJSON_VERSION_NAMESPACE \
|
||||||
ARDUINOJSON_CONCAT4(ARDUINOJSON_VERSION_MACRO, \
|
ARDUINOJSON_CONCAT4(ARDUINOJSON_VERSION_MACRO, \
|
||||||
|
@ -7498,11 +7498,11 @@ ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||||
#define ARDUINOJSON_NAMESPACE _Pragma ("GCC warning \"ARDUINOJSON_NAMESPACE is deprecated, use ArduinoJson instead\"") ArduinoJson
|
#define ARDUINOJSON_NAMESPACE _Pragma ("GCC warning \"ARDUINOJSON_NAMESPACE is deprecated, use ArduinoJson instead\"") ArduinoJson
|
||||||
#define JSON_ARRAY_SIZE(N) _Pragma ("GCC warning \"JSON_ARRAY_SIZE is deprecated, you don't need to compute the size anymore\"") (ArduinoJson::detail::sizeofArray(N))
|
#define JSON_ARRAY_SIZE(N) _Pragma ("GCC warning \"JSON_ARRAY_SIZE is deprecated, you don't need to compute the size anymore\"") (ArduinoJson::detail::sizeofArray(N))
|
||||||
#define JSON_OBJECT_SIZE(N) _Pragma ("GCC warning \"JSON_OBJECT_SIZE is deprecated, you don't need to compute the size anymore\"") (ArduinoJson::detail::sizeofObject(N))
|
#define JSON_OBJECT_SIZE(N) _Pragma ("GCC warning \"JSON_OBJECT_SIZE is deprecated, you don't need to compute the size anymore\"") (ArduinoJson::detail::sizeofObject(N))
|
||||||
#define JSON_STRING_SIZE(N) _Pragma ("GCC warning \"JSON_STRING_SIZE is deprecated, you don't need to compute the size anymore\"") (ArduinoJson::detail::sizeofString(N))
|
#define JSON_STRING_SIZE(N) _Pragma ("GCC warning \"JSON_STRING_SIZE is deprecated, you don't need to compute the size anymore\"") (N+1)
|
||||||
#else
|
#else
|
||||||
#define JSON_ARRAY_SIZE(N) (ArduinoJson::detail::sizeofArray(N))
|
#define JSON_ARRAY_SIZE(N) (ArduinoJson::detail::sizeofArray(N))
|
||||||
#define JSON_OBJECT_SIZE(N) (ArduinoJson::detail::sizeofObject(N))
|
#define JSON_OBJECT_SIZE(N) (ArduinoJson::detail::sizeofObject(N))
|
||||||
#define JSON_STRING_SIZE(N) (ArduinoJson::detail::sizeofString(N))
|
#define JSON_STRING_SIZE(N) (N+1)
|
||||||
#endif
|
#endif
|
||||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
|
|
|
@ -98,7 +98,7 @@ public:
|
||||||
// Return number of coils set to 0 (or OFF)
|
// Return number of coils set to 0 (or OFF)
|
||||||
uint16_t coilsSetOFF() const;
|
uint16_t coilsSetOFF() const;
|
||||||
|
|
||||||
#if !ISLINUX
|
#if !LINUX
|
||||||
// Helper function to dump out coils in logical order
|
// Helper function to dump out coils in logical order
|
||||||
void print(const char *label, Print& s);
|
void print(const char *label, Print& s);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,8 +29,8 @@ public:
|
||||||
ModbusBridge();
|
ModbusBridge();
|
||||||
|
|
||||||
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
||||||
ModbusBridge(HardwareSerial& serial, uint32_t timeout, int rtsPin = -1);
|
ModbusBridge(uint32_t timeout, int rtsPin = -1);
|
||||||
ModbusBridge(HardwareSerial& serial, uint32_t timeout, RTScallback rts);
|
ModbusBridge(uint32_t timeout, RTScallback rts);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~ModbusBridge();
|
~ModbusBridge();
|
||||||
|
@ -85,13 +85,13 @@ ModbusBridge<SERVERCLASS>::ModbusBridge() :
|
||||||
|
|
||||||
// Constructors for RTU variant
|
// Constructors for RTU variant
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
ModbusBridge<SERVERCLASS>::ModbusBridge(HardwareSerial& serial, uint32_t timeout, int rtsPin) :
|
ModbusBridge<SERVERCLASS>::ModbusBridge(uint32_t timeout, int rtsPin) :
|
||||||
SERVERCLASS(serial, timeout, rtsPin) { }
|
SERVERCLASS(timeout, rtsPin) { }
|
||||||
|
|
||||||
// Alternate constructors for RTU variant
|
// Alternate constructors for RTU variant
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
ModbusBridge<SERVERCLASS>::ModbusBridge(HardwareSerial& serial, uint32_t timeout, RTScallback rts) :
|
ModbusBridge<SERVERCLASS>::ModbusBridge(uint32_t timeout, RTScallback rts) :
|
||||||
SERVERCLASS(serial, timeout, rts) { }
|
SERVERCLASS(timeout, rts) { }
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
// Constructor takes Serial reference and optional DE/RE pin
|
// Constructor takes an optional DE/RE pin and queue size
|
||||||
ModbusClientRTU::ModbusClientRTU(int8_t rtsPin, uint16_t queueLimit) :
|
ModbusClientRTU::ModbusClientRTU(int8_t rtsPin, uint16_t queueLimit) :
|
||||||
ModbusClient(),
|
ModbusClient(),
|
||||||
MR_serial(nullptr),
|
MR_serial(nullptr),
|
||||||
|
@ -32,7 +32,7 @@ ModbusClientRTU::ModbusClientRTU(int8_t rtsPin, uint16_t queueLimit) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternative constructor takes Serial reference and RTS callback function
|
// Alternative constructor takes an RTS callback function
|
||||||
ModbusClientRTU::ModbusClientRTU(RTScallback rts, uint16_t queueLimit) :
|
ModbusClientRTU::ModbusClientRTU(RTScallback rts, uint16_t queueLimit) :
|
||||||
ModbusClient(),
|
ModbusClient(),
|
||||||
MR_serial(nullptr),
|
MR_serial(nullptr),
|
||||||
|
@ -54,20 +54,20 @@ ModbusClientRTU::~ModbusClientRTU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin: start worker task - general version
|
// begin: start worker task - general version
|
||||||
void ModbusClientRTU::begin(Stream& serial, uint32_t baudRate, int coreID) {
|
void ModbusClientRTU::begin(Stream& serial, uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
MR_serial = &serial;
|
MR_serial = &serial;
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin: start worker task - HardwareSerial version
|
// begin: start worker task - HardwareSerial version
|
||||||
void ModbusClientRTU::begin(HardwareSerial& serial, int coreID) {
|
void ModbusClientRTU::begin(HardwareSerial& serial, int coreID, uint32_t userInterval) {
|
||||||
MR_serial = &serial;
|
MR_serial = &serial;
|
||||||
uint32_t baudRate = serial.baudRate();
|
uint32_t baudRate = serial.baudRate();
|
||||||
serial.setRxFIFOFull(1);
|
serial.setRxFIFOFull(1);
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID) {
|
void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
// Task already running? End it in case
|
// Task already running? End it in case
|
||||||
end();
|
end();
|
||||||
|
|
||||||
|
@ -77,11 +77,16 @@ void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID) {
|
||||||
// Set minimum interval time
|
// Set minimum interval time
|
||||||
MR_interval = RTUutils::calculateInterval(baudRate);
|
MR_interval = RTUutils::calculateInterval(baudRate);
|
||||||
|
|
||||||
|
// If user defined interval is longer, use that
|
||||||
|
if (MR_interval < userInterval) {
|
||||||
|
MR_interval = userInterval;
|
||||||
|
}
|
||||||
|
|
||||||
// Create unique task name
|
// Create unique task name
|
||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
||||||
// Start task to handle the queue
|
// Start task to handle the queue
|
||||||
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, 4096, this, 6, &worker, coreID >= 0 ? coreID : NULL);
|
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 6, &worker, coreID >= 0 ? coreID : NULL);
|
||||||
|
|
||||||
LOG_D("Client task %d started. Interval=%d\n", (uint32_t)worker, MR_interval);
|
LOG_D("Client task %d started. Interval=%d\n", (uint32_t)worker, MR_interval);
|
||||||
}
|
}
|
||||||
|
@ -102,6 +107,7 @@ void ModbusClientRTU::end() {
|
||||||
// Kill task
|
// Kill task
|
||||||
vTaskDelete(worker);
|
vTaskDelete(worker);
|
||||||
LOG_D("Client task %d killed.\n", (uint32_t)worker);
|
LOG_D("Client task %d killed.\n", (uint32_t)worker);
|
||||||
|
worker = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +146,14 @@ uint32_t ModbusClientRTU::pendingRequests() {
|
||||||
return requests.size();
|
return requests.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove all pending request from queue
|
||||||
|
void ModbusClientRTU::clearQueue()
|
||||||
|
{
|
||||||
|
std::queue<RequestEntry> empty;
|
||||||
|
LOCK_GUARD(lockGuard, qLock);
|
||||||
|
std::swap(requests, empty);
|
||||||
|
}
|
||||||
|
|
||||||
// Base addRequest taking a preformatted data buffer and length as parameters
|
// Base addRequest taking a preformatted data buffer and length as parameters
|
||||||
Error ModbusClientRTU::addRequestM(ModbusMessage msg, uint32_t token) {
|
Error ModbusClientRTU::addRequestM(ModbusMessage msg, uint32_t token) {
|
||||||
Error rc = SUCCESS; // Return value
|
Error rc = SUCCESS; // Return value
|
||||||
|
|
|
@ -21,19 +21,19 @@ using std::queue;
|
||||||
|
|
||||||
class ModbusClientRTU : public ModbusClient {
|
class ModbusClientRTU : public ModbusClient {
|
||||||
public:
|
public:
|
||||||
// Constructor takes Serial reference and optional DE/RE pin and queue limit
|
// Constructor takes an optional DE/RE pin and queue limit
|
||||||
explicit ModbusClientRTU(int8_t rtsPin = -1, uint16_t queueLimit = 100);
|
explicit ModbusClientRTU(int8_t rtsPin = -1, uint16_t queueLimit = 100);
|
||||||
|
|
||||||
// Alternative Constructor takes Serial reference and RTS line toggle callback
|
// Alternative Constructor takes an RTS line toggle callback
|
||||||
explicit ModbusClientRTU(RTScallback rts, uint16_t queueLimit = 100);
|
explicit ModbusClientRTU(RTScallback rts, uint16_t queueLimit = 100);
|
||||||
|
|
||||||
// Destructor: clean up queue, task etc.
|
// Destructor: clean up queue, task etc.
|
||||||
~ModbusClientRTU();
|
~ModbusClientRTU();
|
||||||
|
|
||||||
// begin: start worker task
|
// begin: start worker task
|
||||||
void begin(Stream& serial, uint32_t baudrate, int coreID = -1);
|
void begin(Stream& serial, uint32_t baudrate, int coreID = -1, uint32_t userInterval = 0);
|
||||||
// Special variant for HardwareSerial
|
// Special variant for HardwareSerial
|
||||||
void begin(HardwareSerial& serial, int coreID = -1);
|
void begin(HardwareSerial& serial, int coreID = -1, uint32_t userInterval = 0);
|
||||||
|
|
||||||
// end: stop the worker
|
// end: stop the worker
|
||||||
void end();
|
void end();
|
||||||
|
@ -56,6 +56,9 @@ public:
|
||||||
// Return number of unprocessed requests in queue
|
// Return number of unprocessed requests in queue
|
||||||
uint32_t pendingRequests();
|
uint32_t pendingRequests();
|
||||||
|
|
||||||
|
// Remove all pending request from queue
|
||||||
|
void clearQueue();
|
||||||
|
|
||||||
// addBroadcastMessage: create a fire-and-forget message to all servers on the RTU bus
|
// addBroadcastMessage: create a fire-and-forget message to all servers on the RTU bus
|
||||||
Error addBroadcastMessage(const uint8_t *data, uint8_t len);
|
Error addBroadcastMessage(const uint8_t *data, uint8_t len);
|
||||||
|
|
||||||
|
@ -84,7 +87,7 @@ protected:
|
||||||
ModbusMessage receive(const ModbusMessage request);
|
ModbusMessage receive(const ModbusMessage request);
|
||||||
|
|
||||||
// start background task
|
// start background task
|
||||||
void doBegin(uint32_t baudRate, int coreID);
|
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
||||||
|
|
||||||
void isInstance() { return; } // make class instantiable
|
void isInstance() { return; } // make class instantiable
|
||||||
queue<RequestEntry> requests; // Queue to hold requests to be processed
|
queue<RequestEntry> requests; // Queue to hold requests to be processed
|
||||||
|
|
|
@ -18,30 +18,48 @@ void ModbusServer::registerWorker(uint8_t serverID, uint8_t functionCode, MBSwor
|
||||||
|
|
||||||
// getWorker: if a worker function is registered, return its address, nullptr otherwise
|
// getWorker: if a worker function is registered, return its address, nullptr otherwise
|
||||||
MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
||||||
|
bool serverFound = false;
|
||||||
|
LOG_D("Need worker for %02X-%02X : ", serverID, functionCode);
|
||||||
// Search the FC map associated with the serverID
|
// Search the FC map associated with the serverID
|
||||||
auto svmap = workerMap.find(serverID);
|
auto svmap = workerMap.find(serverID);
|
||||||
// Is there one?
|
// Is there one?
|
||||||
if (svmap != workerMap.end()) {
|
if (svmap != workerMap.end()) {
|
||||||
|
serverFound = true;
|
||||||
|
// No explicit serverID entry found, but we may have one for ANY_SERVER
|
||||||
|
} else {
|
||||||
|
svmap = workerMap.find(ANY_SERVER);
|
||||||
|
if (svmap != workerMap.end()) {
|
||||||
|
serverFound = true;
|
||||||
|
serverID = ANY_SERVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Did we find a serverID?
|
||||||
|
if (serverFound) {
|
||||||
// Yes. Now look for the function code in the inner map
|
// Yes. Now look for the function code in the inner map
|
||||||
|
bool functionCodeFound = false;
|
||||||
auto fcmap = svmap->second.find(functionCode);;
|
auto fcmap = svmap->second.find(functionCode);;
|
||||||
// Found it?
|
// Found it?
|
||||||
if (fcmap != svmap->second.end()) {
|
if (fcmap != svmap->second.end()) {
|
||||||
// Yes. Return the function pointer for it.
|
// Yes. Return the function pointer for it.
|
||||||
LOG_D("Worker found for %02X/%02X\n", serverID, functionCode);
|
functionCodeFound = true;
|
||||||
return fcmap->second;
|
|
||||||
// No, no explicit worker found, but may be there is one for ANY_FUNCTION_CODE?
|
// No, no explicit worker found, but may be there is one for ANY_FUNCTION_CODE?
|
||||||
} else {
|
} else {
|
||||||
fcmap = svmap->second.find(ANY_FUNCTION_CODE);;
|
fcmap = svmap->second.find(ANY_FUNCTION_CODE);;
|
||||||
// Found it?
|
// Found it?
|
||||||
if (fcmap != svmap->second.end()) {
|
if (fcmap != svmap->second.end()) {
|
||||||
// Yes. Return the function pointer for it.
|
// Yes. Return the function pointer for it.
|
||||||
LOG_D("Worker found for %02X/ANY\n", serverID);
|
functionCodeFound = true;
|
||||||
return fcmap->second;
|
functionCode = ANY_FUNCTION_CODE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (functionCodeFound) {
|
||||||
|
// Yes. Return the function pointer for it.
|
||||||
|
LOGRAW_D("Worker found for %02X/%02X\n", serverID, functionCode);
|
||||||
|
return fcmap->second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// No matching function pointer found
|
// No matching function pointer found
|
||||||
LOG_D("No matching worker found\n");
|
LOGRAW_D("No matching worker found\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,16 +86,29 @@ bool ModbusServer::unregisterWorker(uint8_t serverID, uint8_t functionCode) {
|
||||||
return (numEntries ? true : false);
|
return (numEntries ? true : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// isServerFor: if any worker function is registered for the given serverID, return true
|
// isServerFor: if a worker function is registered for the given serverID, return true
|
||||||
bool ModbusServer::isServerFor(uint8_t serverID) {
|
// functionCode defaults to ANY_FUNCTION_CODE and will yield true for any function code,
|
||||||
// Search the FC map for the serverID
|
// including ANY_FUNCTION_CODE :D
|
||||||
auto svmap = workerMap.find(serverID);
|
bool ModbusServer::isServerFor(uint8_t serverID, uint8_t functionCode) {
|
||||||
// Is it there? Then return true
|
// Check if there is a non-nullptr function for the given combination
|
||||||
if (svmap != workerMap.end()) return true;
|
if (getWorker(serverID, functionCode)) {
|
||||||
// No, serverID was not found. Return false
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isServerFor: short version to look up if the server is known at all
|
||||||
|
bool ModbusServer::isServerFor(uint8_t serverID) {
|
||||||
|
// Check if there is a non-nullptr function for the given combination
|
||||||
|
auto svmap = workerMap.find(serverID);
|
||||||
|
// Is there one?
|
||||||
|
if (svmap != workerMap.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// getMessageCount: read number of messages processed
|
// getMessageCount: read number of messages processed
|
||||||
uint32_t ModbusServer::getMessageCount() {
|
uint32_t ModbusServer::getMessageCount() {
|
||||||
return messageCount;
|
return messageCount;
|
||||||
|
@ -104,6 +135,7 @@ ModbusMessage ModbusServer::localRequest(ModbusMessage msg) {
|
||||||
uint8_t functionCode = msg.getFunctionCode();
|
uint8_t functionCode = msg.getFunctionCode();
|
||||||
LOG_D("Local request for %02X/%02X\n", serverID, functionCode);
|
LOG_D("Local request for %02X/%02X\n", serverID, functionCode);
|
||||||
HEXDUMP_V("Request", msg.data(), msg.size());
|
HEXDUMP_V("Request", msg.data(), msg.size());
|
||||||
|
messageCount++;
|
||||||
// Try to get a worker for the request
|
// Try to get a worker for the request
|
||||||
MBSworker worker = getWorker(serverID, functionCode);
|
MBSworker worker = getWorker(serverID, functionCode);
|
||||||
// Did we get one?
|
// Did we get one?
|
||||||
|
@ -129,6 +161,9 @@ ModbusMessage ModbusServer::localRequest(ModbusMessage msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HEXDUMP_V("Response", m.data(), m.size());
|
HEXDUMP_V("Response", m.data(), m.size());
|
||||||
|
if (m.getError() != SUCCESS) {
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
return m;
|
return m;
|
||||||
} else {
|
} else {
|
||||||
LOG_D("No worker found. Error response.\n");
|
LOG_D("No worker found. Error response.\n");
|
||||||
|
@ -140,11 +175,13 @@ ModbusMessage ModbusServer::localRequest(ModbusMessage msg) {
|
||||||
// No. Respond with "Invalid server ID"
|
// No. Respond with "Invalid server ID"
|
||||||
m.setError(serverID, functionCode, INVALID_SERVER);
|
m.setError(serverID, functionCode, INVALID_SERVER);
|
||||||
}
|
}
|
||||||
|
errorCount++;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
// We should never get here...
|
// We should never get here...
|
||||||
LOG_C("Internal problem: should not get here!\n");
|
LOG_C("Internal problem: should not get here!\n");
|
||||||
m.setError(serverID, functionCode, UNDEFINED_ERROR);
|
m.setError(serverID, functionCode, UNDEFINED_ERROR);
|
||||||
|
errorCount++;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,10 @@ public:
|
||||||
// Returns true if the worker was found and removed
|
// Returns true if the worker was found and removed
|
||||||
bool unregisterWorker(uint8_t serverID, uint8_t functionCode = 0);
|
bool unregisterWorker(uint8_t serverID, uint8_t functionCode = 0);
|
||||||
|
|
||||||
// isServerFor: if any worker function is registered for the given serverID, return true
|
// isServerFor: if a worker function is registered for the given serverID, return true
|
||||||
|
bool isServerFor(uint8_t serverID, uint8_t functionCode);
|
||||||
|
|
||||||
|
// isServerFor: short version to look up if the server is known at all
|
||||||
bool isServerFor(uint8_t serverID);
|
bool isServerFor(uint8_t serverID);
|
||||||
|
|
||||||
// getMessageCount: read number of messages processed
|
// getMessageCount: read number of messages processed
|
||||||
|
|
|
@ -64,32 +64,37 @@ ModbusServerRTU::~ModbusServerRTU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// start: create task with RTU server - general version
|
// start: create task with RTU server - general version
|
||||||
void ModbusServerRTU::begin(Stream& serial, uint32_t baudRate, int coreID) {
|
void ModbusServerRTU::begin(Stream& serial, uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
MSRserial = &serial;
|
MSRserial = &serial;
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start: create task with RTU server - HardwareSerial versions
|
// start: create task with RTU server - HardwareSerial versions
|
||||||
void ModbusServerRTU::begin(HardwareSerial& serial, int coreID) {
|
void ModbusServerRTU::begin(HardwareSerial& serial, int coreID, uint32_t userInterval) {
|
||||||
MSRserial = &serial;
|
MSRserial = &serial;
|
||||||
uint32_t baudRate = serial.baudRate();
|
uint32_t baudRate = serial.baudRate();
|
||||||
serial.setRxFIFOFull(1);
|
serial.setRxFIFOFull(1);
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID) {
|
void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
// Task already running? Stop it in case.
|
// Task already running? Stop it in case.
|
||||||
end();
|
end();
|
||||||
|
|
||||||
// Set minimum interval time
|
// Set minimum interval time
|
||||||
MSRinterval = RTUutils::calculateInterval(baudRate);
|
MSRinterval = RTUutils::calculateInterval(baudRate);
|
||||||
|
|
||||||
|
// If user defined interval is longer, use that
|
||||||
|
if (MSRinterval < userInterval) {
|
||||||
|
MSRinterval = userInterval;
|
||||||
|
}
|
||||||
|
|
||||||
// Create unique task name
|
// Create unique task name
|
||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
||||||
|
|
||||||
// Start task to handle the client
|
// Start task to handle the client
|
||||||
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, 4096, this, 8, &serverTask, coreID >= 0 ? coreID : NULL);
|
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : NULL);
|
||||||
|
|
||||||
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
|
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
|
||||||
}
|
}
|
||||||
|
@ -180,10 +185,12 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
|
||||||
}
|
}
|
||||||
// Is it a broadcast?
|
// Is it a broadcast?
|
||||||
if (request[0] == 0) {
|
if (request[0] == 0) {
|
||||||
|
LOG_D("Broadcast!\n");
|
||||||
// Yes. Do we have a listener?
|
// Yes. Do we have a listener?
|
||||||
if (myServer->listener) {
|
if (myServer->listener) {
|
||||||
// Yes. call it
|
// Yes. call it
|
||||||
myServer->listener(request);
|
myServer->listener(request);
|
||||||
|
LOG_D("Broadcast served.\n");
|
||||||
}
|
}
|
||||||
// else we simply ignore it
|
// else we simply ignore it
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -32,8 +32,8 @@ public:
|
||||||
~ModbusServerRTU();
|
~ModbusServerRTU();
|
||||||
|
|
||||||
// begin: create task with RTU server to accept requests
|
// begin: create task with RTU server to accept requests
|
||||||
void begin(Stream& serial, uint32_t baudRate, int coreID = -1);
|
void begin(Stream& serial, uint32_t baudRate, int coreID = -1, uint32_t userInterval = 0);
|
||||||
void begin(HardwareSerial& serial, int coreID = -1);
|
void begin(HardwareSerial& serial, int coreID = -1, uint32_t userInterval = 0);
|
||||||
|
|
||||||
// end: kill server task
|
// end: kill server task
|
||||||
void end();
|
void end();
|
||||||
|
@ -64,7 +64,7 @@ protected:
|
||||||
inline void isInstance() { } // Make class instantiable
|
inline void isInstance() { } // Make class instantiable
|
||||||
|
|
||||||
// internal common begin function
|
// internal common begin function
|
||||||
void doBegin(uint32_t baudRate, int coreID);
|
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
||||||
|
|
||||||
static uint8_t instanceCounter; // Number of RTU servers created (for task names)
|
static uint8_t instanceCounter; // Number of RTU servers created (for task names)
|
||||||
TaskHandle_t serverTask; // task of the started server
|
TaskHandle_t serverTask; // task of the started server
|
||||||
|
|
|
@ -50,7 +50,7 @@ FCType FCT::getType(uint8_t functionCode) {
|
||||||
return table[functionCode & 0x7F];
|
return table[functionCode & 0x7F];
|
||||||
}
|
}
|
||||||
|
|
||||||
// setType: change the type of a function code.
|
// redefineType: change the type of a function code.
|
||||||
// This is possible only for the codes undefined yet and will return
|
// This is possible only for the codes undefined yet and will return
|
||||||
// the effective type
|
// the effective type
|
||||||
FCType FCT::redefineType(uint8_t functionCode, const FCType type) {
|
FCType FCT::redefineType(uint8_t functionCode, const FCType type) {
|
||||||
|
|
|
@ -84,6 +84,9 @@ enum Error : uint8_t {
|
||||||
UNDEFINED_ERROR = 0xFF // otherwise uncovered communication error
|
UNDEFINED_ERROR = 0xFF // otherwise uncovered communication error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Readable expression for the "illegal" server ID of 0
|
||||||
|
#define ANY_SERVER 0x00
|
||||||
|
|
||||||
#ifndef MINIMAL
|
#ifndef MINIMAL
|
||||||
|
|
||||||
// Constants for float and double re-ordering
|
// Constants for float and double re-ordering
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
|
#if HAS_FREERTOS
|
||||||
// calcCRC: calculate Modbus CRC16 on a given array of bytes
|
// calcCRC: calculate Modbus CRC16 on a given array of bytes
|
||||||
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
|
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
|
||||||
// CRC16 pre-calculated tables
|
// CRC16 pre-calculated tables
|
||||||
|
@ -463,3 +464,4 @@ const char RTUutils::ASCIIread[] = {
|
||||||
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||||
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
|
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#define HAS_ETHERNET 1
|
#define HAS_ETHERNET 1
|
||||||
#define IS_LINUX 0
|
#define IS_LINUX 0
|
||||||
#define NEED_UART_PATCH 1
|
#define NEED_UART_PATCH 1
|
||||||
|
const unsigned int SERVER_TASK_STACK = 4096;
|
||||||
|
const unsigned int CLIENT_TASK_STACK = 4096;
|
||||||
|
|
||||||
/* === ESP8266 DEFINITIONS AND MACROS === */
|
/* === ESP8266 DEFINITIONS AND MACROS === */
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue