From 1c51999f8e4a4d926eab49302eb91b0ab44341d8 Mon Sep 17 00:00:00 2001 From: Bert Melis Date: Thu, 30 Nov 2023 11:07:16 +0100 Subject: [PATCH 1/5] first try GWG --- README.md | 2 +- examples/simple-read-GWG/simple-read-GWG.ino | 81 ++++++ src/Constants.h | 5 + src/Datapoint/Converter.cpp | 17 ++ src/Datapoint/Converter.h | 7 + src/GWG/GWG.cpp | 251 +++++++++++++++++++ src/GWG/GWG.h | 86 +++++++ src/GWG/PacketGWG.cpp | 117 +++++++++ src/GWG/PacketGWG.h | 47 ++++ src/VS1/PacketVS1.cpp | 16 +- src/VS1/PacketVS1.h | 1 - src/VS1/VS1.cpp | 18 +- 12 files changed, 625 insertions(+), 23 deletions(-) create mode 100644 examples/simple-read-GWG/simple-read-GWG.ino create mode 100644 src/GWG/GWG.cpp create mode 100644 src/GWG/GWG.h create mode 100644 src/GWG/PacketGWG.cpp create mode 100644 src/GWG/PacketGWG.h diff --git a/README.md b/README.md index 7630598..4dc1a37 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ In the table below you can find how to define your datapoints: |---|---|---|---|---| |Temperature|2|div10|float|| |Temperature short|1|noconv|uint8_t|equivalent to Mode| -|Power|1|div2|float|| +|Power|1|div2|float|also used for temperature in GWG| |Status|1|noconv|bool|this is the same as 'Temperature short' and 'Mode'. The `uint8_t` value will be implicitely converted to bool.| |Hours|4|div3600|float|this is in fact a `Count` datapoint (seconds) converted to hours.| |Count|4|noconv|uint32_t|| diff --git a/examples/simple-read-GWG/simple-read-GWG.ino b/examples/simple-read-GWG/simple-read-GWG.ino new file mode 100644 index 0000000..65cef42 --- /dev/null +++ b/examples/simple-read-GWG/simple-read-GWG.ino @@ -0,0 +1,81 @@ +#include + +#include + +#if defined(ARDUINO_ARCH_ESP8266) +// optolink on full UART, logging output on secondary +#define SERIAL1 Serial +#define SERIAL2 Serial1 +#define SERIALBAUDRATE 74880 +#elif defined(ARDUINO_ARCH_ESP32) +// optolink on UART2, logging output on UART1 (connected to USB) +#define SERIAL1 Serial1 +#define SERIAL2 Serial +#define SERIALBAUDRATE 115200 +#endif + +#ifndef SERIALBAUDRATE + #error Target platform not supported +#endif + +VitoWiFi::VitoWiFi vitoWiFi(&SERIAL1); +ssize_t datapointIndex = -1; +constexpr size_t numberDatapoints = 1; +VitoWiFi::Datapoint datapoints[] = { + VitoWiFi::Datapoint("temp", 0x6F, 1, VitoWiFi::div2), +}; + + +void onResponse(const uint8_t* data, uint8_t length, const VitoWiFi::Datapoint& request) { + // raw data can be accessed through the 'response' argument + SERIAL2.print("Raw data received:"); + for (uint8_t i = 0; i < length; ++i) { + SERIAL2.printf(" %02x", data[i]); + } + SERIAL2.print("\n"); + + // the raw data can be decoded using the datapoint. Be sure to use the correct type + SERIAL2.printf("%s: ", request.name()); + if (request.converter() == VitoWiFi::div2) { + float value = request.decode(data, length); + SERIAL2.printf("%.1f\n", value); + } +} + +void onError(VitoWiFi::OptolinkResult error, const VitoWiFi::Datapoint& request) { + SERIAL2.printf("Datapoint \"%s\" error: ", request.name()); + if (error == VitoWiFi::OptolinkResult::TIMEOUT) { + SERIAL2.print("timeout\n"); + } +} + +void setup() { + delay(1000); + SERIAL2.begin(SERIALBAUDRATE); + SERIAL2.print("Setting up vitoWiFi\n"); + + vitoWiFi.onResponse(onResponse); + vitoWiFi.onError(onError); + vitoWiFi.begin(); + + SERIAL2.print("Setup finished\n"); +} + +void loop() { + static uint32_t lastMillis = 0; + if (millis() - lastMillis > 60000UL) { // read all values every 60 seconds + lastMillis = millis(); + datapointIndex = 0; + } + + if (datapointIndex >= 0) { + if (vitoWiFi.read(datapoints[datapointIndex])) { + ++datapointIndex; + } + if (datapointIndex == numberDatapoints) { + datapointIndex = -1; + } + } + + vitoWiFi.loop(); +} diff --git a/src/Constants.h b/src/Constants.h index e7618ce..4e1b240 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -37,6 +37,11 @@ constexpr struct { uint8_t WRITE = 0xF4; } PacketVS1Type; +constexpr struct { + uint8_t READ = 0xCB; + uint8_t WRITE = 0xC8; +} PacketGWGType; + enum class OptolinkResult { CONTINUE, PACKET, diff --git a/src/Datapoint/Converter.cpp b/src/Datapoint/Converter.cpp index 84ae9fe..7d20856 100644 --- a/src/Datapoint/Converter.cpp +++ b/src/Datapoint/Converter.cpp @@ -35,6 +35,22 @@ void Div10Convert::encode(uint8_t* buf, uint8_t len, const VariantValue& val) co buf[0] = tmp & 0xFF; } +VariantValue Div2Convert::decode(const uint8_t* data, uint8_t len) const { + assert(len == 1); + float retVal = 0; + int8_t val = data[0]; + retVal = val / 2.f; + return VariantValue(retVal); +} + +void Div2Convert::encode(uint8_t* buf, uint8_t len, const VariantValue& val) const { + assert(len == 1); + (void) len; + float srcVal = val; + int8_t tmp = floor((srcVal * 2.f) + 0.5); + buf[0] = tmp; +} + VariantValue Div3600Convert::decode(const uint8_t* data, uint8_t len) const { assert(len == 4); (void) len; @@ -94,6 +110,7 @@ void NoconvConvert::encode(uint8_t* buf, uint8_t len, const VariantValue& val) c } Div10Convert div10; +Div2Convert div2; Div3600Convert div3600; NoconvConvert noconv; diff --git a/src/Datapoint/Converter.h b/src/Datapoint/Converter.h index 00b3516..b60f7a8 100644 --- a/src/Datapoint/Converter.h +++ b/src/Datapoint/Converter.h @@ -62,6 +62,12 @@ class Div10Convert : public Converter { void encode(uint8_t* buf, uint8_t len, const VariantValue& val) const override; }; +class Div2Convert : public Converter { + public: + VariantValue decode(const uint8_t* data, uint8_t len) const override; + void encode(uint8_t* buf, uint8_t len, const VariantValue& val) const override; +}; + class Div3600Convert : public Converter { public: VariantValue decode(const uint8_t* data, uint8_t len) const override; @@ -75,6 +81,7 @@ class NoconvConvert : public Converter { }; extern Div10Convert div10; +extern Div2Convert div2; extern Div3600Convert div3600; extern NoconvConvert noconv; diff --git a/src/GWG/GWG.cpp b/src/GWG/GWG.cpp new file mode 100644 index 0000000..f5fd235 --- /dev/null +++ b/src/GWG/GWG.cpp @@ -0,0 +1,251 @@ +/* +Copyright (c) 2023 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#include "GWG.h" + +namespace VitoWiFi { + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +GWG::GWG(HardwareSerial* interface) +: _state(State::UNDEFINED) +, _currentMillis(millis()) +, _lastMillis(_currentMillis) +, _requestTime(0) +, _bytesTransferred(0) +, _interface(nullptr) +, _currentDatapoint(Datapoint(nullptr, 0x0000, 0, VitoWiFi::noconv)) +, _currentRequest() +, _responseBuffer(nullptr) +, _allocatedLength(0) +, _onResponseCallback(nullptr) +, _onErrorCallback(nullptr) { + assert(interface != nullptr); + _interface = new(std::nothrow) VitoWiFiInternals::HardwareSerialInterface(interface); + if (!_interface) { + vw_log_e("Could not create serial interface"); + vw_abort(); + } + _responseBuffer = reinterpret_cast(malloc(START_PAYLOAD_LENGTH)); + if (!_responseBuffer) { + vw_log_e("Could not create response buffer"); + vw_abort(); + } +} + +GWG::GWG(SoftwareSerial* interface) +: _state(State::UNDEFINED) +, _currentMillis(millis()) +, _lastMillis(_currentMillis) +, _requestTime(0) +, _bytesTransferred(0) +, _interface(nullptr) +, _currentDatapoint(Datapoint(nullptr, 0x0000, 0, VitoWiFi::noconv)) +, _currentRequest() +, _responseBuffer(nullptr) +, _allocatedLength(0) +, _onResponseCallback(nullptr) +, _onErrorCallback(nullptr) { + assert(interface != nullptr); + _interface = new(std::nothrow) VitoWiFiInternals::SoftwareSerialInterface(interface); + if (!_interface) { + vw_log_e("Could not create serial interface"); + vw_abort(); + } + _responseBuffer = reinterpret_cast(malloc(START_PAYLOAD_LENGTH)); + if (!_responseBuffer) { + vw_log_e("Could not create response buffer"); + vw_abort(); + } +} +#else +GWG::GWG(const char* interface) +: _state(State::UNDEFINED) +, _currentMillis(millis()) +, _lastMillis(_currentMillis) +, _requestTime(0) +, _bytesTransferred(0) +, _interface(nullptr) +, _currentDatapoint(Datapoint(nullptr, 0x0000, 0, VitoWiFi::noconv)) +, _currentRequest() +, _responseBuffer(nullptr) +, _allocatedLength(0) +, _onResponseCallback(nullptr) +, _onErrorCallback(nullptr) { + assert(interface != nullptr); + _interface = new(std::nothrow) VitoWiFiInternals::LinuxSerialInterface(interface); + if (!_interface) { + vw_log_e("Could not create serial interface"); + vw_abort(); + } + _responseBuffer = reinterpret_cast(malloc(START_PAYLOAD_LENGTH)); + if (!_responseBuffer) { + vw_log_e("Could not create response buffer"); + vw_abort(); + } +} +#endif + +GWG::~GWG() { + delete _interface; + free(_responseBuffer); +} + +void GWG::onResponse(OnResponseCallback callback) { + _onResponseCallback = callback; +} +void GWG::onError(OnErrorCallback callback) { + _onErrorCallback = callback; +} + +bool GWG::read(const Datapoint& datapoint) { + if (_currentDatapoint) { + return false; + } + if (_currentRequest.createPacket(PacketGWGType.READ, + datapoint.address(), + datapoint.length()) && + _expandResponseBuffer(datapoint.length())) { + _currentDatapoint = datapoint; + _requestTime = _currentMillis; + vw_log_i("reading packet OK"); + return true; + } + vw_log_i("reading not possible, packet creation error"); + return false; +} + +bool GWG::write(const Datapoint& datapoint, const VariantValue& value) { + if (_currentDatapoint) { + return false; + } + uint8_t* payload = reinterpret_cast(malloc(datapoint.length())); + if (!payload) { + vw_log_i("writing not possible, packet creation error"); + return false; + } + datapoint.encode(payload, datapoint.length(), value); + return write(datapoint, payload, datapoint.length()); +} + +bool GWG::write(const Datapoint& datapoint, const uint8_t* data, uint8_t length) { + if (_currentDatapoint) { + return false; + } + if (length != datapoint.length()) { + vw_log_i("writing not possible, length mismatch"); + return false; + } + if (_currentRequest.createPacket(PacketGWGType.WRITE, + datapoint.address(), + datapoint.length(), + data) && + _expandResponseBuffer(datapoint.length())) { + _currentDatapoint = datapoint; + _requestTime = _currentMillis; + vw_log_i("writing packet OK"); + return true; + } + vw_log_i("writing not possible, packet creation error"); + return false; +} + +bool GWG::begin() { + _setState(State::INIT); + return _interface->begin(); +} + +void GWG::loop() { + _currentMillis = millis(); + switch (_state) { + case State::INIT: + _init(); + break; + case State::SEND: + _send(); + break; + case State::RECEIVE: + _receive(); + break; + case State::UNDEFINED: + // begin() not yet called + break; + } + // double timeout to accomodate for connection initialization + if (_currentDatapoint && _currentMillis - _requestTime > 3000UL) { + _setState(State::INIT); + _tryOnError(OptolinkResult::TIMEOUT); + } +} + +void GWG::end() { + _interface->end(); + _setState(State::UNDEFINED); + _currentDatapoint = Datapoint(nullptr, 0x0000, 0, VitoWiFi::noconv); +} + +void GWG::_setState(State state) { + vw_log_i("state %i --> %i", static_cast::type>(_state), static_cast::type>(state)); + _state = state; +} + +void GWG::_init() { + if (_interface->available()) { + if (_interface->read() == VitoWiFiInternals::ProtocolBytes.ENQ && _currentDatapoint) { + _setState(State::SEND); + } + } +} + +void GWG::_send() { + _bytesTransferred += _interface->write(&_currentRequest[_bytesTransferred], _currentRequest.length() - _bytesTransferred); + if (_bytesTransferred == _currentRequest.length()) { + _bytesTransferred = 0; + _lastMillis = _currentMillis; + _setState(State::RECEIVE); + } +} + +void GWG::_receive() { + while (_interface->available()) { + _responseBuffer[_bytesTransferred] = _interface->read(); + ++_bytesTransferred; + _lastMillis = _currentMillis; + } + if (_bytesTransferred == _currentRequest.length()) { + _bytesTransferred = 0; + _setState(State::IDLE); + _tryOnResponse(); + } +} + +void GWG::_tryOnResponse() { + if (_onResponseCallback) { + _onResponseCallback(_responseBuffer, _currentRequest.length(), _currentDatapoint); + } +} + +void GWG::_tryOnError(OptolinkResult result) { + if (_onErrorCallback) { + _onErrorCallback(result, _currentDatapoint); + } + _currentDatapoint = Datapoint(nullptr, 0, 0, noconv); +} + +bool GWG::_expandResponseBuffer(uint8_t newSize) { + if (newSize > _allocatedLength) { + uint8_t* newBuffer = reinterpret_cast(realloc(_responseBuffer, newSize)); + if (!newBuffer) { + return false; + } + _responseBuffer = newBuffer; + _allocatedLength = newSize; + } + return true; +} + +} // end namespace VitoWiFi diff --git a/src/GWG/GWG.h b/src/GWG/GWG.h new file mode 100644 index 0000000..b5760d8 --- /dev/null +++ b/src/GWG/GWG.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2023 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#pragma once + +#include + +#include "Logging.h" +#include "../Constants.h" +#include "../Helpers.h" +#include "PacketGWG.h" +#include "../Datapoint/Datapoint.h" +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#include "../Interface/HardwareSerialInterface.h" +#include "../Interface/SoftwareSerialInterface.h" +#elif defined(__linux__) +#include "../Interface/LinuxSerialInterface.h" +#else +#error "platform not supported" +#endif + +namespace VitoWiFi { + +class GWG { + public: + typedef std::function OnResponseCallback; + typedef std::function OnErrorCallback; + + #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + explicit GWG(HardwareSerial* interface); + explicit GWG(SoftwareSerial* interface); + #else + explicit GWG(const char* interface); + #endif + ~GWG(); + GWG(const GWG&) = delete; + GWG & operator=(const GWG&) = delete; + + void onResponse(OnResponseCallback callback); + void onError(OnErrorCallback callback); + + bool read(const Datapoint& datapoint); + bool write(const Datapoint& datapoint, const VariantValue& value); + bool write(const Datapoint& datapoint, const uint8_t* data, uint8_t length); + + bool begin(); + void loop(); + void end(); + + private: + enum class State { + INIT, + SEND, + RECEIVE, + UNDEFINED + } _state; + uint32_t _currentMillis; + uint32_t _lastMillis; + uint32_t _requestTime; + uint8_t _bytesTransferred; + VitoWiFiInternals::SerialInterface* _interface; + Datapoint _currentDatapoint; + PacketVS1 _currentRequest; + uint8_t* _responseBuffer; + uint8_t _allocatedLength; + OnResponseCallback _onResponseCallback; + OnErrorCallback _onErrorCallback; + + inline void _setState(State state); + + void _init(); + void _send(); + void _receive(); + + void _tryOnResponse(); + void _tryOnError(OptolinkResult result); + + bool _expandResponseBuffer(uint8_t newSize); +}; + +} // end namespace VitoWiFi diff --git a/src/GWG/PacketGWG.cpp b/src/GWG/PacketGWG.cpp new file mode 100644 index 0000000..6b9db97 --- /dev/null +++ b/src/GWG/PacketGWG.cpp @@ -0,0 +1,117 @@ +/* +Copyright (c) 2023 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#include "PacketGWG.h" + +namespace VitoWiFi { + +PacketGWG::PacketGWG() +: _allocatedLength(START_PAYLOAD_LENGTH + 5) +, _buffer(nullptr) { + _buffer = reinterpret_cast(malloc(_allocatedLength)); + if (!_buffer) { + _allocatedLength = 0; + vw_abort(); + } + reset(); +} + +PacketGWG::~PacketGWG() { + free(_buffer); +} + +PacketGWG::operator bool() const { + if (_buffer && _buffer[3] != 0) return true; + return false; +} + +/* +uint8_t PacketGWG::operator[](std::size_t index) const { + return _buffer[index]; +} +*/ + +uint8_t& PacketGWG::operator[](std::size_t index) { + return _buffer[index]; +} + +bool PacketGWG::createPacket(uint8_t packetType, uint16_t addr, uint8_t len, const uint8_t* data) { + reset(); + + // check arguments + if (len == 0) { + vw_log_w("Zero length given"); + return false; + } + if (addr > 0xFF) { + vw_log_w("GWG doesn't support addresses > 0xFF"); + return false; + } + if (packetType != PacketGWGType.READ && packetType != PacketGWGType.WRITE) { + vw_log_w("Packet type error: 0x%02x", packetType); + return false; + } + if (packetType == PacketGWGType.WRITE && !data) { + vw_log_w("No data for write packet"); + return false; + } + + // reserve memory + std::size_t toAllocate = (packetType == PacketGWGType.WRITE) ? len + 5 : 5; + if (toAllocate > _allocatedLength) { + uint8_t* newBuffer = reinterpret_cast(realloc(_buffer, toAllocate)); + if (!newBuffer) { + return false; + } + _buffer = newBuffer; + _allocatedLength = toAllocate; + } + + // 2. Serialize into buffer + size_t step = 0; + _buffer[step++] = VitoWiFiInternals::ProtocolBytes.ENQ_ACK; + _buffer[step++] = packetType; + _buffer[step++] = addr & 0xFF; + _buffer[step++] = len; + if (packetType == PacketGWGType.WRITE) { + for (uint8_t i = 0; i < len; ++i) { + _buffer[step++] = data[i]; + } + } + _buffer[step] = VitoWiFiInternals::ProtocolBytes.EOT; + return true; +} + +uint8_t PacketGWG::length() const { + if (_buffer[3] == 0) return 0; + if (_buffer[1] == PacketGWGType.READ) return 5; + if (_buffer[1] == PacketGWGType.WRITE) return _buffer[3] + 5; + return 0; // should not be possible +} + +uint8_t PacketGWG::packetType() const { + return _buffer[1]; +} + +uint16_t PacketGWG::address() const { + return _buffer[2]; +} + +uint8_t PacketGWG::dataLength() const { + return _buffer[3]; +} + +const uint8_t* PacketGWG::data() const { + return &_buffer[4]; +} + +void PacketGWG::reset() { + _buffer[3] = 0x00; +} + +} // end namespace VitoWiFi diff --git a/src/GWG/PacketGWG.h b/src/GWG/PacketGWG.h new file mode 100644 index 0000000..40805b4 --- /dev/null +++ b/src/GWG/PacketGWG.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2023 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../Constants.h" +#include "../Helpers.h" +#include "../Logging.h" + +namespace VitoWiFi { + +class PacketGWG { + public: + PacketGWG(); + ~PacketGWG(); + PacketGWG (const PacketGWG&) = delete; + PacketGWG& operator =(const PacketGWG&) = delete; + operator bool() const; + /* uint8_t operator[](std::size_t index) const; */ + uint8_t& operator[](std::size_t index); + + public: + bool createPacket(uint8_t packetType, uint16_t addr, uint8_t len, const uint8_t* data = nullptr); + uint8_t length() const; + uint8_t packetType() const; + uint16_t address() const; + uint8_t dataLength() const; + const uint8_t* data() const; + + void reset(); + + protected: + std::size_t _allocatedLength; + uint8_t* _buffer; +}; + +} // end namespace VitoWiFi diff --git a/src/VS1/PacketVS1.cpp b/src/VS1/PacketVS1.cpp index f975203..c24507d 100644 --- a/src/VS1/PacketVS1.cpp +++ b/src/VS1/PacketVS1.cpp @@ -79,22 +79,8 @@ bool PacketVS1::createPacket(uint8_t packetType, uint16_t addr, uint8_t len, con return true; } -bool PacketVS1::setLength(uint8_t length) { - std::size_t toAllocate = length + 4; - if (toAllocate > _allocatedLength) { - uint8_t* newBuffer = reinterpret_cast(realloc(_buffer, toAllocate)); - if (!newBuffer) { - return false; - } - _allocatedLength = toAllocate; - _buffer = newBuffer; - } - _buffer[0] = length; - return true; -} - uint8_t PacketVS1::length() const { - if (_buffer[0] == 0) return 0; + if (_buffer[3] == 0) return 0; if (_buffer[0] == PacketVS1Type.READ) return 4; if (_buffer[0] == PacketVS1Type.WRITE) return _buffer[3] + 4; return 0; // should not be possible diff --git a/src/VS1/PacketVS1.h b/src/VS1/PacketVS1.h index 70814d2..a7d6a75 100644 --- a/src/VS1/PacketVS1.h +++ b/src/VS1/PacketVS1.h @@ -31,7 +31,6 @@ class PacketVS1 { public: bool createPacket(uint8_t packetType, uint16_t addr, uint8_t len, const uint8_t* data = nullptr); - bool setLength(uint8_t length); uint8_t length() const; uint8_t packetType() const; uint16_t address() const; diff --git a/src/VS1/VS1.cpp b/src/VS1/VS1.cpp index d67b7e6..23f74da 100644 --- a/src/VS1/VS1.cpp +++ b/src/VS1/VS1.cpp @@ -103,7 +103,7 @@ void VS1::onError(OnErrorCallback callback) { } bool VS1::read(const Datapoint& datapoint) { - if (_state >= State::SEND) { + if (_currentDatapoint) { return false; } if (_currentRequest.createPacket(PacketVS1Type.READ, @@ -120,7 +120,7 @@ bool VS1::read(const Datapoint& datapoint) { } bool VS1::write(const Datapoint& datapoint, const VariantValue& value) { - if (_state >= State::SEND) { + if (_currentDatapoint) { return false; } uint8_t* payload = reinterpret_cast(malloc(datapoint.length())); @@ -133,7 +133,7 @@ bool VS1::write(const Datapoint& datapoint, const VariantValue& value) { } bool VS1::write(const Datapoint& datapoint, const uint8_t* data, uint8_t length) { - if (_state >= State::SEND) { + if (_currentDatapoint) { return false; } if (length != datapoint.length()) { @@ -204,7 +204,7 @@ void VS1::_setState(State state) { void VS1::_init() { if (_interface->available()) { - if (_interface->read() == 0x05) { + if (_interface->read() == VitoWiFiInternals::ProtocolBytes.ENQ) { _setState(State::IDLE); } } else { @@ -270,11 +270,17 @@ void VS1::_receive() { } void VS1::_tryOnResponse() { - if (_onResponseCallback) _onResponseCallback(_responseBuffer, _currentRequest.length(), _currentDatapoint); + if (_onResponseCallback) { + _onResponseCallback(_responseBuffer, _currentRequest.length(), _currentDatapoint); + } + _currentDatapoint = Datapoint(nullptr, 0, 0, noconv); } void VS1::_tryOnError(OptolinkResult result) { - if (_onErrorCallback) _onErrorCallback(result, _currentDatapoint); + if (_onErrorCallback) { + _onErrorCallback(result, _currentDatapoint); + } + _currentDatapoint = Datapoint(nullptr, 0, 0, noconv); } bool VS1::_expandResponseBuffer(uint8_t newSize) { From 06de64476f48724cd91f35b825fedce621ed0455 Mon Sep 17 00:00:00 2001 From: Bert Melis Date: Thu, 30 Nov 2023 11:11:38 +0100 Subject: [PATCH 2/5] fix forgotten --- src/GWG/GWG.cpp | 2 +- src/GWG/GWG.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GWG/GWG.cpp b/src/GWG/GWG.cpp index f5fd235..2994e89 100644 --- a/src/GWG/GWG.cpp +++ b/src/GWG/GWG.cpp @@ -218,7 +218,7 @@ void GWG::_receive() { } if (_bytesTransferred == _currentRequest.length()) { _bytesTransferred = 0; - _setState(State::IDLE); + _setState(State::INIT); _tryOnResponse(); } } diff --git a/src/GWG/GWG.h b/src/GWG/GWG.h index b5760d8..a28e7b1 100644 --- a/src/GWG/GWG.h +++ b/src/GWG/GWG.h @@ -65,7 +65,7 @@ class GWG { uint8_t _bytesTransferred; VitoWiFiInternals::SerialInterface* _interface; Datapoint _currentDatapoint; - PacketVS1 _currentRequest; + PacketGWG _currentRequest; uint8_t* _responseBuffer; uint8_t _allocatedLength; OnResponseCallback _onResponseCallback; From fa42daa3d91a66635eb7300cee21edf6527ea2dc Mon Sep 17 00:00:00 2001 From: Bert Melis Date: Thu, 30 Nov 2023 11:14:16 +0100 Subject: [PATCH 3/5] build testing GWG --- .github/workflows/build_arduino_ide.yml | 2 ++ .github/workflows/build_platformio.yml | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_arduino_ide.yml b/.github/workflows/build_arduino_ide.yml index bb07e7a..83b039f 100644 --- a/.github/workflows/build_arduino_ide.yml +++ b/.github/workflows/build_arduino_ide.yml @@ -26,6 +26,7 @@ jobs: - examples/simple-read-VS2 - examples/simple-write-VS1 - examples/simple-write-VS2 + - examples/simple-read-GWG libraries: | - name: VitoWiFi source-path: ./ @@ -53,6 +54,7 @@ jobs: - examples/simple-read-VS2 - examples/simple-write-VS1 - examples/simple-write-VS2 + - examples/simple-read-GWG libraries: | - name: VitoWiFi source-path: ./ diff --git a/.github/workflows/build_platformio.yml b/.github/workflows/build_platformio.yml index 93e31cf..0c962d8 100644 --- a/.github/workflows/build_platformio.yml +++ b/.github/workflows/build_platformio.yml @@ -11,7 +11,8 @@ jobs: examples/simple-read-VS1/simple-read-VS1.ino, examples/simple-read-VS2/simple-read-VS2.ino, examples/simple-write-VS1/simple-write-VS1.ino, - examples/simple-write-VS2/simple-write-VS2.ino + examples/simple-write-VS2/simple-write-VS2.ino, + examples/simple-read-GWG/simple-read-GWG.ino ] steps: - uses: actions/checkout@v3 @@ -41,7 +42,8 @@ jobs: examples/simple-read-VS1/simple-read-VS1.ino, examples/simple-read-VS2/simple-read-VS2.ino, examples/simple-write-VS1/simple-write-VS1.ino, - examples/simple-write-VS2/simple-write-VS2.ino + examples/simple-write-VS2/simple-write-VS2.ino, + examples/simple-read-GWG/simple-read-GWG.ino ] steps: - uses: actions/checkout@v3 From 44228efac563f725baa53a7531dd48297d2d0863 Mon Sep 17 00:00:00 2001 From: Bert Melis Date: Thu, 30 Nov 2023 11:19:58 +0100 Subject: [PATCH 4/5] add keywords --- README.md | 2 +- keywords.txt | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4dc1a37..8c7076b 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,7 @@ Returns a pointer to the payload. ##### `VitoWiFi(IFACE* interface)` -Constructor of the VitoWiFi class. `PROTOCOL_VERSION` can be `VS1` or `VS2`. If your Viessmann device is somewhat modern, you should use `VS2`. +Constructor of the VitoWiFi class. `PROTOCOL_VERSION` can be `GWG`, `VS1` or `VS2`. If your Viessmann device is somewhat modern, you should use `VS2`. `interface` can be any of the `HardwareSerial` interfaces (`Serial`, `Serial1`...), `SoftwareSerial` or if you are on Linux, pass the c-string depicting your device (for example `"/dev/ttyUSB0"`). ##### `void onResponse(typename PROTOCOLVERSION::OnResponseCallback callback)` diff --git a/keywords.txt b/keywords.txt index 2a4c156..852f3d0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -27,7 +27,6 @@ encode KEYWORD2 #PacketVS2 public methods createPacket KEYWORD2 -#setLength KEYWORD2 length KEYWORD2 packetType KEYWORD2 functionCode KEYWORD2 @@ -42,8 +41,20 @@ checksum KEYWORD2 # Constants (LITERAL1) ####################################### +#Protocols +GWG LITERAL1 +VS1 LITERAL1 +VS2 LITERAL1 + +#Enums +TIMEOUT LITERAL1 +LENGTH LITERAL1 +NACK LITERAL1 +CRC LITERAL1 +ERROR LITERAL1 + #Transformation +div2 LITERAL2 div10 LITERAL2 div3600 LITERAL2 noconv LITERAL2 -raw LITERAL2 From 55092cb15ae531b3a596a42dab3e51aa9b2b07f1 Mon Sep 17 00:00:00 2001 From: Bert Melis Date: Thu, 30 Nov 2023 11:20:49 +0100 Subject: [PATCH 5/5] fix include --- src/VitoWiFi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VitoWiFi.h b/src/VitoWiFi.h index d6f7315..9e4ae7c 100644 --- a/src/VitoWiFi.h +++ b/src/VitoWiFi.h @@ -10,6 +10,7 @@ the LICENSE file. #include "VS2/VS2.h" #include "VS1/VS1.h" +#include "GWG/GWG.h" namespace VitoWiFi {