Skip to content

Commit

Permalink
Adding Huawei CAN interface, web-api, websocket and Mqtt extensions t…
Browse files Browse the repository at this point in the history
…o access the data
  • Loading branch information
MalteSchm committed Mar 24, 2023
1 parent 63c956a commit 8576034
Show file tree
Hide file tree
Showing 13 changed files with 761 additions and 1 deletion.
68 changes: 68 additions & 0 deletions include/Huawei_can.h
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;
21 changes: 21 additions & 0 deletions include/MqttHandleHuawei.h
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;
8 changes: 7 additions & 1 deletion include/PinMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ struct PinMapping_t {
uint8_t victron_rx;
uint8_t battery_rx;
uint8_t battery_tx;
uint8_t huawei_miso;
uint8_t huawei_mosi;
uint8_t huawei_clk;
uint8_t huawei_irq;
uint8_t huawei_cs;
};

class PinMappingClass {
Expand All @@ -45,7 +50,8 @@ class PinMappingClass {
bool isValidEthConfig();
bool isValidVictronConfig();
bool isValidBatteryConfig();

bool isValidHuaweiConfig();

private:
PinMapping_t _pinMapping;
};
Expand Down
5 changes: 5 additions & 0 deletions include/WebApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "WebApi_ws_live.h"
#include "WebApi_ws_vedirect_live.h"
#include "WebApi_vedirect.h"
#include "WebApi_ws_Huawei.h"
#include "WebApi_Huawei.h"
#include <ESPAsyncWebServer.h>

class WebApiClass {
Expand Down Expand Up @@ -64,6 +66,9 @@ class WebApiClass {
WebApiWsLiveClass _webApiWsLive;
WebApiWsVedirectLiveClass _webApiWsVedirectLive;
WebApiVedirectClass _webApiVedirect;
WebApiHuaweiClass _webApiHuaweiClass;
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;

};

extern WebApiClass WebApi;
16 changes: 16 additions & 0 deletions include/WebApi_Huawei.h
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;
};
24 changes: 24 additions & 0 deletions include/WebApi_ws_Huawei.h
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;
};
181 changes: 181 additions & 0 deletions src/Huawei_can.cpp
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...");
}
}
Loading

0 comments on commit 8576034

Please sign in to comment.