mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 19:26:37 +00:00
6.2.1.16 Add TasmotaModbus lib
6.2.1.16 20181015 * Add TasmotaModbus library for very basic modbus wrapper for TasmotaSerial * Change xsns_17_senseair.ino to use TasmotaModbus library * Fix xnrg_05_pzem2.ino for PZEM-014/016 support using TasmotaModbus library (#3694)
This commit is contained in:
parent
6b660026cd
commit
fb6cc194a9
7
lib/TasmotaModbus-1.0.0/README.md
Normal file
7
lib/TasmotaModbus-1.0.0/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# TasmotaSerial
|
||||
|
||||
Implementation of software serial with hardware serial fallback library for the ESP8266
|
||||
|
||||
Allows for several instances to be active at the same time.
|
||||
|
||||
Please note that due to the fact that the ESP always have other activities ongoing, there will be some inexactness in interrupt timings. This may lead to bit errors when having heavy data traffic.
|
31
lib/TasmotaModbus-1.0.0/examples/modbustest/modbustest.ino
Normal file
31
lib/TasmotaModbus-1.0.0/examples/modbustest/modbustest.ino
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
#include <TasmotaModbus.h>
|
||||
|
||||
TasmotaModbus Modbus(14, 12);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Modbus.Begin(9600);
|
||||
|
||||
Serial.println("\nTasmotaModbus test started");
|
||||
|
||||
Modbus.Send(0x01, 0x04, 0, 8);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (Modbus.ReceiveReady()) {
|
||||
uint8_t buffer[26];
|
||||
|
||||
uint8_t error = Modbus.ReceiveBuffer(buffer, 8);
|
||||
if (error) {
|
||||
Serial.print("Modbus response error ");
|
||||
Serial.println(error);
|
||||
} else {
|
||||
Serial.print("Modbus received:");
|
||||
for (int i = 0; i < (buffer[2]) ? buffer[2] +5 : sizeof(buffer); i++) {
|
||||
Serial.print(" ");
|
||||
Serial.print(buffer[i], HEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
lib/TasmotaModbus-1.0.0/keywords.txt
Normal file
26
lib/TasmotaModbus-1.0.0/keywords.txt
Normal file
@ -0,0 +1,26 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map for TasmotaModbus
|
||||
# (esp8266)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
TasmotaModbus KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
Begin KEYWORD2
|
||||
Send KEYWORD2
|
||||
ReceiveReady KEYWORD2
|
||||
ReceiveBuffer KEYWORD2
|
||||
Receive16BitRegister KEYWORD2
|
||||
Receive32BitRegister KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
15
lib/TasmotaModbus-1.0.0/library.json
Normal file
15
lib/TasmotaModbus-1.0.0/library.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "TasmotaModbus",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"serial", "io", "TasmotaModbus"
|
||||
],
|
||||
"description": "Basic modbus wrapper for TasmotaSerial for ESP8266.",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/arendst/Sonoff-Tasmota/lib/TasmotaModbus"
|
||||
},
|
||||
"frameworks": "arduino",
|
||||
"platforms": "espressif8266"
|
||||
}
|
9
lib/TasmotaModbus-1.0.0/library.properties
Normal file
9
lib/TasmotaModbus-1.0.0/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=TasmotaModbus
|
||||
version=1.0.0
|
||||
author=Theo Arends
|
||||
maintainer=Theo Arends <theo@arends.com>
|
||||
sentence=Basic modbus wrapper for TasmotaSerial for ESP8266.
|
||||
paragraph=
|
||||
category=Signal Input/Output
|
||||
url=
|
||||
architectures=esp8266
|
141
lib/TasmotaModbus-1.0.0/src/TasmotaModbus.cpp
Normal file
141
lib/TasmotaModbus-1.0.0/src/TasmotaModbus.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
TasmotaModbus.cpp - Basic modbus wrapper for TasmotaSerial for Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends
|
||||
|
||||
This library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TasmotaModbus.h"
|
||||
|
||||
TasmotaModbus::TasmotaModbus(int receive_pin, int transmit_pin) : TasmotaSerial(receive_pin, transmit_pin, 1)
|
||||
{
|
||||
}
|
||||
|
||||
TasmotaModbus::~TasmotaModbus()
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t CalculateCRC(uint8_t *frame, uint8_t num)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
uint16_t flag;
|
||||
|
||||
for (uint8_t i = 0; i < num; i++) {
|
||||
crc ^= frame[i];
|
||||
for (uint8_t j = 8; j; j--) {
|
||||
if ((crc & 0x0001) != 0) { // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
} else { // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
int TasmotaModbus::Begin(long speed)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (begin(speed)) {
|
||||
result = 1;
|
||||
if (hardwareSerial()) { result = 2; }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint16_t start_address, uint16_t register_count)
|
||||
{
|
||||
uint8_t frame[8];
|
||||
|
||||
frame[0] = device_address; // 0xFE default device address or dedicated like 0x01
|
||||
frame[1] = function_code;
|
||||
frame[2] = (uint8_t)(start_address >> 8);
|
||||
frame[3] = (uint8_t)(start_address);
|
||||
frame[4] = (uint8_t)(register_count >> 8);
|
||||
frame[5] = (uint8_t)(register_count);
|
||||
uint16_t crc = CalculateCRC(frame, 6);
|
||||
frame[6] = (uint8_t)(crc);
|
||||
frame[7] = (uint8_t)(crc >> 8);
|
||||
|
||||
flush();
|
||||
write(frame, sizeof(frame));
|
||||
}
|
||||
|
||||
bool TasmotaModbus::ReceiveReady()
|
||||
{
|
||||
return (available() > 4);
|
||||
}
|
||||
|
||||
uint8_t TasmotaModbus::ReceiveBuffer(uint8_t *buffer, uint8_t register_count)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
uint32_t last = millis();
|
||||
while ((available() > 0) && (len < (register_count *2) + 5) && (millis() - last < 10)) {
|
||||
buffer[len++] = (uint8_t)read();
|
||||
if (3 == len) {
|
||||
if (buffer[1] & 0x80) { // fe 84 02 f2 f1
|
||||
return buffer[2]; // 1 = Illegal Function, 2 = Illegal Address, 3 = Illegal Data, 4 = Slave Error
|
||||
}
|
||||
}
|
||||
last = millis();
|
||||
}
|
||||
|
||||
if (len < 7) { return 7; } // 7 = Not enough data
|
||||
if (len != buffer[2] + 5) { return 8; } // 8 = Unexpected result
|
||||
|
||||
uint16_t crc = (buffer[len -1] << 8) | buffer[len -2];
|
||||
if (CalculateCRC(buffer, len -2) != crc) { return 9; } // 9 = crc error
|
||||
|
||||
return 0; // 0 = No error
|
||||
}
|
||||
|
||||
uint8_t TasmotaModbus::Receive16BitRegister(uint16_t *value)
|
||||
{
|
||||
// 0 1 2 3 4 5 6
|
||||
// 01 04 02 43 21 HH LL
|
||||
// Id Cc Sz Regis Crc--
|
||||
|
||||
uint8_t buffer[7];
|
||||
|
||||
uint8_t error = ReceiveBuffer(buffer, 1); // 1 x 16bit register
|
||||
if (!error) {
|
||||
*value = (buffer[3] << 8) | buffer[4];
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t TasmotaModbus::Receive32BitRegister(float *value)
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
// 01 04 04 87 65 43 21 HH LL
|
||||
// Id Cc Sz Register--- Crc--
|
||||
|
||||
uint8_t buffer[9];
|
||||
|
||||
*value = NAN;
|
||||
|
||||
uint8_t error = ReceiveBuffer(buffer, 2); // 1 x 32bit register
|
||||
if (!error) {
|
||||
((uint8_t*)value)[3] = buffer[3];
|
||||
((uint8_t*)value)[2] = buffer[4];
|
||||
((uint8_t*)value)[1] = buffer[5];
|
||||
((uint8_t*)value)[0] = buffer[6];
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
54
lib/TasmotaModbus-1.0.0/src/TasmotaModbus.h
Normal file
54
lib/TasmotaModbus-1.0.0/src/TasmotaModbus.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
TasmotaModbus.h - Basic modbus wrapper for TasmotaSerial for Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends
|
||||
|
||||
This library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TasmotaModbus_h
|
||||
#define TasmotaModbus_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
#define TM_MODBUS_BAUDRATE 9600 // Default baudrate
|
||||
|
||||
class TasmotaModbus : public TasmotaSerial {
|
||||
public:
|
||||
TasmotaModbus(int receive_pin, int transmit_pin);
|
||||
~TasmotaModbus();
|
||||
|
||||
int Begin(long speed = TM_MODBUS_BAUDRATE);
|
||||
|
||||
void Send(uint8_t device_address, uint8_t function_code, uint16_t start_address, uint16_t register_count);
|
||||
|
||||
bool ReceiveReady();
|
||||
|
||||
/* Return codes:
|
||||
* 0 - No error
|
||||
* 1 - Illegal function
|
||||
* 2 - Illegal address
|
||||
* 3 - Illegal data
|
||||
* 4 - Slave error
|
||||
* 7 - Not enough minimal data received
|
||||
* 8 - Not enough data receieved
|
||||
* 9 - Crc error
|
||||
*/
|
||||
uint8_t ReceiveBuffer(uint8_t *buffer, uint8_t register_count);
|
||||
uint8_t Receive16BitRegister(uint16_t *value);
|
||||
uint8_t Receive32BitRegister(float *value);
|
||||
};
|
||||
|
||||
#endif // TasmotaModbus_h
|
@ -1,4 +1,9 @@
|
||||
/* 6.2.1.15 20181012
|
||||
/* 6.2.1.16 20181015
|
||||
* Add TasmotaModbus library for very basic modbus wrapper for TasmotaSerial
|
||||
* Change xsns_17_senseair.ino to use TasmotaModbus library
|
||||
* Fix xnrg_05_pzem2.ino for PZEM-014/016 support using TasmotaModbus library (#3694)
|
||||
*
|
||||
* 6.2.1.15 20181012
|
||||
* Fix Color Temperature slider functionality regression from 6.2.1.5 (#4037)
|
||||
* Add auto reload of main web page to some web restarts
|
||||
* Add whitespace removal from RfRaw and SerialSend5 (#4020)
|
||||
|
@ -20,7 +20,7 @@
|
||||
#ifndef _SONOFF_VERSION_H_
|
||||
#define _SONOFF_VERSION_H_
|
||||
|
||||
#define VERSION 0x0602010F
|
||||
#define VERSION 0x06020110
|
||||
|
||||
#define D_PROGRAMNAME "Sonoff-Tasmota"
|
||||
#define D_AUTHOR "Theo Arends"
|
||||
|
@ -34,108 +34,40 @@
|
||||
|
||||
#define XNRG_05 5
|
||||
|
||||
#define PZEM2_TYPES_003_017 8 // Result 16 bit register count
|
||||
#define PZEM2_TYPES_014_016 10 // Result 16 bit register count
|
||||
#define PZEM2_MODBUS_SPEED 9600
|
||||
#define PZEM2_DEVICE_ADDRESS 0x01 // PZEM default address
|
||||
#define PZEM2_READ_RESULT 0x04 // Command Read result
|
||||
|
||||
#define PZEM2_READ_RESULT 0x04
|
||||
#define PZEM2_TYPES_003_017 8 // Result 8 x 16 bit register count
|
||||
#define PZEM2_TYPES_014_016 10 // Result 10 x 16 bit register count
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
TasmotaSerial *Pzem2Serial;
|
||||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *Pzem2Modbus;
|
||||
|
||||
uint8_t pzem2_type = PZEM2_TYPES_014_016;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
uint16_t Pzem2ModbusCalculateCRC(uint8_t *frame, uint8_t num)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
uint16_t flag;
|
||||
|
||||
for (uint8_t i = 0; i < num; i++) {
|
||||
crc ^= frame[i];
|
||||
for (uint8_t j = 8; j; j--) {
|
||||
if ((crc & 0x0001) != 0) { // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
} else { // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void Pzem2ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count)
|
||||
{
|
||||
uint8_t frame[8];
|
||||
|
||||
frame[0] = 0xFE; // Any Address
|
||||
frame[1] = function_code;
|
||||
frame[2] = (uint8_t)(start_address >> 8);
|
||||
frame[3] = (uint8_t)(start_address);
|
||||
frame[4] = (uint8_t)(register_count >> 8);
|
||||
frame[5] = (uint8_t)(register_count);
|
||||
uint16_t crc = Pzem2ModbusCalculateCRC(frame, 6);
|
||||
frame[6] = (uint8_t)((crc >> 8) & 0xFF);
|
||||
frame[7] = (uint8_t)(crc & 0xFF);
|
||||
|
||||
Pzem2Serial->flush();
|
||||
Pzem2Serial->write(frame, sizeof(frame));
|
||||
}
|
||||
|
||||
bool Pzem2ModbusReceiveReady()
|
||||
{
|
||||
return (Pzem2Serial->available() >= 5); // 5 - Error frame, 21 or 25 - Ok frame
|
||||
}
|
||||
|
||||
uint8_t Pzem2ModbusReceive(uint8_t *buffer, uint8_t register_count)
|
||||
{
|
||||
// 0 1 2 3 4 5 6
|
||||
// FE 04 02 08 98 HH LL
|
||||
// Id Cc Sz Regis Crc--
|
||||
|
||||
uint8_t len = 0;
|
||||
while ((Pzem2Serial->available() > 0) && (len < (register_count *2) + 5)) {
|
||||
buffer[len++] = (uint8_t)Pzem2Serial->read();
|
||||
if (3 == len) {
|
||||
if (buffer[1] & 0x80) { // fe 84 02 f2 f1
|
||||
return buffer[2]; // 1 = Illegal Function, 2 = Illegal Address, 3 = Illegal Data, 4 = Slave Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, len);
|
||||
|
||||
if (len < 7) { return 7; } // 7 = Not enough data
|
||||
if (len != buffer[2] + 5) { return 8; } // 8 = Unexpected result
|
||||
|
||||
uint16_t crc = (buffer[len -2] << 8) | buffer[len -1];
|
||||
if (Pzem2ModbusCalculateCRC(buffer, len -3) != crc) { return 9; } // 9 = crc error
|
||||
|
||||
return 0; // 0 = No error
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
uint8_t pzem2_sendRetry = 0;
|
||||
|
||||
void Pzem2Every200ms()
|
||||
void Pzem2EverySecond()
|
||||
{
|
||||
bool data_ready = Pzem2ModbusReceiveReady();
|
||||
bool data_ready = Pzem2Modbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[26];
|
||||
uint8_t error = Pzem2ModbusReceive(buffer, pzem2_type);
|
||||
|
||||
uint8_t error = Pzem2Modbus->ReceiveBuffer(buffer, pzem2_type);
|
||||
AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer));
|
||||
|
||||
if (error) {
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "PZEM2 response error %d"), error);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
// if (9 == error) {
|
||||
/*
|
||||
if (PZEM2_TYPES_014_016 == pzem2_type) {
|
||||
pzem2_type = PZEM2_TYPES_003_017;
|
||||
} else {
|
||||
pzem2_type = PZEM2_TYPES_014_016;
|
||||
}
|
||||
*/
|
||||
// }
|
||||
} else {
|
||||
float energy = 0;
|
||||
@ -143,12 +75,12 @@ void Pzem2Every200ms()
|
||||
if (PZEM2_TYPES_003_017 == pzem2_type) {
|
||||
energy_type_dc = true;
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
||||
// FE 04 10 27 10 00 64 03 E8 00 00 00 00 00 00 00 00 00 00 HH LL = PZEM-017
|
||||
// 01 04 10 27 10 00 64 03 E8 00 00 00 00 00 00 00 00 00 00 HH LL = PZEM-017
|
||||
// Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc--
|
||||
energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V
|
||||
energy_current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A
|
||||
energy_active_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W
|
||||
energy = (float)((uint32_t)buffer[13] << 24 + (uint32_t)buffer[14] << 16 + (uint32_t)buffer[11] << 8 + buffer[12]); // 4294967295 Wh
|
||||
energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V
|
||||
energy_current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A
|
||||
energy_active_power = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W
|
||||
energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh
|
||||
if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and hanlde roll-over if any
|
||||
energy_kWhtoday += (energy - energy_start) * 100;
|
||||
energy_start = energy;
|
||||
@ -157,14 +89,14 @@ void Pzem2Every200ms()
|
||||
else if (PZEM2_TYPES_014_016 == pzem2_type) { // PZEM-014,016
|
||||
energy_type_dc = false;
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
||||
// FE 04 14 08 98 03 E8 00 00 08 98 00 00 00 00 00 00 01 F4 00 64 00 00 HH LL = PZEM-014
|
||||
// 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 = PZEM-014
|
||||
// Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc--
|
||||
energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V
|
||||
energy_current = (float)((uint32_t)buffer[7] << 24 + (uint32_t)buffer[8] << 16 + (uint32_t)buffer[5] << 8 + buffer[6]) / 1000.0; // 4294967.000 A
|
||||
energy_active_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)buffer[9] << 8 + buffer[10]) / 10.0; // 429496729.0 W
|
||||
energy_frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz
|
||||
energy_power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00
|
||||
energy = (float)((uint32_t)buffer[15] << 24 + (uint32_t)buffer[16] << 16 + (uint32_t)buffer[13] << 8 + buffer[14]); // 4294967295 Wh
|
||||
energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V
|
||||
energy_current = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A
|
||||
energy_active_power = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W
|
||||
energy_frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz
|
||||
energy_power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00
|
||||
energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh
|
||||
if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and hanlde roll-over if any
|
||||
energy_kWhtoday += (energy - energy_start) * 100;
|
||||
energy_start = energy;
|
||||
@ -175,7 +107,7 @@ void Pzem2Every200ms()
|
||||
|
||||
if (0 == pzem2_sendRetry || data_ready) {
|
||||
pzem2_sendRetry = 5;
|
||||
Pzem2ModbusSend(PZEM2_READ_RESULT, 0, pzem2_type);
|
||||
Pzem2Modbus->Send(PZEM2_DEVICE_ADDRESS, PZEM2_READ_RESULT, 0, pzem2_type);
|
||||
}
|
||||
else {
|
||||
pzem2_sendRetry--;
|
||||
@ -184,10 +116,10 @@ void Pzem2Every200ms()
|
||||
|
||||
void Pzem2SnsInit()
|
||||
{
|
||||
// Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
|
||||
Pzem2Serial = new TasmotaSerial(pin[GPIO_PZEM2_RX], pin[GPIO_PZEM2_TX], 1);
|
||||
if (Pzem2Serial->begin(9600)) {
|
||||
if (Pzem2Serial->hardwareSerial()) { ClaimSerial(); }
|
||||
Pzem2Modbus = new TasmotaModbus(pin[GPIO_PZEM2_RX], pin[GPIO_PZEM2_TX]);
|
||||
uint8_t result = Pzem2Modbus->Begin(PZEM2_MODBUS_SPEED);
|
||||
if (result) {
|
||||
if (2 == result) { ClaimSerial(); }
|
||||
} else {
|
||||
energy_flg = ENERGY_NONE;
|
||||
}
|
||||
@ -218,8 +150,8 @@ int Xnrg05(byte function)
|
||||
case FUNC_INIT:
|
||||
Pzem2SnsInit();
|
||||
break;
|
||||
case FUNC_EVERY_200_MSECOND:
|
||||
Pzem2Every200ms();
|
||||
case FUNC_EVERY_SECOND:
|
||||
Pzem2EverySecond();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,9 @@
|
||||
* Hardware Serial will be selected if GPIO1 = [SAir Rx] and GPIO3 = [SAir Tx]
|
||||
\*********************************************************************************************/
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
#define SENSEAIR_MODBUS_SPEED 9600
|
||||
#define SENSEAIR_DEVICE_ADDRESS 0xFE // Any address
|
||||
#define SENSEAIR_READ_REGISTER 0x04 // Command Read
|
||||
|
||||
#ifndef CO2_LOW
|
||||
#define CO2_LOW 800 // Below this CO2 value show green light
|
||||
@ -35,7 +37,8 @@
|
||||
#define CO2_HIGH 1200 // Above this CO2 value show red light
|
||||
#endif
|
||||
|
||||
TasmotaSerial *SensairSerial;
|
||||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *SenseairModbus;
|
||||
|
||||
const char kSenseairTypes[] PROGMEM = "Kx0|S8";
|
||||
|
||||
@ -48,68 +51,6 @@ float senseair_humidity = 0;
|
||||
|
||||
//uint8_t senseair_state = 0;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count)
|
||||
{
|
||||
uint8_t frame[8];
|
||||
|
||||
frame[0] = 0xFE; // Any Address
|
||||
frame[1] = function_code;
|
||||
frame[2] = (uint8_t)(start_address >> 8);
|
||||
frame[3] = (uint8_t)(start_address);
|
||||
frame[4] = (uint8_t)(register_count >> 8);
|
||||
frame[5] = (uint8_t)(register_count);
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (uint8_t pos = 0; pos < sizeof(frame) -2; pos++) {
|
||||
crc ^= (uint16_t)frame[pos]; // XOR byte into least sig. byte of crc
|
||||
for (uint8_t i = 8; i != 0; i--) { // Loop over each bit
|
||||
if ((crc & 0x0001) != 0) { // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
}
|
||||
else { // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
}
|
||||
frame[7] = (uint8_t)((crc >> 8) & 0xFF);
|
||||
frame[6] = (uint8_t)(crc & 0xFF);
|
||||
|
||||
SensairSerial->flush();
|
||||
SensairSerial->write(frame, sizeof(frame));
|
||||
}
|
||||
|
||||
bool ModbusReceiveReady()
|
||||
{
|
||||
return (SensairSerial->available() >= 5); // 5 - Error frame, 7 - Ok frame
|
||||
}
|
||||
|
||||
uint8_t ModbusReceive(uint16_t *value)
|
||||
{
|
||||
uint8_t buffer[7];
|
||||
|
||||
uint8_t len = 0;
|
||||
while (SensairSerial->available() > 0) {
|
||||
buffer[len++] = (uint8_t)SensairSerial->read();
|
||||
if (3 == len) {
|
||||
if (buffer[1] & 0x80) { // fe 84 02 f2 f1
|
||||
return buffer[2]; // 1 = Illegal Function, 2 = Illegal Data Address, 3 = Illegal Data Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, len);
|
||||
|
||||
if (len != sizeof(buffer)) {
|
||||
return 9; // 9 = Unexpected result
|
||||
}
|
||||
*value = (buffer[3] << 8) | buffer[4];
|
||||
return 0; // 0 = No error
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A };
|
||||
|
||||
uint8_t senseair_read_state = 0;
|
||||
@ -122,10 +63,10 @@ void Senseair250ms() // Every 250 mSec
|
||||
// senseair_state = 0;
|
||||
|
||||
uint16_t value = 0;
|
||||
bool data_ready = ModbusReceiveReady();
|
||||
bool data_ready = SenseairModbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t error = ModbusReceive(&value);
|
||||
uint8_t error = SenseairModbus->Receive16BitRegister(&value);
|
||||
if (error) {
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir response error %d"), error);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
@ -179,7 +120,7 @@ void Senseair250ms() // Every 250 mSec
|
||||
|
||||
if (0 == senseair_send_retry || data_ready) {
|
||||
senseair_send_retry = 5;
|
||||
ModbusSend(0x04, (uint16_t)start_addresses[senseair_read_state], 1);
|
||||
SenseairModbus->Send(SENSEAIR_DEVICE_ADDRESS, SENSEAIR_READ_REGISTER, (uint16_t)start_addresses[senseair_read_state], 1);
|
||||
} else {
|
||||
senseair_send_retry--;
|
||||
}
|
||||
@ -193,9 +134,10 @@ void SenseairInit()
|
||||
{
|
||||
senseair_type = 0;
|
||||
if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) {
|
||||
SensairSerial = new TasmotaSerial(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX], 1);
|
||||
if (SensairSerial->begin(9600)) {
|
||||
if (SensairSerial->hardwareSerial()) { ClaimSerial(); }
|
||||
SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]);
|
||||
uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED);
|
||||
if (result) {
|
||||
if (2 == result) { ClaimSerial(); }
|
||||
senseair_type = 1;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user