From 9b71d6928a98b8508c0079e4c85b7da44a4b544e Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 24 Jan 2021 18:46:55 -0600 Subject: [PATCH] Added QR Code logic HomeSpan now broadcasts a Hashed Setup ID as MDNS "sh", which is used when pairing with a QR Code instead of a Setup Code. A text version of the resulting QR code is output to the Serial Monitor whenever the 9-digit Setup Code is generated or changed. The text version of the QR code can then be input into any QR Code Generator to create a pairable QR Code. The default Setup ID used to create the Hashed Setup ID is "HSPN". This can be changed with homeSpan.setQRCode(const char *id), where id is exactly 4 alphanumeric characters. If not, the request to change the Setup ID is silently ignored and remains "HSPN." --- src/HAP.cpp | 7 +++++-- src/HomeSpan.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ src/HomeSpan.h | 4 ++++ src/SRP.h | 1 + src/Settings.h | 2 ++ src/src.ino | 5 +---- 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index 947c021..f788d6a 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -58,11 +58,14 @@ void HAPClient::init(){ } else { char c[128]; - sprintf(c,"Generating SRP verification data for default Setup Code: %.3s-%.2s-%.3s\n\n",homeSpan.defaultSetupCode,homeSpan.defaultSetupCode+3,homeSpan.defaultSetupCode+5); + sprintf(c,"Generating SRP verification data for default Setup Code: %.3s-%.2s-%.3s\n",homeSpan.defaultSetupCode,homeSpan.defaultSetupCode+3,homeSpan.defaultSetupCode+5); Serial.print(c); srp.createVerifyCode(homeSpan.defaultSetupCode,verifyData.verifyCode,verifyData.salt); // create verification code from default Setup Code and random salt nvs_set_blob(srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data - nvs_commit(srpNVS); // commit to NVS + nvs_commit(srpNVS); // commit to NVS + Serial.print("Optional QR Code: "); + Serial.print(homeSpan.getQRCode(homeSpan.defaultSetupCode)); + Serial.print("\n\n"); } if(!nvs_get_blob(hapNVS,"ACCESSORY",NULL,&len)){ // if found long-term Accessory data in NVS diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 4823b3d..caab137 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -410,6 +410,17 @@ void Span::checkConnect(){ else mdns_service_txt_item_set("_hap","_tcp","sf","0"); // set Status Flag = 0 + uint8_t hashInput[22]; + uint8_t hashOutput[64]; + char setupHash[9]; + size_t len; + + memcpy(hashInput,qrID,4); // Create the Seup ID for use with optional QR Codes. This is an undocumented feature of HAP R2! + memcpy(hashInput+4,id,17); // Step 1: Concatenate 4-character Setup ID and 17-character Accessory ID into hashInput + mbedtls_sha512_ret(hashInput,21,hashOutput,0); // Step 2: Perform SHA-512 hash on combined 21-byte hashInput to create 64-byte hashOutput + mbedtls_base64_encode((uint8_t *)setupHash,9,&len,hashOutput,4); // Step 3: Encode the first 4 bytes of hashOutput in base64, which results in an 8-character, null-terminated, setupHash + mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash + Serial.print("\nStarting Web (HTTP) Server supporting up to "); Serial.print(maxConnections); Serial.print(" simultaneous connections...\n\n"); @@ -426,6 +437,19 @@ void Span::checkConnect(){ /////////////////////////////// +void Span::setQRID(const char *id){ + + char tBuf[5]; + sscanf(id,"%4[0-9A-Za-z]",tBuf); + + if(strlen(id)==4 && strlen(tBuf)==4){ + qrID=id; + } + +} // setQRID + +/////////////////////////////// + void Span::processSerialCommand(const char *c){ switch(c[0]){ @@ -515,6 +539,10 @@ void Span::processSerialCommand(const char *c){ nvs_set_blob(HAPClient::srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data nvs_commit(HAPClient::srpNVS); // commit to NVS Serial.print("New Code Saved!\n"); + + Serial.print("Optional QR Code: "); + Serial.print(getQRCode(setupCode)); + Serial.print("\n\n"); } } break; @@ -725,6 +753,32 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// +const char *Span::getQRCode(const char *setupCode){ + + uint64_t n; + uint64_t iSetupCode; + uint64_t iCategory; + + sscanf(category,"%llu",&iCategory); + sscanf(setupCode,"%llu",&iSetupCode); + + n=(iCategory << 31) | (0xA << 27) | iSetupCode; + + qrCode=""; + + while(n>0){ + char c=n%36+48; + if(c>57) + c+=7; + qrCode=c+qrCode; + n/=36; + } + qrCode="X-HM://00" + qrCode + qrID; + return(qrCode.c_str()); +} + +/////////////////////////////// + int Span::sprintfAttributes(char *cBuf){ int nBytes=0; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index d1864a3..3ead256 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -85,6 +85,7 @@ struct Span{ int nFatalErrors=0; // number of fatal errors in user-defined configuration String configLog; // log of configuration process, including any errors boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation) + String qrCode; // optional QR Code to use for pairing boolean connected=false; // WiFi connection status unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -97,6 +98,7 @@ struct Span{ uint8_t maxConnections=DEFAULT_MAX_CONNECTIONS; // number of simultaneous HAP connections unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan + const char *qrID=DEFAULT_QR_ID; // optional Setup ID used to pair with QR Code WiFiServer *hapServer; // pointer to the HAP Server connection Blinker statusLED; // indicates HomeSpan status @@ -146,6 +148,8 @@ struct Span{ void setMaxConnections(uint8_t nCon){maxConnections=nCon;} // sets maximum number of simultaneous HAP connections (HAP requires devices support at least 8) void setHostNameSuffix(const char *suffix){hostNameSuffix=suffix;} // sets the hostName suffix to be used instead of the 6-byte AccessoryID void setPortNum(uint16_t port){tcpPortNum=port;} // sets the TCP port number to use for communications between HomeKit and HomeSpan + void setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code + const char *getQRCode(const char *setupCode); // gets an optional QR code from setupCode }; /////////////////////////////// diff --git a/src/SRP.h b/src/SRP.h index 6b8a64d..d5f1066 100644 --- a/src/SRP.h +++ b/src/SRP.h @@ -29,6 +29,7 @@ #include #include +#include #include "HAPConstants.h" diff --git a/src/Settings.h b/src/Settings.h index 6631e06..9b1ccc6 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -61,6 +61,8 @@ #define DEFAULT_SETUP_CODE "46637726" // changed during network setup or with 'S' command +#define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID); + #define DEFAULT_CONTROL_PIN 21 // change with homeSpan.setControlPin(pin) #define DEFAULT_STATUS_PIN 13 // change with homeSpan.setStatusPin(pin) diff --git a/src/src.ino b/src/src.ino index 519b1cb..1a83208 100644 --- a/src/src.ino +++ b/src/src.ino @@ -8,10 +8,7 @@ void setup() { Serial.begin(115200); - homeSpan.setLogLevel(2); - - homeSpan.setHostNameSuffix(""); - homeSpan.setPortNum(1200); + homeSpan.setLogLevel(1); homeSpan.begin(Category::Lighting,"HomeSpanTest");