esp-multical21/src/main.cpp
2020-02-29 22:11:52 +01:00

419 lines
No EOL
9.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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