From f5767e61ef12ccb0ac91884a4d7bee0dd26a34f7 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Thu, 6 Apr 2023 22:18:05 +0200 Subject: [PATCH] Implement CMD56 as own command. By doing so, it's possible to send all packets via the sendEsbPacket method. A lot of stuff could be removed which is no more used. --- lib/Hoymiles/src/Hoymiles.cpp | 4 + lib/Hoymiles/src/HoymilesRadio_CMT.cpp | 132 ++---------------- lib/Hoymiles/src/HoymilesRadio_CMT.h | 20 +-- .../src/commands/ChannelChangeCommand.cpp | 39 ++++++ .../src/commands/ChannelChangeCommand.h | 16 +++ lib/Hoymiles/src/commands/README.md | 1 + lib/Hoymiles/src/inverters/HMS_Abstract.cpp | 20 ++- lib/Hoymiles/src/inverters/HMS_Abstract.h | 2 + lib/Hoymiles/src/inverters/HMT_Abstract.cpp | 16 +++ lib/Hoymiles/src/inverters/HMT_Abstract.h | 2 + .../src/inverters/InverterAbstract.cpp | 5 + lib/Hoymiles/src/inverters/InverterAbstract.h | 1 + 12 files changed, 121 insertions(+), 137 deletions(-) create mode 100644 lib/Hoymiles/src/commands/ChannelChangeCommand.cpp create mode 100644 lib/Hoymiles/src/commands/ChannelChangeCommand.h diff --git a/lib/Hoymiles/src/Hoymiles.cpp b/lib/Hoymiles/src/Hoymiles.cpp index 7bb26b65..4ae3223d 100644 --- a/lib/Hoymiles/src/Hoymiles.cpp +++ b/lib/Hoymiles/src/Hoymiles.cpp @@ -58,6 +58,10 @@ void HoymilesClass::loop() _messageOutput->print("Fetch inverter: "); _messageOutput->println(iv->serial(), HEX); + if (!iv->isReachable()) { + iv->sendChangeChannelRequest(); + } + iv->sendStatsRequest(); // Fetch event log diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp index 4262e24c..771895d6 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp @@ -54,39 +54,6 @@ bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_freq_kHz) return true; } -bool HoymilesRadio_CMT::cmtSwitchInvAndDtuFreq(const uint64_t inv_serial, const uint32_t from_freq_kHz, const uint32_t to_freq_kHz) -{ - const uint8_t fromChannel = getChannelFromFrequency(from_freq_kHz); - const uint8_t toChannel = getChannelFromFrequency(to_freq_kHz); - if (fromChannel == 0xFF || toChannel == 0xFF) { - return false; - } - - _radio->setChannel(fromChannel); - cmtTx56toCh = toChannel; - - // CMD56 for inverter frequency/channel switch - cmtTxBuffer[0] = 0x56; - // cmtTxBuffer[1-4] = last inverter serial - // cmtTxBuffer[5-8] = dtu serial - cmtTxBuffer[9] = 0x02; - cmtTxBuffer[10] = 0x15; - cmtTxBuffer[11] = 0x21; - cmtTxBuffer[12] = (uint8_t)(CMT_BASE_CH_OFFSET860 + toChannel); - cmtTxBuffer[13] = 0x14; - cmtTxBuffer[14] = crc8(cmtTxBuffer, 14); - - Hoymiles.getMessageOutput()->printf("TX CMD56 %.2f MHz --> ", getFrequencyFromChannel(_radio->getChannel())); - dumpBuf(cmtTxBuffer, 15); - - cmtTxLength = 15; - _txTimeout.set(100); - - cmtNextState = CMT_STATE_TX_START; - - return true; -} - enumCMTresult HoymilesRadio_CMT::cmtProcess(void) { enumCMTresult nRes = CMT_BUSY; @@ -142,7 +109,6 @@ enumCMTresult HoymilesRadio_CMT::cmtProcess(void) uint8_t state = CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG); if ((state & 0x1b) == 0x1b) { - cmtRxTimeoutCnt = 0; if (!(_rxBuffer.size() > FRAGMENT_BUFFER_SIZE)) { fragment_t f; @@ -188,91 +154,9 @@ enumCMTresult HoymilesRadio_CMT::cmtProcess(void) cmtNextState = CMT_STATE_IDLE; - // send CMD56 after 3 Rx timeouts - if (cmtRxTimeoutCnt < 2) { - cmtRxTimeoutCnt++; - } else { - uint32_t invSerial = cmtTxBuffer[1] << 24 | cmtTxBuffer[2] << 16 | cmtTxBuffer[3] << 8 | cmtTxBuffer[4]; // read inverter serial from last Tx buffer - cmtSwitchInvAndDtuFreq(invSerial, HOY_BOOT_FREQ / 1000, _inverterTargetFrequency); - } - nRes = CMT_RX_TIMEOUT; break; - case CMT_STATE_TX_START: - CMT2300A_GoStby(); - CMT2300A_ClearInterruptFlags(); - - /* Must clear FIFO after enable SPI to read or write the FIFO */ - CMT2300A_EnableWriteFifo(); - CMT2300A_ClearTxFifo(); - - CMT2300A_WriteReg(CMT2300A_CUS_PKT15, cmtTxLength); // set Tx length - /* The length need be smaller than 32 */ - CMT2300A_WriteFifo(cmtTxBuffer, cmtTxLength); - - if (!(CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG) & CMT2300A_MASK_TX_FIFO_NMTY_FLG)) { - cmtNextState = CMT_STATE_ERROR; - } - - if (!CMT2300A_GoTx()) { - cmtNextState = CMT_STATE_ERROR; - } else { - cmtNextState = CMT_STATE_TX_WAIT; - } - - _txTimeout.reset(); - - break; - - case CMT_STATE_TX_WAIT: - if (!_gpio2_configured) { - if (CMT2300A_MASK_TX_DONE_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1)) { // read INT1, TX_DONE flag - _packetSent = true; - } - } - if (_packetSent) { - Hoymiles.getMessageOutput()->println(F("Interrupt 1 received")); - _packetSent = false; // reset interrupt 1 - cmtNextState = CMT_STATE_TX_DONE; - } - - if (_txTimeout.occured()) { - cmtNextState = CMT_STATE_TX_TIMEOUT; - } - - break; - - case CMT_STATE_TX_DONE: - CMT2300A_ClearInterruptFlags(); - CMT2300A_GoSleep(); - - if (cmtTx56toCh != 0xFF) { - _radio->setChannel(cmtTx56toCh); - cmtTx56toCh = 0xFF; - cmtNextState = CMT_STATE_IDLE; - } else { - cmtNextState = CMT_STATE_RX_START; // receive answer - } - - nRes = CMT_TX_DONE; - break; - - case CMT_STATE_TX_TIMEOUT: - CMT2300A_GoSleep(); - - Hoymiles.getMessageOutput()->println("TX timeout!"); - - if (cmtTx56toCh != 0xFF) { - cmtTx56toCh = 0xFF; - cmtNextState = CMT_STATE_IDLE; - } - - cmtNextState = CMT_STATE_IDLE; - - nRes = CMT_TX_TIMEOUT; - break; - case CMT_STATE_ERROR: CMT2300A_SoftReset(); CMT2300A_DelayMs(20); @@ -437,6 +321,11 @@ void HoymilesRadio_CMT::setInverterTargetFrequency(uint32_t frequency) cmtSwitchDtuFreq(_inverterTargetFrequency); } +uint32_t HoymilesRadio_CMT::getInverterTargetFrequency() +{ + return _inverterTargetFrequency; +} + bool HoymilesRadio_CMT::isConnected() { if (!_isInitialized) { @@ -471,19 +360,22 @@ void HoymilesRadio_CMT::sendEsbPacket(CommandAbstract* cmd) cmd->setRouterAddress(DtuSerial().u64); + uint8_t oldChannel; + oldChannel = _radio->getChannel(); + if (cmd->getDataPayload()[0] == 0x56) { // @todo(tbnobody) Bad hack to identify ChannelChange Command + cmtSwitchDtuFreq(HOY_BOOT_FREQ / 1000); + } + Hoymiles.getMessageOutput()->printf("TX %s %.2f MHz --> ", cmd->getCommandName().c_str(), getFrequencyFromChannel(_radio->getChannel())); cmd->dumpDataPayload(Hoymiles.getMessageOutput()); - // Still here for to handle CMD56 correctly (inverter serial etc.) - memcpy(cmtTxBuffer, cmd->getDataPayload(), cmd->getDataSize()); - if (_radio->write(cmd->getDataPayload(), cmd->getDataSize())) { - _packetSent = false; // still bad hack, to be removed cmtNextState = CMT_STATE_RX_START; } else { Hoymiles.getMessageOutput()->println("TX SPI Timeout"); } + _radio->setChannel(oldChannel); _busyFlag = true; _rxTimeout.set(cmd->getTimeout()); diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.h b/lib/Hoymiles/src/HoymilesRadio_CMT.h index a615507f..0ba8f97c 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.h +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.h @@ -24,10 +24,6 @@ typedef enum { CMT_STATE_RX_WAIT, CMT_STATE_RX_DONE, CMT_STATE_RX_TIMEOUT, - CMT_STATE_TX_START, - CMT_STATE_TX_WAIT, - CMT_STATE_TX_DONE, - CMT_STATE_TX_TIMEOUT, CMT_STATE_ERROR, } enumCMTstate; @@ -37,8 +33,6 @@ typedef enum { CMT_BUSY, CMT_RX_DONE, CMT_RX_TIMEOUT, - CMT_TX_DONE, - CMT_TX_TIMEOUT, CMT_ERROR, } enumCMTresult; @@ -48,12 +42,16 @@ public: void loop(); void setPALevel(int8_t paLevel); void setInverterTargetFrequency(uint32_t frequency); + uint32_t getInverterTargetFrequency(); bool isConnected(); static uint32_t getMinFrequency(); static uint32_t getMaxFrequency(); + static float getFrequencyFromChannel(const uint8_t channel); + static uint8_t getChannelFromFrequency(const uint32_t freq_kHz); + private: void ARDUINO_ISR_ATTR handleInt1(); void ARDUINO_ISR_ATTR handleInt2(); @@ -74,21 +72,11 @@ private: uint32_t _inverterTargetFrequency = HOYMILES_CMT_WORK_FREQ; - static float getFrequencyFromChannel(const uint8_t channel); - static uint8_t getChannelFromFrequency(const uint32_t freq_kHz); - bool cmtSwitchDtuFreq(const uint32_t to_freq_kHz); - bool cmtSwitchInvAndDtuFreq(const uint64_t inv_serial, const uint32_t from_freq_kHz, const uint32_t to_freq_kHz); enumCMTresult cmtProcess(void); enumCMTstate cmtNextState = CMT_STATE_IDLE; - uint8_t cmtTxBuffer[32]; - uint8_t cmtTxLength = 0; uint32_t cmtRxTimeout = 200; uint32_t cmtRxTimeCount = 0; - - uint8_t cmtTx56toCh = 0xFF; // send CMD56 active to Channel xx, inactive = 0xFF - - uint8_t cmtRxTimeoutCnt = 0; // Rx timeout counter !!! should be stored per inverter !!! }; \ No newline at end of file diff --git a/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp new file mode 100644 index 00000000..c6f8dde7 --- /dev/null +++ b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Thomas Basler and others + */ +#include "ChannelChangeCommand.h" + +ChannelChangeCommand::ChannelChangeCommand(uint64_t target_address, uint64_t router_address, uint8_t channel) + : CommandAbstract(target_address, router_address) +{ + _payload[0] = 0x56; + _payload[9] = 0x02; + _payload[10] = 0x15; + _payload[11] = 0x21; + _payload[13] = 0x14; + _payload_size = 14; + + setChannel(channel); + setTimeout(10); +} + +String ChannelChangeCommand::getCommandName() +{ + return "ChannelChangeCommand"; +} + +void ChannelChangeCommand::setChannel(uint8_t channel) +{ + _payload[12] = channel; +} + +uint8_t ChannelChangeCommand::getChannel() +{ + return _payload[12]; +} + +bool ChannelChangeCommand::handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id) +{ + return true; +} diff --git a/lib/Hoymiles/src/commands/ChannelChangeCommand.h b/lib/Hoymiles/src/commands/ChannelChangeCommand.h new file mode 100644 index 00000000..f8f0eabb --- /dev/null +++ b/lib/Hoymiles/src/commands/ChannelChangeCommand.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include "CommandAbstract.h" + +class ChannelChangeCommand : public CommandAbstract { +public: + explicit ChannelChangeCommand(uint64_t target_address = 0, uint64_t router_address = 0, uint8_t channel = 0); + + virtual String getCommandName(); + + void setChannel(uint8_t channel); + uint8_t getChannel(); + + virtual bool handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id); +}; \ No newline at end of file diff --git a/lib/Hoymiles/src/commands/README.md b/lib/Hoymiles/src/commands/README.md index 7e34a210..90ca62d3 100644 --- a/lib/Hoymiles/src/commands/README.md +++ b/lib/Hoymiles/src/commands/README.md @@ -13,3 +13,4 @@ * ParaSetCommand * SingleDataCommand * RequestFrameCommand + * ChannelChangeCommand diff --git a/lib/Hoymiles/src/inverters/HMS_Abstract.cpp b/lib/Hoymiles/src/inverters/HMS_Abstract.cpp index 7c0ea34c..30de0038 100644 --- a/lib/Hoymiles/src/inverters/HMS_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HMS_Abstract.cpp @@ -3,6 +3,24 @@ * Copyright (C) 2023 Thomas Basler and others */ #include "HMS_Abstract.h" +#include "Hoymiles.h" +#include "HoymilesRadio_CMT.h" +#include "commands/ChannelChangeCommand.h" HMS_Abstract::HMS_Abstract(HoymilesRadio* radio, uint64_t serial) - : HM_Abstract(radio, serial) {}; + : HM_Abstract(radio, serial) +{ +} + +bool HMS_Abstract::sendChangeChannelRequest() +{ + if (!(getEnableCommands() && getEnablePolling())) { + return false; + } + + ChannelChangeCommand* cmdChannel = _radio->enqueCommand(); + cmdChannel->setChannel(HoymilesRadio_CMT::getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); + cmdChannel->setTargetAddress(serial()); + + return true; +}; diff --git a/lib/Hoymiles/src/inverters/HMS_Abstract.h b/lib/Hoymiles/src/inverters/HMS_Abstract.h index 5ec60a01..6d363f6e 100644 --- a/lib/Hoymiles/src/inverters/HMS_Abstract.h +++ b/lib/Hoymiles/src/inverters/HMS_Abstract.h @@ -6,4 +6,6 @@ class HMS_Abstract : public HM_Abstract { public: explicit HMS_Abstract(HoymilesRadio* radio, uint64_t serial); + + virtual bool sendChangeChannelRequest(); }; \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HMT_Abstract.cpp b/lib/Hoymiles/src/inverters/HMT_Abstract.cpp index d2fc7a4f..9aa2d093 100644 --- a/lib/Hoymiles/src/inverters/HMT_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HMT_Abstract.cpp @@ -3,6 +3,9 @@ * Copyright (C) 2023 Thomas Basler and others */ #include "HMT_Abstract.h" +#include "Hoymiles.h" +#include "HoymilesRadio_CMT.h" +#include "commands/ChannelChangeCommand.h" #include "parser/AlarmLogParser.h" HMT_Abstract::HMT_Abstract(HoymilesRadio* radio, uint64_t serial) @@ -10,3 +13,16 @@ HMT_Abstract::HMT_Abstract(HoymilesRadio* radio, uint64_t serial) { EventLog()->setMessageType(AlarmMessageType_t::HMT); }; + +bool HMT_Abstract::sendChangeChannelRequest() +{ + if (!(getEnableCommands() && getEnablePolling())) { + return false; + } + + ChannelChangeCommand* cmdChannel = _radio->enqueCommand(); + cmdChannel->setChannel(HoymilesRadio_CMT::getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); + cmdChannel->setTargetAddress(serial()); + + return true; +}; \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HMT_Abstract.h b/lib/Hoymiles/src/inverters/HMT_Abstract.h index 17116a95..9e10a2c3 100644 --- a/lib/Hoymiles/src/inverters/HMT_Abstract.h +++ b/lib/Hoymiles/src/inverters/HMT_Abstract.h @@ -6,4 +6,6 @@ class HMT_Abstract : public HM_Abstract { public: explicit HMT_Abstract(HoymilesRadio* radio, uint64_t serial); + + virtual bool sendChangeChannelRequest(); }; \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.cpp b/lib/Hoymiles/src/inverters/InverterAbstract.cpp index 372772f0..25b35e5d 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.cpp +++ b/lib/Hoymiles/src/inverters/InverterAbstract.cpp @@ -96,6 +96,11 @@ bool InverterAbstract::getEnableCommands() return _enableCommands; } +bool InverterAbstract::sendChangeChannelRequest() +{ + return false; +} + HoymilesRadio* InverterAbstract::getRadio() { return _radio; diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.h b/lib/Hoymiles/src/inverters/InverterAbstract.h index e2a35862..7663bbbe 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.h +++ b/lib/Hoymiles/src/inverters/InverterAbstract.h @@ -63,6 +63,7 @@ public: virtual bool sendPowerControlRequest(bool turnOn) = 0; virtual bool sendRestartControlRequest() = 0; virtual bool resendPowerControlRequest() = 0; + virtual bool sendChangeChannelRequest(); HoymilesRadio* getRadio();