added CRC check, README update, bug fixes,

This commit is contained in:
Chester 2023-11-26 19:05:51 +01:00
parent b076743f54
commit ff61047706
13 changed files with 769 additions and 434 deletions

View file

@ -1,41 +0,0 @@
#ifndef __WMBUS_FRAME__
#define __WMBUS_FRAME__
#include <Arduino.h>
#include <Crypto.h>
#include <AES.h>
#include <CTR.h>
#include "credentials.h"
class WMBusFrame
{
public:
static const uint8_t MAX_LENGTH = 64;
private:
CTR<AESSmall128> aes128;
const uint8_t meterId[4] = { W_SERIAL_NUMBER }; // Multical21 serial number
const uint8_t key[16] = { W_ENCRYPTION_KEY }; // AES-128 key
uint8_t cipher[MAX_LENGTH];
uint8_t plaintext[MAX_LENGTH];
uint8_t iv[16];
void check(void);
void printMeterInfo(uint8_t *data, size_t len);
public:
// check frame and decrypt it
void decode(void);
// true, if meter information is valid for the last received frame
bool isValid = false;
// payload length
uint8_t length = 0;
// payload data
uint8_t payload[MAX_LENGTH];
// constructor
WMBusFrame();
};
#endif // __WMBUS_FRAME__

View file

@ -17,7 +17,12 @@
#include <Arduino.h>
#include <SPI.h>
#include "WMBusFrame.h"
#include <Crypto.h>
#include <AES.h>
#include <CTR.h>
#include <PubSubClient.h>
#include "config.h"
#include "utils.h"
#define MARCSTATE_SLEEP 0x00
#define MARCSTATE_IDLE 0x01
@ -179,50 +184,72 @@
class WaterMeter
{
private:
const uint32_t RECEIVE_TIMEOUT = 300000UL; // in millis
const uint32_t PACKET_TIMEOUT = 180000UL; // in seconds
uint32_t lastPacketDecoded = -PACKET_TIMEOUT;
uint32_t lastFrameReceived = 0;
volatile boolean packetAvailable = false;
uint8_t meterId[4];
uint8_t aesKey[16];
inline void selectCC1101(void);
inline void deselectCC1101(void);
inline void waitMiso(void);
static const uint8_t MAX_LENGTH = 64;
CTR<AESSmall128> aes128;
uint8_t cipher[MAX_LENGTH];
uint8_t plaintext[MAX_LENGTH];
uint8_t iv[16];
bool isValid = false; // true, if meter information is valid for the last received frame
uint8_t length = 0; // payload length
uint8_t payload[MAX_LENGTH]; // payload data
uint32_t totalWater;
uint32_t targetWater;
uint32_t lastTarget=0;
uint8_t flowTemp;
uint8_t ambientTemp;
uint8_t infoCodes;
PubSubClient &mqttClient;
bool mqttEnabled;
// reset HW and restart receiver
void restartRadio(void);
// flush fifo and (re)start receiver
void startReceiver(void);
// burst write registers of cc1101
void writeBurstReg(uint8_t regaddr, uint8_t* buffer, uint8_t len);
// burst read registers of cc1101
//void writeBurstReg(uint8_t regaddr, uint8_t* buffer, uint8_t len);
void readBurstReg(uint8_t * buffer, uint8_t regaddr, uint8_t len);
// strobe command
void cmdStrobe(uint8_t cmd);
// read a register of cc1101
uint8_t readReg(uint8_t regaddr, uint8_t regtype);
// read a byte from fifo
uint8_t readByteFromFifo(void);
// write a register of cc1101
void writeReg(uint8_t regaddr, uint8_t value);
// initialize cc1101 registers
void initializeRegisters(void);
// reset cc1101
void reset(void);
// static ISR calls instanceISR via this pointer
IRAM_ATTR static void cc1101Isr(void *p);
// receive a wmbus frame
void receive(WMBusFrame *payload);
void receive(void); // read frame from CC1101
bool checkFrame(void); // check id, CRC
void getMeterInfo(uint8_t *data, size_t len);
void publishMeterInfo();
public:
// constructor
WaterMeter(void);
WaterMeter(PubSubClient &mqtt);
void enableMqtt(bool enable);
// startup CC1101 for receiving wmbus mode c
void begin();
void begin(uint8_t *key, uint8_t *id);
// must be called frequently, returns true if a valid frame was received
bool isFrameAvailable(void);
// must be called frequently
void loop(void);
IRAM_ATTR void instanceCC1101Isr();
};
#endif // _WATERMETER_H_
#endif // _WATERMETER_H_

