arduino-sketches – Rev 3

Subversion Repositories:
Rev:
/*
  This file is part of esp-find3-client by Sylwester aka DatanoiseTV.
  The original source can be found at https://github.com/DatanoiseTV/esp-find3-client.

  26/04/2020: Adjustments by Wizardry and Steamworks.

  esp-find3-client 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.

  esp-find3-client 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 esp-find3-client.  If not, see <http://www.gnu.org/licenses/>.
*/

///////////////////////////////////////////////////////////////////////////
//                             CONFIGURATION                             //
///////////////////////////////////////////////////////////////////////////

// Set to the WiFi AP name.
#define WIFI_SSID ""
// Set to the WiFi AP password.
#define WIFI_PSK ""

// Set to 1 for learning mode.
#define MODE_LEARNING 1
#define LOCATION ""

// Family name.
#define FAMILY_NAME ""

// BLE requires large app partition or the sketch will not fit.
// Please choose:
// * Tools -> Partition scheme -> Minimal SPIFFS (1.9MB APP / 190KB SPIFFS)
// and set to 1 to enable BLE.
#define USE_BLE 1
#define BLE_SCANTIME 5

// Official server: cloud.internalpositioning.com
#define FIND_HOST "cloud.internalpositioning.com"
// Official port: 443 and SSL set to 1
#define FIND_PORT 443
// Whether to use SSL for the HTTP connection.
// Set to 1 for official cloud server.
#define USE_HTTP_SSL 1
// Timeout connecting to find3 server expressed in milliseconds.
#define HTTP_TIMEOUT 2500

// The NTP server to use for time updates.
#define NTP_HOST "pool.ntp.org"
// The offset in seconds from UTC, ie: 3600 for +1 Hour.
#define UTC_OFFSET 2 * 3600

// The password to use for OTA updates.
#define OTA_PASSWORD ""

// Set to 1 to enable. Used for verbose debugging.
#define DEBUG 1

///////////////////////////////////////////////////////////////////////////
//                              INTERNALS                                //
///////////////////////////////////////////////////////////////////////////

#ifdef ARDUINO_ARCH_ESP32
#include <WiFiClientSecure.h>
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <ArduinoOTA.h>

#if defined(ARDUINO_ARCH_ESP8266)
#define GET_CHIP_ID() String(ESP.getChipId(), HEX)
#elif defined(ARDUINO_ARCH_ESP32)
#define GET_CHIP_ID() String(((uint16_t)(ESP.getEfuseMac()>>32)), HEX)
#endif

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */

#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>

// Automagically disable BLE on ESP8266
#if defined(ARDUINO_ARCH_ESP8266) && !defined(ARDUINO_ARCH_ESP32)
#define USE_BLE 0
#endif

#if defined(ARDUINO_ARCH_ESP32) && (USE_BLE == 1)
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      // Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
};
#endif

#ifdef ARDUINO_ARCH_ESP32
RTC_DATA_ATTR int bootCount = 0;
#endif

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_HOST, UTC_OFFSET, 60000);

// Retrieves the WiFi MAC address.
String getWiFiMAC() {
  String result;
  byte mac[6];
  WiFi.macAddress(mac);
  for (int i = 5; i > -1; --i) {
    result += String(mac[i], HEX);
    if (i != 0) {
      result += ":";
    }
  }
  return result;
}

