/espFindClient.ino |
@@ -0,0 +1,351 @@ |
/* |
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(); |
} |