From 705a0922e1019d62339a289874d98cf6403df62a Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 30 Aug 2020 22:07:16 -0500 Subject: [PATCH] Started work on CaptiveAP Added span::config(hostName) to provide CaptiveAP at start-up. --- src/HomeSpan.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++----- src/HomeSpan.h | 5 ++ 2 files changed, 117 insertions(+), 11 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 99746aa..9ed800d 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "Utils.h" #include "HAP.h" @@ -212,6 +213,16 @@ void Span::initWifi(){ char pwd[MAX_PWD+1]; } wifiData; + char id[18]; // create string version of Accessory ID for MDNS broadcast + memcpy(id,HAPClient::accessory.ID,17); // copy ID bytes + id[17]='\0'; // add terminating null + + // create broadcaset name from server base name plus accessory ID (with ':' replaced by '_') + + int nChars=snprintf(NULL,0,"%s-%.2s_%.2s_%.2s_%.2s_%.2s_%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); + char hostName[nChars+1]; + sprintf(hostName,"%s-%.2s_%.2s_%.2s_%.2s_%.2s_%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); + nvs_handle wifiHandle; size_t len; // not used but required to read blobs from NVS @@ -220,7 +231,9 @@ void Span::initWifi(){ if(!nvs_get_blob(wifiHandle,"WIFIDATA",NULL,&len)){ // if found WiFi data in NVS nvs_get_blob(wifiHandle,"WIFIDATA",&wifiData,&len); // retrieve data } else { - statusLED.start(300,0.3); + statusLED.start(500,0.3,2,1000); + + configure(hostName); Serial.print("Please configure network...\n"); sprintf(wifiData.ssid,"MyNetwork"); @@ -243,16 +256,6 @@ void Span::initWifi(){ nvs_commit(wifiHandle); // commit to NVS } - char id[18]; // create string version of Accessory ID for MDNS broadcast - memcpy(id,HAPClient::accessory.ID,17); // copy ID bytes - id[17]='\0'; // add terminating null - - // create broadcaset name from server base name plus accessory ID (with ':' replaced by '_') - - int nChars=snprintf(NULL,0,"%s-%.2s_%.2s_%.2s_%.2s_%.2s_%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); - char hostName[nChars+1]; - sprintf(hostName,"%s-%.2s_%.2s_%.2s_%.2s_%.2s_%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); - int nTries=0; statusLED.start(1000); @@ -334,6 +337,104 @@ void Span::initWifi(){ /////////////////////////////// +void Span::configure(char *apName){ + + Serial.print("WiFi Configuration required. Please connect to Access Point: "); + Serial.print(apName); + Serial.print("\n"); + + const byte DNS_PORT = 53; + WiFiServer apServer(80); + DNSServer dnsServer; + IPAddress apIP(192, 168, 4, 1); + + WiFi.mode(WIFI_AP); + WiFi.softAP(apName,"homespan2020"); + dnsServer.start(DNS_PORT, "*", apIP); + apServer.begin(); + + String responseHTML = "" + "CaptivePortal" + "

Hello World!

This is a captive portal example. All requests will " + "be redirected here.

"; + + boolean configured=false; + + while(!configured){ + dnsServer.processNextRequest(); + WiFiClient apClient=apServer.available(); + + if(apClient){ // found a new HTTP client + Serial.print("Client Found: "); + Serial.println(apClient.remoteIP()); + + while(apClient.available()){ // data is available + + int nBytes=apClient.read(HAPClient::httpBuf,HAPClient::MAX_HTTP); // read all available bytes up to maximum allowed + char *p=(char *)HAPClient::httpBuf; + p[nBytes]='\0'; + Serial.print(p); + Serial.print("\n"); + + if(!strncmp(p,"GET /",5)){ + apClient.println("HTTP/1.1 200 OK"); + apClient.println("Content-type:text/html"); + apClient.println(); + + apClient.print("HomeSpan Configuration"); + apClient.print("

HomeSpan_12_54_DD_E4_23_F5

"); + apClient.print("

Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network, and to create a Setup Code for pairing this device to HomeKit.

"); + apClient.print("

WiFi Network is a required field. If your network name is broadcast you can select it from the dropdown list. If you don't see your network name in the dropdown list you may type it into the text box.

"); + apClient.print("

WiFi Password is required only if your network is password-protected.

"); + apClient.print("

Every HomeKit device requires an 8-digit Setup Code. If this device is being configured for the first time, or if the device has been factory reset, you must create a new Setup Code. "); + apClient.print("Otherwise, either leave this field blank to retain the current Setup Code, or type a new 8-digit Setup Code to replace the current one.

"); + apClient.print("

Note that HomeSpan cannot display a previously saved Setup Code, so if you've forgotten what it is, this is the place to create a new one.

"); + apClient.print("

The LED on this device should be double-blinking during this configuration.

"); + + apClient.print("

"); + apClient.print(""); + apClient.print(""); + apClient.print(""); + apClient.print(""); + apClient.print(""); + apClient.print(""); + apClient.print(""); + apClient.print("

"); + + apClient.print(""); + apClient.print(""); + apClient.print("

"); + + apClient.print(""); + apClient.print(""); + + apClient.print("

"); + apClient.print("This device already has a Setup Code. You may leave Setup Code blank to retain the current one, or type a new Setup Code to change."); + apClient.print("This device does not yet have a Setup Code. You must create one above."); + apClient.print("

"); + + apClient.print("
"); + apClient.print(""); + } // GET + + if(!strncmp(p,"POST /",6)){ + apClient.println("HTTP/1.1 200 OK"); + apClient.println("Content-type:text/html"); + apClient.println(); + apClient.println("SUCCESS"); + } + + } // data available + + apClient.stop(); + } + + } + +} + +/////////////////////////////// + void Span::processSerialCommand(char *c){ switch(c[0]){ diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 66ba284..005f662 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -1,4 +1,8 @@ +#ifndef ARDUINO_ARCH_ESP32 +#error ERROR: HOMESPAN IS ONLY AVAILABLE FOR ESP32 MICROCONTROLLERS! +#endif + #include #include @@ -67,6 +71,7 @@ struct Span{ int getFreeSlot(); // returns free HAPClient slot number. HAPClients slot keep track of each active HAPClient connection void initWifi(); // initialize and connect to WiFi network void processSerialCommand(char *c); // process command 'c' (typically from readSerial, though can be called with any 'c') + void configure(char *hostName); // configure homeSpan WiFi and Setup Code using temporary Captive Access Point 'hostName' int sprintfAttributes(char *cBuf); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL void prettyPrint(char *buf, int nsp=2); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces