You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

403 lines
13 KiB

//-----------------------------------------------------------------------------
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "ahoywifi.h"
// NTP CONFIG
#define NTP_PACKET_SIZE 48
//-----------------------------------------------------------------------------
ahoywifi::ahoywifi() : mApIp(192, 168, 4, 1) {}
//-----------------------------------------------------------------------------
void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb) {
mConfig = config;
mUtcTimestamp = utcTimestamp;
mAppWifiCb = cb;
mStaConn = DISCONNECTED;
mCnt = 0;
mScanActive = false;
#if defined(ESP8266)
wifiConnectHandler = WiFi.onStationModeConnected(std::bind(&ahoywifi::onConnect, this, std::placeholders::_1));
wifiGotIPHandler = WiFi.onStationModeGotIP(std::bind(&ahoywifi::onGotIP, this, std::placeholders::_1));
wifiDisconnectHandler = WiFi.onStationModeDisconnected(std::bind(&ahoywifi::onDisconnect, this, std::placeholders::_1));
#else
WiFi.onEvent(std::bind(&ahoywifi::onWiFiEvent, this, std::placeholders::_1));
#endif
setupWifi(true);
}
//-----------------------------------------------------------------------------
void ahoywifi::setupWifi(bool startAP = false) {
#if !defined(FB_WIFI_OVERRIDDEN)
if(startAP) {
setupAp();
delay(1000);
}
#endif
#if !defined(AP_ONLY)
if(mConfig->valid) {
#if !defined(FB_WIFI_OVERRIDDEN)
if(strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) != 0)
setupStation();
#else
setupStation();
#endif
}
#endif
}
//-----------------------------------------------------------------------------
void ahoywifi::tickWifiLoop() {
#if !defined(AP_ONLY)
if(mStaConn != GOT_IP) {
if (WiFi.softAPgetStationNum() > 0) { // do not reconnect if any AP connection exists
if(mStaConn != IN_AP_MODE) {
mStaConn = IN_AP_MODE;
// first time switch to AP Mode
if (mScanActive) {
2 years ago
WiFi.scanDelete();
mScanActive = false;
2 years ago
}
DBGPRINTLN(F("AP client connected"));
welcome(mApIp.toString());
WiFi.mode(WIFI_AP);
mDns.start(53, "*", mApIp);
mAppWifiCb(true);
}
2 years ago
mDns.processNextRequest();
return;
}
else if(mStaConn == IN_AP_MODE) {
mCnt = 0;
2 years ago
mDns.stop();
WiFi.mode(WIFI_AP_STA);
mStaConn = DISCONNECTED;
}
mCnt++;
if(!mScanActive && mBSSIDList.empty()) { // start scanning APs with the given SSID
2 years ago
DBGPRINT(F("scanning APs with SSID "));
DBGPRINTLN(String(mConfig->sys.stationSsid));
mScanCnt = 0;
2 years ago
mScanActive = true;
#if defined(ESP8266)
WiFi.scanNetworks(true, false, 0U, (uint8_t *)mConfig->sys.stationSsid);
#else
WiFi.scanNetworks(true, false, false, 300U, 0U, mConfig->sys.stationSsid);
#endif
return;
}
uint8_t timeout = 10; // seconds
if (mStaConn == CONNECTED) // connected but no ip
timeout = 20;
DBGPRINT(F("reconnect in "));
DBGPRINT(String(timeout-mCnt));
DBGPRINTLN(F(" seconds"));
2 years ago
if(mScanActive) {
getBSSIDs();
if(!mScanActive) // scan completed
if ((mCnt % timeout) < timeout - 2)
mCnt = timeout - 2;
2 years ago
}
if((mCnt % timeout) == 0) { // try to reconnect after x sec without connection
if(mStaConn != CONNECTED)
mStaConn = CONNECTING;
2 years ago
if(mBSSIDList.size() > 0) { // get first BSSID in list
DBGPRINT("try to connect to AP with BSSID:");
uint8_t bssid[6];
for (int j = 0; j < 6; j++) {
bssid[j] = mBSSIDList.front();
mBSSIDList.pop_front();
DBGPRINT(" " + String(bssid[j], HEX));
}
DBGPRINTLN("");
WiFi.disconnect();
WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd, 0, &bssid[0]);
}
mCnt = 0;
}
}
#endif
}
//-----------------------------------------------------------------------------
void ahoywifi::setupAp(void) {
DPRINTLN(DBG_VERBOSE, F("wifi::setupAp"));
DBGPRINTLN(F("\n---------\nAhoyDTU Info:"));
DBGPRINT(F("Version: "));
DBGPRINTLN(String(VERSION_MAJOR) + F(".") + String(VERSION_MINOR) + F(".") + String(VERSION_PATCH));
DBGPRINT(F("Github Hash: "));
DBGPRINTLN(String(AUTO_GIT_HASH));
2 years ago
DBGPRINT(F("\n---------\nAP MODE\nSSID: "));
DBGPRINTLN(WIFI_AP_SSID);
2 years ago
DBGPRINT(F("PWD: "));
DBGPRINTLN(WIFI_AP_PWD);
DBGPRINTLN("IP Address: http://" + mApIp.toString());
DBGPRINTLN(F("---------\n"));
WiFi.mode(WIFI_AP_STA);
WiFi.softAPConfig(mApIp, mApIp, IPAddress(255, 255, 255, 0));
WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PWD);
}
//-----------------------------------------------------------------------------
void ahoywifi::setupStation(void) {
DPRINTLN(DBG_VERBOSE, F("wifi::setupStation"));
if(mConfig->sys.ip.ip[0] != 0) {
IPAddress ip(mConfig->sys.ip.ip);
IPAddress mask(mConfig->sys.ip.mask);
IPAddress dns1(mConfig->sys.ip.dns1);
IPAddress dns2(mConfig->sys.ip.dns2);
IPAddress gateway(mConfig->sys.ip.gateway);
if(!WiFi.config(ip, gateway, mask, dns1, dns2))
DPRINTLN(DBG_ERROR, F("failed to set static IP!"));
}
2 years ago
mBSSIDList.clear();
if(String(mConfig->sys.deviceName) != "")
WiFi.hostname(mConfig->sys.deviceName);
WiFi.mode(WIFI_AP_STA);
2 years ago
DBGPRINT(F("connect to network '"));
DBGPRINT(mConfig->sys.stationSsid);
DBGPRINTLN(F("' ..."));
}
//-----------------------------------------------------------------------------
bool ahoywifi::getNtpTime(void) {
if(GOT_IP != mStaConn)
return false;
IPAddress timeServer;
uint8_t buf[NTP_PACKET_SIZE];
uint8_t retry = 0;
if (WiFi.hostByName(mConfig->ntp.addr, timeServer) != 1)
return false;
mUdp.begin(mConfig->ntp.port);
sendNTPpacket(timeServer);
while(retry++ < 5) {
int wait = 150;
while(--wait) {
if(NTP_PACKET_SIZE <= mUdp.parsePacket()) {
uint64_t secsSince1900;
mUdp.read(buf, NTP_PACKET_SIZE);
secsSince1900 = (buf[40] << 24);
secsSince1900 |= (buf[41] << 16);
secsSince1900 |= (buf[42] << 8);
secsSince1900 |= (buf[43] );
*mUtcTimestamp = secsSince1900 - 2208988800UL; // UTC time
DPRINTLN(DBG_INFO, "[NTP]: " + ah::getDateTimeStr(*mUtcTimestamp) + " UTC");
return true;
} else
delay(10);
}
}
DPRINTLN(DBG_INFO, F("[NTP]: getNtpTime failed"));
return false;
}
//-----------------------------------------------------------------------------
void ahoywifi::sendNTPpacket(IPAddress& address) {
//DPRINTLN(DBG_VERBOSE, F("wifi::sendNTPpacket"));
uint8_t buf[NTP_PACKET_SIZE] = {0};
buf[0] = B11100011; // LI, Version, Mode
buf[1] = 0; // Stratum
buf[2] = 6; // Max Interval between messages in seconds
buf[3] = 0xEC; // Clock Precision
// bytes 4 - 11 are for Root Delay and Dispersion and were set to 0 by memset
buf[12] = 49; // four-byte reference ID identifying
buf[13] = 0x4E;
buf[14] = 49;
buf[15] = 52;
mUdp.beginPacket(address, 123); // NTP request, port 123
mUdp.write(buf, NTP_PACKET_SIZE);
mUdp.endPacket();
}
2 years ago
//-----------------------------------------------------------------------------
void ahoywifi::sortRSSI(int *sort, int n) {
for (int i = 0; i < n; i++)
sort[i] = i;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (WiFi.RSSI(sort[j]) > WiFi.RSSI(sort[i]))
std::swap(sort[i], sort[j]);
}
//-----------------------------------------------------------------------------
void ahoywifi::scanAvailNetworks(void) {
if(!mScanActive) {
mScanActive = true;
if(WIFI_AP == WiFi.getMode())
WiFi.mode(WIFI_AP_STA);
WiFi.scanNetworks(true);
}
}
//-----------------------------------------------------------------------------
void ahoywifi::getAvailNetworks(JsonObject obj) {
JsonArray nets = obj.createNestedArray("networks");
int n = WiFi.scanComplete();
2 years ago
if (n < 0)
return;
if(n > 0) {
int sort[n];
2 years ago
sortRSSI(&sort[0], n);
for (int i = 0; i < n; ++i) {
2 years ago
nets[i]["ssid"] = WiFi.SSID(sort[i]);
nets[i]["rssi"] = WiFi.RSSI(sort[i]);
}
}
2 years ago
mScanActive = false;
WiFi.scanDelete();
if(mStaConn == IN_AP_MODE)
WiFi.mode(WIFI_AP);
}
2 years ago
//-----------------------------------------------------------------------------
void ahoywifi::getBSSIDs() {
int n = WiFi.scanComplete();
if (n < 0) {
mScanCnt++;
if (mScanCnt < 20)
return;
}
2 years ago
if(n > 0) {
mBSSIDList.clear();
int sort[n];
sortRSSI(&sort[0], n);
for (int i = 0; i < n; i++) {
DBGPRINT("BSSID " + String(i) + ":");
uint8_t *bssid = WiFi.BSSID(sort[i]);
for (int j = 0; j < 6; j++){
DBGPRINT(" " + String(bssid[j], HEX));
mBSSIDList.push_back(bssid[j]);
}
DBGPRINTLN("");
}
}
mScanActive = false;
WiFi.scanDelete();
}
//-----------------------------------------------------------------------------
void ahoywifi::connectionEvent(WiFiStatus_t status) {
switch(status) {
case CONNECTED:
if(mStaConn != CONNECTED) {
mStaConn = CONNECTED;
DBGPRINTLN(F("\n[WiFi] Connected"));
}
break;
case GOT_IP:
mStaConn = GOT_IP;
2 years ago
if (mScanActive) { // maybe another scan has started
WiFi.scanDelete();
mScanActive = false;
}
welcome(WiFi.localIP().toString() + F(" (Station)"));
2 years ago
WiFi.mode(WIFI_STA);
DBGPRINTLN(F("[WiFi] AP disabled"));
mAppWifiCb(true);
break;
case DISCONNECTED:
if(mStaConn != CONNECTING) {
mStaConn = DISCONNECTED;
mCnt = 5; // try to reconnect in 5 sec
setupWifi(); // reconnect with AP / Station setup
mAppWifiCb(false);
DPRINTLN(DBG_INFO, "[WiFi] Connection Lost");
}
break;
default:
break;
}
}
//-----------------------------------------------------------------------------
#if defined(ESP8266)
//-------------------------------------------------------------------------
void ahoywifi::onConnect(const WiFiEventStationModeConnected& event) {
connectionEvent(CONNECTED);
}
//-------------------------------------------------------------------------
void ahoywifi::onGotIP(const WiFiEventStationModeGotIP& event) {
connectionEvent(GOT_IP);
}
//-------------------------------------------------------------------------
void ahoywifi::onDisconnect(const WiFiEventStationModeDisconnected& event) {
connectionEvent(DISCONNECTED);
}
#else
//-------------------------------------------------------------------------
void ahoywifi::onWiFiEvent(WiFiEvent_t event) {
DBGPRINT(F("Wifi event: "));
DBGPRINTLN(String(event));
switch(event) {
case SYSTEM_EVENT_STA_CONNECTED:
connectionEvent(CONNECTED);
break;
case SYSTEM_EVENT_STA_GOT_IP:
connectionEvent(GOT_IP);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
connectionEvent(DISCONNECTED);
break;
default:
break;
}
}
#endif
//-----------------------------------------------------------------------------
void ahoywifi::welcome(String msg) {
DBGPRINTLN(F("\n\n--------------------------------"));
DBGPRINTLN(F("Welcome to AHOY!"));
DBGPRINT(F("\npoint your browser to http://"));
DBGPRINTLN(msg);
DBGPRINTLN(F("to configure your device"));
DBGPRINTLN(F("--------------------------------\n"));
}