284 lines
7.9 KiB
C++
Executable File
284 lines
7.9 KiB
C++
Executable File
/*
|
|
* ProvisionSoftAP.cpp
|
|
*
|
|
* Created on: Apr 17, 2019
|
|
* Modified: 2024
|
|
* Author: miro
|
|
*/
|
|
#include <DNSServer.h>
|
|
#include "WiFi.h"
|
|
#include "Arduino.h"
|
|
#include <list>
|
|
#include <iterator>
|
|
#include "Settings.h"
|
|
#include <functional>
|
|
#include "freertos/event_groups.h"
|
|
|
|
#include <ProvisionSoftAP.h>
|
|
|
|
static const char *TAG = "soft_ap";
|
|
|
|
using namespace std::placeholders;
|
|
|
|
extern const uint8_t provision_html_start[] asm("_binary_provision_html_start");
|
|
extern const uint8_t provision_html_end[] asm("_binary_provision_html_end");
|
|
extern const uint8_t logo_png_start[] asm("_binary_logo_png_start");
|
|
extern const uint8_t logo_png_end[] asm("_binary_logo_png_end");
|
|
|
|
const uint8_t sta_ip[] = {172, 68, 4, 1};
|
|
const uint8_t net_mask[] = {255, 255, 255, 0};
|
|
const uint8_t first_ip[] = {172, 68, 4, 10};
|
|
const uint8_t last_ip[] = {172, 68, 4, 100};
|
|
|
|
|
|
ProvisionSoftAP::ProvisionSoftAP(int port)
|
|
{
|
|
m_webServer = new AsyncWebServer(port);
|
|
m_webSocket = new AsyncWebSocket("/provision");
|
|
}
|
|
|
|
void ProvisionSoftAP::wifiEvent(arduino_event_id_t event, arduino_event_info_t info)
|
|
{
|
|
switch (event)
|
|
{
|
|
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
|
|
ESP_LOGI(TAG, "station " MACSTR " joined, AID=%d", MAC2STR(info.wifi_ap_staconnected.mac), info.wifi_ap_staconnected.aid);
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
|
|
ESP_LOGI(TAG, "station " MACSTR " left", MAC2STR(info.wifi_ap_stadisconnected.mac));
|
|
m_webSocket->closeAll();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ProvisionSoftAP::websocketEvent(AsyncWebSocket * server, AsyncWebSocketClient * client,
|
|
AwsEventType type, void * arg, uint8_t *data, size_t len)
|
|
{
|
|
ESP_LOGI(TAG, "type:%d", type);
|
|
|
|
if (type == WS_EVT_CONNECT)
|
|
{
|
|
ESP_LOGI(TAG, "Websocket client id:%d connection received", client->id());
|
|
m_clients.push_back(client->id());
|
|
}
|
|
else if (type == WS_EVT_DISCONNECT)
|
|
{
|
|
ESP_LOGI(TAG, "Client id:%d disconnected", client->id());
|
|
m_clients.remove(client->id());
|
|
}
|
|
else if (type == WS_EVT_DATA)
|
|
{
|
|
ESP_LOGI(TAG, "Data client %d received: ", client->id());
|
|
parseWebsocketCommand((char*) data, len);
|
|
}
|
|
}
|
|
|
|
void ProvisionSoftAP::handlePortal(AsyncWebServerRequest* request)
|
|
{
|
|
ESP_LOGI(TAG, "Serving html, requested url: %s", request->url().c_str());
|
|
AsyncWebServerResponse * response = request->beginResponse_P(200, "text/html", (PGM_P)provision_html_start);
|
|
request->send(response);
|
|
}
|
|
|
|
void ProvisionSoftAP::handleLogo(AsyncWebServerRequest* request)
|
|
{
|
|
AsyncWebServerResponse * response = request->beginResponse_P(200, "image/png", logo_png_start, logo_png_end-logo_png_start);
|
|
response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
response->addHeader("Pragma", "no-cache");
|
|
response->addHeader("Expires", "-1");
|
|
request->send(response);
|
|
}
|
|
|
|
/**
|
|
* Initializes WiFi as AP, configures web, dhcp and dns servers and sets up captive portal.
|
|
* When provisioning is done, credentials are saved and device is reset.
|
|
*
|
|
* ssid[in] ssid of the AP
|
|
* password[in] password for the AP
|
|
*/
|
|
void ProvisionSoftAP::init(const char * ssid, const char * password)
|
|
{
|
|
// start AP
|
|
|
|
/* Soft AP network parameters */
|
|
IPAddress apIP(sta_ip);
|
|
IPAddress netMsk(net_mask);
|
|
IPAddress firstIp(first_ip);
|
|
wifiEventId = WiFi.onEvent(std::bind(&ProvisionSoftAP::wifiEvent, this, _1, _2));
|
|
|
|
WiFi.mode(WIFI_MODE_AP);
|
|
WiFi.softAPConfig(apIP, apIP, netMsk, firstIp);
|
|
WiFi.softAP(ssid, password);
|
|
|
|
delay(200);
|
|
|
|
// setup the dns
|
|
m_dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
|
m_dnsServer.setTTL(30);
|
|
m_dnsServer.start(53, "*", apIP);
|
|
|
|
// setup the http server
|
|
m_webSocket->onEvent(std::bind(&ProvisionSoftAP::websocketEvent, this, _1, _2, _3, _4, _5, _6));
|
|
m_webServer->addHandler(m_webSocket);
|
|
m_webServer->on("/", std::bind(&ProvisionSoftAP::handlePortal, this, _1));
|
|
m_webServer->on("/provision.html", std::bind(&ProvisionSoftAP::handlePortal, this, _1));
|
|
m_webServer->on("/generate_204", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
|
m_webServer->on("/fwlink", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
|
m_webServer->on("/logo.png", std::bind(&ProvisionSoftAP::handleLogo, this, _1));
|
|
m_webServer->onNotFound(std::bind(&ProvisionSoftAP::handlePortal, this, _1));
|
|
|
|
m_webServer->begin();
|
|
|
|
while (true)
|
|
{
|
|
vTaskDelay(1);
|
|
m_dnsServer.processNextRequest();
|
|
|
|
// if (m_startWifiScan)
|
|
// {
|
|
// m_startWifiScan = false;
|
|
// ESP_LOGI(TAG, "Starting scan");
|
|
// int num_networks = WiFi.scanNetworks();
|
|
|
|
// ESP_LOGI(TAG, "Found %d networks", num_networks);
|
|
|
|
// // m_jsonBuffer.clear();
|
|
// // JsonObject& root = m_jsonBuffer.createObject();
|
|
// // root[TYPE] = WIFI_SCAN;
|
|
// // JsonArray& data = root.createNestedArray(DATA);
|
|
// // for (int n = 0; n < num_networks; n++)
|
|
// // {
|
|
// // JsonArray& data_inner = data.createNestedArray();
|
|
// // data_inner.add(WiFi.SSID(n));
|
|
// // data_inner.add(WiFi.encryptionType(n) == WIFI_AUTH_OPEN ? 0 : 1);
|
|
// // data_inner.add(WiFi.RSSI(n));
|
|
// // }
|
|
// // size_t len = root.printTo(m_buff, sizeof(m_buff));
|
|
// // m_webSocket->textAll(m_buff, len);
|
|
// // waitBufferEmpty();
|
|
// }
|
|
// if(m_tryConnect)
|
|
// {
|
|
// m_tryConnect = false;
|
|
// tryConnect(m_ssid, m_password);
|
|
// }
|
|
// if(m_tailgate)
|
|
// {
|
|
// m_dnsServer.stop();
|
|
// m_webSocket->closeAll(0, "bye");
|
|
// waitBufferEmpty();
|
|
// WiFi.removeEvent(wifiEventId);
|
|
// return;
|
|
// }
|
|
}
|
|
|
|
for (int i = 200; i >= 0; i--)
|
|
{
|
|
ESP_LOGI(TAG, "sleeping %d seconds...", i);
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
void ProvisionSoftAP::parseWebsocketCommand(char* data, size_t len)
|
|
{
|
|
data[len] = 0;
|
|
ESP_LOGI(TAG, "'%s'", data);
|
|
// m_jsonBuffer.clear();
|
|
// if(len<1024)
|
|
// data[len] = '\0';
|
|
|
|
// JsonObject& root = m_jsonBuffer.parseObject(data);
|
|
|
|
// const char * command = root[COMMAND];
|
|
// ESP_LOGI(TAG, "command: %s", command);
|
|
|
|
// if (command && strcmp(command, WIFI_SCAN) == 0)
|
|
// {
|
|
// m_startWifiScan = true;
|
|
// ESP_LOGI(TAG, "Start scan command received");
|
|
// }
|
|
// else if (command && strcmp(command, WIFI_CONFIG) == 0)
|
|
// {
|
|
// const char * ssid = root[SSID];
|
|
// const char * pwd = root[PWD];
|
|
// strcpy(m_ssid, ssid);
|
|
// strcpy(m_password, pwd);
|
|
// ESP_LOGI(TAG, "ssid/credentials received: '%s'/'%s'", m_ssid, m_password);
|
|
// m_tryConnect = true;
|
|
// }
|
|
// else if (command && strcmp(command, TAILGATE) == 0)
|
|
// {
|
|
// m_tailgate = true;
|
|
// ESP_LOGI(TAG, "tailgate command received");
|
|
// }
|
|
}
|
|
|
|
void ProvisionSoftAP::waitBufferEmpty()
|
|
{
|
|
// ESP_LOGI(TAG, "waitBufferEmpty start");
|
|
// bool empty = false;
|
|
// while (!empty)
|
|
// {
|
|
// empty = true;
|
|
// for (const auto& c : m_clients)
|
|
// {
|
|
// if (m_webSocket->client(c) && !m_webSocket->client(c)->isQueueEmpty())
|
|
// {
|
|
// empty = false;
|
|
// break;
|
|
// }
|
|
// }
|
|
// vTaskDelay(10);
|
|
// }
|
|
// ESP_LOGI(TAG, "waitBufferEmpty end");
|
|
}
|
|
|
|
void ProvisionSoftAP::tryConnect(const char * ssid, const char * pwd)
|
|
{
|
|
ESP_LOGI(TAG, "trying to connect to %s", ssid);
|
|
|
|
WiFi.begin(ssid, pwd);
|
|
int connRes = WiFi.waitForConnectResult();
|
|
if(connRes == WL_CONNECTED)
|
|
{
|
|
// all is gud!
|
|
ESP_LOGI(TAG, "we're connected, sending confirmation");
|
|
|
|
// m_settings.storeSsidPwd(ssid, pwd);
|
|
|
|
// m_jsonBuffer.clear();
|
|
// JsonObject& root = m_jsonBuffer.createObject();
|
|
|
|
// root[TYPE] = ERROR;
|
|
// root[CODE] = 0;
|
|
|
|
// size_t len = root.printTo(m_buff, sizeof(m_buff));
|
|
// m_webSocket->textAll(m_buff, len);
|
|
// waitBufferEmpty();
|
|
// vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
|
|
ESP_LOGI(TAG, "restarting...");
|
|
esp_restart();
|
|
}
|
|
// ESP_LOGE(TAG, "nuh-uh peppernip, error %d", connRes);
|
|
// m_jsonBuffer.clear();
|
|
// JsonObject& root = m_jsonBuffer.createObject();
|
|
// root[TYPE] = ERROR;
|
|
// root[CODE] = 1;
|
|
// size_t len = root.printTo(m_buff, sizeof(m_buff));
|
|
// m_webSocket->textAll(m_buff, len);
|
|
// waitBufferEmpty();
|
|
|
|
m_webSocket->closeAll();
|
|
}
|
|
|
|
ProvisionSoftAP::~ProvisionSoftAP()
|
|
{
|
|
delete m_webServer;
|
|
}
|
|
|