update
This commit is contained in:
357
app/main.cpp
357
app/main.cpp
@@ -1,6 +1,10 @@
|
||||
#include <SmingCore.h>
|
||||
#include <Network/Mqtt/MqttBuffer.h>
|
||||
#include <Data/WebHelpers/base64.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <MultipartParser.h>
|
||||
#include <HttpMultipartResource.h>
|
||||
#include <OtaUpgradeStream.h>
|
||||
#include "utils.h"
|
||||
#include "main.h"
|
||||
|
||||
@@ -10,17 +14,98 @@ CMain::CMain() : m_FingerLogic(this)
|
||||
{
|
||||
}
|
||||
|
||||
static void STAGotIP(IpAddress ip, IpAddress mask, IpAddress gateway)
|
||||
static void fileUploadMapper(HttpFiles& files)
|
||||
{
|
||||
debugf("GOTIP - IP: %s, MASK: %s, GW: %s\n", ip.toString().c_str(), mask.toString().c_str(),
|
||||
gateway.toString().c_str());
|
||||
files["firmware"] = new OtaUpgradeStream;
|
||||
}
|
||||
|
||||
static void STADisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason)
|
||||
static int onMessageDelivered(MqttClient& client, mqtt_message_t* message)
|
||||
{
|
||||
Serial1 << _F("Message with id ") << message->puback.message_id << _F(" and QoS ") << message->puback.qos
|
||||
<< _F(" was delivered successfully.") << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Callback for messages, arrived from MQTT server
|
||||
static int onMessageReceived(MqttClient& client, mqtt_message_t* message)
|
||||
{
|
||||
Serial1 << _F("Received: ") << MqttBuffer(message->publish.topic_name) << ':' << endl;
|
||||
Serial1 << '\t' << MqttBuffer(message->publish.content) << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMain::OnStationGotIP(IpAddress ip, IpAddress mask, IpAddress gateway)
|
||||
{
|
||||
m_StationConnected = true;
|
||||
debugf("GOTIP - IP: %s, MASK: %s, GW: %s\n", ip.toString().c_str(), mask.toString().c_str(), gateway.toString().c_str());
|
||||
StartMqttClient();
|
||||
}
|
||||
|
||||
void CMain::OnStationDisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason)
|
||||
{
|
||||
m_StationConnected = false;
|
||||
debugf("DISCONNECT - SSID: %s, REASON: %d", ssid.c_str(), WifiEvents.getDisconnectReasonDesc(reason).c_str());
|
||||
}
|
||||
|
||||
int CMain::MqttOnConnect(MqttClient& client, mqtt_message_t* message)
|
||||
{
|
||||
debugf("MQTT Connected!");
|
||||
|
||||
MqttSendMessage("safeweb/status", "online");
|
||||
|
||||
MqttSendDescription();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMain::MqttOnDisconnect(TcpClient& client, bool flag)
|
||||
{
|
||||
if(flag == true)
|
||||
debugf("MQTT Broker Disconnected!!");
|
||||
else
|
||||
debugf("MQTT Broker Unreachable!!");
|
||||
|
||||
// Restart connection attempt after few seconds
|
||||
if(!m_StationConnected)
|
||||
m_MqttTimer.initializeMs(2 * 1000, TimerDelegate(&CMain::StartMqttClient, this)).start();
|
||||
}
|
||||
|
||||
void CMain::MqttSendDescription()
|
||||
{
|
||||
if(m_Mqtt.getConnectionState() != eTCS_Connected) {
|
||||
StartMqttClient(); // Auto reconnect
|
||||
}
|
||||
}
|
||||
|
||||
void CMain::MqttSendMessage(const char *topic, const char *msg)
|
||||
{
|
||||
if(m_Mqtt.getConnectionState() != eTCS_Connected) {
|
||||
StartMqttClient(); // Auto reconnect
|
||||
}
|
||||
|
||||
m_Mqtt.publish(topic, msg);
|
||||
}
|
||||
|
||||
void CMain::StartMqttClient()
|
||||
{
|
||||
m_MqttTimer.stop();
|
||||
if(!m_StationConnected || !Settings().m_MqttURL.Port || !Settings().m_MqttURL.Host)
|
||||
return;
|
||||
|
||||
if(!m_Mqtt.setWill("safeweb/status", "offline", MqttClient::getFlags(MQTT_QOS_AT_LEAST_ONCE, MQTT_RETAIN_TRUE))) {
|
||||
debugf("Unable to mqtt.setWill");
|
||||
}
|
||||
|
||||
m_Mqtt.setEventHandler(MQTT_TYPE_PUBACK, onMessageDelivered);
|
||||
|
||||
m_Mqtt.setConnectedHandler(MqttDelegate(&CMain::MqttOnConnect, this));
|
||||
|
||||
m_Mqtt.setCompleteDelegate(TcpClientCompleteDelegate(&CMain::MqttOnDisconnect, this));
|
||||
m_Mqtt.setMessageHandler(onMessageReceived);
|
||||
|
||||
m_Mqtt.connect(Settings().m_MqttURL, Settings().m_aMqttName);
|
||||
}
|
||||
|
||||
|
||||
void CMain::Init(HardwareSerial &serial)
|
||||
{
|
||||
m_Settings.Load();
|
||||
@@ -45,27 +130,61 @@ void CMain::Init(HardwareSerial &serial)
|
||||
WifiAccessPoint.enable(true);
|
||||
}
|
||||
|
||||
WifiEvents.onStationGotIP(STAGotIP);
|
||||
WifiEvents.onStationDisconnect(STADisconnect);
|
||||
WifiEvents.onStationGotIP(StationGotIPDelegate(&CMain::OnStationGotIP, this));
|
||||
WifiEvents.onStationDisconnect(StationDisconnectDelegate(&CMain::OnStationDisconnect, this));
|
||||
|
||||
m_FTP.listen(21);
|
||||
m_FTP.addUser(m_Settings.m_aUsername, m_Settings.m_aPassword);
|
||||
|
||||
HttpServerSettings settings;
|
||||
settings.closeOnContentError = false;
|
||||
settings.keepAliveSeconds = 5;
|
||||
|
||||
m_HttpServer.configure(settings);
|
||||
m_HttpServer.setBodyParser(MIME_JSON, bodyToStringParser);
|
||||
m_HttpServer.setBodyParser(MIME_FORM_MULTIPART, formMultipartParser);
|
||||
m_HttpServer.listen(80);
|
||||
m_HttpServer.setBodyParser("application/json", bodyToStringParser);
|
||||
|
||||
m_HttpServer.paths.set("/api", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/state", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/dashboard", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/fingerprint", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/fingerprint/label", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/fingerprint/delete", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/fingerprint/enroll", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/fingerprint/security", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/unlock", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/api/reset", HttpPathDelegate(&CMain::HttpOnApi, this));
|
||||
m_HttpServer.paths.set("/upgrade", new HttpMultipartResource(fileUploadMapper, HttpResourceDelegate(&CMain::HttpOnUpload, this)));
|
||||
|
||||
m_HttpServer.paths.setDefault(HttpPathDelegate(&CMain::HttpOnFile, this));
|
||||
|
||||
m_FingerPrint.Init(serial, 0xFFFFFFFF, 0x00000000);
|
||||
m_FingerLogic.Init(&m_FingerPrint);
|
||||
|
||||
m_LightSleepTimer.initializeMs(60 * 1000, TimerDelegate(&CMain::EnterLightSleep, this));
|
||||
}
|
||||
|
||||
static void wakeupCallback() {
|
||||
debugf("Wakeing up @ %lu", millis());
|
||||
debugf("Wakeing up @ %s", SystemClock.getSystemTimeString().c_str());
|
||||
wifi_fpm_close();
|
||||
wifi_set_opmode(STATION_MODE);
|
||||
wifi_station_connect();
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
|
||||
void CMain::EnterLightSleep()
|
||||
{
|
||||
debugf("Going to sleep @ %lu", millis());
|
||||
debugf("Going to sleep @ %s", SystemClock.getSystemTimeString().c_str());
|
||||
wifi_station_disconnect();
|
||||
wifi_set_opmode(NULL_MODE);
|
||||
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
|
||||
wifi_fpm_open();
|
||||
wifi_fpm_set_wakeup_cb(wakeupCallback);
|
||||
wifi_fpm_do_sleep(0xFFFFFFF);
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
|
||||
bool CMain::HttpAuthorized(HttpRequest &request, HttpResponse &response)
|
||||
@@ -124,7 +243,6 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
HttpStatus status = HandleApi(request.method, endpoint, jsonReq, jsonResp);
|
||||
if(status != HTTP_STATUS_OK)
|
||||
{
|
||||
@@ -140,73 +258,96 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response)
|
||||
|
||||
HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp)
|
||||
{
|
||||
String test;
|
||||
serializeJsonPretty(req, test);
|
||||
|
||||
debugf("request: %s\n", test.c_str());
|
||||
|
||||
if(endpoint == "dashboard")
|
||||
if(endpoint == "state" || endpoint == "dashboard")
|
||||
{
|
||||
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED;
|
||||
|
||||
JsonObject lock = resp.createNestedObject("lock");
|
||||
lock["locked"] = true;
|
||||
JsonObject state = resp.createNestedObject("state");
|
||||
state["unlocked"] = (bool)!digitalRead(SAFELOCK_DETECT_PIN);
|
||||
state["opened"] = (bool)digitalRead(DOOR_DETECT_PIN);
|
||||
state["battery"] = (system_adc_read() / 1024.f) / 0.237;
|
||||
|
||||
JsonObject door = resp.createNestedObject("door");
|
||||
door["closed"] = true;
|
||||
|
||||
JsonObject battery = resp.createNestedObject("battery");
|
||||
debugf("adc: %d\n", system_adc_read());
|
||||
battery["voltage"] = (system_adc_read() / 1024.f) / 0.23;
|
||||
battery["color"] = "success";
|
||||
if(battery["voltage"] < 3.6f)
|
||||
battery["color"] = "warning";
|
||||
|
||||
JsonObject network = resp.createNestedObject("network");
|
||||
if(WifiStation.isEnabled())
|
||||
if(endpoint == "dashboard")
|
||||
{
|
||||
network["type"] = "Station";
|
||||
network["ssid"] = WifiStation.getSSID();
|
||||
network["channel"] = WifiStation.getChannel();
|
||||
network["dhcp"] = WifiStation.isEnabledDHCP();
|
||||
network["rssi"] = WifiStation.getRssi();
|
||||
network["signal"] = Rssi2Quality(WifiStation.getRssi());
|
||||
network["address"] = WifiStation.getIP().toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
network["type"] = "Access Point";
|
||||
network["ssid"] = WifiAccessPoint.getSSID();
|
||||
network["channel"] = WifiStation.getChannel();
|
||||
network["dhcp"] = true;
|
||||
network["address"] = WifiAccessPoint.getIP().toString();
|
||||
JsonObject network = resp.createNestedObject("network");
|
||||
if(WifiStation.isEnabled())
|
||||
{
|
||||
network["type"] = "Station";
|
||||
network["ssid"] = WifiStation.getSSID();
|
||||
network["channel"] = WifiStation.getChannel();
|
||||
network["dhcp"] = WifiStation.isEnabledDHCP();
|
||||
network["rssi"] = WifiStation.getRssi();
|
||||
network["signal"] = Rssi2Quality(network["rssi"]);
|
||||
network["address"] = WifiStation.getIP().toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
network["type"] = "Access Point";
|
||||
network["ssid"] = WifiAccessPoint.getSSID();
|
||||
network["channel"] = WifiStation.getChannel();
|
||||
network["dhcp"] = true;
|
||||
network["address"] = WifiAccessPoint.getIP().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(endpoint == "unlock")
|
||||
{
|
||||
if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED;
|
||||
|
||||
LockUnlock();
|
||||
resp["success"] = "true";
|
||||
if(!req.containsKey("unlock"))
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if(req["unlock"])
|
||||
LockUnlock();
|
||||
}
|
||||
else if(endpoint == "reset")
|
||||
{
|
||||
if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED;
|
||||
|
||||
if(!req.containsKey("reset"))
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if(req["reset"])
|
||||
System.restart(500);
|
||||
}
|
||||
else if(endpoint == "fingerprint")
|
||||
{
|
||||
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED;
|
||||
|
||||
JsonArray fingerprints = resp.createNestedArray("fingerprints");
|
||||
uint16_t tmp = Settings().m_FingerPrints.count();
|
||||
for(uint16_t i = 0; i < tmp; i++)
|
||||
if(method == HTTP_GET)
|
||||
{
|
||||
JsonObject obj = fingerprints.createNestedObject();
|
||||
const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(i);
|
||||
JsonArray fingerprints = resp.createNestedArray("fingerprints");
|
||||
uint16_t tmp = Settings().m_FingerPrints.count();
|
||||
for(uint16_t i = 0; i < tmp; i++)
|
||||
{
|
||||
JsonObject obj = fingerprints.createNestedObject();
|
||||
const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(i);
|
||||
|
||||
obj["num"] = finger.m_FingerNum;
|
||||
obj["label"] = String(finger.m_aLabel);
|
||||
obj["num"] = finger.m_FingerNum;
|
||||
obj["label"] = String(finger.m_aLabel);
|
||||
|
||||
char aHexDigest[SHA256_SIZE*2+1];
|
||||
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
|
||||
obj["digest"] = String(aHexDigest);
|
||||
char aHexDigest[SHA256_SIZE*2+1];
|
||||
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
|
||||
obj["digest"] = String(aHexDigest);
|
||||
}
|
||||
resp["securityLevel"] = Settings().m_SecurityLevel;
|
||||
}
|
||||
else if(method == HTTP_POST)
|
||||
{
|
||||
if(!req.containsKey("securityLevel"))
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
int newLevel = req["securityLevel"].as<int>();
|
||||
if(newLevel > FINGERPRINT_SECURITY_LEVEL_5 || newLevel < FINGERPRINT_SECURITY_LEVEL_1)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if(newLevel != Settings().m_SecurityLevel)
|
||||
{
|
||||
Settings().m_SecurityLevel = newLevel;
|
||||
Settings().Save();
|
||||
FingerLogic().SetSecurityLevel(newLevel);
|
||||
}
|
||||
}
|
||||
else
|
||||
return HTTP_STATUS_METHOD_NOT_ALLOWED;
|
||||
}
|
||||
else if(endpoint == "fingerprint/label")
|
||||
{
|
||||
@@ -224,8 +365,6 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
|
||||
CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(index);
|
||||
strncpy(finger.m_aLabel, label.c_str(), sizeof(finger.m_aLabel));
|
||||
Settings().Save();
|
||||
|
||||
resp["success"] = true;
|
||||
}
|
||||
else if(endpoint == "fingerprint/delete")
|
||||
{
|
||||
@@ -243,8 +382,6 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
|
||||
FingerPrint().DeleteTemplate(finger.m_FingerNum, 1);
|
||||
Settings().m_FingerPrints.removeAt(index);
|
||||
Settings().Save();
|
||||
|
||||
resp["success"] = true;
|
||||
}
|
||||
else if(endpoint == "fingerprint/enroll")
|
||||
{
|
||||
@@ -254,7 +391,7 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
|
||||
{
|
||||
if(req["cancel"].as<bool>() && m_Enrolling)
|
||||
{
|
||||
resp["success"] = FingerLogic().EnrollFinger(false);
|
||||
FingerLogic().EnrollFinger(false);
|
||||
m_Enrolling = false;
|
||||
m_Enrolled = false;
|
||||
}
|
||||
@@ -336,15 +473,100 @@ void CMain::HttpOnFile(HttpRequest &request, HttpResponse &response)
|
||||
response.sendFile(file);
|
||||
}
|
||||
|
||||
void CMain::OnFingerInterrupt(bool finger)
|
||||
int CMain::HttpOnUpload(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
m_FingerLogic.OnFingerInterrupt(finger);
|
||||
if(!HttpAuthorized(request, response))
|
||||
return 1;
|
||||
|
||||
ReadWriteStream* file = request.files["firmware"];
|
||||
auto otaStream = static_cast<OtaUpgradeStream*>(file);
|
||||
if(otaStream == nullptr) {
|
||||
debug_e("Something went wrong with the file upload");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(response.isSuccess() && !otaStream->hasError()) {
|
||||
// defer the reboot by 1000 milliseconds to give time to the web server to return the response
|
||||
System.restart(1000);
|
||||
|
||||
response.sendFile("otadone.html");
|
||||
response.headers[HTTP_HEADER_CONNECTION] = "close";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
response.code = HTTP_STATUS_BAD_REQUEST;
|
||||
response.setContentType(MIME_HTML);
|
||||
String html = toString(otaStream->errorCode);
|
||||
response.headers[HTTP_HEADER_CONTENT_LENGTH] = html.length();
|
||||
response.sendString(html);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMain::FingerEnable(bool enable)
|
||||
void CMain::CheckSleep()
|
||||
{
|
||||
if(!m_FingerPlaced &&
|
||||
!m_LockUnlocked &&
|
||||
!m_DoorOpened)
|
||||
{
|
||||
m_LightSleepTimer.startOnce();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LightSleepTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void CMain::OnFinger(bool finger)
|
||||
{
|
||||
m_FingerPlaced = finger;
|
||||
m_FingerLogic.OnFinger(finger);
|
||||
|
||||
if(finger)
|
||||
MqttSendMessage("safeweb/finger/placed", "true");
|
||||
else
|
||||
MqttSendMessage("safeweb/finger/placed", "false");
|
||||
CheckSleep();
|
||||
}
|
||||
|
||||
void CMain::OnLock(bool unlocked)
|
||||
{
|
||||
m_LockUnlocked = unlocked;
|
||||
|
||||
if(unlocked)
|
||||
MqttSendMessage("safeweb/lock/unlocked", "true");
|
||||
else
|
||||
MqttSendMessage("safeweb/lock/unlocked", "false");
|
||||
CheckSleep();
|
||||
}
|
||||
|
||||
void CMain::OnDoor(bool opened)
|
||||
{
|
||||
m_DoorOpened = opened;
|
||||
|
||||
if(opened)
|
||||
MqttSendMessage("safeweb/door/opened", "true");
|
||||
else
|
||||
MqttSendMessage("safeweb/door/opened", "false");
|
||||
CheckSleep();
|
||||
}
|
||||
|
||||
void CMain::FingerPower(bool enable)
|
||||
{
|
||||
const int pin = FINGER_ENABLE_PIN;
|
||||
digitalWrite(pin, !enable);
|
||||
|
||||
if(enable)
|
||||
{
|
||||
wifi_set_sleep_level(MAX_SLEEP_T);
|
||||
wifi_set_listen_interval(3);
|
||||
wifi_set_sleep_type(MODEM_SLEEP_T);
|
||||
}
|
||||
else
|
||||
{
|
||||
wifi_set_sleep_type(LIGHT_SLEEP_T);
|
||||
}
|
||||
}
|
||||
|
||||
void CMain::LockSendBytes(uint8_t *pBytes, uint8_t len)
|
||||
@@ -443,11 +665,6 @@ void CMain::OnFingerEnrolled(uint16_t fingerNum, uint8_t digest[SHA256_SIZE])
|
||||
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
|
||||
debugf("OnFingerEnrolled: \"%s\"", aHexDigest);
|
||||
|
||||
Serial1.printf("(sz: %d) Finger hexdigest: ", sizeof(finger.m_aDigest));
|
||||
for(uint8_t i = 0; i < sizeof(finger.m_aDigest); i++)
|
||||
Serial1.printf("%x", finger.m_aDigest[i]);
|
||||
Serial1.printf("\n");
|
||||
|
||||
Settings().m_FingerPrints[fingerNum] = finger;
|
||||
Settings().Save();
|
||||
m_Enrolled = true;
|
||||
|
||||
Reference in New Issue
Block a user