/*
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 .
*/
#include
#include
#include
#include
#include
#include
#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");
}
}