From 71860d0719cb0a943cf3fd25b95655de73a27d29 Mon Sep 17 00:00:00 2001 From: Roar Fredriksen Date: Tue, 13 Mar 2018 22:07:09 +0100 Subject: [PATCH] Created project Started on boot as access point --- .../AmsToMqttBridge/AmsToMqttBridge.ino | 45 ++++ .../AmsToMqttBridge/AmsToMqttBridge.sln | 25 ++ .../AmsToMqttBridge/AmsToMqttBridge.vcxproj | 90 +++++++ .../AmsToMqttBridge.vcxproj.filters | 25 ++ Code/Arduino/AmsToMqttBridge/accesspoint.cpp | 113 +++++++++ Code/Arduino/AmsToMqttBridge/accesspoint.h | 44 ++++ .../Arduino/AmsToMqttBridge/configuration.cpp | 231 ++++++++++++++++++ Code/Arduino/AmsToMqttBridge/configuration.h | 53 ++++ 8 files changed, 626 insertions(+) create mode 100644 Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.ino create mode 100644 Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.sln create mode 100644 Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj create mode 100644 Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj.filters create mode 100644 Code/Arduino/AmsToMqttBridge/accesspoint.cpp create mode 100644 Code/Arduino/AmsToMqttBridge/accesspoint.h create mode 100644 Code/Arduino/AmsToMqttBridge/configuration.cpp create mode 100644 Code/Arduino/AmsToMqttBridge/configuration.h diff --git a/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.ino b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.ino new file mode 100644 index 00000000..ce276d1d --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.ino @@ -0,0 +1,45 @@ +/* + Name: AmsToMqttBridge.ino + Created: 3/13/2018 7:40:28 PM + Author: roarf +*/ + + +#include "configuration.h" +#include "accesspoint.h" +#include + +accesspoint ap; + + +// the setup function runs once when you press reset or power the board +void setup() +{ + // Setup serial port for debugging + Serial.begin(115200); + while (!Serial); + Serial.println("Started..."); + + // Assign pin for boot as AP + delay(1000); + pinMode(0, INPUT_PULLUP); + + // Flash the blue LED, to indicate we can boot as AP now + pinMode(2, OUTPUT); + digitalWrite(2, LOW); + + // Initialize the AP + ap.setup(0, Serial); + + // Turn off the blue LED + digitalWrite(2, HIGH); +} + +// the loop function runs over and over again until power down or reset +void loop() +{ + if (!ap.loop()) + { + // Only do normal stupp if we're not booted as AP + } +} diff --git a/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.sln b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.sln new file mode 100644 index 00000000..edff2767 --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AmsToMqttBridge", "AmsToMqttBridge.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32 + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32 + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32 + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5DFC14B6-4C33-4307-8BDA-C050F68A74F6} + EndGlobalSection +EndGlobal diff --git a/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj new file mode 100644 index 00000000..51e7b949 --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj @@ -0,0 +1,90 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C5F80730-F44F-4478-BDAE-6634EFC2CA88} + AmsToMqttBridge + AmsToMqttBridge + + + + Application + true + + + MultiByte + + + Application + false + + + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + $(ProjectDir)..\AmsToMqttBridge;$(ProjectDir)..\..\..\..\..\..\..\..\..\Program Files (x86)\Arduino\libraries;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\libraries;$(ProjectDir)..\..\..\..\..\..\..\Google Drive\Private\Elektronikk\Arduino\libraries;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\libb64;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\spiffs;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\umm_malloc;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\variants\generic;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\tools\sdk\include;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\tools\sdk\lwip\include;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\1.20.0-26-gb404fb9-2\xtensa-lx106-elf\include\c++\4.8.2;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\1.20.0-26-gb404fb9-2\xtensa-lx106-elf\include\c++\4.8.2\xtensa-lx106-elf;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\1.20.0-26-gb404fb9-2\xtensa-lx106-elf\include;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\1.20.0-26-gb404fb9-2\lib\gcc\xtensa-lx106-elf\4.8.2\include;$(ProjectDir)..\..\..\..\..\..\..\AppData\Local\arduino15\packages\esp8266\hardware\esp8266\2.3.0\tools\sdk\include;%(AdditionalIncludeDirectories) + $(ProjectDir)__vm\.AmsToMqttBridge.vsarduino.h;%(ForcedIncludeFiles) + false + __ESP8266_ESp8266__;__ESP8266_ESP8266__;__ets__;ICACHE_FLASH;F_CPU=80000000L;LWIP_OPEN_SRC;ARDUINO=106012;ARDUINO_ESP8266_ESP01;ARDUINO_ARCH_ESP8266;ESP8266;__cplusplus=201103L;_VMICRO_INTELLISENSE;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + VisualMicroDebugger + + + + CppCode + + + + + + + + + \ No newline at end of file diff --git a/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj.filters b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj.filters new file mode 100644 index 00000000..509b0459 --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/AmsToMqttBridge.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + \ No newline at end of file diff --git a/Code/Arduino/AmsToMqttBridge/accesspoint.cpp b/Code/Arduino/AmsToMqttBridge/accesspoint.cpp new file mode 100644 index 00000000..81f1dd89 --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/accesspoint.cpp @@ -0,0 +1,113 @@ +// +// +// + +#include "accesspoint.h" + +ESP8266WebServer accesspoint::server(80); + +void accesspoint::setup(int accessPointButtonPin, Stream& debugger) +{ + this->debugger = &debugger; + + // Test if we're missing configuration + if (!config.hasConfig()) + { + this->print("No config. We're booting as AP. Look for SSID "); + this->println(this->AP_SSID); + isActivated = true; + } + else + { + // Test if we're holding down the AP pin, over 5 seconds + int time = millis() + 5000; + this->print("Press the AP button now to boot as access point"); + while (millis() < time) + { + this->print("."); + if (digitalRead(accessPointButtonPin) == LOW) + { + this->println(""); + this->println("AP button was pressed. Booting as access point now"); + isActivated = true; + break; + } + delay(100); + } + } + + if (isActivated) + { + WiFi.disconnect(true); + WiFi.softAPdisconnect(true); + WiFi.mode(WIFI_OFF); + delay(2000); + + WiFi.softAP(AP_SSID); + WiFi.mode(WIFI_AP); + + /* Setup the DNS server redirecting all the domains to this IP */ + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); + + server.on("/", handleRoot); + server.begin(); // Web server start + + this->print("Web server is ready for config at http://"); + this->print(WiFi.softAPIP()); + this->println("/"); + } +} + +/** Handle root or redirect to captive portal */ +void accesspoint::handleRoot() { + Serial.println("Serving / over http..."); + + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.sendContent( + "" + "

