forked from tbnobody/OpenDTU
-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding Huawei CAN interface, web-api, websocket and Mqtt extensions t…
…o access the data
- Loading branch information
Showing
13 changed files
with
761 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include "SPI.h" | ||
#include <mcp_can.h> | ||
|
||
#define HUAWEI_MINIMAL_OFFLINE_VOLTAGE 48 | ||
#define HUAWEI_MINIMAL_ONLINE_VOLTAGE 42 | ||
|
||
#define MAX_CURRENT_MULTIPLIER 20 | ||
|
||
#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 | ||
|
||
struct RectifierParameters_t { | ||
float input_voltage; | ||
float input_frequency; | ||
float input_current; | ||
float input_power; | ||
float input_temp; | ||
float efficiency; | ||
float output_voltage; | ||
float output_current; | ||
float max_output_current; | ||
float output_power; | ||
float output_temp; | ||
float amp_hour; | ||
}; | ||
|
||
class HuaweiCanClass { | ||
public: | ||
void init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs); | ||
void loop(); | ||
void setValue(float in, uint8_t parameterType); | ||
RectifierParameters_t& get(); | ||
unsigned long getLastUpdate(); | ||
|
||
private: | ||
void sendRequest(); | ||
void onReceive(uint8_t* frame, uint8_t len); | ||
|
||
unsigned long previousMillis; | ||
unsigned long lastUpdate; | ||
RectifierParameters_t _rp; | ||
|
||
SPIClass *hspi; | ||
MCP_CAN *CAN; | ||
uint8_t _huawei_irq; | ||
|
||
}; | ||
|
||
extern HuaweiCanClass HuaweiCan; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include "Configuration.h" | ||
#include <Huawei_can.h> | ||
#include <espMqttClient.h> | ||
|
||
class MqttHandleHuaweiClass { | ||
public: | ||
void init(); | ||
void loop(); | ||
|
||
private: | ||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); | ||
|
||
uint32_t _lastPublishStats; | ||
uint32_t _lastPublish; | ||
|
||
}; | ||
|
||
extern MqttHandleHuaweiClass MqttHandleHuawei; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include <ESPAsyncWebServer.h> | ||
|
||
class WebApiHuaweiClass { | ||
public: | ||
void init(AsyncWebServer* server); | ||
void loop(); | ||
|
||
private: | ||
void onStatus(AsyncWebServerRequest* request); | ||
void onPost(AsyncWebServerRequest* request); | ||
|
||
AsyncWebServer* _server; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include "ArduinoJson.h" | ||
#include <ESPAsyncWebServer.h> | ||
//#include <HuaweiFrameHandler.h> | ||
|
||
class WebApiWsHuaweiLiveClass { | ||
public: | ||
WebApiWsHuaweiLiveClass(); | ||
void init(AsyncWebServer* server); | ||
void loop(); | ||
|
||
private: | ||
void generateJsonResponse(JsonVariant& root); | ||
void onLivedataStatus(AsyncWebServerRequest* request); | ||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); | ||
|
||
AsyncWebServer* _server; | ||
AsyncWebSocket _ws; | ||
|
||
uint32_t _lastWsCleanup = 0; | ||
uint32_t _lastUpdateCheck = 0; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
/* | ||
* Copyright (C) 2023 Malte Schmidt and others | ||
*/ | ||
#include "Huawei_can.h" | ||
#include "MessageOutput.h" | ||
#include <SPI.h> | ||
#include <mcp_can.h> | ||
|
||
#include <math.h> | ||
|
||
HuaweiCanClass HuaweiCan; | ||
|
||
void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs) | ||
{ | ||
|
||
hspi = new SPIClass(VSPI); | ||
hspi->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; | ||
|
||
CAN = new MCP_CAN(hspi, huawei_cs); | ||
if(CAN->begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) == CAN_OK) { | ||
MessageOutput.println("MCP2515 Initialized Successfully!"); | ||
} | ||
else { | ||
MessageOutput.println("Error Initializing MCP2515..."); | ||
} | ||
|
||
CAN->setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted | ||
|
||
} | ||
|
||
RectifierParameters_t& HuaweiCanClass::get() | ||
{ | ||
return _rp; | ||
} | ||
|
||
unsigned long HuaweiCanClass::getLastUpdate() | ||
{ | ||
return lastUpdate; | ||
} | ||
uint8_t data[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
|
||
// Requests current values from Huawei unit. Response is handled in onReceive | ||
void HuaweiCanClass::sendRequest() | ||
{ | ||
if (previousMillis < millis()) { | ||
|
||
// Send extended message | ||
byte sndStat = CAN->sendMsgBuf(0x108040FE, 1, 8, data); | ||
if(sndStat == CAN_OK){ | ||
MessageOutput.println("Message Sent Successfully!"); | ||
} else { | ||
MessageOutput.println("Error Sending Message..."); | ||
} | ||
|
||
previousMillis += 5000; | ||
} | ||
} | ||
|
||
void HuaweiCanClass::onReceive(uint8_t* frame, uint8_t len) | ||
{ | ||
if (len != 8) { | ||
return; | ||
} | ||
|
||
uint32_t value = __bswap32(*(uint32_t*)&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; | ||
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 = value / MAX_CURRENT_MULTIPLIER; | ||
break; | ||
|
||
case R48xx_DATA_INPUT_VOLTAGE: | ||
_rp.input_voltage = value / 1024.0; | ||
break; | ||
|
||
case R48xx_DATA_OUTPUT_TEMPERATURE: | ||
_rp.output_temp = value / 1024.0; | ||
break; | ||
|
||
case R48xx_DATA_INPUT_TEMPERATURE: | ||
_rp.input_temp = value / 1024.0; | ||
break; | ||
|
||
case R48xx_DATA_OUTPUT_CURRENT1: | ||
// printf("Output Current(1) %.02fA\r\n", value / 1024.0); | ||
// output_current = value / 1024.0; | ||
break; | ||
|
||
case R48xx_DATA_OUTPUT_CURRENT: | ||
_rp.output_current = value / 1024.0; | ||
|
||
/* This is normally the last parameter received. Print */ | ||
lastUpdate = millis(); | ||
|
||
MessageOutput.printf("In: %.02fV, %.02fA, %.02fW\n", _rp.input_voltage, _rp.input_current, _rp.input_power); | ||
MessageOutput.printf("Out: %.02fV, %.02fA of %.02fA, %.02fW\n", _rp.output_voltage, _rp.output_current, _rp.max_output_current, _rp.output_power); | ||
MessageOutput.printf("Eff: %.01f%%, Temp in: %.01fC, Temp out: %.01fC\n", _rp.efficiency * 100, _rp.input_temp, _rp.output_temp); | ||
|
||
break; | ||
|
||
default: | ||
// printf("Unknown parameter 0x%02X, 0x%04X\r\n",frame[1], value); | ||
break; | ||
} | ||
} | ||
|
||
void HuaweiCanClass::loop() | ||
{ | ||
|
||
long unsigned int rxId; | ||
unsigned char len = 0; | ||
unsigned char rxBuf[8]; | ||
|
||
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/ | ||
} | ||
} | ||
sendRequest(); | ||
} | ||
|
||
void HuaweiCanClass::setValue(float in, uint8_t parameterType) | ||
{ | ||
uint16_t value; | ||
if (parameterType == HUAWEI_OFFLINE_VOLTAGE || parameterType == HUAWEI_ONLINE_VOLTAGE) { | ||
value = in * 1024; | ||
} else if (parameterType == HUAWEI_OFFLINE_CURRENT || parameterType == HUAWEI_ONLINE_CURRENT) { | ||
value = in * MAX_CURRENT_MULTIPLIER; | ||
} else { | ||
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("Message Sent Successfully!"); | ||
} else { | ||
MessageOutput.println("Error Sending Message..."); | ||
} | ||
} |
Oops, something went wrong.