added CRC check, README update, bug fixes,
This commit is contained in:
parent
b076743f54
commit
ff61047706
13 changed files with 769 additions and 434 deletions
|
@ -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__
|
|
@ -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
67
include/config_template.h
Normal 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__
|
31
include/credentials_template.h
Normal file
31
include/credentials_template.h
Normal 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__
|
|
@ -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
15
include/utils.h
Normal 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__
|
Loading…
Add table
Add a link
Reference in a new issue