-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
387 additions
and
197 deletions.
There are no files selected for viewing
Binary file not shown.
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 |
---|---|---|
@@ -1,205 +1,9 @@ | ||
#include <Arduino.h> | ||
#include <ModbusMaster.h> | ||
#include <Ethernet.h> | ||
|
||
#include "globals.h" | ||
#include "janitza.h" | ||
#include "eth.h" | ||
|
||
|
||
ModbusMaster mb; | ||
uint32_t _serialNumber; | ||
uint16_t _ctRatio = 1; | ||
EthernetClient client; | ||
|
||
|
||
void handleHttpResponse() { | ||
// print response from HTTP request | ||
if (client.available()) { | ||
int len = client.available(); | ||
uint8_t buf[512]; | ||
if (len > 512) { | ||
len = 512; | ||
} | ||
client.read(buf, len); | ||
|
||
if (strncmp((const char *)buf, "HTTP/1.1 204", 12) == 0) { | ||
DEBUG.println("InfluxDB request successful."); | ||
} | ||
else { | ||
DEBUG.println("InfluxDB request failed!"); | ||
DEBUG.write(buf, len); | ||
} | ||
} | ||
} | ||
|
||
void sendInfluxRequest(char *lineProtocolCommand) { | ||
if (!client.connected()) { | ||
if (!client.connect(INFLUX_HOST, INFLUX_PORT)) { | ||
DEBUG.printf("Connection to InfluxDB failed\n"); | ||
return; | ||
} | ||
} | ||
|
||
client.printf("POST /write?db=%s HTTP/1.1\n", INFLUX_DB); | ||
client.printf("Host: %s\n", INFLUX_HOST); | ||
client.printf("Authorization: Basic %s\n", INFLUX_AUTH_BASE64); | ||
client.printf("Content-Length: %d\n", strlen(lineProtocolCommand)); | ||
client.printf("Connection: close\n"); | ||
client.printf("\n"); | ||
client.write(lineProtocolCommand); | ||
client.printf("\n"); | ||
client.flush(); | ||
while (client.connected()) { | ||
handleHttpResponse(); | ||
} | ||
} | ||
|
||
uint32_t readSerialNumber() { | ||
uint8_t ret = mb.readHoldingRegisters(REG_SERIAL, 2); | ||
if (ret != mb.ku8MBSuccess) { | ||
DEBUG.printf("Error reading from ModBus in readSerialNumber. Status = %02X\n", ret); | ||
return 0; | ||
} | ||
return mb.getResponseBuffer(0) << 16 | mb.getResponseBuffer(1); | ||
} | ||
|
||
uint16_t readCtRatio() { | ||
uint8_t ret = mb.readHoldingRegisters(REG_CT_PRIM, 2); | ||
if (ret != mb.ku8MBSuccess) { | ||
DEBUG.printf("Error reading from ModBus in readCtRatio. Status = %02X\n", ret); | ||
return 0; | ||
} | ||
// calculate CT ratio, only integers supported currently | ||
return mb.getResponseBuffer(0) / mb.getResponseBuffer(1); | ||
} | ||
|
||
registerDefinition_t *getRegDef(uint16_t address) { | ||
// currently the definition is still continuous, so we don't actually have to search through it | ||
if (address >= REG_DEF_START_ADDR && address <= REG_DEF_START_ADDR + REG_DEF_NUM) { | ||
return ®isterDefinition[address - REG_DEF_START_ADDR]; | ||
} | ||
else { | ||
return nullptr; | ||
} | ||
} | ||
|
||
#define MB_CHUNK_SIZE 32 | ||
|
||
bool modbusReadBulk(int16_t *destBuf, uint16_t startAddr, uint16_t count) { | ||
int iterations = (count + (MB_CHUNK_SIZE - 1)) / MB_CHUNK_SIZE; // division, but rounded up | ||
for (int i = 0; i < iterations; i++) { | ||
uint16_t mbStart = startAddr + i * MB_CHUNK_SIZE; | ||
uint8_t mbCount = MB_CHUNK_SIZE; | ||
if (MB_CHUNK_SIZE * (i + 1) > count) { | ||
mbCount = count % MB_CHUNK_SIZE; | ||
} | ||
|
||
DEBUG.printf("Reading Modbus %d/%d (Addr: %d, Count: %d) \r", i+1, iterations, mbStart, mbCount); | ||
|
||
uint8_t ret = mb.readHoldingRegisters(mbStart, mbCount); | ||
|
||
if (ret != mb.ku8MBSuccess) { | ||
DEBUG.printf("\nError reading from ModBus. Status = %02X\n", ret); | ||
return false; | ||
} | ||
|
||
for (int j = 0; j < mbCount; j++) { | ||
destBuf[i * MB_CHUNK_SIZE + j] = mb.getResponseBuffer(j); | ||
} | ||
} | ||
DEBUG.printf("\n"); | ||
return true; | ||
|
||
} | ||
|
||
int16_t modbusBuf[REG_DEF_NUM]; | ||
char influxQueryBuf[512*10]; | ||
|
||
void readMeter() { | ||
bool success = modbusReadBulk(modbusBuf, 200, 226); | ||
|
||
if (!success) { | ||
DEBUG.printf("Error reading bulk from ModBus\n"); | ||
return; | ||
} | ||
|
||
int influxQueryLen = 0; | ||
|
||
for (int phase = 0; phase < P_TAG_NUM; phase++) { | ||
const char* phaseStr = phaseTagStr[phase]; | ||
|
||
influxQueryLen += sprintf(influxQueryBuf + influxQueryLen, "%s", INFLUX_MEASUREMENT); // measurement name | ||
influxQueryLen += sprintf(influxQueryBuf + influxQueryLen, ",serial=%d,phase=%s ", _serialNumber, phaseStr); // print tags here | ||
|
||
|
||
for (int i = 0; i < REG_DEF_NUM; i++) { | ||
registerDefinition_t *regDef = ®isterDefinition[i]; | ||
|
||
if (regDef->address == 0) { // skip "disabled" addresses | ||
continue; | ||
} | ||
|
||
if (regDef->phaseTag == phase) { | ||
int32_t mbValue = modbusBuf[i]; | ||
if (regDef->type == LONG) { | ||
mbValue = mbValue << 16 | (modbusBuf[i + 1] & 0xFFFF); | ||
i++; // increment counter / skip next address | ||
} | ||
|
||
float val = (float)mbValue / (float)regDef->multiplier; | ||
|
||
// apply current transformer ratio if needed | ||
if (regDef->applyCtRatio) { | ||
val *= (float)_ctRatio; | ||
} | ||
|
||
influxQueryLen += sprintf(influxQueryBuf + influxQueryLen, "%s=%g,", regDef->influxStr, val); | ||
} | ||
} | ||
|
||
influxQueryBuf[influxQueryLen - 1] = '\n'; // replace trailing comma with newline | ||
} | ||
// DEBUG.write(influxQueryBuf); | ||
DEBUG.print("Sending InfluxDB Request... "); | ||
sendInfluxRequest(influxQueryBuf); | ||
|
||
} | ||
|
||
void setup() { | ||
DEBUG.begin(115200); | ||
DEBUG.println("\nJanitza Power Meter to Influx\n"); | ||
|
||
MODUBS_SERIAL.begin(MODBUS_BAUD); | ||
mb.begin(MODBUS_ADDR, MODUBS_SERIAL); | ||
|
||
while (_serialNumber == 0) { | ||
_serialNumber = readSerialNumber(); | ||
if (_serialNumber != 0) { | ||
DEBUG.printf("Serial: %d\n", _serialNumber); | ||
} | ||
else { | ||
DEBUG.printf("Error: Could not read serial number, make sure the meter is connected and address and baud rate are correct. Retrying in 5s...\n"); | ||
delay(5000); | ||
} | ||
} | ||
|
||
_ctRatio = readCtRatio(); | ||
DEBUG.printf("CT Ratio: %d\n", _ctRatio); | ||
|
||
initEthernet(); | ||
connectEthernet(); | ||
|
||
readMeter(); | ||
} | ||
|
||
uint32_t lastUpdate = 0; | ||
void loop () { | ||
|
||
void loop() { | ||
if (millis() - lastUpdate >= UPDATE_INTERVAL) { | ||
lastUpdate = millis(); | ||
readMeter(); | ||
} | ||
|
||
handleHttpResponse(); | ||
} |
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,10 @@ | ||
.pio | ||
.vscode/.browse.c_cpp.db* | ||
.vscode/c_cpp_properties.json | ||
.vscode/launch.json | ||
.vscode/ipch | ||
.vscode/settings.json | ||
|
||
README | ||
%AppData% | ||
secrets.h |
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,7 @@ | ||
{ | ||
// See http://go.microsoft.com/fwlink/?LinkId=827846 | ||
// for the documentation about the extensions.json format | ||
"recommendations": [ | ||
"platformio.platformio-ide" | ||
] | ||
} |
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 @@ | ||
; PlatformIO Project Configuration File | ||
; | ||
; Build options: build flags, source filter | ||
; Upload options: custom upload port, speed and extra flags | ||
; Library options: dependencies, extra library storages | ||
; Advanced options: extra scripting | ||
; | ||
; Please visit documentation for the other options and examples | ||
; https://docs.platformio.org/page/projectconf.html | ||
|
||
[env:genericSTM32F103C8] | ||
platform = [email protected] | ||
board = bluepill_f103c8 | ||
; board = genericSTM32F103C8 | ||
framework = arduino | ||
lib_deps = | ||
[email protected] | ||
[email protected] | ||
build_flags = | ||
-D ENABLE_HWSERIAL3 | ||
-Wl,-u_printf_float ; enable float printf |
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,98 @@ | ||
#include "eth.h" | ||
|
||
uint8_t macAddr[6]; | ||
char macStr[13]; | ||
// EthernetClient net; | ||
|
||
static inline uint64_t mix(uint64_t h) { | ||
h ^= h >> 23; | ||
h *= 0x2127599bf4325c37ULL; | ||
h ^= h >> 47; | ||
// | ||
return h; | ||
} | ||
|
||
// hash function copied from https://stackoverflow.com/a/47895889 | ||
uint64_t fastHash64(const void * buf, size_t len, uint64_t seed) { | ||
const uint64_t m = 0x880355f21e6d1965ULL; | ||
const uint64_t * pos = (const uint64_t*)buf; | ||
const uint64_t * end = pos + (len / 8); | ||
const unsigned char * pos2; | ||
uint64_t h = seed ^ (len * m); | ||
uint64_t v; | ||
|
||
while(pos != end) | ||
{ | ||
v = *pos++; | ||
h ^= mix(v); | ||
h *= m; | ||
} | ||
|
||
pos2 = (const unsigned char*)pos; | ||
v = 0; | ||
|
||
switch(len & 7) | ||
{ | ||
case 7: v ^= (uint64_t)pos2[6] << 48; | ||
case 6: v ^= (uint64_t)pos2[5] << 40; | ||
case 5: v ^= (uint64_t)pos2[4] << 32; | ||
case 4: v ^= (uint64_t)pos2[3] << 24; | ||
case 3: v ^= (uint64_t)pos2[2] << 16; | ||
case 2: v ^= (uint64_t)pos2[1] << 8; | ||
case 1: v ^= (uint64_t)pos2[0]; | ||
h ^= mix(v); | ||
h *= m; | ||
} | ||
|
||
return mix(h); | ||
} | ||
|
||
void generateMAC(uint8_t *macArray) { | ||
|
||
// uint32_t uid[3] = {HAL_GetUIDw0(), HAL_GetUIDw1(), HAL_GetUIDw2()}; | ||
uint64_t hash = fastHash64((uint8_t *)UID_BASE, 12, 0x421337f00beefULL); | ||
memcpy(macArray, &hash, 6); | ||
macArray[0] = 0x42; // set first byte to predefined value, where private bit is set | ||
|
||
snprintf(macStr, 13, "%02X%02X%02X%02X%02X%02X", macArray[0], macArray[1], macArray[2], macArray[3], macArray[4], macArray[5]); | ||
} | ||
|
||
void initEthernet() { | ||
SPI.setMOSI(SPI2_MOSI); | ||
SPI.setMISO(SPI2_MISO); | ||
SPI.setSCLK(SPI2_SCK); | ||
|
||
Ethernet.init(SPI2_CS); | ||
|
||
generateMAC(macAddr); | ||
} | ||
|
||
bool connectEthernet() { | ||
DEBUG.println("Initialize Ethernet"); | ||
|
||
bool success= true; | ||
|
||
// DHCP | ||
if (Ethernet.begin(macAddr, 10000) == 0) { | ||
DEBUG.println("Failed to configure Ethernet using DHCP"); | ||
success = false; | ||
} | ||
// Static IP | ||
// Ethernet.begin(macAddr, IPAddress(192,168,13,245)); | ||
|
||
if (Ethernet.hardwareStatus() == EthernetNoHardware) { | ||
DEBUG.println("Ethernet module was not found. Sorry, can't run without hardware. :("); | ||
// delay(5000); | ||
// HAL_NVIC_SystemReset(); | ||
} else if (Ethernet.linkStatus() == LinkOFF) { | ||
DEBUG.println("Ethernet cable is not connected."); | ||
success= false; | ||
} | ||
|
||
if(success) { | ||
// print your local IP address: | ||
DEBUG.print("My IP address: "); | ||
DEBUG.println(Ethernet.localIP()); | ||
} | ||
return success; | ||
} |
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,12 @@ | ||
#pragma once | ||
#include <SPI.h> | ||
#include <Ethernet.h> | ||
|
||
#include "globals.h" | ||
|
||
extern uint8_t macAddr[6]; | ||
extern char macStr[13]; | ||
extern EthernetClient net; | ||
|
||
void initEthernet(); | ||
bool connectEthernet(); |
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 @@ | ||
#pragma once | ||
#include <stdint.h> | ||
|
||
#include "secrets.h" // don't forget to create secrets.h based on the .sample file | ||
|
||
#define DEBUG Serial1 | ||
|
||
#define UPDATE_INTERVAL 5000 // ms | ||
|
||
#define MODUBS_SERIAL Serial3 | ||
#define MODBUS_BAUD 38400 | ||
#define MODBUS_ADDR 1 | ||
|
||
// put InfluxDB credentials into secrets.h | ||
|
||
#define SPI2_MOSI PB15 | ||
#define SPI2_MISO PB14 | ||
#define SPI2_SCK PB13 | ||
#define SPI2_CS PB12 | ||
|
||
#define SPI1_MOSI PA7 | ||
#define SPI1_MISO PA6 | ||
#define SPI1_SCK PA5 | ||
#define SPI1_CS PA4 |
File renamed without changes.
Oops, something went wrong.