#include <FS.h> //this needs to be first, e folosit de salvarea credentialelor pe FS
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson pentru salvarea credentialelor in eprom
#include <WiFiClient.h>
#include <OneWire.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#define DEBUG 0 //0=off 1=on
#define PIR D1 // pinul de date D1 la care se conecteaza PIRul
bool ARM,lastPIR;
unsigned int interval_alarma = 60000; // la ce interval sa trimita notificarile 60 sec = 60000 in cazul in care a re doua alarme consecutive
unsigned long t_alarma=0;
const unsigned long interval_update=1000*60*5; // face update la 5 min
unsigned long t_update=0;
char mqtt_user[9]; //le va lua din FS
char mqtt_devid[5]; //le va lua din FS
char mqttPassword[15]; //urmatoarele le va genera cu functia gen
char mqttSUB[23];
char espName[14];
char mqttESP[23];
char mqttALM[25];
char mqttPIR[23];
char mqttLWT[23];
char mqttSTA[25];
char wifiID[15];
const String model = "NodeMCU PIR";
const String ver = "v2.1.2";
const char* mqttServer = "mqtt.clickhome.ro";
const int mqttPort = 1883;
String mqttMessage;
WiFiClient espClient;
PubSubClient client(espClient);
bool readConfigFile();
void generate_vars(){
strcpy (mqttPassword, "8219CH");
strcat (mqttPassword, mqtt_user);
strcpy (espName, mqtt_user);
strcat (espName, "-");
strcat (espName, mqtt_devid);
strcpy (mqttSUB, "cmnd/");
strcat (mqttSUB, espName);
strcat (mqttSUB, "/ESP");
strcpy (mqttESP, "stat/");
strcat (mqttESP, espName);
strcat (mqttESP, "/ESP");
strcpy (mqttPIR, "stat/");
strcat (mqttPIR, espName);
strcat (mqttPIR, "/PIR");
strcpy (mqttALM, "stat/");
strcat (mqttALM, espName);
strcat (mqttALM, "/ALARM");
strcpy (mqttLWT, "tele/");
strcat (mqttLWT, espName);
strcat (mqttLWT, "/LWT");
strcpy (mqttSTA, "tele/");
strcat (mqttSTA, espName);
strcat (mqttSTA, "/STATE");
strcpy (wifiID, "ClickHome-");
strcat (wifiID, mqtt_devid);
}
String ipToString(IPAddress ip){
String s="";
for (int i=0; i<4; i++)
s += i ? "." + String(ip[i]) : String(ip[i]);
return s;
}
String getMacAddress() {
byte mac[6];
WiFi.macAddress(mac);
String cMac = "";
for (int i = 0; i < 6; ++i) {
cMac += String(mac[i],HEX);
if(i<5)
cMac += "-";
}
cMac.toUpperCase();
return cMac;
}
void reconectez() {
while (String(mqtt_user)==""){
#if DEBUG
Serial.println("Invalid user!");
#endif
delay(99999999);
}
// ma conectez la mqtt server
while (!client.connected()) {
client.setServer(mqttServer, mqttPort);
#if DEBUG
Serial.print("AtloopTempting MQTT connection...");
#endif
// Incerc sa ma reconectez cu LWT din 5 in 5 secunde
if (client.connect(espName, mqtt_user, mqttPassword, mqttLWT, 1, 1, "Offline")) {
#if DEBUG
Serial.println("connected");
#endif
client.publish(mqttLWT,"Online",TRUE);
// trimit informatii utile cand ma conectez
String esp_info = "{\"ESPMac\":\"";
esp_info += getMacAddress();
esp_info += "\",\"IPAddress\":\"";
esp_info += ipToString(WiFi.localIP());
esp_info += "\"}";
String netinfo = "{\"Module\":\"";
netinfo += String (model);
netinfo += "\",\"Version\":\"";
netinfo += String (ver);
netinfo += "\"}";
client.publish(mqttESP, netinfo.c_str(),TRUE);
#if DEBUG
Serial.println(netinfo);
#endif
client.publish(mqttESP, esp_info.c_str(),TRUE);
#if DEBUG
Serial.println(esp_info);
#endif
client.subscribe(mqttSUB);
} else {
#if DEBUG
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 60 seconds");
#endif
// Wait 60 seconds before retrying
delay(60000);
}
}
}
void setup()
{
pinMode(PIR,INPUT_PULLUP);
#if DEBUG
Serial.begin(115200);
#endif
// Mount the filesystem
bool result = SPIFFS.begin();
#if DEBUG
//Serial.println("SPIFFS opened: " + result);
#endif
readConfigFile(); //citesc user si devid din memorie
generate_vars(); //genereaza topicurile de mqtt in baza mqtt_user si mqtt_devid
// ma conectez la AP via wifi
WiFiManager wifi;
wifi.setConfigPortalTimeout(120); // a timeout so the ESP doesn't hang waiting to be configured, for instance after a power failure
wifi.setTimeout(180); // sta AP 3 minute apoi se reseteaza din nou
if (!wifi.autoConnect(wifiID)) {
#if DEBUG
Serial.println("timeout - going to sleep");
#endif
}
delay(200);
reconectez();
client.setCallback(getMessage); //setez functia care parseaza mesajele venite prin mqtt
String json = "{\"ARM\":\"";
json += String (ARM);
json += "\"}";
client.publish(mqttPIR, json.c_str(),TRUE);
update_state(); //facem update la mqtt cu Uptime
}
void getMessage(char* topic, byte* payload, unsigned int length) {
float t = 0;
mqttMessage="";
#if DEBUG
Serial.print("Message arrived in topic: ");
Serial.println(topic);
Serial.print("Message:");
#endif
for (int i = 0; i < length; i++) {
mqttMessage += (char)payload[i];
}
#if DEBUG
Serial.println(mqttMessage);
#endif
// procedura de software update via WEB
if (mqttMessage == "update")
{
String msg="Software update: ";
t_httpUpdate_return ret;
//ESPhttpUpdate.rebootOnUpdate(false);
ret = ESPhttpUpdate.update("http://update.clickhome.ro/pir/arduino.bin");
switch(ret) {
case HTTP_UPDATE_FAILED:
msg.concat(" eroare:");
msg.concat(ESPhttpUpdate.getLastError());
msg.concat(" motiv:");
msg.concat(ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
msg.concat(" no update.");
break;
case HTTP_UPDATE_OK:
msg.concat(" success.");
break;
}
#if DEBUG
Serial.println(msg);
#endif
}
if (mqttMessage == "stare")
{
// citesc starea si o trimit
String json = "{\"ARM\":\"";
json += String (ARM);
json += "\"}";
#if DEBUG
Serial.println(json);
#endif
client.publish(mqttPIR, json.c_str(),TRUE);
}
if (mqttMessage == "ARM" || mqttMessage == "DISARM")
{
ARM = (mqttMessage=="ARM") ? true : false;
String json = "{\"ARM\":\"";
json += String (ARM);
json += "\"}";
client.publish(mqttPIR, json.c_str(),TRUE);
}
// reset la ESP
if (mqttMessage == "reset")
{
String lastwords="Am fost resetat ...";
client.publish(mqttPIR, lastwords.c_str(), TRUE);
delay (3000);
ESP.reset();
delay (5000);
}
}
bool readConfigFile() {
// this opens the config file in read-mode
File f = SPIFFS.open("/config.json", "r");
if (!f) {
#if DEBUG
Serial.println("Configuration file not found");
#endif
return false;
} else {
// we could open the file
size_t size = f.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
// Read and store file contents in buf
f.readBytes(buf.get(), size);
// Closing file
f.close();
// Using dynamic JSON buffer which is not the recommended memory model, but anyway
// See https://github.com/bblanchon/ArduinoJson/wiki/Memory%20model
DynamicJsonBuffer jsonBuffer;
// Parse JSON string
JsonObject& json = jsonBuffer.parseObject(buf.get());
// Test if parsing succeeds.
if (!json.success()) {
#if DEBUG
Serial.println("JSON parseObject() failed");
#endif
return false;
}
#if DEBUG
json.printTo(Serial);
#endif
strcpy(mqtt_user, json["mqtt_user"]);
strcpy(mqtt_devid, json["mqtt_devid"]);
}
return true;
}
void update_state(){ //facem update la mqtt cu Uptime
String json = "{\"Uptime\":";
json += (int)millis()/3600000; //exprimat in ore
json += "}";
client.publish(mqttSTA, json.c_str(),FALSE);
}
void loop()
{
if (!client.connected())
{
reconectez();
}
client.loop();
//facem update la $interval_update cu uptime
if (millis() - t_update > interval_update){
update_state();
t_update=millis();
}
//end update
if (ARM==1){ //daca e armat
if (digitalRead(PIR)!=lastPIR){ //daca s-a schimbat valoarea
if (digitalRead(PIR)==1){ //daca am alarma
if (millis() - t_alarma > interval_alarma){ //daca a trecut timpul de la alarma anterioara
String json = "{\"PIR\":1}";
client.publish(mqttALM, json.c_str(),FALSE);
t_alarma=millis();
#if DEBUG
Serial.print("Trimis mqtt alarm:");
#endif
}
}
#if DEBUG
Serial.print("Direct PIN:");
Serial.println (digitalRead(PIR));
#endif
lastPIR=digitalRead(PIR);
}
}
}