67
include/config_template.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include <vector>
#define DEBUG 1 // set to 1 for detailed logging at UART
#define ESP_NAME "esp-multical21" // for dns resolving
#define MQTT_PREFIX "watermeter/0" // mqtt prefix topic
#define MQTT_total "/total"
#define MQTT_target "/target"
#define MQTT_ftemp "/flowtemp"
#define MQTT_atemp "/ambienttemp"
#define MQTT_info "/infocode"
// ask your water supplier for your personal encryption key
#define ENCRYPTION_KEY 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
// serial number is printed on your multical21
#define SERIAL_NUMBER 0x12, 0x34, 0x56, 0x78
// WIFI configuration, supports more than one WIFI, first found first served
// if you dont use MQTT, leave broker/user/pass empty ("")
// if you dont need user/pass for MQTT, leave it empty ("")
struct CREDENTIAL {
char const* ssid; // Wifi ssid
char const* password; // Wifi password
char const* mqtt_broker; // MQTT broker ip address
char const* mqtt_username; // MQTT username
char const* mqtt_password; // MQTT password
};
// more than one wifi credentials are supported, upper one wins
// "ssid", "wifi_passphrase", "mqtt_broker", "mqtt_username", "mqtt_password"
std::vector<CREDENTIAL> const credentials = {
{ "ssid1", "********", "", "", ""} // no MQTT
, { "ssid2", "********", "10.14.0.1", "", ""} // MQTT without auth
, { "ssid3", "********", "10.0.0.111", "mqttuser", "mqtt1234"} // MQTT with auth
};
#if defined(ESP8266)
// Attach CC1101 pins to ESP8266 SPI pins
// VCC => 3V3
// GND => GND
// CSN => D8
// MOSI => D7
// MISO => D6
// SCK => D5
// GD0 => D2 A valid interrupt pin for your platform (defined below this)
// GD2 => not connected
#define CC1101_GDO0 D2 // GDO0 input interrupt pin
#define PIN_LED_BUILTIN D4
#elif defined(ESP32)
// Attach CC1101 pins to ESP32 SPI pins
// VCC => 3V3
// GND => GND
// CSN => 4
// MOSI => 23
// MISO => 19
// SCK => 18
// GD0 => 32 any valid interrupt pin for your platform will do
// GD2 => not connected
// attach CC1101 pins to ESP32 SPI pins
#define CC1101_GDO0 32
#define PIN_LED_BUILTIN 2
#endif
#endif // __CONFIG_H__

View file

@ -0,0 +1,31 @@
#ifndef __CREDENTIALS_H__
#define __CREDENTIALS_H__
#include <vector>
// ask your supplier for your personal encryption key (16 bytes)
#define ENCRYPTION_KEY 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
// serial number is printed on your multical21
#define SERIAL_NUMBER 0x12, 0x34, 0x56, 0x78
// WIFI configuration, supports more than one WIFI, first found first served
// if you dont use MQTT, leave it empty ("")
// if you dont need user/pass for MQTT, leave it empty ("")
struct CREDENTIAL {
char const* ssid;
char const* password;
char const* mqtt_broker; // MQTT broker
char const* mqtt_username; // MQTT username
char const* mqtt_password; // MQTT password
};
CREDENTIAL currentWifi; // global to store found wifi
// "ssid", "wifi_passphrase", "mqtt_broker", "mqtt_username", "mqtt_password"
std::vector<CREDENTIAL> const credentials = {
{ "ssid1", "********", "", "", ""} // no MQTT - just serial output
, { "ssid3", "********", "10.0.0.11", "", ""} // MQTT without authentication
, { "ssid2", "********", "10.0.0.10", "loxone", "loxy1234"} // MQTT with user/pass
};
#endif // __CREDENTIALS_H__

View file

@ -1,33 +0,0 @@
#ifndef __HWCONFIG_H__
#define __HWCONFIG_H__
#if defined(ESP8266)
// Attach CC1101 pins to ESP8266 SPI pins
// VCC => 3V3
// GND => GND
// CSN => D8
// MOSI => D7
// MISO => D6
// SCK => D5
// GD0 => D2 A valid interrupt pin for your platform (defined below this)
// GD2 => not connected
#define CC1101_GDO0 D2 // GDO0 input interrupt pin
#define PIN_LED_BUILTIN D4
#elif defined(ESP32)
// Attach CC1101 pins to ESP32 SPI pins
// VCC => 3V3
// GND => GND
// CSN => 4
// MOSI => 23
// MISO => 19
// SCK => 18
// GD0 => 32 any valid interrupt pin for your platform will do
// GD2 => not connected
// attach CC1101 pins to ESP32 SPI pins
#define CC1101_GDO0 32
#define PIN_LED_BUILTIN 2
#endif
#endif //__HWCONFIG_H__

15
include/utils.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef __UTILS_H__
#define __UTILS_H__
#include <Arduino.h>
#include <inttypes.h>
void printHex(uint8_t * buf, size_t len);
uint16_t crcEN13575(uint8_t *payload, uint16_t length);
uint16_t mirror(uint16_t crc, uint8_t bitnum);
uint16_t crcInternal(uint8_t *p, uint16_t len, uint16_t poly, uint16_t init, bool revIn, bool revOut);
void bin2hex(char *xp, uint8_t *bb, int n);
void hex2bin(const char *in, size_t len, uint8_t *out);
#endif //__UTILS_H__