inital commit
This commit is contained in:
parent
bc135ecd66
commit
a7976820b2
7 changed files with 1098 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
include/credentials.h
|
41
include/WMbusFrame.h
Normal file
41
include/WMbusFrame.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#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__
|
230
include/WaterMeter.h
Normal file
230
include/WaterMeter.h
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
Copyright (C) 2020 chester4444@wolke7.net
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _WATERMETER_H_
|
||||
#define _WATERMETER_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "WMBusFrame.h"
|
||||
|
||||
#define MARCSTATE_SLEEP 0x00
|
||||
#define MARCSTATE_IDLE 0x01
|
||||
#define MARCSTATE_XOFF 0x02
|
||||
#define MARCSTATE_VCOON_MC 0x03
|
||||
#define MARCSTATE_REGON_MC 0x04
|
||||
#define MARCSTATE_MANCAL 0x05
|
||||
#define MARCSTATE_VCOON 0x06
|
||||
#define MARCSTATE_REGON 0x07
|
||||
#define MARCSTATE_STARTCAL 0x08
|
||||
#define MARCSTATE_BWBOOST 0x09
|
||||
#define MARCSTATE_FS_LOCK 0x0A
|
||||
#define MARCSTATE_IFADCON 0x0B
|
||||
#define MARCSTATE_ENDCAL 0x0C
|
||||
#define MARCSTATE_RX 0x0D
|
||||
#define MARCSTATE_RX_END 0x0E
|
||||
#define MARCSTATE_RX_RST 0x0F
|
||||
#define MARCSTATE_TXRX_SWITCH 0x10
|
||||
#define MARCSTATE_RXFIFO_OVERFLOW 0x11
|
||||
#define MARCSTATE_FSTXON 0x12
|
||||
#define MARCSTATE_TX 0x13
|
||||
#define MARCSTATE_TX_END 0x14
|
||||
#define MARCSTATE_RXTX_SWITCH 0x15
|
||||
#define MARCSTATE_TXFIFO_UNDERFLOW 0x16
|
||||
|
||||
#define CC1101_GDO0 D2 // GDO0 input interrupt pin
|
||||
|
||||
#define WRITE_BURST 0x40
|
||||
#define READ_SINGLE 0x80
|
||||
#define READ_BURST 0xC0
|
||||
|
||||
#define CC1101_CONFIG_REGISTER READ_SINGLE
|
||||
#define CC1101_STATUS_REGISTER READ_BURST
|
||||
|
||||
#define CC1101_PATABLE 0x3E // PATABLE address
|
||||
#define CC1101_TXFIFO 0x3F // TX FIFO address
|
||||
#define CC1101_RXFIFO 0x3F // RX FIFO address
|
||||
|
||||
#define CC1101_SRES 0x30 // Reset CC1101 chip
|
||||
#define CC1101_SFSTXON 0x31 // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA):
|
||||
// Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround).
|
||||
#define CC1101_SXOFF 0x32 // Turn off crystal oscillator
|
||||
#define CC1101_SCAL 0x33 // Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without
|
||||
// setting manual calibration mode (MCSM0.FS_AUTOCAL=0)
|
||||
#define CC1101_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1
|
||||
#define CC1101_STX 0x35 // In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1.
|
||||
// If in RX state and CCA is enabled: Only go to TX if channel is clear
|
||||
#define CC1101_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable
|
||||
#define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if
|
||||
// WORCTRL.RC_PD=0
|
||||
#define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high
|
||||
#define CC1101_SFRX 0x3A // Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states
|
||||
#define CC1101_SFTX 0x3B // Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states
|
||||
#define CC1101_SWORRST 0x3C // Reset real time clock to Event1 value
|
||||
#define CC1101_SNOP 0x3D // No operation. May be used to get access to the chip status byte
|
||||
|
||||
#define CC1101_IOCFG2 0x00 // GDO2 Output Pin Configuration
|
||||
#define CC1101_IOCFG1 0x01 // GDO1 Output Pin Configuration
|
||||
#define CC1101_IOCFG0 0x02 // GDO0 Output Pin Configuration
|
||||
#define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO Thresholds
|
||||
#define CC1101_SYNC1 0x04 // Sync Word, High Byte
|
||||
#define CC1101_SYNC0 0x05 // Sync Word, Low Byte
|
||||
#define CC1101_PKTLEN 0x06 // Packet Length
|
||||
#define CC1101_PKTCTRL1 0x07 // Packet Automation Control
|
||||
#define CC1101_PKTCTRL0 0x08 // Packet Automation Control
|
||||
#define CC1101_ADDR 0x09 // Device Address
|
||||
#define CC1101_CHANNR 0x0A // Channel Number
|
||||
#define CC1101_FSCTRL1 0x0B // Frequency Synthesizer Control
|
||||
#define CC1101_FSCTRL0 0x0C // Frequency Synthesizer Control
|
||||
#define CC1101_FREQ2 0x0D // Frequency Control Word, High Byte
|
||||
#define CC1101_FREQ1 0x0E // Frequency Control Word, Middle Byte
|
||||
#define CC1101_FREQ0 0x0F // Frequency Control Word, Low Byte
|
||||
#define CC1101_MDMCFG4 0x10 // Modem Configuration
|
||||
#define CC1101_MDMCFG3 0x11 // Modem Configuration
|
||||
#define CC1101_MDMCFG2 0x12 // Modem Configuration
|
||||
#define CC1101_MDMCFG1 0x13 // Modem Configuration
|
||||
#define CC1101_MDMCFG0 0x14 // Modem Configuration
|
||||
#define CC1101_DEVIATN 0x15 // Modem Deviation Setting
|
||||
#define CC1101_MCSM2 0x16 // Main Radio Control State Machine Configuration
|
||||
#define CC1101_MCSM1 0x17 // Main Radio Control State Machine Configuration
|
||||
#define CC1101_MCSM0 0x18 // Main Radio Control State Machine Configuration
|
||||
#define CC1101_FOCCFG 0x19 // Frequency Offset Compensation Configuration
|
||||
#define CC1101_BSCFG 0x1A // Bit Synchronization Configuration
|
||||
#define CC1101_AGCCTRL2 0x1B // AGC Control
|
||||
#define CC1101_AGCCTRL1 0x1C // AGC Control
|
||||
#define CC1101_AGCCTRL0 0x1D // AGC Control
|
||||
#define CC1101_WOREVT1 0x1E // High Byte Event0 Timeout
|
||||
#define CC1101_WOREVT0 0x1F // Low Byte Event0 Timeout
|
||||
#define CC1101_WORCTRL 0x20 // Wake On Radio Control
|
||||
#define CC1101_FREND1 0x21 // Front End RX Configuration
|
||||
#define CC1101_FREND0 0x22 // Front End TX Configuration
|
||||
#define CC1101_FSCAL3 0x23 // Frequency Synthesizer Calibration
|
||||
#define CC1101_FSCAL2 0x24 // Frequency Synthesizer Calibration
|
||||
#define CC1101_FSCAL1 0x25 // Frequency Synthesizer Calibration
|
||||
#define CC1101_FSCAL0 0x26 // Frequency Synthesizer Calibration
|
||||
#define CC1101_RCCTRL1 0x27 // RC Oscillator Configuration
|
||||
#define CC1101_RCCTRL0 0x28 // RC Oscillator Configuration
|
||||
#define CC1101_FSTEST 0x29 // Frequency Synthesizer Calibration Control
|
||||
#define CC1101_PTEST 0x2A // Production Test
|
||||
#define CC1101_AGCTEST 0x2B // AGC Test
|
||||
#define CC1101_TEST2 0x2C // Various Test Settings
|
||||
#define CC1101_TEST1 0x2D // Various Test Settings
|
||||
#define CC1101_TEST0 0x2E // Various Test Settings
|
||||
|
||||
#define CC1101_PARTNUM 0x30 // Chip ID
|
||||
#define CC1101_VERSION 0x31 // Chip ID
|
||||
#define CC1101_FREQEST 0x32 // Frequency Offset Estimate from Demodulator
|
||||
#define CC1101_LQI 0x33 // Demodulator Estimate for Link Quality
|
||||
#define CC1101_RSSI 0x34 // Received Signal Strength Indication
|
||||
#define CC1101_MARCSTATE 0x35 // Main Radio Control State Machine State
|
||||
#define CC1101_WORTIME1 0x36 // High Byte of WOR Time
|
||||
#define CC1101_WORTIME0 0x37 // Low Byte of WOR Time
|
||||
#define CC1101_PKTSTATUS 0x38 // Current GDOx Status and Packet Status
|
||||
#define CC1101_VCO_VC_DAC 0x39 // Current Setting from PLL Calibration Module
|
||||
#define CC1101_TXBYTES 0x3A // Underflow and Number of Bytes
|
||||
#define CC1101_RXBYTES 0x3B // Overflow and Number of Bytes
|
||||
#define CC1101_RCCTRL1_STATUS 0x3C // Last RC Oscillator Calibration Result
|
||||
#define CC1101_RCCTRL0_STATUS 0x3D // Last RC Oscillator Calibration Result
|
||||
|
||||
#define CC1101_DEFVAL_SYNC1 0x54 // Synchronization word, high byte
|
||||
#define CC1101_DEFVAL_SYNC0 0x3D // Synchronization word, low byte
|
||||
#define CC1101_DEFVAL_MCSM1 0x00 // Main Radio Control State Machine Configuration
|
||||
|
||||
#define CC1101_DEFVAL_IOCFG2 0x2E // GDO2 Output Pin Configuration
|
||||
#define CC1101_DEFVAL_IOCFG0 0x06 // GDO0 Output Pin Configuration
|
||||
|
||||
#define CC1101_DEFVAL_FSCTRL1 0x08 // Frequency Synthesizer Control
|
||||
#define CC1101_DEFVAL_FSCTRL0 0x00 // Frequency Synthesizer Control
|
||||
#define CC1101_DEFVAL_FREQ2 0x21 // Frequency Control Word, High Byte
|
||||
#define CC1101_DEFVAL_FREQ1 0x6B // Frequency Control Word, Middle Byte
|
||||
#define CC1101_DEFVAL_FREQ0 0xD0 // Frequency Control Word, Low Byte
|
||||
#define CC1101_DEFVAL_MDMCFG4 0x5C // Modem configuration. Speed = 103 Kbps
|
||||
#define CC1101_DEFVAL_MDMCFG3 0x04 // Modem Configuration
|
||||
#define CC1101_DEFVAL_MDMCFG2 0x06 // Modem Configuration
|
||||
#define CC1101_DEFVAL_MDMCFG1 0x22 // Modem Configuration
|
||||
#define CC1101_DEFVAL_MDMCFG0 0xF8 // Modem Configuration
|
||||
#define CC1101_DEFVAL_CHANNR 0x00 // Channel Number
|
||||
#define CC1101_DEFVAL_DEVIATN 0x44 // Modem Deviation Setting
|
||||
#define CC1101_DEFVAL_FREND1 0xB6 // Front End RX Configuration
|
||||
#define CC1101_DEFVAL_FREND0 0x10 // Front End TX Configuration
|
||||
#define CC1101_DEFVAL_MCSM0 0x18 // Main Radio Control State Machine Configuration
|
||||
#define CC1101_DEFVAL_FOCCFG 0x2E // Frequency Offset Compensation Configuration
|
||||
#define CC1101_DEFVAL_BSCFG 0xBF // Bit Synchronization Configuration
|
||||
#define CC1101_DEFVAL_AGCCTRL2 0x43 // AGC Control
|
||||
#define CC1101_DEFVAL_AGCCTRL1 0x09 // AGC Control
|
||||
#define CC1101_DEFVAL_AGCCTRL0 0xB5 // AGC Control
|
||||
#define CC1101_DEFVAL_FSCAL3 0xEA // Frequency Synthesizer Calibration
|
||||
#define CC1101_DEFVAL_FSCAL2 0x2A // Frequency Synthesizer Calibration
|
||||
#define CC1101_DEFVAL_FSCAL1 0x00 // Frequency Synthesizer Calibration
|
||||
#define CC1101_DEFVAL_FSCAL0 0x1F // Frequency Synthesizer Calibration
|
||||
#define CC1101_DEFVAL_FSTEST 0x59 // Frequency Synthesizer Calibration Control
|
||||
#define CC1101_DEFVAL_TEST2 0x81 // Various Test Settings
|
||||
#define CC1101_DEFVAL_TEST1 0x35 // Various Test Settings
|
||||
#define CC1101_DEFVAL_TEST0 0x09 // Various Test Settings
|
||||
#define CC1101_DEFVAL_PKTCTRL1 0x00 // Packet Automation Control
|
||||
#define CC1101_DEFVAL_PKTCTRL0 0x02 // 2 - infinite length
|
||||
#define CC1101_DEFVAL_ADDR 0x00 // Device Address
|
||||
#define CC1101_DEFVAL_PKTLEN 0x30 // Packet Length
|
||||
#define CC1101_DEFVAL_FIFOTHR 0x00 // RX 4 bytes and TX 61 bytes Thresholds
|
||||
|
||||
class WaterMeter
|
||||
{
|
||||
private:
|
||||
inline void selectCC1101(void);
|
||||
inline void deselectCC1101(void);
|
||||
inline void waitMiso(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 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);
|
||||
|
||||
// receive a wmbus frame
|
||||
void receive(WMBusFrame *payload);
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
WaterMeter(void);
|
||||
|
||||
// startup CC1101 for receiving wmbus mode c
|
||||
void begin();
|
||||
|
||||
// must be called frequently, returns true if a valid frame was received
|
||||
bool isFrameAvailable(void);
|
||||
};
|
||||
|
||||
#endif // _WATERMETER_H_
|
22
platformio.ini
Normal file
22
platformio.ini
Normal file
|
@ -0,0 +1,22 @@
|
|||
; 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:nodemcuv2]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
|
||||
lib_deps =
|
||||
Crypto
|
||||
|
||||
; OTA
|
||||
upload_port = 10.0.0.86
|
||||
#upload_port = 10.14.0.139
|
||||
upload_protocol = espota
|
115
src/WMBusFrame.cpp
Normal file
115
src/WMBusFrame.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Copyright (C) 2020 chester4444@wolke7.net
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "WMbusFrame.h"
|
||||
|
||||
WMBusFrame::WMBusFrame()
|
||||
{
|
||||
aes128.setKey(key, sizeof(key));
|
||||
}
|
||||
|
||||
void WMBusFrame::check()
|
||||
{
|
||||
// check meterId
|
||||
for (uint8_t i = 0; i< 4; i++)
|
||||
{
|
||||
if (meterId[i] != payload[6-i])
|
||||
{
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TBD: check crc
|
||||
isValid = true;
|
||||
}
|
||||
|
||||
void WMBusFrame::printMeterInfo(uint8_t *data, size_t len)
|
||||
{
|
||||
// init positions for compact frame
|
||||
int pos_tt = 9; // total consumption
|
||||
int pos_tg = 13; // target consumption
|
||||
int pos_ic = 7; // info codes
|
||||
int pos_ft = 17; // flow temp
|
||||
int pos_at = 18; // ambient temp
|
||||
|
||||
if (data[2] == 0x78) // long frame
|
||||
{
|
||||
// overwrite it with long frame positions
|
||||
pos_tt = 10;
|
||||
pos_tg = 16;
|
||||
pos_ic = 6;
|
||||
pos_ft = 22;
|
||||
pos_at = 25;
|
||||
}
|
||||
|
||||
char total[10];
|
||||
uint32_t tt = data[pos_tt]
|
||||
+ (data[pos_tt+1] << 8)
|
||||
+ (data[pos_tt+2] << 16)
|
||||
+ (data[pos_tt+3] << 24);
|
||||
snprintf(total, sizeof(total), "%d.%03d", tt/1000, tt%1000 );
|
||||
Serial.printf("total: %s m%c - ", total, 179);
|
||||
|
||||
char target[10];
|
||||
uint32_t tg = data[pos_tg]
|
||||
+ (data[pos_tg+1] << 8)
|
||||
+ (data[pos_tg+2] << 16)
|
||||
+ (data[pos_tg+3] << 24);
|
||||
snprintf(target, sizeof(target), "%d.%03d", tg/1000, tg%1000 );
|
||||
Serial.printf("target: %s m%c - ", target, 179);
|
||||
|
||||
char flow_temp[3];
|
||||
snprintf(flow_temp, sizeof(flow_temp), "%2d", data[pos_ft]);
|
||||
Serial.printf("%s %cC - ", flow_temp, 176);
|
||||
|
||||
char ambient_temp[3];
|
||||
snprintf(ambient_temp, sizeof(ambient_temp), "%2d", data[pos_at]);
|
||||
Serial.printf("%s %cC\n\r", ambient_temp, 176);
|
||||
}
|
||||
|
||||
void WMBusFrame::decode()
|
||||
{
|
||||
// check meterId, CRC
|
||||
check();
|
||||
if (!isValid) return;
|
||||
|
||||
uint8_t cipherLength = length - 2 - 16; // cipher starts at index 16, remove 2 crc bytes
|
||||
memcpy(cipher, &payload[16], cipherLength);
|
||||
|
||||
memset(iv, 0, sizeof(iv)); // padding with 0
|
||||
memcpy(iv, &payload[1], 8);
|
||||
iv[8] = payload[10];
|
||||
memcpy(&iv[9], &payload[12], 4);
|
||||
|
||||
aes128.setIV(iv, sizeof(iv));
|
||||
aes128.decrypt(plaintext, (const uint8_t *) cipher, cipherLength);
|
||||
|
||||
/*
|
||||
Serial.printf("C: ");
|
||||
for (size_t i = 0; i < cipherLength; i++)
|
||||
{
|
||||
Serial.printf("%02X", cipher[i]);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.printf("P(%d): ", cipherLength);
|
||||
for (size_t i = 0; i < cipherLength; i++)
|
||||
{
|
||||
Serial.printf("%02X", plaintext[i]);
|
||||
}
|
||||
Serial.println();
|
||||
*/
|
||||
|
||||
printMeterInfo(plaintext, cipherLength);
|
||||
}
|
265
src/WaterMeter.cpp
Normal file
265
src/WaterMeter.cpp
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
Copyright (C) 2020 chester4444@wolke7.net
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "WaterMeter.h"
|
||||
|
||||
WaterMeter::WaterMeter()
|
||||
{
|
||||
}
|
||||
|
||||
// ChipSelect assert
|
||||
inline void WaterMeter::selectCC1101(void)
|
||||
{
|
||||
digitalWrite(SS, LOW);
|
||||
}
|
||||
|
||||
// ChipSelect deassert
|
||||
inline void WaterMeter::deselectCC1101(void)
|
||||
{
|
||||
digitalWrite(SS, HIGH);
|
||||
}
|
||||
|
||||
// wait for MISO pulling down
|
||||
inline void WaterMeter::waitMiso(void)
|
||||
{
|
||||
while(digitalRead(MISO) == HIGH);
|
||||
}
|
||||
|
||||
// write a single register of CC1101
|
||||
void WaterMeter::writeReg(uint8 regAddr, uint8 value)
|
||||
{
|
||||
selectCC1101(); // Select CC1101
|
||||
waitMiso(); // Wait until MISO goes low
|
||||
SPI.transfer(regAddr); // Send register address
|
||||
SPI.transfer(value); // Send value
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
}
|
||||
|
||||
// send a strobe command to CC1101
|
||||
void WaterMeter::cmdStrobe(uint8 cmd)
|
||||
{
|
||||
selectCC1101(); // Select CC1101
|
||||
delayMicroseconds(5);
|
||||
waitMiso(); // Wait until MISO goes low
|
||||
SPI.transfer(cmd); // Send strobe command
|
||||
delayMicroseconds(5);
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
}
|
||||
|
||||
// read CC1101 register (status or configuration)
|
||||
uint8 WaterMeter::readReg(uint8 regAddr, uint8 regType)
|
||||
{
|
||||
uint8 addr, val;
|
||||
|
||||
addr = regAddr | regType;
|
||||
selectCC1101(); // Select CC1101
|
||||
waitMiso(); // Wait until MISO goes low
|
||||
SPI.transfer(addr); // Send register address
|
||||
val = SPI.transfer(0x00); // Read result
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
//
|
||||
void WaterMeter::readBurstReg(uint8 * buffer, uint8 regAddr, uint8 len)
|
||||
{
|
||||
uint8 addr, i;
|
||||
|
||||
addr = regAddr | READ_BURST;
|
||||
selectCC1101(); // Select CC1101
|
||||
delayMicroseconds(5);
|
||||
waitMiso(); // Wait until MISO goes low
|
||||
SPI.transfer(addr); // Send register address
|
||||
for(i=0 ; i<len ; i++)
|
||||
buffer[i] = SPI.transfer(0x00); // Read result byte by byte
|
||||
delayMicroseconds(2);
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
}
|
||||
|
||||
// power on reset
|
||||
void WaterMeter::reset(void)
|
||||
{
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
delayMicroseconds(3);
|
||||
|
||||
digitalWrite(MOSI, LOW);
|
||||
digitalWrite(SCK, HIGH); // see CC1101 datasheet 11.3
|
||||
|
||||
selectCC1101(); // Select CC1101
|
||||
delayMicroseconds(3);
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
delayMicroseconds(45); // at least 40 us
|
||||
|
||||
selectCC1101(); // Select CC1101
|
||||
|
||||
waitMiso(); // Wait until MISO goes low
|
||||
SPI.transfer(CC1101_SRES); // Send reset command strobe
|
||||
waitMiso(); // Wait until MISO goes low
|
||||
|
||||
deselectCC1101(); // Deselect CC1101
|
||||
}
|
||||
|
||||
// set IDLE state, flush FIFO and (re)start receiver
|
||||
void WaterMeter::startReceiver(void)
|
||||
{
|
||||
cmdStrobe(CC1101_SIDLE); // Enter IDLE state
|
||||
while (readReg(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) != MARCSTATE_IDLE);
|
||||
{
|
||||
delay(1);
|
||||
}
|
||||
|
||||
cmdStrobe(CC1101_SFRX); // flush receive queue
|
||||
|
||||
cmdStrobe(CC1101_SRX); // Enter RX state
|
||||
while (readReg(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) != MARCSTATE_RX);
|
||||
{
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
// initialize all the CC1101 registers
|
||||
void WaterMeter::initializeRegisters(void)
|
||||
{
|
||||
writeReg(CC1101_IOCFG2, CC1101_DEFVAL_IOCFG2);
|
||||
writeReg(CC1101_IOCFG0, CC1101_DEFVAL_IOCFG0);
|
||||
writeReg(CC1101_FIFOTHR, CC1101_DEFVAL_FIFOTHR);
|
||||
writeReg(CC1101_PKTLEN, CC1101_DEFVAL_PKTLEN);
|
||||
writeReg(CC1101_PKTCTRL1, CC1101_DEFVAL_PKTCTRL1);
|
||||
writeReg(CC1101_PKTCTRL0, CC1101_DEFVAL_PKTCTRL0);
|
||||
writeReg(CC1101_SYNC1, CC1101_DEFVAL_SYNC1);
|
||||
writeReg(CC1101_SYNC0, CC1101_DEFVAL_SYNC0);
|
||||
writeReg(CC1101_ADDR, CC1101_DEFVAL_ADDR);
|
||||
writeReg(CC1101_CHANNR, CC1101_DEFVAL_CHANNR);
|
||||
writeReg(CC1101_FSCTRL1, CC1101_DEFVAL_FSCTRL1);
|
||||
writeReg(CC1101_FSCTRL0, CC1101_DEFVAL_FSCTRL0);
|
||||
writeReg(CC1101_FREQ2, CC1101_DEFVAL_FREQ2);
|
||||
writeReg(CC1101_FREQ1, CC1101_DEFVAL_FREQ1);
|
||||
writeReg(CC1101_FREQ0, CC1101_DEFVAL_FREQ0);
|
||||
writeReg(CC1101_MDMCFG4, CC1101_DEFVAL_MDMCFG4);
|
||||
writeReg(CC1101_MDMCFG3, CC1101_DEFVAL_MDMCFG3);
|
||||
writeReg(CC1101_MDMCFG2, CC1101_DEFVAL_MDMCFG2);
|
||||
writeReg(CC1101_MDMCFG1, CC1101_DEFVAL_MDMCFG1);
|
||||
writeReg(CC1101_MDMCFG0, CC1101_DEFVAL_MDMCFG0);
|
||||
writeReg(CC1101_DEVIATN, CC1101_DEFVAL_DEVIATN);
|
||||
writeReg(CC1101_MCSM1, CC1101_DEFVAL_MCSM1);
|
||||
writeReg(CC1101_MCSM0, CC1101_DEFVAL_MCSM0);
|
||||
writeReg(CC1101_FOCCFG, CC1101_DEFVAL_FOCCFG);
|
||||
writeReg(CC1101_BSCFG, CC1101_DEFVAL_BSCFG);
|
||||
writeReg(CC1101_AGCCTRL2, CC1101_DEFVAL_AGCCTRL2);
|
||||
writeReg(CC1101_AGCCTRL1, CC1101_DEFVAL_AGCCTRL1);
|
||||
writeReg(CC1101_AGCCTRL0, CC1101_DEFVAL_AGCCTRL0);
|
||||
writeReg(CC1101_FREND1, CC1101_DEFVAL_FREND1);
|
||||
writeReg(CC1101_FREND0, CC1101_DEFVAL_FREND0);
|
||||
writeReg(CC1101_FSCAL3, CC1101_DEFVAL_FSCAL3);
|
||||
writeReg(CC1101_FSCAL2, CC1101_DEFVAL_FSCAL2);
|
||||
writeReg(CC1101_FSCAL1, CC1101_DEFVAL_FSCAL1);
|
||||
writeReg(CC1101_FSCAL0, CC1101_DEFVAL_FSCAL0);
|
||||
writeReg(CC1101_FSTEST, CC1101_DEFVAL_FSTEST);
|
||||
writeReg(CC1101_TEST2, CC1101_DEFVAL_TEST2);
|
||||
writeReg(CC1101_TEST1, CC1101_DEFVAL_TEST1);
|
||||
writeReg(CC1101_TEST0, CC1101_DEFVAL_TEST0);
|
||||
}
|
||||
|
||||
volatile boolean packetAvailable = false;
|
||||
void ICACHE_RAM_ATTR GD0_ISR(void);
|
||||
|
||||
// handle interrupt from CC1101 via GDO0
|
||||
void GD0_ISR(void) {
|
||||
// set the flag that a package is available
|
||||
packetAvailable = true;
|
||||
}
|
||||
|
||||
// should be called frequently, handles the ISR flag
|
||||
// does the frame checkin and decryption
|
||||
bool WaterMeter::isFrameAvailable(void)
|
||||
{
|
||||
if (packetAvailable)
|
||||
{
|
||||
//Serial.println("packet received");
|
||||
// Disable wireless reception interrupt
|
||||
detachInterrupt(digitalPinToInterrupt(CC1101_GDO0));
|
||||
|
||||
// clear the flag
|
||||
packetAvailable = false;
|
||||
|
||||
WMBusFrame frame;
|
||||
|
||||
receive(&frame);
|
||||
|
||||
// Enable wireless reception interrupt
|
||||
attachInterrupt(digitalPinToInterrupt(CC1101_GDO0), GD0_ISR, FALLING);
|
||||
return frame.isValid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize CC1101 to receive WMBus MODE C1
|
||||
void WaterMeter::begin()
|
||||
{
|
||||
pinMode(SS, OUTPUT); // SS Pin -> Output
|
||||
SPI.begin(); // Initialize SPI interface
|
||||
pinMode(CC1101_GDO0, INPUT); // Config GDO0 as input
|
||||
|
||||
reset(); // power on CC1101
|
||||
|
||||
//Serial.println("Setting CC1101 registers");
|
||||
initializeRegisters(); // init CC1101 registers
|
||||
|
||||
cmdStrobe(CC1101_SCAL);
|
||||
delay(1);
|
||||
|
||||
attachInterrupt(digitalPinToInterrupt(CC1101_GDO0), GD0_ISR, FALLING);
|
||||
startReceiver();
|
||||
}
|
||||
|
||||
// reads a single byte from the RX fifo
|
||||
uint8_t WaterMeter::readByteFromFifo(void)
|
||||
{
|
||||
return readReg(CC1101_RXFIFO, CC1101_CONFIG_REGISTER);
|
||||
}
|
||||
|
||||
// handles a received frame and restart the CC1101 receiver
|
||||
void WaterMeter::receive(WMBusFrame * frame)
|
||||
{
|
||||
// read preamble, should be 0x543D
|
||||
uint8_t p1 = readByteFromFifo();
|
||||
uint8_t p2 = readByteFromFifo();
|
||||
//Serial.printf("%02x%02x", p1, p2);
|
||||
|
||||
uint8_t payloadLength = readByteFromFifo();
|
||||
|
||||
// is it Mode C1, frame B and does it fit in the buffer
|
||||
if ( (payloadLength < WMBusFrame::MAX_LENGTH )
|
||||
&& (p1 == 0x54) && (p2 == 0x3D) )
|
||||
{
|
||||
// 3rd byte is payload length
|
||||
frame->length = payloadLength;
|
||||
|
||||
//Serial.printf("%02X", lfield);
|
||||
|
||||
// starting with 1! index 0 is lfield
|
||||
for (int i = 0; i < payloadLength; i++)
|
||||
{
|
||||
frame->payload[i] = readByteFromFifo();
|
||||
}
|
||||
|
||||
// do some checks: my meterId, crc ok
|
||||
frame->decode();
|
||||
}
|
||||
|
||||
// flush RX fifo and restart receiver
|
||||
startReceiver();
|
||||
//Serial.printf("rxStatus: 0x%02x\n\r", readStatusReg(CC1101_RXBYTES));
|
||||
}
|
419
src/main.cpp
Normal file
419
src/main.cpp
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
Copyright (C) 2020 chester4444@wolke7.net
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <SoftwareSerial.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include "WaterMeter.h"
|
||||
#include "credentials.h"
|
||||
|
||||
#define ESP_NAME "ESP-Meter"
|
||||
|
||||
// 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 DEBUG 0
|
||||
|
||||
//Wifi settings: SSID, PW, MQTT broker
|
||||
#define NUM_SSID_CREDENTIALS 3
|
||||
const char *credentials[NUM_SSID_CREDENTIALS][4] =
|
||||
// SSID, PW, MQTT
|
||||
{ {SSID1, PW1, MQTT1 }
|
||||
, {SSID2, PW2, MQTT2 }
|
||||
, {SSID3, PW3, MQTT3 }
|
||||
};
|
||||
|
||||
WaterMeter waterMeter;
|
||||
|
||||
WiFiClient espMqttClient;
|
||||
PubSubClient mqttClient(espMqttClient);
|
||||
|
||||
char MyIp[16];
|
||||
int cred = -1;
|
||||
|
||||
int getWifiToConnect(int numSsid)
|
||||
{
|
||||
for (int i = 0; i < NUM_SSID_CREDENTIALS; i++)
|
||||
{
|
||||
//Serial.println(WiFi.SSID(i));
|
||||
|
||||
for (int j = 0; j < numSsid; ++j)
|
||||
{
|
||||
/*Serial.print(j);
|
||||
Serial.print(": ");
|
||||
Serial.print(WiFi.SSID(i).c_str());
|
||||
Serial.print(" = ");
|
||||
Serial.println(credentials[j][0]);*/
|
||||
if (strcmp(WiFi.SSID(j).c_str(), credentials[i][0]) == 0)
|
||||
{
|
||||
Serial.println("Credentials found for: ");
|
||||
Serial.println(credentials[i][0]);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// connect to wifi – returns true if successful or false if not
|
||||
bool ConnectWifi(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
Serial.println("starting scan");
|
||||
// scan for nearby networks:
|
||||
int numSsid = WiFi.scanNetworks();
|
||||
|
||||
Serial.print("scanning WIFI, found ");
|
||||
Serial.print(numSsid);
|
||||
Serial.println(" available access points:");
|
||||
|
||||
if (numSsid == -1)
|
||||
{
|
||||
Serial.println("Couldn't get a wifi connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numSsid; i++)
|
||||
{
|
||||
Serial.print(i+1);
|
||||
Serial.print(") ");
|
||||
Serial.println(WiFi.SSID(i));
|
||||
}
|
||||
|
||||
// search for given credentials
|
||||
cred = getWifiToConnect(numSsid);
|
||||
if (cred == -1)
|
||||
{
|
||||
Serial.println("No Wifi!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to connect
|
||||
WiFi.begin(credentials[cred][0], credentials[cred][1]);
|
||||
Serial.println("");
|
||||
Serial.print("Connecting to WiFi ");
|
||||
Serial.println(credentials[cred][0]);
|
||||
|
||||
i = 0;
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(300);
|
||||
Serial.print(".");
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(300);
|
||||
if (i++ > 30)
|
||||
{
|
||||
// giving up
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mqttDebug(const char* debug_str)
|
||||
{
|
||||
String s="/watermeter/debug";
|
||||
mqttClient.publish(s.c_str(), debug_str);
|
||||
}
|
||||
|
||||
void mqttCallback(char* topic, byte* payload, unsigned int len)
|
||||
{
|
||||
// create a local copies of topic and payload
|
||||
// PubSubClient overwrites it internally
|
||||
String t(topic);
|
||||
byte *p = new byte[len];
|
||||
memcpy(p, payload, len);
|
||||
|
||||
/* Serial.print("MQTT-RECV: ");
|
||||
Serial.print(topic);
|
||||
Serial.print(" ");
|
||||
Serial.println((char)payload[0]); // FIXME LEN
|
||||
*/
|
||||
if (strstr(topic, "/smarthomeNG/start"))
|
||||
{
|
||||
if (len == 4) // True
|
||||
{
|
||||
// maybe to something
|
||||
}
|
||||
}
|
||||
else if (strstr(topic, "/espmeter/reset"))
|
||||
{
|
||||
if (len == 4) // True
|
||||
{
|
||||
// maybe to something
|
||||
const char *topic = "/espmeter/reset/status";
|
||||
const char *msg = "False";
|
||||
mqttClient.publish(topic, msg);
|
||||
mqttClient.loop();
|
||||
delay(200);
|
||||
|
||||
// reboot
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
// and of course, free it
|
||||
delete[] p;
|
||||
}
|
||||
|
||||
bool mqttConnect()
|
||||
{
|
||||
mqttClient.setServer(credentials[cred][2], 1883);
|
||||
mqttClient.setCallback(mqttCallback);
|
||||
|
||||
// connect client to retainable last will message
|
||||
return mqttClient.connect(ESP_NAME, "/watermeter/online", 0, true, "False");
|
||||
}
|
||||
|
||||
void mqttSubscribe()
|
||||
{
|
||||
String s;
|
||||
// publish online status
|
||||
s = "/watermeter/online";
|
||||
mqttClient.publish(s.c_str(), "True", true);
|
||||
// Serial.print("MQTT-SEND: ");
|
||||
// Serial.print(s);
|
||||
// Serial.println(" True");
|
||||
|
||||
// publish ip address
|
||||
s="/watermeter/ipaddr";
|
||||
IPAddress MyIP = WiFi.localIP();
|
||||
snprintf(MyIp, 16, "%d.%d.%d.%d", MyIP[0], MyIP[1], MyIP[2], MyIP[3]);
|
||||
mqttClient.publish(s.c_str(), MyIp, true);
|
||||
// Serial.print("MQTT-SEND: ");
|
||||
// Serial.print(s);
|
||||
// Serial.print(" ");
|
||||
// Serial.println(MyIp);
|
||||
|
||||
// if smarthome.py restarts -> publish init values
|
||||
s = "/smarthomeNG/start";
|
||||
mqttClient.subscribe(s.c_str());
|
||||
|
||||
// if True; meter data are published every 5 seconds
|
||||
// if False: meter data are published once a minute
|
||||
s = "/watermeter/liveData";
|
||||
mqttClient.subscribe(s.c_str());
|
||||
|
||||
// if True -> perform an reset
|
||||
s = "/espmeter/reset";
|
||||
mqttClient.subscribe(s.c_str());
|
||||
}
|
||||
|
||||
void setupOTA()
|
||||
{
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
ArduinoOTA.setHostname(ESP_NAME);
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword((const char *)"123");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
||||
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
||||
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
||||
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
||||
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
}
|
||||
|
||||
// receive encrypted packets -> send it via MQTT to decrypter
|
||||
void waterMeterLoop()
|
||||
{
|
||||
if (waterMeter.isFrameAvailable())
|
||||
{
|
||||
// publish meter info via MQTT
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
waterMeter.begin();
|
||||
Serial.println("Setup done...");
|
||||
}
|
||||
|
||||
enum ControlStateType
|
||||
{ StateInit
|
||||
, StateNotConnected
|
||||
, StateWifiConnect
|
||||
, StateMqttConnect
|
||||
, StateConnected
|
||||
, StateOperating
|
||||
};
|
||||
ControlStateType ControlState = StateInit;
|
||||
|
||||
void loop()
|
||||
{
|
||||
switch (ControlState)
|
||||
{
|
||||
case StateInit:
|
||||
//Serial.println("StateInit:");
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
ControlState = StateNotConnected;
|
||||
break;
|
||||
|
||||
case StateNotConnected:
|
||||
//Serial.println("StateNotConnected:");
|
||||
|
||||
ControlState = StateWifiConnect;
|
||||
break;
|
||||
|
||||
case StateWifiConnect:
|
||||
//Serial.println("StateWifiConnect:");
|
||||
// station mode
|
||||
ConnectWifi();
|
||||
|
||||
delay(500);
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(credentials[cred][0]); // FIXME
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
setupOTA();
|
||||
|
||||
ControlState = StateMqttConnect;
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("");
|
||||
Serial.println("Connection failed.");
|
||||
|
||||
// try again
|
||||
ControlState = StateNotConnected;
|
||||
|
||||
// reboot
|
||||
ESP.restart();
|
||||
}
|
||||
break;
|
||||
|
||||
case StateMqttConnect:
|
||||
Serial.println("StateMqttConnect:");
|
||||
digitalWrite(LED_BUILTIN, HIGH); // off
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
ControlState = StateNotConnected;
|
||||
break; // exit (hopefully) switch statement
|
||||
}
|
||||
|
||||
Serial.print("try to connect to MQTT server ");
|
||||
Serial.println(credentials[cred][2]); // FIXME
|
||||
|
||||
if (mqttConnect())
|
||||
{
|
||||
ControlState = StateConnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("MQTT connect failed");
|
||||
|
||||
delay(1000);
|
||||
// try again
|
||||
}
|
||||
ArduinoOTA.handle();
|
||||
|
||||
break;
|
||||
|
||||
case StateConnected:
|
||||
Serial.println("StateConnected:");
|
||||
|
||||
if (!mqttClient.connected())
|
||||
{
|
||||
ControlState = StateMqttConnect;
|
||||
delay(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
// subscribe to given topics
|
||||
mqttSubscribe();
|
||||
|
||||
ControlState = StateOperating;
|
||||
digitalWrite(LED_BUILTIN, LOW); // on
|
||||
Serial.println("StateOperating:");
|
||||
//mqttDebug("up and running");
|
||||
}
|
||||
ArduinoOTA.handle();
|
||||
|
||||
break;
|
||||
|
||||
case StateOperating:
|
||||
//Serial.println("StateOperating:");
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
ControlState = StateWifiConnect;
|
||||
break; // exit (hopefully switch statement)
|
||||
}
|
||||
|
||||
if (!mqttClient.connected())
|
||||
{
|
||||
Serial.println("not connected to MQTT server");
|
||||
ControlState = StateMqttConnect;
|
||||
}
|
||||
|
||||
// here we go
|
||||
waterMeterLoop();
|
||||
|
||||
mqttClient.loop();
|
||||
|
||||
ArduinoOTA.handle();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
Serial.println("Error: invalid ControlState");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue