Subversion Repositories ESP32_P1_Meter

Rev

Rev 4 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

////////////////////////////////  Include libraries  /////////////////////////////////////////
#include <Arduino.h>
#include <ArduinoOTA.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <FS.h>
#include <LittleFS.h>
#include <ezButton.h>
#include <PubSubClient.h>              // https://github.com/knolleary/pubsubclient/
#include <AsyncFsWebServer.h>          //https://github.com/cotestatnt/async-esp-fs-webserver

////////////////////////////////  Include settings  /////////////////////////////////////////
#include "settings.h"

////////////////////////////////  Define constants  /////////////////////////////////////////
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
#define FILESYSTEM LittleFS
#define MYTZ "CET-1CEST,M3.5.0,M10.5.0/3"          // Timezone definition to get properly time from NTP server
#ifndef Default_MQTT_Port
  #define Default_MQTT_Port "1883"
#endif
#define BTN_SAVE  5
// added labels for /setup webpage
#define USER_LABEL "Username"
#define PASSWORD_LABEL "Password"
#define EXTERNAL_ANTENNA_LABEL "External (Extra Internal) Antenna"

#define MQTT_ENABLED_LABEL "MQTT Enabled"
#define MQTT_CLIENT_LABEL "MQTT Client"
#define MQTT_SERVER_LABEL "MQTT Server"
#define MQTT_PORT_LABEL "MQTT Server Port"
#define MQTT_USER_LABEL "MQTT User"
#define MQTT_PASSWORD_LABEL "MQTT Password"
#define MQTT_TOPIC_LABEL "MQTT Topic"

#define SHORT_PRESS_TIME_MIN 1000 // 1 second
#define SHORT_PRESS_TIME_MAX 2500 // 2.5 seconds
#define LONG_PRESS_TIME  10000 // 10 seconds

const uint8_t logo_png[] PROGMEM = {
  0x89, 0x50, 0x4E, 0x47, /* ... */
};

////////////////////////////////  Create Objects  /////////////////////////////////////////
struct tm Time;
Ticker ticker;
AsyncFsWebServer server(FILESYSTEM, 80, "P1_Meter");
ezButton button(9); // create ezButton object that attach to pin GPIO9


// * Define variables
bool captiveRun = false;
// Setup Settings
String Username = "admin";
String Password = "admin";
bool External_Antenna = true;
// MQTT Settings
bool MQTT_Enabled = false;
String MQTT_Client = "ESP32_P1_Meter";
String MQTT_Server = "";
//uint16_t MQTT_Port = Default_MQTT_Port;
String MQTT_Port = Default_MQTT_Port;
String MQTT_User = "";
String MQTT_Password = "";
String MQTT_Topic = "P1_Meter";
int LEDSpeed = 0;

bool isPressing = false;
unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;

////////////////////////////////  Filesystem  /////////////////////////////////////////
bool startFilesystem() {
  if (FILESYSTEM.begin()){
    server.printFileList(FILESYSTEM, "/", 2), Serial;
    return true;
  }
  else {
    Serial.println("ERROR on mounting filesystem. It will be reformatted!");
    FILESYSTEM.format();
    ESP.restart();
  }
  return false;
}
/*
* Getting FS info (total and free bytes) is strictly related to
* filesystem library used (LittleFS, FFat, SPIFFS etc etc) and ESP framework
*/
#ifdef ESP32
void getFsInfo(fsInfo_t* fsInfo) {
        fsInfo->fsName = "LittleFS";
        fsInfo->totalBytes = LittleFS.totalBytes();
        fsInfo->usedBytes = LittleFS.usedBytes();
}
#endif

////////////////////////////////   WebSocket Handler  /////////////////////////////
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
  switch (type) {
    case WS_EVT_DISCONNECT:
      Serial.print("WebSocket client disconnected!\n");
      break;
    case WS_EVT_CONNECT:  {
        IPAddress ip = client->remoteIP();
        Serial.printf("WebSocket client %d.%d.%d.%d connected.\n", ip[0], ip[1], ip[2], ip[3]);
        client->printf("%s", "{\"Connected\": true}");
      }
      break;
    default:
        break;
  }
}

////////////////////////////////  NTP Time  /////////////////////////////////////
void getUpdatedtime(const uint32_t timeout)
{
  uint32_t start = millis();
  Serial.print("Sync time...");
  while (millis() - start < timeout  && Time.tm_year <= (1970 - 1900)) {
    time_t now = time(nullptr);
    Time = *localtime(&now);
    delay(5);
  }
  Serial.println(" done.");
}

////////////////////  Load application options from filesystem  ////////////////////
bool loadOptions() {
  if (FILESYSTEM.exists(server.getConfiFileName())) {
    server.getOptionValue(USER_LABEL, Username);
    server.getOptionValue(PASSWORD_LABEL, Password);
    server.getOptionValue(EXTERNAL_ANTENNA_LABEL, External_Antenna);

    server.getOptionValue(MQTT_ENABLED_LABEL, MQTT_Enabled);
    server.getOptionValue(MQTT_CLIENT_LABEL, MQTT_Client);
    server.getOptionValue(MQTT_SERVER_LABEL, MQTT_Server);
    server.getOptionValue(MQTT_PORT_LABEL, MQTT_Port);
    server.getOptionValue(MQTT_USER_LABEL, MQTT_User);
    server.getOptionValue(MQTT_PASSWORD_LABEL, MQTT_Password);
    server.getOptionValue(MQTT_TOPIC_LABEL, MQTT_Topic);

    Serial.println("\nThis are the current values stored: \n");
    Serial.printf("MQTT Enabled: %s\n", MQTT_Enabled ? "true" : "false");
    Serial.printf("MQTT Client: %s\n", MQTT_Client.c_str());
    Serial.printf("MQTT Server: %s\n", MQTT_Server.c_str());
//    Serial.printf("MQTT Server Port: %d\n", MQTT_Port);
    Serial.printf("MQTT Server Port: %s\n", MQTT_Port.c_str());
    Serial.printf("MQTT User: %s\n", MQTT_User.c_str());
    Serial.printf("MQTT password: %s\n", MQTT_Password.c_str());
    Serial.printf("MQTT Topic: %s\n", MQTT_Topic.c_str());
    return true;
  }
  else
    Serial.println(F("Config file not exist"));
  return false;
}

bool _was_connected = false;
void saveOptions() {
   server.saveOptionValue(USER_LABEL, Username);
   server.saveOptionValue(PASSWORD_LABEL, Password);
   server.saveOptionValue(EXTERNAL_ANTENNA_LABEL, External_Antenna);

   server.saveOptionValue(MQTT_ENABLED_LABEL, MQTT_Enabled);
   server.saveOptionValue(MQTT_CLIENT_LABEL, MQTT_Client);
   server.saveOptionValue(MQTT_SERVER_LABEL, MQTT_Server);
   server.saveOptionValue(MQTT_PORT_LABEL, MQTT_Port);
   server.saveOptionValue(MQTT_USER_LABEL, MQTT_User);
   server.saveOptionValue(MQTT_PASSWORD_LABEL, MQTT_Password);
   server.saveOptionValue(MQTT_TOPIC_LABEL, MQTT_Topic);
  Serial.println(F("Application options saved."));
}

////////////////////////////  HTTP Request Handlers  ////////////////////////////////////
void handleLoadOptions(AsyncWebServerRequest *request) {
  request->send(200, "text/plain", "Options loaded");
  loadOptions();
  Serial.println("Application option loaded after web request");
}




// * ==========================================================================================
// * = HIERONDER NOG NETJES MAKEN == HIERONDER NOG NETJES MAKEN == HIERONDER NOG NETJES MAKEN =
// * ==========================================================================================

//MQTT_Port_String = String(MQTT_Port);
//sprintf(MQTT_Port_String, "%u", MQTT_Port);
//MQTTRemote _mqtt_remote(MQTT_Client.c_str(), MQTT_Server.c_str(), MQTT_Port, MQTT_User.c_str(), MQTT_Password.c_str(),
//                        {.rx_buffer_size = 2048, .tx_buffer_size = 2048, .keep_alive_s = 10});


////////////////////////////////  SETUP  /////////////////////////////////////////
void setup() {
  Serial.begin(115200);
  button.setDebounceTime(50); // set debounce time to 50 milliseconds
  pinMode(BTN_SAVE, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);

  
  // FILESYSTEM INIT
  if (startFilesystem()){
    // Load configuration (if not present, default will be created when webserver will start)
    if (loadOptions())
      Serial.println(F("Application option loaded"));
    else
      Serial.println(F("Application options NOT loaded!"));
  }

  server.setSetupPageTitle("Instellingen P1 Meter");
  server.setSetupPageLogo(logo_png, sizeof(logo_png), "image/png", false);
  server.closeSetupConfiguration();

  // Try to connect to stored SSID, start AP with captive portal if fails after timeout
  IPAddress myIP = server.startWiFi(15000);
  if (!myIP) {
    Serial.println("\n\nNo WiFi connection, start AP and Captive Portal\n");
    server.startCaptivePortal("ESP_AP", "123456789", "/setup");
    myIP = WiFi.softAPIP();
    captiveRun = true;
    LEDSpeed = 250;
  } else {
    LEDSpeed = 1000;
  }

  // Add custom page handlers to webserver
  server.on("/reload", HTTP_GET, handleLoadOptions);

  // Configure /setup page and start Web Server
  server.addOptionBox("Device Setup");
  server.addOption(USER_LABEL, Username);
  server.addOption(PASSWORD_LABEL, Password);
  server.addOption(EXTERNAL_ANTENNA_LABEL, External_Antenna);
  server.addComment(EXTERNAL_ANTENNA_LABEL, "Restart is necessary after changing values.");
  
  server.addOptionBox("MQTT Setup");
  server.addOption(MQTT_ENABLED_LABEL, MQTT_Enabled);
  server.addOption(MQTT_CLIENT_LABEL, MQTT_Client);
  server.addOption(MQTT_SERVER_LABEL, MQTT_Server);
  server.addOption(MQTT_PORT_LABEL, MQTT_Port);
  server.addOption(MQTT_USER_LABEL, MQTT_User);
  server.addOption(MQTT_PASSWORD_LABEL, MQTT_Password);
  server.addOption(MQTT_TOPIC_LABEL, MQTT_Topic);
  server.addComment(MQTT_TOPIC_LABEL, "Restart is necessary after changing values.");

  // Enable ACE FS file web editor and add FS info callback function
  server.enableFsCodeEditor();
  #ifdef ESP32
  server.setFsInfoCallback(getFsInfo);
  #endif

  // set /setup and /edit page authentication
  server.setAuthentication(Username.c_str(), Password.c_str());

   // Start server with custom websocket event handler
  server.init(onWsEvent);
  Serial.print(F("ESP Web Server started on IP Address: "));
  Serial.println(server.getServerIP());

  if (External_Antenna) {
    pinMode(3, OUTPUT);    // RF switch power on
    digitalWrite(3, LOW);
    pinMode(14, OUTPUT);   // select external antenna
    digitalWrite(14, HIGH);
    Serial.println("External antenna selected.");
 } else {
    pinMode(14, INPUT);    // select on-board antenna
    pinMode(3, INPUT);   // RF switch power off
    Serial.println("Internal antenna selected.");
 }
 long rssi = WiFi.RSSI();
 Serial.print("WiFi Strength (RSSI): ");
 if ((rssi <= -30) && (rssi > -50)) {
   Serial.print("***** (");
   Serial.print(rssi);
   Serial.print("dBm.)");
 } else   if ((rssi <= -50) && (rssi > -60)) {
   Serial.print("****. (");
   Serial.print(rssi);
   Serial.print("dBm.)");
 } else   if ((rssi <= -60) && (rssi > -70)) {
   Serial.print("***.. (");
   Serial.print(rssi);
   Serial.print("dBm.)");
 } else   if ((rssi <= -70) && (rssi > -80)) {
   Serial.print("**... (");
   Serial.print(rssi);
   Serial.print("dBm.)");
 } else   if ((rssi <= -80) && (rssi > -90)) {
   Serial.print("*.... (");
   Serial.print(rssi);
   Serial.print("dBm.)");
 } else   if ((rssi <= -90)) {
   Serial.print("..... (");
   Serial.print(rssi);
   Serial.print("dBm.)");
 }
 
 // if (MQTT_Enabled) {
      
 // }

//  if (MQTT_Enabled) {
//    _mqtt_remote.start([](bool connected) {
//    if (connected) {
//      _mqtt_remote.subscribe(
//            _mqtt_remote.publishMessageVerbose(_mqtt_remote.clientId() + "/initial_message", "oh hello!");
//          });
//    };
}

////////////////////////////////  LOOP  /////////////////////////////////////////
void loop() {

  // Send ESP system time (epoch) and heap stats to WS client
  static uint32_t sendToClientTime;
  if (millis() - sendToClientTime > LEDSpeed ) {
    sendToClientTime = millis();
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));

    time_t now = time(nullptr);
    using namespace AsyncFSWebServer;
    CJSON::Json doc;
    doc.setBool("addPoint", true);
    doc.setNumber("timestamp", (double)now);
#ifdef ESP32
    doc.setNumber("totalHeap", (double)heap_caps_get_free_size(0));
    doc.setNumber("maxBlock", (double)heap_caps_get_largest_free_block(0));
#elif defined(ESP8266)
    uint32_t free;
    uint32_t max;
    ESP.getHeapStats(&free, &max, nullptr);
    doc.setNumber("totalHeap", (double)free);
    doc.setNumber("maxBlock", (double)max);
#endif
    String msg = doc.serialize();
    server.wsBroadcast(msg.c_str());
  }

  if (captiveRun)
    server.updateDNS();

  // Savew options also on button click
  if (! digitalRead(BTN_SAVE)) {
    saveOptions();
    delay(1000);
  }

  button.loop(); // MUST call the loop() function first

  if (button.isPressed()) {
    pressedTime = millis();
    isPressing = true;
  }
  
  long pressingDuration = millis() - pressedTime;
  
  if ((isPressing == true) && (pressingDuration > LONG_PRESS_TIME))
      digitalWrite(LED_BUILTIN, LOW);

  if (button.isReleased()) {
    releasedTime = millis();
    isPressing = false;

    long pressDuration = releasedTime - pressedTime;

    if ((pressDuration > SHORT_PRESS_TIME_MIN) && (pressDuration < SHORT_PRESS_TIME_MAX)) {
      ESP.restart();
    }

    if (pressDuration > LONG_PRESS_TIME) {
      server.clearConfigFile();
      ESP.restart();
    }
  }
}