void SubmitWiFi(void)
{
  Serial.println("[ INFO ]\tWiFi MAC: " + getWiFiMAC());

  String request;

  StaticJsonDocument<256> jsonBuffer;
  JsonObject root = jsonBuffer.to<JsonObject>();

  root["d"] = "esp-" + GET_CHIP_ID();
  root["f"] = FAMILY_NAME;
  root["t"] = timeClient.getEpochTime();
#if (MODE_LEARNING == 1)
  Serial.println("[ iNFO ]\tLearning enabled, sending learning data.");
  root["l"] = LOCATION;
#endif

  JsonObject data = root.createNestedObject("s");

  Serial.println("[ INFO ]\tWiFi scan starting..");
  int n = WiFi.scanNetworks(false, true);
  Serial.println("[ INFO ]\tWiFi Scan finished.");
  if (n == 0) {
    Serial.println("[ ERROR ]\tNo networks found");
  } else {
    Serial.print("[ INFO ]\t");
    Serial.print(n);
    Serial.println(" WiFi networks found.");
    JsonObject wifi_network = data.createNestedObject("wifi");
    for (int i = 0; i < n; ++i) {
      wifi_network[WiFi.BSSIDstr(i)] = WiFi.RSSI(i);
    }

#if (USE_BLE == 1)
    Serial.println("[ INFO ]\tBLE scan starting..");
    BLEDevice::init("");
    BLEAddress btMAC = BLEDevice::getAddress();
    Serial.println("[ INFO ]\tBT MAC: " + String(btMAC.toString().c_str()));
    BLEScan* pBLEScan = BLEDevice::getScan(); // create new scan
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setActiveScan(true); // active scan uses more power, but get results faster
    BLEScanResults foundDevices = pBLEScan->start(BLE_SCANTIME);

    Serial.print("[ INFO ]\t");
    Serial.print(foundDevices.getCount());
    Serial.println(" BLE devices found.");

    JsonObject bt_network = data.createNestedObject("bluetooth");
    for (int i = 0; i < foundDevices.getCount(); i++)
    {
      std::string mac = foundDevices.getDevice(i).getAddress().toString();
      bt_network[(String)mac.c_str()] = (int)foundDevices.getDevice(i).getRSSI();
    }
#else
    Serial.println("[ INFO ]\tBLE scan skipped (BLE disabled)..");
#endif // USE_BLE

    serializeJson(root, request);

#if (DEBUG == 1)
    Serial.println("[ DEBUG ]\t" + request);
#endif

#if (USE_HTTP_SSL == 1)
    WiFiClientSecure client;
#else
    WiFiClient client;
#endif
    if (!client.connect(FIND_HOST, FIND_PORT)) {
      Serial.println("[ WARN ]\tConnection to server failed, restarting in 5s...");
      delay(5000);
      ESP.restart();
    }

    // We now create a URI for the request
    String url = "/data";

    Serial.print("[ INFO ]\tRequesting URL: ");
    Serial.println(url);

    // This will send the request to the server
    client.print(String("POST ") + url + " HTTP/1.1\r\n" +
                 "Host: " + FIND_HOST + "\r\n" +
                 "Content-Type: application/json\r\n" +
                 "Content-Length: " + request.length() + "\r\n\r\n" +
                 request +
                 "\r\n\r\n"
                );
                
    client.flush();

    unsigned long timeout = millis();
    while (client.available() == 0) {
      if (millis() - timeout > HTTP_TIMEOUT) {
        Serial.println("[ ERROR ]\tHTTP Client Timeout!");
        client.stop();
        return;
      }
    }

    // Check HTTP status
    char status[60] = {0};
    client.readBytesUntil('\r', status, sizeof(status));
    if (strcmp(status, "HTTP/1.1 200 OK") != 0) {
      Serial.print(F("[ ERROR ]\tUnexpected Response: "));
      Serial.println(status);
      return;
    }
    else
    {
      Serial.println(F("[ INFO ]\tGot a 200 OK."));
    }

    char endOfHeaders[] = "\r\n\r\n";
    if (!client.find(endOfHeaders)) {
      Serial.println(F("[ ERROR ]\t Invalid Response"));
      return;
    }
    else
    {
      Serial.println("[ INFO ]\tLooks like a valid response.");
    }

    Serial.println("[ INFO ]\tClosing connection.");
    Serial.println("=============================================================");
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);

#if defined(ARDUINO_ARCH_ESP32) && (USE_BLE == 1)
  Serial.println("Find3 ESP client by DatanoiseTV (WiFi + BLE support.)");
#else
  Serial.println("Find3 ESP client by DatanoiseTV (WiFi support WITHOUT BLE.)");
#endif

  Serial.print("[ INFO ]\tESP ID is: ");
  Serial.println("esp-" + GET_CHIP_ID());

  // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setHostname(String("esp-" + GET_CHIP_ID()).c_str());
  // Set the OTA password
  ArduinoOTA.setPassword(OTA_PASSWORD);
  ArduinoOTA.onStart([]() {
    Serial.println("=============================================================");
    switch (ArduinoOTA.getCommand()) {
      case U_FLASH: // Sketch
        Serial.println("[ INFO ]\tOTA start updating sketch.");
        break;
#if defined(ARDUINO_ARCH_ESP8266)
      case U_FS:
#elif defined(ARDUINO_ARCH_ESP32)
      case U_SPIFFS:
#endif
        Serial.println("[ INFO ]\tOTA start updating filesystem.");
        break;
      default:
        Serial.println("[ WARN ]\tUnknown OTA update type.");
        break;
    }
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("[ INFO ]\tOTA update complete.");
    Serial.println("=============================================================");
    ESP.restart();
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("[ INFO ]\tOTA update progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("[ ERROR ]\tOTA update error [%u]: ", error);
    switch (error) {
      case OTA_AUTH_ERROR:
        Serial.println("OTA authentication failed");
        break;
      case OTA_BEGIN_ERROR:
        Serial.println("OTA begin failed");
        break;
      case OTA_CONNECT_ERROR:
        Serial.println("OTA connect failed");
        break;
      case OTA_RECEIVE_ERROR:
        Serial.println("OTA receive failed");
        break;
      case OTA_END_ERROR:
        Serial.println("OTA end failed");
        break;
      default:
        Serial.println("Unknown OTA failure");
        break;
    }
    ESP.restart();
  });
}

void loop() {
  // If WiFi is not connected, attempt to reconnect and if that fails then restart.
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("[ WARN ]\tWiFi not connected, retrying...");
    WiFi.disconnect();
#if defined(ARDUINO_ARCH_ESP32)
    WiFi.setHostname(String("esp-" + GET_CHIP_ID()).c_str());
#elif defined(ARDUINO_ARCH_ESP8266)
    WiFi.hostname(String("esp-" + GET_CHIP_ID()).c_str());
#endif
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PSK);
    while (WiFi.waitForConnectResult() != WL_CONNECTED) {
      Serial.println("[ ERROR ]\tFailed to connect to Wifi, rebooting in 5s...");
      delay(5000);
      ESP.restart();
    }
    Serial.println("[ INFO ]\tStarting OTA...");
    ArduinoOTA.begin();

    Serial.println("[ INFO ]\tStarting NTP...");
    timeClient.begin();
  }
  ArduinoOTA.handle();
  timeClient.update();
  SubmitWiFi();
}