HELLO WORLD!!

" + ); + server.sendContent( + "

You may want to config the wifi connection.

" + "" + ); + server.client().stop(); // Stop is needed because we sent no content length +} + +bool accesspoint::loop() { + if (isActivated) + { + //DNS + dnsServer.processNextRequest(); + //HTTP + server.handleClient(); + return true; + } + else + { + return false; + } +} + + +size_t accesspoint::print(const char* text) +{ + if (this->debugger) this->debugger->print(text); +} +size_t accesspoint::println(const char* text) +{ + if (this->debugger) this->debugger->println(text); +} +size_t accesspoint::print(const Printable& data) +{ + if (this->debugger) this->debugger->print(data); +} +size_t accesspoint::println(const Printable& data) +{ + if (this->debugger) this->debugger->println(data); +} diff --git a/Code/Arduino/AmsToMqttBridge/accesspoint.h b/Code/Arduino/AmsToMqttBridge/accesspoint.h new file mode 100644 index 00000000..0a4c18f5 --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/accesspoint.h @@ -0,0 +1,44 @@ +// ap.h + +#ifndef _ACCESSPOINT_h +#define _ACCESSPOINT_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include +#include +#include +#include "configuration.h" + +class accesspoint { +public: + void setup(int accessPointButtonPin, Stream& debugger); + bool loop(); + +private: + const char* AP_SSID = "AMS2MQTT"; + + configuration config; + bool isActivated = false; + Stream* debugger = NULL; + + // DNS server + const byte DNS_PORT = 53; + DNSServer dnsServer; + + size_t print(const char* text); + size_t println(const char* text); + size_t print(const Printable& data); + size_t println(const Printable& data); + + // Web server + static void handleRoot(); + static ESP8266WebServer server; +}; + +#endif + diff --git a/Code/Arduino/AmsToMqttBridge/configuration.cpp b/Code/Arduino/AmsToMqttBridge/configuration.cpp new file mode 100644 index 00000000..9ea14acd --- /dev/null +++ b/Code/Arduino/AmsToMqttBridge/configuration.cpp @@ -0,0 +1,231 @@ +// +// +// + +#include "configuration.h" + +bool configuration::hasConfig() +{ + bool has = false; + EEPROM.begin(EEPROM_SIZE); + has = EEPROM.read(EEPROM_CONFIG_ADDRESS) == EEPROM_CHECK_SUM; + EEPROM.end(); + return has; +} + +bool configuration::save() +{ + int address = EEPROM_CONFIG_ADDRESS; + + EEPROM.begin(EEPROM_SIZE); + EEPROM.put(address, EEPROM_CHECK_SUM); + address++; + + address += saveString(address, ssid); + address += saveString(address, ssidPassword); + address += saveString(address, mqtt); + address += saveInt(address, mqttPort); + address += saveString(address, mqttClientID); + address += saveString(address, mqttPublishTopic); + address += saveString(address, mqttSubscribeTopic); + + if (isSecure()) { + address += saveBool(address, true); + address += saveString(address, mqttUser); + address += saveString(address, mqttPass); + } + else + address += saveBool(address, false); + + bool vRet = EEPROM.commit(); + EEPROM.end(); + + return vRet; +} + + +bool configuration::load() +{ + int address = EEPROM_CONFIG_ADDRESS; + bool success = false; + + EEPROM.begin(EEPROM_SIZE); + if (EEPROM.read(address) == EEPROM_CHECK_SUM) + { + address++; + + address += readString(address, &ssid); + address += readString(address, &ssidPassword); + address += readString(address, &mqtt); + address += readInt(address, &mqttPort); + address += readString(address, &mqttClientID); + address += readString(address, &mqttPublishTopic); + address += readString(address, &mqttSubscribeTopic); + + bool secure = false; + address += readBool(address, &secure); + + if (secure) + { + address += readString(address, &mqttUser); + address += readString(address, &mqttPass); + } + else + { + mqttUser = 0; + mqttPass = 0; + } + + success = true; + } + else + { + ssid = (char*)String("").c_str(); + ssidPassword = (char*)String("").c_str(); + mqtt = (char*)String("").c_str(); + mqttClientID = (char*)String("").c_str(); + mqttPublishTopic = (char*)String("").c_str(); + mqttSubscribeTopic = (char*)String("").c_str(); + mqttUser = 0; + mqttPass = 0; + mqttPort = 1883; + } + EEPROM.end(); + return success; +} + +bool configuration::isSecure() +{ + return (mqttUser != 0) && (String(mqttUser).length() > 0); +} + +int configuration::readInt(int pAddress, int *pValue) +{ + int lower = EEPROM.read(pAddress); + int higher = EEPROM.read(pAddress + 1); + *pValue = lower + (higher << 8); + return 2; +} +int configuration::saveInt(int pAddress, int pValue) +{ + byte lowByte = pValue & 0xFF; + byte highByte = ((pValue >> 8) & 0xFF); + + EEPROM.write(pAddress, lowByte); + EEPROM.write(pAddress + 1, highByte); + + return 2; +} + +int configuration::readBool(int pAddress, bool *pValue) +{ + byte y = EEPROM.read(pAddress); + *pValue = (bool)y; + //Serial.printf("Read bool as %#x [%s]\r\n", y, (*pValue ? "true" : "false")); + return 1; +} + +int configuration::saveBool(int pAddress, bool pValue) +{ + byte y = (byte)pValue; + //Serial.printf("Writing bool as %#x [%s]\r\n", y, (pValue ? "true" : "false")); + EEPROM.write(pAddress, y); + return 1; +} +void configuration::print(Stream& serial) +{ + + /* + char* ssid; + char* ssidPassword; + char* mqtt; + int mqttPort; + char* mqttClientID; + char* mqttPublishTopic; + char* mqttSubscribeTopic; + bool secure; + char* mqttUser; + char* mqttPass; + */ + + serial.println("Configuration:"); + serial.println("-----------------------------------------------"); + serial.printf("ssid: %s\r\n", this->ssid); + serial.printf("ssidPassword: %s\r\n", this->ssidPassword); + serial.printf("mqtt: %s\r\n", this->mqtt); + serial.printf("mqttPort: %i\r\n", this->mqttPort); + serial.printf("mqttClientID: %s\r\n", this->mqttClientID); + serial.printf("mqttPublishTopic: %s\r\n", this->mqttPublishTopic); + serial.printf("mqttSubscribeTopic: %s\r\n", this->mqttSubscribeTopic); + + if (this->isSecure()) + { + serial.printf("SECURE MQTT CONNECTION:\r\n"); + serial.printf("mqttUser: %s\r\n", this->mqttUser); + serial.printf("mqttPass: %s\r\n", this->mqttPass); + } + serial.println("-----------------------------------------------"); +} + + + +template int configuration::writeAnything(int ee, const T& value) +{ + const byte* p = (const byte*)(const void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + EEPROM.write(ee++, *p++); + return i; +} + +template int configuration::readAnything(int ee, T& value) +{ + byte* p = (byte*)(void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + *p++ = EEPROM.read(ee++); + return i; +} + +int configuration::readString(int pAddress, char* pString[]) +{ + int address = 0; + + byte vLength = EEPROM.read(pAddress + address); + address++; + + //Serial.print("Found length of string: "); + //Serial.println(vLength); + + char* buffer = new char[vLength]; + for (int i = 0; i + + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +class configuration { +public: + char* ssid; + char* ssidPassword; + char* mqtt; + int mqttPort; + char* mqttClientID; + char* mqttPublishTopic; + char* mqttSubscribeTopic; + char* mqttUser; + char* mqttPass; + + bool hasConfig(); + bool isSecure(); + bool save(); + bool load(); + + void print(Stream& serial); +protected: + +private: + const int EEPROM_SIZE = 512; + const byte EEPROM_CHECK_SUM = 124; // Used to check if config is stored. Change if structure changes + const int EEPROM_CONFIG_ADDRESS = 0; + + int saveString(int pAddress, char* pString); + int readString(int pAddress, char* pString[]); + int saveInt(int pAddress, int pValue); + int readInt(int pAddress, int *pValue); + int saveBool(int pAddress, bool pValue); + int readBool(int pAddress, bool *pValue); + + + template int writeAnything(int ee, const T& value); + template int readAnything(int ee, T& value); +}; + +#endif +