diff --git a/include/Huawei_can.h b/include/Huawei_can.h index f49a31fb2..3b0634110 100644 --- a/include/Huawei_can.h +++ b/include/Huawei_can.h @@ -4,6 +4,7 @@ #include #include "SPI.h" #include +#include #ifndef HUAWEI_PIN_MISO #define HUAWEI_PIN_MISO 12 @@ -34,35 +35,45 @@ #define MAX_CURRENT_MULTIPLIER 20 +// Index values for rec_values array +#define HUAWEI_INPUT_POWER_IDX 0 +#define HUAWEI_INPUT_FREQ_IDX 1 +#define HUAWEI_INPUT_CURRENT_IDX 2 +#define HUAWEI_OUTPUT_POWER_IDX 3 +#define HUAWEI_EFFICIENCY_IDX 4 +#define HUAWEI_OUTPUT_VOLTAGE_IDX 5 +#define HUAWEI_OUTPUT_CURRENT_MAX_IDX 6 +#define HUAWEI_INPUT_VOLTAGE_IDX 7 +#define HUAWEI_OUTPUT_TEMPERATURE_IDX 8 +#define HUAWEI_INPUT_TEMPERATURE_IDX 9 +#define HUAWEI_OUTPUT_CURRENT_IDX 10 +#define HUAWEI_OUTPUT_CURRENT1_IDX 11 + +// Defines and index values for tx_values array #define HUAWEI_OFFLINE_VOLTAGE 0x01 #define HUAWEI_ONLINE_VOLTAGE 0x00 #define HUAWEI_OFFLINE_CURRENT 0x04 #define HUAWEI_ONLINE_CURRENT 0x03 -#define R48xx_DATA_INPUT_POWER 0x70 -#define R48xx_DATA_INPUT_FREQ 0x71 -#define R48xx_DATA_INPUT_CURRENT 0x72 -#define R48xx_DATA_OUTPUT_POWER 0x73 -#define R48xx_DATA_EFFICIENCY 0x74 -#define R48xx_DATA_OUTPUT_VOLTAGE 0x75 -#define R48xx_DATA_OUTPUT_CURRENT_MAX 0x76 -#define R48xx_DATA_INPUT_VOLTAGE 0x78 -#define R48xx_DATA_OUTPUT_TEMPERATURE 0x7F -#define R48xx_DATA_INPUT_TEMPERATURE 0x80 -#define R48xx_DATA_OUTPUT_CURRENT 0x81 -#define R48xx_DATA_OUTPUT_CURRENT1 0x82 - +// Modes of operation #define HUAWEI_MODE_OFF 0 #define HUAWEI_MODE_ON 1 #define HUAWEI_MODE_AUTO_EXT 2 #define HUAWEI_MODE_AUTO_INT 3 +// Error codes +#define HUAWEI_ERROR_CODE_RX 0x01 +#define HUAWEI_ERROR_CODE_TX 0x02 + // Wait time/current before shuting down the PSU / charger // This is set to allow the fan to run for some time #define HUAWEI_AUTO_MODE_SHUTDOWN_DELAY 60000 #define HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT 1.0 -struct RectifierParameters_t { +// Updateinterval used to request new values from the PSU +#define HUAWEI_DATA_REQUEST_INTERVAL_MS 2500 + +typedef struct RectifierParameters { float input_voltage; float input_frequency; float input_current; @@ -75,6 +86,33 @@ struct RectifierParameters_t { float output_power; float output_temp; float amp_hour; +} RectifierParameters_t; + +class HuaweiCanCommClass { +public: + bool init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs); + void loop(); + bool gotNewRxDataFrame(bool clear); + uint8_t getErrorCode(bool clear); + uint32_t getParameterValue(uint8_t parameter); + void setParameterValue(uint16_t in, uint8_t parameterType); + +private: + void sendRequest(); + + SPIClass *SPI; + MCP_CAN *_CAN; + uint8_t _huaweiIrq; // IRQ pin + uint32_t _nextRequestMillis = 0; // When to send next data request to PSU + + std::mutex _mutex; + + uint32_t _recValues[12]; + uint16_t _txValues[5]; + bool _hasNewTxValue[5]; + + uint8_t _errorCode; + bool _completeUpdateReceived; }; class HuaweiCanClass { @@ -89,26 +127,24 @@ class HuaweiCanClass { bool getAutoPowerStatus(); private: - void sendRequest(); - void onReceive(uint8_t* frame, uint8_t len); + void processReceivedParameters(); - SPIClass *spi; - MCP_CAN *CAN; + TaskHandle_t _HuaweiCanCommunicationTaskHdl = NULL; bool _initialized = false; - uint8_t _huawei_irq; // IRQ pin - uint8_t _huawei_power; // Power pin + uint8_t _huaweiPower; // Power pin uint8_t _mode = HUAWEI_MODE_AUTO_EXT; RectifierParameters_t _rp; uint32_t _lastUpdateReceivedMillis; // Timestamp for last data seen from the PSU - uint32_t _nextRequestMillis = 0; // When to send next data request to PSU - uint32_t _nextAutoModePeriodicIntMillis; // When to send the next output volume request in Automatic mode - uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last power meter value uint32_t _outputCurrentOnSinceMillis; // Timestamp since when the PSU was idle at zero amps - bool _newOutputPowerReceived = false; - uint8_t _autoPowerEnabled = false; - bool _autoPowerActive = false; + uint32_t _nextAutoModePeriodicIntMillis; // When to set the next output voltage in automatic mode + uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last seen power meter value + uint32_t _autoModeBlockedTillMillis = 0; // Timestamp to block running auto mode for some time + + uint8_t _autoPowerEnabledCounter = 0; + bool _autoPowerEnabled = false; }; extern HuaweiCanClass HuaweiCan; +extern HuaweiCanCommClass HuaweiCanComm; \ No newline at end of file diff --git a/src/Huawei_can.cpp b/src/Huawei_can.cpp index daef2ac25..5a6341d19 100644 --- a/src/Huawei_can.cpp +++ b/src/Huawei_can.cpp @@ -10,191 +10,275 @@ #include #include +#include +#include +#include #include HuaweiCanClass HuaweiCan; +HuaweiCanCommClass HuaweiCanComm; -void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power) -{ - if (_initialized) { - return; - } - - const CONFIG_T& config = Configuration.get(); +// ******************************************************* +// Huawei CAN Communication +// ******************************************************* - if (!config.Huawei_Enabled) { - return; - } +// Using a C function to avoid static C++ member +void HuaweiCanCommunicationTask(void* parameter) { + for( ;; ) { + HuaweiCanComm.loop(); + yield(); + } +} - spi = new SPIClass(HSPI); - spi->begin(huawei_clk, huawei_miso, huawei_mosi, huawei_cs); +bool HuaweiCanCommClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs) { + SPI = new SPIClass(HSPI); + SPI->begin(huawei_clk, huawei_miso, huawei_mosi, huawei_cs); pinMode(huawei_cs, OUTPUT); digitalWrite(huawei_cs, HIGH); pinMode(huawei_irq, INPUT_PULLUP); - _huawei_irq = huawei_irq; + _huaweiIrq = huawei_irq; - CAN = new MCP_CAN(spi, huawei_cs); - if (!CAN->begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) == CAN_OK) { - MessageOutput.println("[HuaweiCanClass::init] Error Initializing MCP2515..."); - return; + _CAN = new MCP_CAN(SPI, huawei_cs); + if (!_CAN->begin(MCP_STDEXT, CAN_125KBPS, MCP_8MHZ) == CAN_OK) { + return false; } - MessageOutput.println("[HuaweiCanClass::init] MCP2515 Initialized Successfully!"); - _initialized = true; + const uint32_t myMask = 0xFFFFFFFF; // Look at all incoming bits and... + const uint32_t myFilter = 0x1081407F; // filter for this message only + _CAN->init_Mask(0, 1, myMask); + _CAN->init_Filt(0, 1, myFilter); + _CAN->init_Mask(1, 1, myMask); // Change to normal mode to allow messages to be transmitted - CAN->setMode(MCP_NORMAL); + _CAN->setMode(MCP_NORMAL); - pinMode(huawei_power, OUTPUT); - digitalWrite(huawei_power, HIGH); - _huawei_power = huawei_power; + return true; +} - if (config.Huawei_Auto_Power_Enabled) { - _mode = HUAWEI_MODE_AUTO_INT; +// Public methods need to obtain semaphore + +void HuaweiCanCommClass::loop() +{ + std::lock_guard lock(_mutex); + + INT32U rxId; + unsigned char len = 0; + unsigned char rxBuf[8]; + uint8_t i; + + if (!digitalRead(_huaweiIrq)) { + // If CAN_INT pin is low, read receive buffer + _CAN->readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + if((rxId & 0x80000000) == 0x80000000) { // Determine if ID is standard (11 bits) or extended (29 bits) + if ((rxId & 0x1FFFFFFF) == 0x1081407F && len == 8) { + + uint32_t value = __bswap32(* reinterpret_cast (rxBuf + 4)); + + // Input power 0x70, Input frequency 0x71, Input current 0x72 + // Output power 0x73, Efficiency 0x74, Output Voltage 0x75 and Output Current 0x76 + if(rxBuf[1] >= 0x70 && rxBuf[1] <= 0x76 ) { + _recValues[rxBuf[1] - 0x70] = value; + } + + // Input voltage + if(rxBuf[1] == 0x78 ) { + _recValues[HUAWEI_INPUT_VOLTAGE_IDX] = value; + } + + // Output Temperature + if(rxBuf[1] == 0x7F ) { + _recValues[HUAWEI_OUTPUT_TEMPERATURE_IDX] = value; + } + + // Input Temperature 0x80, Output Current 1 0x81 and Output Current 2 0x82 + if(rxBuf[1] >= 0x80 && rxBuf[1] <= 0x82 ) { + _recValues[rxBuf[1] - 0x80 + HUAWEI_INPUT_TEMPERATURE_IDX] = value; + } + + // This is the last value that is send + if(rxBuf[1] == 0x81) { + _completeUpdateReceived = true; + } + } + } + // Other emitted codes not handled here are: 0x1081407E (Ack), 0x1081807E (Ack Frame), 0x1081D27F (Description), 0x1001117E (Whr meter), 0x100011FE (unclear), 0x108111FE (output enabled), 0x108081FE (unclear). See: + // https://github.com/craigpeacock/Huawei_R4850G2_CAN/blob/main/r4850.c + // https://www.beyondlogic.org/review-huawei-r4850g2-power-supply-53-5vdc-3kw/ + } + + // Transmit values + for (i = 0; i < HUAWEI_OFFLINE_CURRENT; i++) { + if ( _hasNewTxValue[i] == true) { + uint8_t data[8] = {0x01, i, 0x00, 0x00, 0x00, 0x00, (uint8_t)((_txValues[i] & 0xFF00) >> 8), (uint8_t)(_txValues[i] & 0xFF)}; + + // Send extended message + byte sndStat = _CAN->sendMsgBuf(0x108180FE, 1, 8, data); + if (sndStat == CAN_OK) { + _hasNewTxValue[i] = false; + } else { + _errorCode |= HUAWEI_ERROR_CODE_TX; + } } + } + + if (_nextRequestMillis < millis()) { + sendRequest(); + _nextRequestMillis = millis() + HUAWEI_DATA_REQUEST_INTERVAL_MS; + } + +} + +uint32_t HuaweiCanCommClass::getParameterValue(uint8_t parameter) +{ + std::lock_guard lock(_mutex); + uint32_t v = 0; + if (parameter < HUAWEI_OUTPUT_CURRENT1_IDX) { + v = _recValues[parameter]; + } + return v; } -RectifierParameters_t * HuaweiCanClass::get() -{ - return &_rp; +bool HuaweiCanCommClass::gotNewRxDataFrame(bool clear) +{ + std::lock_guard lock(_mutex); + bool b = false; + b = _completeUpdateReceived; + if (clear) { + _completeUpdateReceived = false; + } + return b; } -uint32_t HuaweiCanClass::getLastUpdate() -{ - return _lastUpdateReceivedMillis; +uint8_t HuaweiCanCommClass::getErrorCode(bool clear) +{ + std::lock_guard lock(_mutex); + uint8_t e = 0; + e = _errorCode; + if (clear) { + _errorCode = 0; + } + return e; } -uint8_t data[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +void HuaweiCanCommClass::setParameterValue(uint16_t in, uint8_t parameterType) +{ + std::lock_guard lock(_mutex); + if (parameterType < HUAWEI_OFFLINE_CURRENT) { + _txValues[parameterType] = in; + _hasNewTxValue[parameterType] = true; + } +} +// Private methods // Requests current values from Huawei unit. Response is handled in onReceive -void HuaweiCanClass::sendRequest() +void HuaweiCanCommClass::sendRequest() { - // Send extended message - byte sndStat = CAN->sendMsgBuf(0x108040FE, 1, 8, data); + uint8_t data[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + //Send extended message + byte sndStat = _CAN->sendMsgBuf(0x108040FE, 1, 8, data); if(sndStat != CAN_OK) { - MessageOutput.println("[HuaweiCanClass::sendRequest] Error Sending Message..."); + _errorCode |= HUAWEI_ERROR_CODE_RX; } } -void HuaweiCanClass::onReceive(uint8_t* frame, uint8_t len) +// ******************************************************* +// Huawei CAN Controller +// ******************************************************* + +void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power) { - if (len != 8) { + if (_initialized) { return; } - uint32_t value = __bswap32(* reinterpret_cast (frame + 4)); - - switch (frame[1]) { - case R48xx_DATA_INPUT_POWER: - _rp.input_power = value / 1024.0; - break; - - case R48xx_DATA_INPUT_FREQ: - _rp.input_frequency = value / 1024.0; - break; - - case R48xx_DATA_INPUT_CURRENT: - _rp.input_current = value / 1024.0; - break; - - case R48xx_DATA_OUTPUT_POWER: - _rp.output_power = value / 1024.0; - _newOutputPowerReceived = true; - // We'll only update last update on the important params - _lastUpdateReceivedMillis = millis(); - break; - - case R48xx_DATA_EFFICIENCY: - _rp.efficiency = value / 1024.0; - break; - - case R48xx_DATA_OUTPUT_VOLTAGE: - _rp.output_voltage = value / 1024.0; - break; - - case R48xx_DATA_OUTPUT_CURRENT_MAX: - _rp.max_output_current = static_cast(value) / MAX_CURRENT_MULTIPLIER; - break; - - case R48xx_DATA_INPUT_VOLTAGE: - _rp.input_voltage = value / 1024.0; - break; + const CONFIG_T& config = Configuration.get(); - case R48xx_DATA_OUTPUT_TEMPERATURE: - _rp.output_temp = value / 1024.0; - break; + if (!config.Huawei_Enabled) { + return; + } - case R48xx_DATA_INPUT_TEMPERATURE: - _rp.input_temp = value / 1024.0; - break; + if (!HuaweiCanComm.init(huawei_miso, huawei_mosi, huawei_clk, huawei_irq, huawei_cs)) { + MessageOutput.println("[HuaweiCanClass::init] Error Initializing Huawei CAN communication..."); + return; + }; - case R48xx_DATA_OUTPUT_CURRENT1: - // printf("Output Current(1) %.02fA\r\n", value / 1024.0); - // output_current = value / 1024.0; - break; + pinMode(huawei_power, OUTPUT); + digitalWrite(huawei_power, HIGH); + _huaweiPower = huawei_power; - case R48xx_DATA_OUTPUT_CURRENT: - _rp.output_current = value / 1024.0; + if (config.Huawei_Auto_Power_Enabled) { + _mode = HUAWEI_MODE_AUTO_INT; + } - if (_rp.output_current > HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT) { - _outputCurrentOnSinceMillis = millis(); - } + xTaskCreate(HuaweiCanCommunicationTask,"HUAWEI_CAN_0",1000,NULL,0,&_HuaweiCanCommunicationTaskHdl); - /* This is normally the last parameter received. Print */ - _lastUpdateReceivedMillis = millis(); // We'll only update last update on the important params + MessageOutput.println("[HuaweiCanClass::init] MCP2515 Initialized Successfully!"); + _initialized = true; +} - MessageOutput.printf("[HuaweiCanClass::onReceive] In: %.02fV, %.02fA, %.02fW\n", _rp.input_voltage, _rp.input_current, _rp.input_power); - MessageOutput.printf("[HuaweiCanClass::onReceive] Out: %.02fV, %.02fA of %.02fA, %.02fW\n", _rp.output_voltage, _rp.output_current, _rp.max_output_current, _rp.output_power); - MessageOutput.printf("[HuaweiCanClass::onReceive] Eff: %.01f%%, Temp in: %.01fC, Temp out: %.01fC\n", _rp.efficiency * 100, _rp.input_temp, _rp.output_temp); +RectifierParameters_t * HuaweiCanClass::get() +{ + return &_rp; +} - break; +uint32_t HuaweiCanClass::getLastUpdate() +{ + return _lastUpdateReceivedMillis; +} - default: - // printf("Unknown parameter 0x%02X, 0x%04X\r\n",frame[1], value); - break; +void HuaweiCanClass::processReceivedParameters() +{ + _rp.input_power = HuaweiCanComm.getParameterValue(HUAWEI_INPUT_POWER_IDX) / 1024.0; + _rp.input_frequency = HuaweiCanComm.getParameterValue(HUAWEI_INPUT_FREQ_IDX) / 1024.0; + _rp.input_current = HuaweiCanComm.getParameterValue(HUAWEI_INPUT_CURRENT_IDX) / 1024.0; + _rp.output_power = HuaweiCanComm.getParameterValue(HUAWEI_OUTPUT_POWER_IDX) / 1024.0; + _rp.efficiency = HuaweiCanComm.getParameterValue(HUAWEI_EFFICIENCY_IDX) / 1024.0; + _rp.output_voltage = HuaweiCanComm.getParameterValue(HUAWEI_OUTPUT_VOLTAGE_IDX) / 1024.0; + _rp.max_output_current = static_cast(HuaweiCanComm.getParameterValue(HUAWEI_OUTPUT_CURRENT_MAX_IDX)) / MAX_CURRENT_MULTIPLIER; + _rp.input_voltage = HuaweiCanComm.getParameterValue(HUAWEI_INPUT_VOLTAGE_IDX) / 1024.0; + _rp.output_temp = HuaweiCanComm.getParameterValue(HUAWEI_OUTPUT_TEMPERATURE_IDX) / 1024.0; + _rp.input_temp = HuaweiCanComm.getParameterValue(HUAWEI_INPUT_TEMPERATURE_IDX) / 1024.0; + _rp.output_current = HuaweiCanComm.getParameterValue(HUAWEI_OUTPUT_CURRENT_IDX) / 1024.0; + + if (HuaweiCanComm.gotNewRxDataFrame(true)) { + _lastUpdateReceivedMillis = millis(); } } + void HuaweiCanClass::loop() { - - INT32U rxId; - unsigned char len = 0; - unsigned char rxBuf[8]; - const CONFIG_T& config = Configuration.get(); if (!config.Huawei_Enabled || !_initialized) { return; } - if (!digitalRead(_huawei_irq)) { - // If CAN_INT pin is low, read receive buffer - CAN->readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) - - if((rxId & 0x80000000) == 0x80000000) { // Determine if ID is standard (11 bits) or extended (29 bits) - // MessageOutput.printf("Extended ID: 0x%.8lX DLC: %1d \n", (rxId & 0x1FFFFFFF), len); - if ((rxId & 0x1FFFFFFF) == 0x1081407F) { - onReceive(rxBuf, len); - } - // Other emitted codes not handled here are: 0x1081407E, 0x1081807E, 0x1081D27F, 0x1001117E, 0x100011FE, 0x108111FE, 0x108081FE. See: - // https://github.com/craigpeacock/Huawei_R4850G2_CAN/blob/main/r4850.c - // https://www.beyondlogic.org/review-huawei-r4850g2-power-supply-53-5vdc-3kw/ - } + processReceivedParameters(); + + uint8_t com_error = HuaweiCanComm.getErrorCode(true); + if (com_error && HUAWEI_ERROR_CODE_RX) { + MessageOutput.println("[HuaweiCanClass::loop] Data request error"); + } + if (com_error && HUAWEI_ERROR_CODE_TX) { + MessageOutput.println("[HuaweiCanClass::loop] Data set error"); } - // Request updated values in regular intervals - if (_nextRequestMillis < millis()) { - MessageOutput.println("[HUAWEI********************* Sending request"); - sendRequest(); - _nextRequestMillis = millis() + 5000; + // Print updated data + if (HuaweiCanComm.gotNewRxDataFrame(false)) { + MessageOutput.printf("[HuaweiCanClass::loop] In: %.02fV, %.02fA, %.02fW\n", _rp.input_voltage, _rp.input_current, _rp.input_power); + MessageOutput.printf("[HuaweiCanClass::loop] Out: %.02fV, %.02fA of %.02fA, %.02fW\n", _rp.output_voltage, _rp.output_current, _rp.max_output_current, _rp.output_power); + MessageOutput.printf("[HuaweiCanClass::loop] Eff : %.01f%%, Temp in: %.01fC, Temp out: %.01fC\n", _rp.efficiency * 100, _rp.input_temp, _rp.output_temp); } - // If the output current is low for a long time, shutdown PSU + // Internal PSU power pin (slot detect) control + if (_rp.output_current > HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT) { + _outputCurrentOnSinceMillis = millis(); + } if (_outputCurrentOnSinceMillis + HUAWEI_AUTO_MODE_SHUTDOWN_DELAY < millis() && (_mode == HUAWEI_MODE_AUTO_EXT || _mode == HUAWEI_MODE_AUTO_INT)) { - digitalWrite(_huawei_power, 1); + digitalWrite(_huaweiPower, 1); } // *********************** @@ -210,21 +294,37 @@ void HuaweiCanClass::loop() _nextAutoModePeriodicIntMillis = millis() + 60000; } + // Check if we should run automatic power calculation at all. + // We may have set a value recently and still wait for output stabilization + if (_autoModeBlockedTillMillis > millis()) { + return; + } + // Re-enable automatic power control if the output voltage has dropped below threshold if(_rp.output_voltage < config.Huawei_Auto_Power_Enable_Voltage_Limit ) { - _autoPowerEnabled = 10; + _autoPowerEnabledCounter = 10; + } + + + // Check if inverter used by the power limiter is active + std::shared_ptr inverter = + Hoymiles.getInverterByPos(config.PowerLimiter_InverterId); + + if (inverter != nullptr) { + if(inverter->isProducing()) { + setValue(0.0, HUAWEI_ONLINE_CURRENT); + // Don't run auto mode for a second now. Otherwise we may send too much over the CAN bus + _autoModeBlockedTillMillis = millis() + 1000; + MessageOutput.printf("[HuaweiCanClass::loop] Inverter is active, disable\r\n"); + return; + } } - if ((PowerLimiter.getPowerLimiterState() == PL_UI_STATE_INACTIVE || - PowerLimiter.getPowerLimiterState() == PL_UI_STATE_CHARGING) && - PowerMeter.getLastPowerMeterUpdate() > _lastPowerMeterUpdateReceivedMillis && - _newOutputPowerReceived && - _autoPowerEnabled > 0) { - // Power Limiter is inactive and we have received both: - // a new PowerMeter and a new output power value. Also we're _autoPowerEnabled + if (PowerMeter.getLastPowerMeterUpdate() > _lastPowerMeterUpdateReceivedMillis && + _autoPowerEnabledCounter > 0) { + // We have received a new PowerMeter value. Also we're _autoPowerEnabled // So we're good to calculate a new limit - _newOutputPowerReceived = false; _lastPowerMeterUpdateReceivedMillis = PowerMeter.getLastPowerMeterUpdate(); // Calculate new power limit @@ -239,14 +339,14 @@ void HuaweiCanClass::loop() // to ramp up from zero output power when starting up if (_rp.output_power < config.Huawei_Auto_Power_Lower_Power_Limit) { MessageOutput.printf("[HuaweiCanClass::loop] Power and voltage limit reached. Disabling automatic power control .... \r\n"); - _autoPowerEnabled--; - if (_autoPowerEnabled == 0) { - _autoPowerActive = false; + _autoPowerEnabledCounter--; + if (_autoPowerEnabledCounter == 0) { + _autoPowerEnabled = false; setValue(0, HUAWEI_ONLINE_CURRENT); return; } } else { - _autoPowerEnabled = 10; + _autoPowerEnabledCounter = 10; } // Limit power to maximum @@ -258,14 +358,14 @@ void HuaweiCanClass::loop() float efficiency = (_rp.efficiency > 0.5 ? _rp.efficiency : 1.0); float outputCurrent = efficiency * (newPowerLimit / _rp.output_voltage); MessageOutput.printf("[HuaweiCanClass::loop] Output current %f \r\n", outputCurrent); - _autoPowerActive = true; + _autoPowerEnabled = true; setValue(outputCurrent, HUAWEI_ONLINE_CURRENT); - // Issue next request for updated output values in 2s to allow for output stabilization - _nextRequestMillis = millis() + 2000; + // Don't run auto mode some time to allow for output stabilization after issuing a new value + _autoModeBlockedTillMillis = millis() + 2 * HUAWEI_DATA_REQUEST_INTERVAL_MS; } else { // requested PL is below minium. Set current to 0 - _autoPowerActive = false; + _autoPowerEnabled = false; setValue(0.0, HUAWEI_ONLINE_CURRENT); } } @@ -274,6 +374,13 @@ void HuaweiCanClass::loop() void HuaweiCanClass::setValue(float in, uint8_t parameterType) { + + const CONFIG_T& config = Configuration.get(); + + if (!config.Huawei_Enabled) { + return; + } + uint16_t value; if (in < 0) { @@ -283,7 +390,7 @@ void HuaweiCanClass::setValue(float in, uint8_t parameterType) // Start PSU if needed if (in > HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT && parameterType == HUAWEI_ONLINE_CURRENT && (_mode == HUAWEI_MODE_AUTO_EXT || _mode == HUAWEI_MODE_AUTO_INT)) { - digitalWrite(_huawei_power, 0); + digitalWrite(_huaweiPower, 0); _outputCurrentOnSinceMillis = millis(); } @@ -295,24 +402,22 @@ void HuaweiCanClass::setValue(float in, uint8_t parameterType) return; } - uint8_t data[8] = {0x01, parameterType, 0x00, 0x00, 0x00, 0x00, (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0xFF)}; - - // Send extended message - byte sndStat = CAN->sendMsgBuf(0x108180FE, 1, 8, data); - if (sndStat != CAN_OK) { - MessageOutput.println("[HuaweiCanClass::setValue] Error Sending Message..."); - } + HuaweiCanComm.setParameterValue(value, parameterType); } void HuaweiCanClass::setMode(uint8_t mode) { const CONFIG_T& config = Configuration.get(); + if (!config.Huawei_Enabled) { + return; + } + if(mode == HUAWEI_MODE_OFF) { - digitalWrite(_huawei_power, 1); + digitalWrite(_huaweiPower, 1); _mode = HUAWEI_MODE_OFF; } if(mode == HUAWEI_MODE_ON) { - digitalWrite(_huawei_power, 0); + digitalWrite(_huaweiPower, 0); _mode = HUAWEI_MODE_ON; } @@ -322,7 +427,7 @@ void HuaweiCanClass::setMode(uint8_t mode) { } if (_mode == HUAWEI_MODE_AUTO_INT && mode != HUAWEI_MODE_AUTO_INT) { - _autoPowerActive = false; + _autoPowerEnabled = false; setValue(0, HUAWEI_ONLINE_CURRENT); } @@ -332,6 +437,6 @@ void HuaweiCanClass::setMode(uint8_t mode) { } bool HuaweiCanClass::getAutoPowerStatus() { - return _autoPowerActive; + return _autoPowerEnabled; } diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 4e244ffa9..6013fb243 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -27,9 +27,9 @@ void MqttHandleVedirectClass::loop() if (!MqttSettings.getConnected() || !config.Vedirect_Enabled) { return; - } + } - if (!VeDirectMppt.isDataValid()) { + if (!VeDirectMppt.isDataValid()) { return; } @@ -56,60 +56,72 @@ void MqttHandleVedirectClass::loop() topic.concat("/"); if (_PublishFull || VeDirectMppt.veFrame.PID != _kvFrame.PID) - MqttSettings.publish(topic + "PID", VeDirectMppt.getPidAsString(VeDirectMppt.veFrame.PID)); + MqttSettings.publish(topic + "PID", VeDirectMppt.getPidAsString(VeDirectMppt.veFrame.PID)); if (_PublishFull || strcmp(VeDirectMppt.veFrame.SER, _kvFrame.SER) != 0) - MqttSettings.publish(topic + "SER", VeDirectMppt.veFrame.SER ); + MqttSettings.publish(topic + "SER", VeDirectMppt.veFrame.SER ); if (_PublishFull || strcmp(VeDirectMppt.veFrame.FW, _kvFrame.FW) != 0) - MqttSettings.publish(topic + "FW", VeDirectMppt.veFrame.FW); + MqttSettings.publish(topic + "FW", VeDirectMppt.veFrame.FW); if (_PublishFull || VeDirectMppt.veFrame.LOAD != _kvFrame.LOAD) - MqttSettings.publish(topic + "LOAD", VeDirectMppt.veFrame.LOAD == true ? "ON": "OFF"); + MqttSettings.publish(topic + "LOAD", VeDirectMppt.veFrame.LOAD == true ? "ON": "OFF"); if (_PublishFull || VeDirectMppt.veFrame.CS != _kvFrame.CS) - MqttSettings.publish(topic + "CS", VeDirectMppt.getCsAsString(VeDirectMppt.veFrame.CS)); + MqttSettings.publish(topic + "CS", VeDirectMppt.getCsAsString(VeDirectMppt.veFrame.CS)); if (_PublishFull || VeDirectMppt.veFrame.ERR != _kvFrame.ERR) - MqttSettings.publish(topic + "ERR", VeDirectMppt.getErrAsString(VeDirectMppt.veFrame.ERR)); + MqttSettings.publish(topic + "ERR", VeDirectMppt.getErrAsString(VeDirectMppt.veFrame.ERR)); if (_PublishFull || VeDirectMppt.veFrame.OR != _kvFrame.OR) - MqttSettings.publish(topic + "OR", VeDirectMppt.getOrAsString(VeDirectMppt.veFrame.OR)); + MqttSettings.publish(topic + "OR", VeDirectMppt.getOrAsString(VeDirectMppt.veFrame.OR)); if (_PublishFull || VeDirectMppt.veFrame.MPPT != _kvFrame.MPPT) - MqttSettings.publish(topic + "MPPT", VeDirectMppt.getMpptAsString(VeDirectMppt.veFrame.MPPT)); + MqttSettings.publish(topic + "MPPT", VeDirectMppt.getMpptAsString(VeDirectMppt.veFrame.MPPT)); if (_PublishFull || VeDirectMppt.veFrame.HSDS != _kvFrame.HSDS) { value = VeDirectMppt.veFrame.HSDS; - MqttSettings.publish(topic + "HSDS", value); + MqttSettings.publish(topic + "HSDS", value); } if (_PublishFull || VeDirectMppt.veFrame.V != _kvFrame.V) { value = VeDirectMppt.veFrame.V; - MqttSettings.publish(topic + "V", value); + MqttSettings.publish(topic + "V", value); } if (_PublishFull || VeDirectMppt.veFrame.I != _kvFrame.I) { value = VeDirectMppt.veFrame.I; - MqttSettings.publish(topic + "I", value); + MqttSettings.publish(topic + "I", value); + } + if (_PublishFull || VeDirectMppt.veFrame.P != _kvFrame.P) { + value = VeDirectMppt.veFrame.P; + MqttSettings.publish(topic + "P", value); } if (_PublishFull || VeDirectMppt.veFrame.VPV != _kvFrame.VPV) { value = VeDirectMppt.veFrame.VPV; - MqttSettings.publish(topic + "VPV", value); + MqttSettings.publish(topic + "VPV", value); + } + if (_PublishFull || VeDirectMppt.veFrame.IPV != _kvFrame.IPV) { + value = VeDirectMppt.veFrame.IPV; + MqttSettings.publish(topic + "IPV", value); } if (_PublishFull || VeDirectMppt.veFrame.PPV != _kvFrame.PPV) { value = VeDirectMppt.veFrame.PPV; - MqttSettings.publish(topic + "PPV", value); + MqttSettings.publish(topic + "PPV", value); + } + if (_PublishFull || VeDirectMppt.veFrame.E != _kvFrame.E) { + value = VeDirectMppt.veFrame.E; + MqttSettings.publish(topic + "E", value); } if (_PublishFull || VeDirectMppt.veFrame.H19 != _kvFrame.H19) { value = VeDirectMppt.veFrame.H19; - MqttSettings.publish(topic + "H19", value); + MqttSettings.publish(topic + "H19", value); } if (_PublishFull || VeDirectMppt.veFrame.H20 != _kvFrame.H20) { value = VeDirectMppt.veFrame.H20; - MqttSettings.publish(topic + "H20", value); + MqttSettings.publish(topic + "H20", value); } if (_PublishFull || VeDirectMppt.veFrame.H21 != _kvFrame.H21) { value = VeDirectMppt.veFrame.H21; - MqttSettings.publish(topic + "H21", value); + MqttSettings.publish(topic + "H21", value); } if (_PublishFull || VeDirectMppt.veFrame.H22 != _kvFrame.H22) { value = VeDirectMppt.veFrame.H22; - MqttSettings.publish(topic + "H22", value); + MqttSettings.publish(topic + "H22", value); } if (_PublishFull || VeDirectMppt.veFrame.H23 != _kvFrame.H23) { value = VeDirectMppt.veFrame.H23; - MqttSettings.publish(topic + "H23", value); + MqttSettings.publish(topic + "H23", value); } if (!_PublishFull) { _kvFrame= VeDirectMppt.veFrame; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 25add8d66..d12d10ac3 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -343,7 +343,7 @@ int32_t PowerLimiterClass::inverterPowerDcToAc(std::shared_ptr CONFIG_T& config = Configuration.get(); float inverterEfficiencyPercent = inverter->Statistics()->getChannelFieldValue( - TYPE_AC, static_cast(config.PowerLimiter_InverterChannelId), FLD_EFF); + TYPE_AC, CH0, FLD_EFF); // fall back to hoymiles peak efficiency as per datasheet if inverter // is currently not producing (efficiency is zero in that case) @@ -440,7 +440,7 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr inve // the produced power of this inverter has also to be taken into account. // We don't use FLD_PAC from the statistics, because that // data might be too old and unreliable. - acPower = static_cast(inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC)); + acPower = static_cast(inverter->Statistics()->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC)); newPowerLimit += acPower; } @@ -583,7 +583,7 @@ float PowerLimiterClass::getLoadCorrectedVoltage() CONFIG_T& config = Configuration.get(); auto channel = static_cast(config.PowerLimiter_InverterChannelId); - float acPower = _inverter->Statistics()->getChannelFieldValue(TYPE_AC, channel, FLD_PAC); + float acPower = _inverter->Statistics()->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC); float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, channel, FLD_UDC); if (dcVoltage <= 0.0) {