├── ESP32_GPS_via_LoRa.ino ├── ESP32_News_reader_04.ino ├── ESP32_SolarEdge_Monitor_epaper42_v3.ino ├── ESP32_e-paper_QRcode_01.ino ├── ESP8266_Erase_Credentials_Factory_Default.ino ├── ESP8266_OLED13_Luka_v02b.ino ├── ESP8266_Time_Services2.ino ├── GPSLoRa.cpp ├── GPSLoRa.h ├── Licence.txt ├── Marc_WU_Code.ino ├── README.md ├── Relay_Control_Example_1.ino ├── Relay_Control_Example_2.ino ├── Relay_Example_SPIFFS_1.ino └── ibrahim_code_v2.ino /ESP32_GPS_via_LoRa.ino: -------------------------------------------------------------------------------- 1 | /***************************************** 2 | * ESP32 GPS VKEL 9600 Bds 3 | ******************************************/ 4 | 5 | #include 6 | TinyGPSPlus gps; 7 | #include 8 | #include "GPSLoRa.h" 9 | 10 | #define SCK 5 // GPIO5 -- SX1278's SCK 11 | #define MISO 19 // GPIO19 -- SX1278's MISnO 12 | #define MOSI 27 // GPIO27 -- SX1278's MOSI 13 | #define SS 18 // GPIO18 -- SX1278's CS 14 | #define RST 14 // GPIO14 -- SX1278's RESET 15 | #define DI0 26 // GPIO26 -- SX1278's IRQ(Interrupt Request) 16 | #define BAND 868E6 // 868Mhz 17 | 18 | unsigned int counter = 0; 19 | 20 | String packet ; 21 | 22 | void setup() { 23 | Serial.begin(115200); 24 | Serial1.begin(9600, SERIAL_8N1, 12, 15); //17-TX 18-RX State GPS 25 | Serial.println("LoRa Sender GPS Data Test"); 26 | SPI.begin(SCK,MISO,MOSI,SS); 27 | LoRa.setPins(SS,RST,DI0); 28 | if (!LoRa.begin(868E6)) { 29 | Serial.println("Starting LoRa failed!"); 30 | while (1); 31 | } 32 | } 33 | 34 | void loop(){ 35 | Serial.print("Latitude : "); 36 | Serial.println(gps.location.lat(), 5); 37 | Serial.print("Longitude : "); 38 | Serial.println(gps.location.lng(), 4); 39 | Serial.print("Satellites: "); 40 | Serial.println(gps.satellites.value()); 41 | Serial.print("Altitude : "); 42 | Serial.print(gps.altitude.feet() / 3.2808); 43 | Serial.println("M"); 44 | Serial.print("Time : "); 45 | Serial.print(gps.time.hour()); 46 | Serial.print(":"); 47 | Serial.print(gps.time.minute()); 48 | Serial.print(":"); 49 | Serial.println(gps.time.second()); 50 | Serial.println("**********************"); 51 | smartDelay(1000); 52 | if (millis() > 5000 && gps.charsProcessed() < 10) Serial.println(F("No GPS data received: check wiring")); 53 | // send packet of data via LoRa 54 | LoRa.beginPacket(); 55 | LoRa.print("GPS-Data "); 56 | LoRa.print("Latitude : "); 57 | LoRa.print(gps.location.lat(), 5); 58 | LoRa.print("Longitude : "); 59 | LoRa.println(gps.location.lng(), 4); 60 | LoRa.endPacket(); 61 | } 62 | 63 | static void smartDelay(unsigned long ms){ 64 | unsigned long start = millis(); 65 | do 66 | { 67 | while (Serial1.available()) 68 | gps.encode(Serial1.read()); 69 | } while (millis() - start < ms); 70 | } 71 | -------------------------------------------------------------------------------- /ESP32_News_reader_04.ino: -------------------------------------------------------------------------------- 1 | /* ESP8266 News Reader, obtains data from a News Server and displays the result on the Serial port 2 | #################################################################################################################################### 3 | This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved. 4 | 5 | Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following: 6 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 7 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 8 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 9 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 10 | 11 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 12 | software use is visible to an end-user. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY 15 | OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | See more at http://www.dsbird.org.uk 19 | */ 20 | #include // Built-in 21 | #include // Built-in 22 | #include // https://github.com/bblanchon/ArduinoJson NOTE: *** MUST BE Version-6 or above *** 23 | 24 | 25 | //################ VERSION ########################## 26 | String version = "1"; // Version of this program 27 | //################ VARIABLES ########################### 28 | 29 | // Change to your WiFi credentials 30 | const char* ssid = "--"; 31 | const char* password = "--"; 32 | int news_index = 0; 33 | String apikey = ""; 34 | String news = ""; 35 | String rxtext = ""; 36 | // feed URL's array 37 | const char *rssFeedURL [] = { 38 | "http://newsapi.org/v2/top-headlines?country=us&apiKey=your_API_KEY", 39 | "http://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=your_API_KEY", 40 | "http://newsapi.org/v2/top-headlines?sources=bloomberg&apiKey=your_API_KEY", 41 | "http://newsapi.org/v2/top-headlines?sources=cbs-news&apiKey=your_API_KEY", 42 | "http://newsapi.org/v2/top-headlines?sources=cnn&apiKey=your_API_KEY" 43 | }; 44 | 45 | //################ PROGRAM VARIABLES and OBJECTS ################ 46 | 47 | WiFiClient client; // wifi client object 48 | 49 | //######################################################################################### 50 | void setup() { 51 | Serial.begin(115200); 52 | StartWiFi(); 53 | } 54 | //######################################################################################### 55 | void loop() { 56 | // Now cycle through the news sources see here: https://newsapi.org/docs/endpoints/sources 57 | if (Get_News_Feed(rssFeedURL[news_index])) Serial.println("News successfully received\n\n"); 58 | news_index++; 59 | if (news_index > 4 ) news_index = 0; 60 | delay(5000); 61 | } 62 | //######################################################################################### 63 | bool Get_News_Feed(const char* url) { 64 | int port = 80; 65 | const char *ptr; 66 | char server[80]; 67 | char request[100]; 68 | 69 | // Clear string storage 70 | memset(server, 0, sizeof(server)); 71 | memset(request, 0, sizeof(request)); 72 | 73 | // Is there a protocol specified ? 74 | // If so skip it 75 | if ((ptr = strchr(url, ':')) != NULL) { 76 | // Yes there was a protocol 77 | ptr += 3; 78 | } else { 79 | // No protocol specified 80 | ptr = url; 81 | } 82 | // Search for host separator 83 | char *endPtr = strchr(ptr, '/'); 84 | if (endPtr == NULL) { 85 | return false; 86 | } 87 | // Copy host string to host storage 88 | int index = 0; 89 | while (ptr != endPtr) { 90 | server[index++] = *ptr++; 91 | } 92 | // The remainder of the string is the path 93 | strcpy(request, endPtr); 94 | Serial.println("Connecting to '" + String(server) + " for current news using... "+String(request)); 95 | client.stop(); // close connection before sending a new request 96 | int connected_status = client.connect(server, port); //-SUCCESS 1 -TIMED_OUT -1 -INVALID_SERVER -2 -TRUNCATED -3 -INVALID_RESPONSE -4 97 | if (connected_status) { // if the connection succeeds 98 | //Serial.println("connecting..."); 99 | // send the HTTP GET request: 100 | client.print(String("GET ") + request + " HTTP/1.1\r\n" + 101 | "Host: " + server + "\r\n" + 102 | "Connection: keep-alive" + "\r\n\r\n" + 103 | "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n" + 104 | "Accept-encoding: identity\r\n" + 105 | "Accept-Language:en-GB,en-US;q=0.9,en;q=0.8\r\n" + 106 | "User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 \r\n" + 107 | "Upgrade-Insecure-Requests:1\r\n" + 108 | "Content-Type: application/json;charset=utf-8\r\n"); 109 | char ch = 0; 110 | int timeoutvalue_mS = 3500; 111 | bool startJson = false; 112 | int jsonend = 0; 113 | bool finished = false; 114 | unsigned long timeoutmS = timeoutvalue_mS + millis(); 115 | while (timeoutmS > millis()) { 116 | ch = client.read(); 117 | // JSON formats contain an equal number of open and close curly brackets, so check that JSON is received correctly by counting open and close brackets 118 | if (ch == '{') { 119 | startJson = true; // set true to indicate JSON message has started 120 | jsonend++; 121 | } 122 | if (ch == '}') { 123 | jsonend--; 124 | } 125 | if (startJson == true) { 126 | rxtext += ch; // Add in the received character 127 | //Serial.print(ch); 128 | } 129 | // if jsonend = 0 then we have have received equal number of curly braces 130 | if (jsonend == 0 && startJson == true) { 131 | //Serial.println("\nReceived OK..."); 132 | //Serial.println("Decoding..."); 133 | //Serial.println(rxtext.length()); 134 | //Serial.println(rxtext); // Display received message 135 | Decode_news(rxtext); 136 | rxtext = ""; 137 | client.stop(); 138 | break; 139 | } 140 | timeoutmS = timeoutvalue_mS + millis(); 141 | yield(); 142 | } 143 | client.stop(); 144 | } 145 | else { 146 | // if no connection was made: 147 | Serial.println("connection failed"); 148 | } 149 | } 150 | //######################################################################################### 151 | int StartWiFi() { 152 | Serial.print(F("\r\nConnecting to: ")); Serial.println(String(ssid)); 153 | WiFi.begin(ssid, password); 154 | while (WiFi.status() != WL_CONNECTED ) { 155 | delay(500); Serial.print("."); 156 | } 157 | Serial.println("WiFi connected at: " + String(WiFi.localIP())); 158 | return 1; 159 | } 160 | 161 | void Decode_news(String json) { 162 | //Serial.print(F("Creating object...")); 163 | DynamicJsonBuffer jsonBuffer (50 * 1024); 164 | JsonObject& root = jsonBuffer.parseObject(const_cast(json.c_str())); 165 | if (!root.success()) { 166 | Serial.print("ParseObject() failed"); 167 | } 168 | const char* status = root["status"]; // "ok" 169 | int totalResults = root["totalResults"]; // e.g. 20 170 | JsonArray& articles = root["articles"]; 171 | const char* title; 172 | const char* description; 173 | for (int i=0; i < totalResults; i++){ 174 | JsonObject& articles0 = articles[i]; 175 | title = articles0["title"]; 176 | description = articles0["description"]; 177 | Serial.print("News item: "); Serial.print(i<10?"0":""); Serial.print(String(i+1)+" - "); 178 | Serial.println(title); 179 | if (description != "") Serial.println(description); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /ESP32_SolarEdge_Monitor_epaper42_v3.ino: -------------------------------------------------------------------------------- 1 | /* ESP32 Solar PV (solar Edge) Monitor using an EPD 4.2" Display, obtains data from Solaredge, decodes it and then displays it. 2 | #################################################################################################################################### 3 | This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved. 4 | 5 | Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following: 6 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 7 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 8 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 9 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 10 | 11 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 12 | software use is visible to an end-user. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY 15 | OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | See more at http://www.dsbird.org.uk 19 | */ 20 | #include "credentials.h" // See 'credentials' tab and enter your OWM API key and set the Wifi SSID and PASSWORD 21 | #include // https://github.com/bblanchon/ArduinoJson NOTE: *** MUST BE Version-6 or above *** 22 | #include // Built-in 23 | #include // Built-in 24 | #include "time.h" // Built-in 25 | #include // Built-in 26 | #include "EPD_WaveShare.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 27 | #include "EPD_WaveShare_42.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 28 | #include "MiniGrafx.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 29 | #include "DisplayDriver.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 30 | #include "ArialRounded.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 31 | 32 | #define SCREEN_WIDTH 400.0 // Set for landscape mode, don't remove the decimal place! 33 | #define SCREEN_HEIGHT 300.0 34 | #define BITS_PER_PIXEL 1 35 | #define EPD_BLACK 0 36 | #define EPD_WHITE 1 37 | uint16_t palette[] = { 0, 1 }; 38 | 39 | // pins_arduino.h, e.g. LOLIN32 LITE 40 | static const uint8_t EPD_BUSY = 4; 41 | static const uint8_t EPD_SS = 5; 42 | static const uint8_t EPD_RST = 16; 43 | static const uint8_t EPD_DC = 17; 44 | static const uint8_t EPD_SCK = 18; 45 | static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display 46 | static const uint8_t EPD_MOSI = 23; 47 | 48 | EPD_WaveShare42 epd(EPD_SS, EPD_RST, EPD_DC, EPD_BUSY); 49 | MiniGrafx gfx = MiniGrafx(&epd, BITS_PER_PIXEL, palette); 50 | 51 | //################ VERSION ########################## 52 | String version = "1"; // Version of this program 53 | //################ VARIABLES ########################### 54 | 55 | const unsigned long UpdateInterval = (30L * 60L - 03) * 1000000L; // Update delay in microseconds, 13-secs is the time to update so compensate for that 56 | 57 | String time_str, Day_time_str; // strings to hold time and received time data; 58 | int wifi_signal, wifisection, displaysection, start_time; 59 | 60 | //################ PROGRAM VARIABLES and OBJECTS ################ 61 | 62 | float Production[12] = {0}; 63 | float Consumption[12] = {0}; 64 | float SelfConsumption[12] = {0}; 65 | float Purchased[12] = {0}; 66 | 67 | float LifeTimeEnergy = 0; 68 | float Revenue = 0; 69 | float LastYearEnergy = 0; 70 | float LastMonthEnergy = 0; 71 | float LastDayEnergy = 0; 72 | 73 | #define autoscale_on true 74 | #define autoscale_off false 75 | #define barchart_on true 76 | #define barchart_off falseco 77 | 78 | WiFiClientSecure client; // wifi client object 79 | 80 | //######################################################################################### 81 | void setup() { 82 | start_time = millis(); 83 | Serial.begin(115200); 84 | StartWiFi(); 85 | wifi_signal = WiFi_Signal(); 86 | SetupTime(); 87 | bool Received_EnergyData_OK = false; 88 | Received_EnergyData_OK = (Obtain_Energy_Reading("Daily") && Obtain_Energy_Reading("Yearly")); 89 | // Now only refresh the screen if all the data was received OK, otherwise wait until the next timed check otherwise wait until the next timed check 90 | if (Received_EnergyData_OK) { 91 | StopWiFi(); // Reduces power consumption 92 | gfx.init(); 93 | gfx.setRotation(0); 94 | gfx.setColor(EPD_BLACK); 95 | gfx.fillBuffer(EPD_WHITE); 96 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 97 | Display_Energy(); 98 | gfx.commit(); 99 | delay(2000); 100 | Serial.println("total time to update = " + String(millis() - start_time)); 101 | } 102 | begin_sleep(); 103 | } 104 | //######################################################################################### 105 | void loop() { // this will never run! 106 | } 107 | //######################################################################################### 108 | void begin_sleep() { 109 | esp_sleep_enable_timer_wakeup(UpdateInterval); 110 | Serial.println(F("Starting deep-sleep period...")); 111 | #ifdef BUILTIN_LED 112 | pinMode(BUILTIN_LED, INPUT); // In case it's on, turn output off, sometimes PIN-5 on some boards is used for SPI-SS 113 | digitalWrite(BUILTIN_LED, HIGH); // In case it's on, turn LED off, as sometimes PIN-5 on some boards is used for SPI-SS 114 | #endif 115 | delay(2000); 116 | esp_deep_sleep_start(); // Sleep for e.g. 30 minutes 117 | } 118 | //######################################################################################### 119 | void Display_Energy() { // 4.2" e-paper display is 400x300 resolution 120 | Display_Heading_Section(); // Top line of the display 121 | Display_YearlyEnergySummary(); 122 | Display_DailyEnergySummary(); 123 | Display_Status_Section(200, 0, wifi_signal); // Wi-Fi signal strength and Battery voltage 124 | } 125 | //######################################################################################### 126 | void Display_Heading_Section() { 127 | gfx.drawLine(0, 15, SCREEN_WIDTH, 15); 128 | gfx.setFont(ArialMT_Plain_10); 129 | gfx.setTextAlignment(TEXT_ALIGN_RIGHT); 130 | gfx.drawString(SCREEN_WIDTH - 3, 0, Day_time_str); 131 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 132 | gfx.drawString(5, 0, time_str); 133 | } 134 | //######################################################################################### 135 | void Display_DailyEnergySummary() { 136 | gfx.setFont(ArialMT_Plain_24); 137 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 138 | float total_consumption = 0; 139 | float total_selfconsumption = 0; 140 | for (int r = 0; r <= 11; r++) { 141 | total_consumption += Consumption[r]; 142 | total_selfconsumption += SelfConsumption[r]; 143 | } 144 | gfx.drawString(10, 20, "Total Production:"); Display_Power(200, 20, LifeTimeEnergy); 145 | gfx.drawString(10, 40, "Revenue:"); 146 | gfx.drawString(200, 40, (String)"£" + (Revenue)); 147 | Serial.println("Total Consumption: " + String(total_consumption, 1)); 148 | Serial.println("Total SelfConsumption: " + String(total_selfconsumption, 1)); 149 | gfx.drawString(280, 40, (String)" + £" + (total_selfconsumption * 0.133245)); // Tarif as of Sept 2018 for Electricity 150 | gfx.drawString(10, 60, "Power this year:"); Display_Power(200, 60, LastYearEnergy); 151 | gfx.drawString(10, 80, "Power this month:"); Display_Power(200, 80, LastMonthEnergy); 152 | gfx.drawString(10, 100, "Power today:"); Display_Power(200, 100, LastDayEnergy / 1000); // Daily consumption is usually in the range 0-30KWhr 153 | gfx.setFont(ArialMT_Plain_10); 154 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 155 | gfx.drawLine(0, 130, SCREEN_WIDTH, 130); 156 | } 157 | //######################################################################################### 158 | void Display_YearlyEnergySummary() { 159 | gfx.setFont(ArialRoundedMTBold_14); 160 | gfx.setTextAlignment(TEXT_ALIGN_CENTER); 161 | gfx.drawString(SCREEN_WIDTH / 2, 150, "Yearly Summary"); 162 | gfx.setFont(ArialMT_Plain_10); 163 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 164 | DrawGraph(25, 190, 150, 75, 0, 1000, "Consumption", Consumption, 12, autoscale_on, barchart_on); 165 | DrawGraph(230, 190, 150, 75, 0, 1000, "Self-Consumption", SelfConsumption, 12, autoscale_on, barchart_on); 166 | } 167 | //######################################################################################### 168 | void Display_Power(int x, int y, float reading) { 169 | String units = "KWhr"; 170 | int string_length; 171 | string_length = 7; 172 | if (reading >= 1000) { 173 | reading /= 1000; 174 | string_length = (String(reading, 1)).length(); 175 | units = "KWhr"; 176 | } 177 | if (reading >= 1000000) { 178 | reading /= 1000000; 179 | string_length = (String(reading, 1)).length(); 180 | units = "MWhr"; 181 | } 182 | if (reading < 50) gfx.drawString(x, y, String(reading, 2)); else gfx.drawString(x, y, String(reading, 0)); 183 | gfx.setFont(ArialRoundedMTBold_14); 184 | gfx.drawString(x + string_length * 9, y + 5, units); 185 | gfx.setFont(ArialMT_Plain_24); 186 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 187 | } 188 | //######################################################################################### 189 | bool Obtain_Energy_Reading(String RequestType) { 190 | String rxtext = ""; 191 | Serial.println("Connecting to server for " + RequestType); 192 | client.stop(); // close connection before sending a new request 193 | if (client.connect(server, 443)) { // if the connection succeeds 194 | Serial.println("connecting..."); 195 | // send the HTTP PUT request: 196 | if (RequestType == "Yearly") 197 | client.println(YearlyRequest); 198 | else 199 | client.println(Daily_Request); 200 | client.println("Host: monitoringapi.solaredge.com"); 201 | client.println("User-Agent: ESP OWM Receiver/1.1"); 202 | client.println("Connection: close"); 203 | client.println(); 204 | unsigned long timeout = millis(); 205 | while (client.available() == 0) { 206 | if (millis() - timeout > 5000) { 207 | Serial.println(">>> Client Timeout !"); 208 | client.stop(); 209 | return false; 210 | } 211 | } 212 | char c = 0; 213 | bool startJson = false; 214 | int jsonend = 0; 215 | while (client.available()) { 216 | c = client.read(); 217 | Serial.print(c); 218 | // JSON formats contain an equal number of open and close curly brackets, so check that JSON is received correctly by counting open and close brackets 219 | if (c == '{') { 220 | startJson = true; // set true to indicate JSON message has started 221 | jsonend++; 222 | } 223 | if (c == '}') { 224 | jsonend--; 225 | } 226 | if (startJson == true) { 227 | rxtext += c; // Add in the received character 228 | } 229 | // if jsonend = 0 then we have have received equal number of curly braces 230 | if (jsonend == 0 && startJson == true) { 231 | Serial.println("Received OK..."); 232 | //Serial.println(rxtext); 233 | if (!DecodeEnergyData(rxtext, RequestType)) return false; 234 | client.stop(); 235 | return true; 236 | } 237 | } 238 | } 239 | else { 240 | // if no connection was made: 241 | Serial.println("connection failed"); 242 | return false; 243 | } 244 | rxtext = ""; 245 | return true; 246 | } 247 | //######################################################################################### 248 | // Problems with stucturing JSON decodes, see here: https://arduinojson.org/assistant/ 249 | bool DecodeEnergyData(String json, String Type) { 250 | Serial.print(F("Creating object...and ")); 251 | DynamicJsonBuffer jsonBuffer (50 * 1024); 252 | JsonObject& root = jsonBuffer.parseObject(const_cast(json.c_str())); 253 | if (!root.success()) { 254 | Serial.print("ParseObject() failed"); 255 | return false; 256 | } 257 | Serial.println(" Decoding " + Type + " data"); 258 | if (Type == "Daily") { 259 | // All Serial.println statements are for diagnostic purposes and not required, remove if not needed 260 | JsonObject& overview = root["overview"]; 261 | const char* overview_lastUpdateTime = overview["lastUpdateTime"]; // "2018-09-20 10:48:56" 262 | long overview_lifeTimeData_energy = overview["lifeTimeData"]["energy"]; // 350820 263 | float overview_lifeTimeData_revenue = overview["lifeTimeData"]["revenue"]; // 22.95513 264 | long overview_lastYearData_energy = overview["lastYearData"]["energy"]; // 350460 265 | long overview_lastMonthData_energy = overview["lastMonthData"]["energy"]; // 225261 266 | int overview_lastDayData_energy = overview["lastDayData"]["energy"]; // 680 267 | int overview_currentPower_power = overview["currentPower"]["power"]; // 264 268 | const char* overview_measuredBy = overview["measuredBy"]; // "METER" 269 | LifeTimeEnergy = overview_lifeTimeData_energy; 270 | Revenue = overview_lifeTimeData_revenue; 271 | LastYearEnergy = overview_lastYearData_energy; 272 | LastMonthEnergy = overview_lastMonthData_energy; 273 | LastDayEnergy = overview_lastDayData_energy; 274 | } 275 | if (Type == "Yearly") 276 | { 277 | //Serial.println(json); 278 | // Consumption 279 | // Purchased 280 | // FeedIn 281 | // Production 282 | // SelfConsumption 283 | JsonObject& energyDetails = root["energyDetails"]; 284 | const char* energyDetails_timeUnit = energyDetails["timeUnit"]; // "MONTH" 285 | const char* energyDetails_unit = energyDetails["unit"]; // "Wh" 286 | JsonArray& energyDetails_meters = energyDetails["meters"]; 287 | for (int type = 0; type < 5; type++) { 288 | String energyDetails_meters_type = energyDetails_meters[type]["type"]; // "Consumption","Purchased","FeedIn","Production" or "SelfConsumption" the order supplied varies! 289 | Serial.println(energyDetails_meters_type); 290 | if (energyDetails_meters_type == "Consumption") { 291 | JsonArray& energyDetails_meters0_values = energyDetails_meters[type]["values"]; 292 | Consumption[0] = energyDetails_meters0_values[0]["value"]; 293 | Consumption[1] = energyDetails_meters0_values[1]["value"]; 294 | Consumption[2] = energyDetails_meters0_values[2]["value"]; 295 | Consumption[3] = energyDetails_meters0_values[3]["value"]; 296 | Consumption[4] = energyDetails_meters0_values[4]["value"]; 297 | Consumption[5] = energyDetails_meters0_values[5]["value"]; 298 | Consumption[6] = energyDetails_meters0_values[6]["value"]; 299 | Consumption[7] = energyDetails_meters0_values[7]["value"]; 300 | Consumption[8] = energyDetails_meters0_values[8]["value"]; 301 | Consumption[9] = energyDetails_meters0_values[9]["value"]; 302 | Consumption[10] = energyDetails_meters0_values[10]["value"]; 303 | Consumption[11] = energyDetails_meters0_values[11]["value"]; 304 | } 305 | if (energyDetails_meters_type == "SelfConsumption") { 306 | JsonArray& energyDetails_meters_values = energyDetails_meters[type]["values"]; 307 | SelfConsumption[0] = energyDetails_meters_values[0]["value"]; 308 | SelfConsumption[1] = energyDetails_meters_values[1]["value"]; 309 | SelfConsumption[2] = energyDetails_meters_values[2]["value"]; 310 | SelfConsumption[3] = energyDetails_meters_values[3]["value"]; 311 | SelfConsumption[4] = energyDetails_meters_values[4]["value"]; 312 | SelfConsumption[5] = energyDetails_meters_values[5]["value"]; 313 | SelfConsumption[6] = energyDetails_meters_values[6]["value"]; 314 | SelfConsumption[7] = energyDetails_meters_values[7]["value"]; 315 | SelfConsumption[8] = energyDetails_meters_values[8]["value"]; 316 | SelfConsumption[9] = energyDetails_meters_values[9]["value"]; 317 | SelfConsumption[10] = energyDetails_meters_values[10]["value"]; 318 | SelfConsumption[11] = energyDetails_meters_values[11]["value"]; 319 | } 320 | } 321 | for (int i = 0; i < 12; i++) { 322 | Serial.println(Consumption[i] / 1000); 323 | Consumption[i] /= 1000; 324 | } 325 | for (int i = 0; i < 12; i++) { 326 | Serial.println(SelfConsumption[i] / 1000); 327 | SelfConsumption[i] /= 1000; 328 | } 329 | } 330 | return true; 331 | } 332 | //######################################################################################### 333 | int StartWiFi() { 334 | int connAttempts = 0; 335 | Serial.print(F("\r\nConnecting to: ")); Serial.println(String(ssid1)); 336 | WiFi.disconnect(); 337 | WiFi.mode(WIFI_STA); 338 | WiFi.begin(ssid1, password1); 339 | while (WiFi.status() != WL_CONNECTED ) { 340 | delay(500); Serial.print("."); 341 | if (connAttempts > 20) { 342 | WiFi.disconnect(); 343 | begin_sleep(); 344 | } 345 | connAttempts++; 346 | } 347 | Serial.println("WiFi connected at: " + String(WiFi.localIP())); 348 | return 1; 349 | } 350 | //######################################################################################### 351 | void StopWiFi() { 352 | WiFi.disconnect(); 353 | WiFi.mode(WIFI_OFF); 354 | wifisection = millis() - wifisection; 355 | } 356 | //######################################################################################### 357 | int WiFi_Signal() { 358 | return WiFi.RSSI(); 359 | } 360 | //######################################################################################### 361 | void Display_Status_Section(int x, int y, int rssi) { 362 | Draw_RSSI(x - 50, y + 13, rssi); 363 | DrawBattery(x + 50, y - 3); 364 | } 365 | //######################################################################################### 366 | void Draw_RSSI(int x, int y, int rssi) { 367 | int WIFIsignal = 0; 368 | int xpos = 1; 369 | for (int _rssi = -100; _rssi <= rssi; _rssi = _rssi + 20) { 370 | if (_rssi <= -20) WIFIsignal = 20; // <20dbm displays 5-bars 371 | if (_rssi <= -40) WIFIsignal = 16; // -40dbm to -21dbm displays 4-bars 372 | if (_rssi <= -60) WIFIsignal = 12; // -60dbm to -41dbm displays 3-bars 373 | if (_rssi <= -80) WIFIsignal = 8; // -80dbm to -61dbm displays 2-bars 374 | if (_rssi <= -100) WIFIsignal = 4; // -100dbm to -81dbm displays 1-bar 375 | gfx.fillRect(x + xpos * 5, y - WIFIsignal, 4, WIFIsignal); 376 | xpos++; 377 | } 378 | gfx.fillRect(x, y - 1, 4, 1); 379 | } 380 | //######################################################################################### 381 | void DrawBattery(int x, int y) { 382 | uint8_t percentage = 100; 383 | float voltage = analogRead(35) / 4096.0 * 7.2; 384 | if (voltage >= 0 ) { // Only display if there is a valid reading 385 | Serial.println("Battery voltage = " + String(voltage, 2)); 386 | if (voltage >= 4.19) percentage = 100; 387 | else if (voltage < 3.20) percentage = 0; 388 | else percentage = (voltage - 3.20) * 100 / (4.20 - 3.20); 389 | gfx.setColor(EPD_BLACK); 390 | gfx.drawRect(x - 22, y + 5, 19, 10); 391 | gfx.fillRect(x - 2, y + 7, 3, 6); 392 | gfx.fillRect(x - 20, y + 7, 17 * percentage / 100.0, 6); 393 | gfx.setFont(ArialMT_Plain_10); 394 | gfx.drawString(x - 48, y + 5, String(percentage) + "%"); 395 | } 396 | } 397 | //######################################################################################### 398 | void SetupTime() { 399 | configTime(0, 0, "0.uk.pool.ntp.org", "time.nist.gov"); 400 | setenv("TZ", Timezone, 1); 401 | delay(500); 402 | UpdateLocalTime(); 403 | } 404 | //######################################################################################### 405 | void UpdateLocalTime() { 406 | struct tm timeinfo; 407 | while (!getLocalTime(&timeinfo)) { 408 | Serial.println(F("Failed to obtain time")); 409 | } 410 | //See http://www.cplusplus.com/reference/ctime/strftime/ 411 | //Serial.println(&timeinfo, "%a %b %d %Y %H:%M:%S"); // Displays: Saturday, June 24 2017 14:05:49 412 | Serial.println(&timeinfo, "%H:%M:%S"); // Displays: 14:05:49 413 | char output[30], day_output[30]; 414 | strftime(day_output, 30, "%a %d-%b-%y", &timeinfo); // Displays: Sat 24/Jun/17 415 | strftime(output, 30, "(Updated: %H:%M:%S )", &timeinfo); // Creates: '@ 14:05:49' 416 | Day_time_str = day_output; 417 | time_str = output; 418 | } 419 | //######################################################################################### 420 | String ConvertUnixTime(int unix_time) { 421 | struct tm *now_tm; 422 | int hour, min, second, day, month, year, wday; 423 | // timeval tv = {unix_time,0}; 424 | time_t tm = unix_time; 425 | now_tm = localtime(&tm); 426 | hour = now_tm->tm_hour; 427 | min = now_tm->tm_min; 428 | second = now_tm->tm_sec; 429 | wday = now_tm->tm_wday; 430 | day = now_tm->tm_mday; 431 | month = now_tm->tm_mon + 1; 432 | year = 1900 + now_tm->tm_year; // To get just YY information 433 | time_str = (hour < 10 ? "0" + String(hour) : String(hour)) + ":" + (min < 10 ? "0" + String(min) : String(min)) + ":" + " "; // HH:MM 05/07/17 434 | time_str += (day < 10 ? "0" + String(day) : String(day)) + "/" + (month < 10 ? "0" + String(month) : String(month)) + "/" + (year < 10 ? "0" + String(year) : String(year)); // HH:MM 05/07/17 435 | //Serial.println(time_str); 436 | return time_str; 437 | } 438 | //######################################################################################### 439 | void DrawGraph(int x_pos, int y_pos, int gwidth, int gheight, float Y1Min, float Y1Max, String title, float DataArray[], int readings, boolean auto_scale, boolean barchart_mode) { 440 | #define auto_scale_margin 0 // Sets the autoscale increment, so axis steps up in units of e.g. 3 441 | #define y_minor_axis 5 // 5 y-axis division markers 442 | int maxYscale = -10000; 443 | int minYscale = 10000; 444 | int last_x, last_y; 445 | float x1, y1, x2, y2; 446 | if (auto_scale == true) { 447 | for (int i = 1; i < readings; i++ ) { 448 | if (DataArray[i] >= maxYscale) maxYscale = DataArray[i]; 449 | if (DataArray[i] <= minYscale) minYscale = DataArray[i]; 450 | } 451 | maxYscale = round(maxYscale + auto_scale_margin); // Auto scale the graph and round to the nearest value defined, default was Y1Max 452 | Y1Max = round(maxYscale + 0.5); 453 | if (minYscale != 0) minYscale = round(minYscale - auto_scale_margin); // Auto scale the graph and round to the nearest value defined, default was Y1Min 454 | Y1Min = round(minYscale); 455 | } 456 | // Draw the graph 457 | last_x = x_pos + 1; 458 | last_y = y_pos + (Y1Max - constrain(DataArray[1], Y1Min, Y1Max)) / (Y1Max - Y1Min) * gheight; 459 | gfx.setColor(EPD_BLACK); 460 | gfx.drawRect(x_pos, y_pos, gwidth + 3, gheight + 2); 461 | gfx.setFont(ArialRoundedMTBold_14); 462 | gfx.setTextAlignment(TEXT_ALIGN_CENTER); 463 | gfx.drawString(x_pos + gwidth / 2, y_pos - 18, title); 464 | gfx.setFont(ArialMT_Plain_10); 465 | gfx.setTextAlignment(TEXT_ALIGN_RIGHT); 466 | // Draw the data 467 | for (int gx = 1; gx < readings; gx++) { 468 | x1 = last_x; 469 | y1 = last_y; 470 | x2 = x_pos + gx * gwidth / (readings - 1) - 1 ; // max_readings is the global variable that sets the maximum data that can be plotted 471 | y2 = y_pos + (Y1Max - constrain(DataArray[gx], Y1Min, Y1Max)) / (Y1Max - Y1Min) * gheight + 1; 472 | if (barchart_mode) { 473 | gfx.fillRect(x2, y2, (gwidth / readings) - 1, y_pos + gheight - y2 + 1); 474 | } else { 475 | gfx.drawLine(last_x, last_y, x2, y2); 476 | } 477 | last_x = x2; 478 | last_y = y2; 479 | } 480 | //Draw the Y-axis scale 481 | for (int spacing = 0; spacing <= y_minor_axis; spacing++) { 482 | #define number_of_dashes 20 483 | for (int j = 0; j < number_of_dashes; j++) { // Draw dashed graph grid lines 484 | if (spacing < y_minor_axis) gfx.drawHorizontalLine((x_pos + 3 + j * gwidth / number_of_dashes), y_pos + (gheight * spacing / y_minor_axis), gwidth / (2 * number_of_dashes)); 485 | } 486 | if ( (Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing) < 10) { 487 | gfx.drawString(x_pos - 2, y_pos + gheight * spacing / y_minor_axis - 5, String((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing + 0.01), 1)); 488 | } 489 | else { 490 | if (Y1Min < 1 && Y1Max < 10) gfx.drawString(x_pos - 2, y_pos + gheight * spacing / y_minor_axis - 5, String((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing + 0.01), 1)); 491 | else gfx.drawString(x_pos - 2, y_pos + gheight * spacing / y_minor_axis - 5, String((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing + 0.01), 0)); // +0.01 prevents -0.00 occurring 492 | } 493 | } 494 | for (int i = 1; i <= 12; i++) { 495 | if (i % 2 == 1) gfx.drawString(4 + x_pos + gwidth / 11 * i, y_pos + gheight + 3, String(i)); 496 | } 497 | gfx.setTextAlignment(TEXT_ALIGN_CENTER); 498 | gfx.drawString(x_pos + gwidth / 2, y_pos + gheight + 12, "Months"); 499 | gfx.setTextAlignment(TEXT_ALIGN_LEFT); 500 | } 501 | 502 | -------------------------------------------------------------------------------- /ESP32_e-paper_QRcode_01.ino: -------------------------------------------------------------------------------- 1 | /* ESP QRcode display on an e-paper 2 | #################################################################################################################################### 3 | This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved. 4 | 5 | Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following: 6 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 7 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 8 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 9 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 10 | 11 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 12 | software use is visible to an end-user. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY 15 | OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | See more at http://www.dsbird.org.uk 19 | */ 20 | #include // Built-in 21 | #include "EPD_WaveShare.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 22 | #include "EPD_WaveShare_42.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 23 | #include "MiniGrafx.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 24 | #include "DisplayDriver.h" // Copyright (c) 2017 by Daniel Eichhorn https://github.com/ThingPulse/minigrafx 25 | #include "qrcode.h" // Copyright (c) //https://github.com/ricmoo/qrcode/ 26 | 27 | #define SCREEN_WIDTH 400.0 // Set for landscape mode, don't remove the decimal place! 28 | #define SCREEN_HEIGHT 300.0 29 | #define BITS_PER_PIXEL 1 30 | #define EPD_BLACK 0 31 | #define EPD_WHITE 1 32 | uint16_t palette[] = { 0, 1 }; 33 | 34 | // pins_arduino.h, e.g. LOLIN32 35 | static const uint8_t EPD_BUSY = 4; 36 | static const uint8_t EPD_SS = 5; 37 | static const uint8_t EPD_RST = 16; 38 | static const uint8_t EPD_DC = 17; 39 | static const uint8_t EPD_SCK = 18; 40 | static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display 41 | static const uint8_t EPD_MOSI = 23; 42 | 43 | EPD_WaveShare42 epd(EPD_SS, EPD_RST, EPD_DC, EPD_BUSY); 44 | MiniGrafx gfx = MiniGrafx(&epd, BITS_PER_PIXEL, palette); 45 | 46 | //################# LIBRARIES ########################## 47 | String version = "1.0"; // Version of this program 48 | //################ VARIABLES ########################## 49 | 50 | QRCode qrcode; 51 | 52 | //######################################################################################### 53 | void setup() { 54 | gfx.init(); 55 | gfx.setRotation(0); 56 | gfx.fillBuffer(EPD_WHITE); 57 | } 58 | 59 | //######################################################################################### 60 | void loop() { 61 | //Display_QRcode(x,y,element_size,QRcode Size,Error Detection/Correction Level,"string for encoding"); 62 | gfx.drawString(150,10,"QRcode Generation Example"); 63 | Display_QRcode(25,25,3,3,3,"Hello World!\n\nG6EJD"); 64 | Display_QRcode(25,175,3,4,1,"http://example.com/"); 65 | Display_QRcode(225,75,2,12,1,"Chapter-1 The quick brown fox jumps over the lazy dog's back\n\rChapter-2 The quick brown fox jumps over the lazy dog's back again\n\rChapter-3 The quick brown fox jumps over the lazy dog's back again and again"); 66 | gfx.commit(); 67 | delay(30000); // Delay before we do it again 68 | Clear_Screen(); 69 | } 70 | 71 | //######################################################################################### 72 | void Display_QRcode(int offset_x, int offset_y, int element_size, int QRsize, int ECC_Mode, const char* Message){ 73 | // QRcode capacity examples Size-12 65 x 65 LOW 883 535 367 74 | // MEDIUM 691 419 287 75 | // QUARTILE 489 296 203 76 | // HIGH 374 227 155 77 | uint8_t qrcodeData[qrcode_getBufferSize(QRsize)]; 78 | //ECC_LOW, ECC_MEDIUM, ECC_QUARTILE and ECC_HIGH. Higher levels of error correction sacrifice data capacity, but ensure damaged codes remain readable. 79 | if (ECC_Mode%4 == 0) qrcode_initText(&qrcode, qrcodeData, QRsize, ECC_LOW, Message); 80 | if (ECC_Mode%4 == 1) qrcode_initText(&qrcode, qrcodeData, QRsize, ECC_MEDIUM, Message); 81 | if (ECC_Mode%4 == 2) qrcode_initText(&qrcode, qrcodeData, QRsize, ECC_QUARTILE, Message); 82 | if (ECC_Mode%4 == 3) qrcode_initText(&qrcode, qrcodeData, QRsize, ECC_HIGH, Message); 83 | for (int y = 0; y < qrcode.size; y++) { 84 | for (int x = 0; x < qrcode.size; x++) { 85 | if (qrcode_getModule(&qrcode, x, y)) { 86 | gfx.setColor(EPD_BLACK); 87 | gfx.fillRect(x*element_size+offset_x,y*element_size+offset_y,element_size,element_size); 88 | } 89 | else 90 | { 91 | gfx.setColor(EPD_WHITE); 92 | gfx.fillRect(x*element_size+offset_x,y*element_size+offset_y,element_size,element_size); 93 | } 94 | } 95 | } 96 | } 97 | //######################################################################################### 98 | void Clear_Screen(){ 99 | gfx.fillBuffer(EPD_WHITE); 100 | gfx.commit(); 101 | delay(4000); 102 | } 103 | 104 | -------------------------------------------------------------------------------- /ESP8266_Erase_Credentials_Factory_Default.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | resetToFactoryDefaults(); 5 | } 6 | 7 | void loop() { 8 | // put your main code here, to run repeatedly: 9 | } 10 | 11 | void resetToFactoryDefaults() { 12 | WiFi.disconnect(); // Erases WiFi Credentials 13 | delay(3000); 14 | ESP.reset(); 15 | delay(3000); 16 | ESP.eraseConfig(); 17 | delay(3000); 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /ESP8266_OLED13_Luka_v02b.ino: -------------------------------------------------------------------------------- 1 | /* Last Update: 05-Jan-18, Added webserver, wifi manager and enabled calibration of pressure and temperature through web interface 2 | * Last Update: 07-Aug-17, Added enumerated weather types, improved code efficiency 3 | * Last update: 06-Aug-17, added improved forecast rules 4 | * 5 | * ESP8266 and BME280 and OLED SH1106 Weather Forecaster 6 | * Using air pressure changes to predict weather based on an advanced set of forecasting rules. 7 | * The MIT License (MIT) Copyright (c) 2018 by David Bird. 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 9 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to 11 | * permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 14 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 15 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | * See more at http://dsbird.org.uk 18 | */ 19 | 20 | #include "SH1106.h" //https://github.com/squix78/esp8266-oled-sh1106 21 | #include "OLEDDisplayUi.h" //https://github.com/squix78/esp8266-oled-sh1106 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include //https://github.com/tzapu/WiFiManager 30 | 31 | #include 32 | 33 | #define icon_width 40 34 | #define icon_height 40 35 | 36 | // Define each of the *icons for display 37 | const char rain_icon[] PROGMEM = { 38 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 39 | 0x81, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0xFE, 0xFF, 0xFF, 0xDF, 0xF0, 0xFC, 40 | 0xFF, 0xFF, 0xE7, 0xFF, 0xFB, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0xFF, 41 | 0xFD, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xFF, 0x0F, 0xFE, 0x0F, 0xF8, 0xFF, 42 | 0x8F, 0xFC, 0xE7, 0xFB, 0xFF, 0xC7, 0xF9, 0xF7, 0xE3, 0xFF, 0xC3, 0xF3, 43 | 0xF3, 0xDF, 0xFF, 0xE8, 0xE7, 0xF9, 0xFF, 0xFF, 0xFE, 0xEF, 0xFD, 0xFF, 44 | 0xFF, 0xFF, 0xE7, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 45 | 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xF9, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 46 | 0xFF, 0xFF, 0x3F, 0xFE, 0x3F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x7D, 0xBF, 47 | 0xEF, 0xFF, 0xFF, 0xBE, 0xDF, 0xF7, 0xFF, 0x7F, 0xDF, 0xEF, 0xFB, 0xFF, 48 | 0xBF, 0xEF, 0xF7, 0xFD, 0xFF, 0xDF, 0xF7, 0xFB, 0xFE, 0xFF, 0xEF, 0xFB, 49 | 0x7D, 0xFF, 0xFF, 0xF7, 0xFD, 0xBE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 50 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 51 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 52 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 53 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 54 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 55 | 56 | const char sunny_icon[] PROGMEM = { 57 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 58 | 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 59 | 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFD, 0xDF, 0xFF, 0xE3, 0xFF, 0xF8, 0x8F, 60 | 0xFF, 0xE3, 0x7F, 0xF0, 0x07, 0xFF, 0xE3, 0x3F, 0xF8, 0x0F, 0xFE, 0xFF, 61 | 0x1F, 0xFC, 0x1F, 0x7C, 0x00, 0x0E, 0xFE, 0x3F, 0x18, 0x00, 0x1C, 0xFF, 62 | 0x7F, 0xCC, 0xFF, 0xB1, 0xFF, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xFF, 0xF3, 63 | 0xFF, 0xCF, 0xFF, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 64 | 0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0x01, 65 | 0xF9, 0xFF, 0x9F, 0x80, 0x01, 0xF9, 0xFF, 0x9F, 0x80, 0x01, 0xF9, 0xFF, 66 | 0x9F, 0x80, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0xFF, 0xF1, 0xFF, 0x8F, 0xFF, 67 | 0xFF, 0xF1, 0xFF, 0x8F, 0xFF, 0xFF, 0xE3, 0xFF, 0xE7, 0xFF, 0xFF, 0xC7, 68 | 0xFF, 0xF3, 0xFF, 0xFF, 0x8D, 0xFF, 0xD8, 0xFF, 0xFF, 0x38, 0x00, 0x8C, 69 | 0xFF, 0x7F, 0x70, 0x00, 0x07, 0xFF, 0x3F, 0xF8, 0xFF, 0x0F, 0xFE, 0x1F, 70 | 0xFC, 0xE3, 0x1F, 0xFC, 0x0F, 0xFE, 0xE3, 0x3F, 0xF8, 0x07, 0xFF, 0xE3, 71 | 0x7F, 0xF0, 0x8F, 0xFF, 0xE3, 0xFF, 0xF8, 0xDF, 0xFF, 0xE3, 0xFF, 0xFD, 72 | 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 73 | 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 74 | 75 | const char mostlysunny_icon[] PROGMEM = { 76 | 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFD, 0x7E, 77 | 0xFF, 0xFF, 0xFF, 0xFB, 0xBF, 0xEF, 0xFF, 0xFF, 0x17, 0xE0, 0xF7, 0xFF, 78 | 0xFF, 0xCF, 0x9F, 0xF9, 0xFF, 0xFF, 0xE6, 0x3F, 0xFD, 0xFF, 0xFF, 0xF5, 79 | 0x7F, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFE, 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 80 | 0xFF, 0xFF, 0xFD, 0x7F, 0x08, 0xFC, 0xFF, 0xFD, 0xBF, 0xE1, 0xF9, 0xFF, 81 | 0xFD, 0xCF, 0xFF, 0xF7, 0xFF, 0xFD, 0xF7, 0xFF, 0xE7, 0xFF, 0xF9, 0xFB, 82 | 0xFF, 0xCF, 0xFF, 0xF3, 0xFB, 0xFF, 0x1F, 0xFC, 0x17, 0xF0, 0xFF, 0x1F, 83 | 0xF9, 0xC7, 0xF7, 0xFF, 0x8F, 0xF3, 0xEF, 0xC7, 0xFF, 0x87, 0xE7, 0xE7, 84 | 0xBF, 0xFF, 0xD1, 0xCF, 0xF3, 0xFF, 0xFF, 0xFD, 0xDF, 0xFB, 0xFF, 0xFF, 85 | 0xFF, 0xCF, 0xF3, 0xFF, 0xFF, 0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xE7, 86 | 0xF7, 0xFF, 0xFF, 0xFF, 0xF3, 0xEF, 0xFF, 0xFF, 0xFF, 0xF9, 0x9F, 0xFF, 87 | 0xFF, 0x7F, 0xFC, 0x7F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 88 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 89 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 90 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 91 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 92 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 93 | 94 | const char cloudy_icon[] PROGMEM = { 95 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 96 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 97 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 98 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 99 | 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x7F, 0x78, 0xFC, 0xFF, 100 | 0xFF, 0xBF, 0xFF, 0xF9, 0xFF, 0xFF, 0xCF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 101 | 0xFF, 0xE7, 0xFF, 0xFF, 0xFB, 0xFF, 0xCF, 0xFF, 0xFF, 0xFB, 0xFF, 0x1F, 102 | 0xFC, 0x3F, 0xF0, 0xFF, 0xE7, 0xFB, 0xCF, 0xF7, 0xFF, 0xF3, 0xF7, 0xEF, 103 | 0xCF, 0xFF, 0xF9, 0xEF, 0xF7, 0xBF, 0xFF, 0xFD, 0xCF, 0xF3, 0xFF, 0xFF, 104 | 0xFD, 0xDF, 0xFB, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF, 0xFF, 0xFF, 0xEF, 105 | 0xF7, 0xFF, 0xFF, 0xFF, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, 0xF3, 0xEF, 0xFF, 106 | 0xFF, 0xFF, 0xF9, 0x9F, 0xFF, 0xFF, 0x7F, 0xFC, 0x7F, 0x00, 0x00, 0x00, 107 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 108 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 109 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 110 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 111 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 112 | 113 | const char tstorms_icon[] PROGMEM = { 114 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 115 | 0x81, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0xFE, 0xFF, 0xFF, 0xDF, 0xF0, 0xFC, 116 | 0xFF, 0xFF, 0xE7, 0xFF, 0xFB, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0xFF, 117 | 0xFD, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xFF, 0x0F, 0xFE, 0x0F, 0xF8, 0xFF, 118 | 0x8F, 0xFC, 0xE7, 0xFB, 0xFF, 0xC7, 0xF9, 0xF7, 0xE3, 0xFF, 0xC3, 0xF3, 119 | 0xF3, 0xDF, 0xFF, 0xE8, 0xE7, 0xF9, 0xFF, 0xFF, 0xFE, 0xEF, 0xFD, 0xFF, 120 | 0xFF, 0xFF, 0xE7, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 121 | 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xF9, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 122 | 0xFF, 0xFF, 0x3F, 0xFE, 0x3F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x7F, 0x7F, 123 | 0xFF, 0xFF, 0xFF, 0xBF, 0xBF, 0xFF, 0xFF, 0xFF, 0xDF, 0x8F, 0xFF, 0xFF, 124 | 0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0x4F, 125 | 0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xF1, 0xFF, 126 | 0xFF, 0xFF, 0xDF, 0xFE, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 127 | 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 128 | 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 129 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 130 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; 131 | 132 | String time_str, weather_text, weather_extra_text, webpage; 133 | int last_reading_hour, reading_hour, hr_cnt; 134 | float Lpressure, Ltemperature, Lhumidity, alt_offset, temp_offset, DST=0; 135 | 136 | char * ssid = "ssid"; 137 | char * password = "password"; 138 | 139 | enum image_names { // enumerated table used to point to images 140 | rain_img, sunny_img, mostlysunny_img, cloudy_img, tstorms_img, 141 | } image; 142 | 143 | // Define and enumerated type and assign values to expected weather types. 144 | // These values help to determine the average weather preceeding a 'no-change' forecast e.g. rain, rain then mostlysun = -1 (-1 + -1 + 1) resulting on balance = more rain 145 | enum weather_type {unknown = 4, 146 | sunny = 2, 147 | mostlysunny = 1, 148 | cloudy = 0, 149 | rain = -1, 150 | tstorms = -2 151 | }; 152 | 153 | enum weather_description {GoodClearWeather, BecomingClearer, 154 | NoChange, ClearSpells, ClearingWithin12hrs, ClearingAndColder, 155 | GettingWarmer, WarmerIn2daysRainLikely, 156 | ExpectRain, WarmerRainWithin36hrs, RainIn18hrs, RainHighWindsClearAndCool, 157 | GalesHeavyRainSnowInWinter 158 | }; 159 | 160 | weather_type current_wx; // Enable the current wx to be recorded 161 | 162 | // An array structure to record pressure, temperaturre, humidity and weather state 163 | typedef struct { 164 | float pressure; // air pressure at the designated hour 165 | float temperature; // temperature at the designated hour 166 | float humidity; // humidity at the designated hour 167 | weather_type wx_state_1hr; // weather state at 1-hour 168 | weather_type wx_state_3hr; // weather state at 3-hour point 169 | } wx_record_type; 170 | 171 | wx_record_type reading[24]; // An array covering 24-hours to enable P, T, % and Wx state to be recorded for every hour 172 | 173 | int wx_average_1hr, wx_average_3hr; // Indicators of average weather 174 | 175 | bool look_3hr = true; 176 | bool look_1hr = false; 177 | 178 | #define SDA D3 179 | #define SCL D4 180 | 181 | SH1106 display(0x3c, SDA, SCL); // OLED display object definition (address, SDA, SCL) 182 | OLEDDisplayUi ui ( &display ); 183 | 184 | Adafruit_BME280 bme; 185 | 186 | WiFiClient client; // wifi client object 187 | 188 | ESP8266WebServer server(80); 189 | 190 | #define pressure_offset 3.3 // Used to adjust sensor reading to correct pressure for your location 191 | 192 | ///////////////////////////////////////////////////////////////////////// 193 | // What's displayed along the top line 194 | void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { 195 | display->setTextAlignment(TEXT_ALIGN_LEFT); 196 | display->drawString(0,0, time_str.substring(0,8)); //HH:MM:SS Sat 05-07-17 197 | display->setTextAlignment(TEXT_ALIGN_RIGHT); 198 | display->drawString(128,0, time_str.substring(9)); 199 | display->setTextAlignment(TEXT_ALIGN_LEFT); 200 | } 201 | 202 | // This frame draws a weather icon based on 3-hours of data for the prediction 203 | void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 204 | float trend = reading[23].pressure - reading[20].pressure; // Trend over the last 3-hours 205 | ForecastToImgTxt(get_forecast_text(reading[23].pressure, trend, look_3hr)); // From forecast and trend determine what image to display 206 | if (image == rain_img) display->drawXbm(x+0,y+15, icon_width, icon_height, rain_icon); // Display corresponding image 207 | if (image == sunny_img) display->drawXbm(x+0,y+15, icon_width, icon_height, sunny_icon); // Display corresponding image 208 | if (image == mostlysunny_img) display->drawXbm(x+0,y+15, icon_width, icon_height, mostlysunny_icon); // Display corresponding image 209 | if (image == cloudy_img) display->drawXbm(x+0,y+15, icon_width, icon_height, cloudy_icon); // Display corresponding image 210 | if (image == tstorms_img) display->drawXbm(x+0,y+15, icon_width, icon_height, tstorms_icon); // Display corresponding image 211 | display->drawStringMaxWidth(x+45,y+12,90,String(reading[23].pressure,1)+" hPA"); // Show current air pressure 212 | display->drawStringMaxWidth(x+45,y+25,90,String(trend,1)+" "+get_trend_text(trend)); // and pressure trend 213 | } 214 | 215 | // This frame shows a weather description based on 3-hours of data for the prediction 216 | void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 217 | float trend = reading[23].pressure - reading[20].pressure; // Get current trend over last 3-hours 218 | weather_description wx_text = get_forecast_text(reading[23].pressure, trend, look_3hr); // Convert to forecast text based on 3-hours 219 | ForecastToImgTxt(wx_text); // Display corresponding text 220 | display->setFont(ArialMT_Plain_16); 221 | display->drawStringMaxWidth(x+0,y+10,127,weather_text+weather_extra_text); 222 | display->setFont(ArialMT_Plain_10); 223 | } 224 | 225 | // This frame draws a graph of pressure (delta) change for the last 24-hours, see Annex* for more details 226 | void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 227 | int gwidth = 75; // Graph width in pixels 228 | int gscale = 30; // Graph height in pixels 229 | int num_bars = 8; // Number of bars to display 230 | #define yscale 8 // Graph +/- y-axis scale e.g. 8 displays +/-8 and scales data accordingly 231 | float bar_width = gwidth / (num_bars+1); // Determine bar width based on graph width 232 | x = 30; // Sets position of graph on screen 233 | y = 15; // Sets position of graph on screen 234 | display->drawVerticalLine(x, y, gscale+1); 235 | display->drawString(x-18,y-6,">+"+String(yscale)); 236 | display->drawString(x-8,y+gscale/2-6,"0"); 237 | display->drawString(x-15,y+gscale-6,"<-"+String(yscale)); 238 | display->drawString(x-30,y+gscale/2-6,String(hr_cnt%24)); 239 | display->drawString(x+2+(bar_width+3)*0, y+gscale,"-24"); // 24hr marker at bar 0 240 | display->drawString(x+2+(bar_width+3)*2, y+gscale,"-12"); // 12hr marker at bar 2 241 | display->drawString(x+2+(bar_width+3)*5, y+gscale,"-2"); // 2hr marker at bar 5 242 | display->drawString(x+2+(bar_width+3)*7, y+gscale,"0"); // 0hr marker at bar 7 243 | int display_points [8] = {0,5,11,17,20,21,22,23}; // Only display time for hours 0,5,11,17,20,21,22,23 244 | float value; 245 | for (int bar_num = 0; bar_num < num_bars; bar_num++){ // Now display a bar at each hour position -24, -18, -12, -6, -3, -2, -1 and 0 hour 246 | value = map(reading[display_points[bar_num]].pressure, reading[23].pressure-yscale, reading[23].pressure+yscale, gscale, 0); 247 | if (value > gscale) value = gscale; // Screen scale is 0 to e.g. 40pixels, this stops drawing beyond graph bounds 248 | if (value < 0 ) value = 0; // 0 is top of graph, this stops drawing beyond graph bounds 249 | display->drawHorizontalLine(x+bar_num*(bar_width+3)+2, y+value, bar_width); 250 | for (int yplus=gscale; yplus > value; yplus = yplus - 1) { 251 | display->drawHorizontalLine(x+bar_num*(bar_width+3)+2, y + yplus, bar_width); 252 | } 253 | } 254 | } 255 | 256 | // This frame draws a weather icon based on 1-hour of data for the prediction 257 | void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 258 | reading[23].pressure = (reading[23].pressure + read_pressure())/2; // Update rolling average, gets reset on the hour transition 259 | float trend = reading[23].pressure - reading[22].pressure; // Get short-term trend for the last 1-hour 260 | weather_description wx_text = get_forecast_text(read_pressure(), trend, look_1hr); // Convert to forecast text based on 1-hours 261 | ForecastToImgTxt(wx_text); 262 | if (image == rain_img) display->drawXbm(x+0,y+15, icon_width, icon_height, rain_icon); // Display corresponding image 263 | if (image == sunny_img) display->drawXbm(x+0,y+15, icon_width, icon_height, sunny_icon); // Display corresponding image 264 | if (image == mostlysunny_img) display->drawXbm(x+0,y+15, icon_width, icon_height, mostlysunny_icon); // Display corresponding image 265 | if (image == cloudy_img) display->drawXbm(x+0,y+15, icon_width, icon_height, cloudy_icon); // Display corresponding image 266 | if (image == tstorms_img) display->drawXbm(x+0,y+15, icon_width, icon_height, tstorms_icon); // Display corresponding image 267 | display->drawStringMaxWidth(x+45,y+12,90,"1-Hr forecast"); 268 | display->drawStringMaxWidth(x+45,y+22,90,String(read_pressure(),1)+" hPA"); 269 | display->drawStringMaxWidth(x+47,y+32,90,String(trend,1)+" "+get_trend_text(trend)); 270 | } 271 | 272 | // This frame shows a weather description based on 1-hour of data for the prediction 273 | void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 274 | reading[23].pressure = (reading[23].pressure + read_pressure())/2; // Update rolling average 275 | float trend = reading[23].pressure - reading[22].pressure; // Get short-term trend 276 | weather_description wx_text = get_forecast_text(read_pressure(), trend, look_1hr); // Convert to forecast text based on 1-hours 277 | ForecastToImgTxt(wx_text); 278 | display->drawString(x+0,y+10,"1-Hr forecast:"); 279 | display->setFont(ArialMT_Plain_16); 280 | display->drawStringMaxWidth(x+0,y+18,127,weather_text+weather_extra_text); 281 | display->setFont(ArialMT_Plain_10); 282 | } 283 | 284 | void drawFrame6(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 285 | display->drawString(x+0,y+10,"SSID: "+String(ssid)); 286 | display->drawString(x+0,y+20,"IP: "+WiFi.localIP().toString()+ " DST:"+String(DST)); 287 | display->drawString(x+0,y+30,"Temp: "+String(Ltemperature+temp_offset,1)+ "°C " + String(Lhumidity,1)+"%"); 288 | display->drawString(x+0,y+40,"Press: "+String(Lpressure+alt_offset,1)+"hPa"); 289 | } 290 | 291 | float read_pressure(){ 292 | int reading = (bme.readPressure()/100.0F+pressure_offset)*10; // Rounded result to 1-decimal place 293 | Lpressure = reading/10.0F; 294 | Ltemperature = bme.readTemperature(); 295 | Lhumidity = bme.readHumidity(); 296 | return (float)reading/10; 297 | } 298 | 299 | // Convert pressure trend to text 300 | String get_trend_text(float trend){ 301 | String trend_str = "Steady"; // Default weather state 302 | if (trend > 3.5) { trend_str = "Rising fast"; } 303 | else if (trend > 1.5 && trend <= 3.5) { trend_str = "Rising"; } 304 | else if (trend > 0.25 && trend <= 1.5) { trend_str = "Rising slow"; } 305 | else if (trend > -0.25 && trend < 0.25) { trend_str = "Steady"; } 306 | else if (trend >= -1.5 && trend < -0.25) { trend_str = "Falling slow"; } 307 | else if (trend >= -3.5 && trend < -1.5) { trend_str = "Falling"; } 308 | else if (trend <= -3.5) { trend_str = "Falling fast"; } 309 | return trend_str; 310 | } 311 | 312 | // Convert forecast text to a corresponding image for display together with a record of the current weather 313 | void ForecastToImgTxt(weather_description wx_text){ 314 | if (wx_text == GoodClearWeather) {image = sunny_img; current_wx = sunny; weather_text = "Good clear weather";} 315 | else if (wx_text == BecomingClearer) {image = mostlysunny_img; current_wx = mostlysunny; weather_text = "Becoming clearer";} 316 | else if (wx_text == NoChange) {image = cloudy_img; current_wx = cloudy; weather_text = "No change, clearing";} 317 | else if (wx_text == ClearSpells) {image = mostlysunny_img; current_wx = mostlysunny; weather_text = "Clear spells";} 318 | else if (wx_text == ClearingWithin12hrs) {image = mostlysunny_img; current_wx = mostlysunny; weather_text = "Clearing within 12-hrs";} 319 | else if (wx_text == ClearingAndColder) {image = mostlysunny_img; current_wx = mostlysunny; weather_text = "Clearing and colder";} 320 | else if (wx_text == GettingWarmer) {image = mostlysunny_img; current_wx = mostlysunny; weather_text = "Getting warmer";} 321 | else if (wx_text == WarmerIn2daysRainLikely) {image = rain_img; current_wx = rain; weather_text = "Warmer in 2-days, rain likely";} 322 | else if (wx_text == ExpectRain) {image = rain_img; current_wx = rain; weather_text = "Expect rain";} 323 | else if (wx_text == WarmerRainWithin36hrs) {image = rain_img; current_wx = rain; weather_text = "Warmer, rain within 36-hrs";} 324 | else if (wx_text == RainIn18hrs) {image = rain_img; current_wx = rain; weather_text = "Rain in 18-hrs";} 325 | else if (wx_text == RainHighWindsClearAndCool) {image = rain_img; current_wx = rain; weather_text = "Rain, high winds, clear and cool";} 326 | else if (wx_text == GalesHeavyRainSnowInWinter) {image = tstorms_img; current_wx = tstorms; weather_text = "Gales, heavy rain, in winter snow";} 327 | } 328 | 329 | // Convert pressure and trend to a weather description either for 1 or 3 hours with the boolean true/false switch 330 | weather_description get_forecast_text(float pressure_now, float trend, bool range) { 331 | String trend_str = get_trend_text(trend); 332 | weather_description wx_text = NoChange; //As a default forecast 333 | weather_extra_text = ""; 334 | image = cloudy_img; // Generally when there is 'no change' then cloudy is the conditions 335 | if (pressure_now >= 1022.68 ) {wx_text = GoodClearWeather;} 336 | if (pressure_now >= 1022.7 && trend_str == "Falling fast") {wx_text = WarmerRainWithin36hrs;} 337 | if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && 338 | (trend_str == "Steady" || trend_str == "Rising slow")) {wx_text = NoChange; (range?wx_history_3hr():wx_history_1hr()); } 339 | if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && 340 | (trend_str == "Rising" || trend_str == "Rising fast")) {wx_text = GettingWarmer;} 341 | if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str == "Rising slow") {wx_text = BecomingClearer;} 342 | if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && 343 | (trend_str == "Falling fast" || trend_str == "Falling slow")) {wx_text = ExpectRain;} 344 | if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str == "Steady") {wx_text = ClearSpells; (range?wx_history_3hr():wx_history_1hr());}; 345 | if (pressure_now <= 1013.2 && (trend_str == "Falling slow" || trend_str == "Falling")) {wx_text = RainIn18hrs;} 346 | if (pressure_now <= 1013.2 && trend_str == "Falling fast") {wx_text = RainHighWindsClearAndCool;} 347 | if (pressure_now <= 1013.2 && 348 | (trend_str == "Rising" || trend_str == "Rising slow"||trend_str == "Rising fast")) {wx_text = ClearingWithin12hrs;} 349 | if (pressure_now <= 1009.14 && trend_str == "Falling fast") {wx_text = GalesHeavyRainSnowInWinter;} 350 | if (pressure_now <= 1009.14 && trend_str == "Rising fast") {wx_text = ClearingAndColder;} 351 | return wx_text; 352 | } 353 | 354 | // Convert 1-hr weather history to text 355 | void wx_history_1hr() { 356 | if (wx_average_1hr > 0) weather_extra_text = ", expect sun"; 357 | else if (wx_average_1hr == 0) weather_extra_text = ", mainly cloudy"; 358 | else if (wx_average_1hr < 0) weather_extra_text = ", expect rain"; 359 | else weather_extra_text = ""; 360 | } 361 | 362 | // Convert 3-hr weather history to text 363 | void wx_history_3hr() { 364 | if (wx_average_3hr > 0) weather_extra_text = ", expect sun"; 365 | else if (wx_average_3hr == 0) weather_extra_text = ", mainly cloudy"; 366 | else if (wx_average_3hr < 0) weather_extra_text = ", expect rain"; 367 | else weather_extra_text = ""; 368 | } 369 | 370 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 371 | 372 | // This array keeps function pointers to all frames 373 | // frames are the single views that slide in 374 | FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5, drawFrame6}; 375 | 376 | // how many frames are there? 377 | int frameCount = 6; 378 | 379 | // Overlays are statically drawn on top of a frame eg. a clock 380 | OverlayCallback overlays[] = { msOverlay }; 381 | int overlaysCount = 1; 382 | 383 | void setup() { 384 | Serial.begin(115200); 385 | start_EEPROM(); 386 | // 0,1,2,3 is floating point for DST 387 | // 4,5,6,7 is floating point for altitude offset 388 | // 8,9,10,11 is floating point offset for temperature 389 | // 12 onwards SSID and Password 390 | EEPROM.get(0,DST); 391 | delay(2000); 392 | EEPROM.get(4,alt_offset); // 4-bytes for floating point altitude offset 393 | delay(2000); 394 | EEPROM.get(8,temp_offset); // 4-bytes for floating point temperature offset 395 | delay(2000); 396 | EEPROM.end(); 397 | Wire.begin(SDA,SCL); 398 | //------------------------------ 399 | //WiFiManager intialisation. Once completed there is no need to repeat the process on the current board 400 | WiFiManager wifiManager; 401 | // A new OOB ESP8266 will have no credentials, so will connect and not need this to be uncommented and compiled in, a used one will, try it to see how it works 402 | // Uncomment the next line for a new device or one that has not connected to your Wi-Fi before or you want to reset the Wi-Fi connection 403 | // Then restart the ESP8266 and connect your PC to the wireless access point called 'ESP8266_AP' or whatever you call it below 404 | // wifiManager.resetSettings(); 405 | // Next connect to http://192.168.4.1/ and follow instructions to make the WiFi connection 406 | // Set a timeout until configuration is turned off, useful to retry or go to sleep in n-seconds 407 | wifiManager.setTimeout(180); 408 | //fetches ssid and password and tries to connect, if connections succeeds it starts an access point with the name called "ESP8266_AP" and waits in a blocking loop for configuration 409 | if(!wifiManager.autoConnect("WX_Forecaster")) { 410 | Serial.println("failed to connect and timeout occurred"); 411 | delay(3000); 412 | ESP.reset(); //reset and try again 413 | delay(5000); 414 | } 415 | // At this stage the WiFi manager will have successfully connected to a network, or if not will try again in 180-seconds 416 | //------------------------------ 417 | configTime(0*3600, DST*3600, "pool.ntp.org"); // e.g for the UK +1hour (1*60*60=3600=+1hour) ahead for DST 418 | time_t now = time(nullptr); 419 | delay(2000); // Wait for time to start 420 | if (!bme.begin()) { Serial.println("Could not find a sensor, check wiring!");} 421 | else 422 | { 423 | Serial.println("Found a sensor continuing"); 424 | while (isnan(bme.readPressure())) { Serial.println(bme.readPressure()); } 425 | } 426 | while (!update_time()); //Get the latest time 427 | for (int i = 0; i <= 23; i++){ // At the start all array values are the same as a baseline 428 | reading[i].pressure = read_pressure(); // A rounded to 1-decimal place version of pressure 429 | reading[i].temperature = bme.readTemperature(); // Although not used, but avialable 430 | reading[i].humidity = bme.readHumidity(); // Although not used, but avialable 431 | reading[i].wx_state_1hr = unknown; // To begin with 432 | reading[i].wx_state_3hr = unknown; // To begin with 433 | } // Note that only 0,5,11,17,20,21,22,23 are used as display positions 434 | last_reading_hour = reading_hour; 435 | wx_average_1hr = 0; // Until we get a better idea 436 | wx_average_3hr = 0; // Until we get a better idea 437 | 438 | // An ESP is capable of rendering 60fps in 80Mhz mode but leaves little time for anything else, run at 160Mhz mode or just set it to about 30 fps 439 | ui.setTargetFPS(20); 440 | ui.setIndicatorPosition(BOTTOM); // You can change this to TOP, LEFT, BOTTOM, RIGHT 441 | ui.setIndicatorDirection(LEFT_RIGHT); // Defines where the first frame is located in the bar 442 | ui.setFrameAnimation(SLIDE_LEFT); // You can change the transition that is used SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN 443 | ui.setFrames(frames, frameCount); // Add frames 444 | ui.setOverlays(overlays, overlaysCount); // Add overlays 445 | ui.init(); // Initialising the UI will init the display too. 446 | display.flipScreenVertically(); 447 | display.setFont(ArialMT_Plain_10); 448 | display.setTextAlignment(TEXT_ALIGN_LEFT); 449 | server.begin(); // Start the webserver 450 | Serial.println("Webserver started..."); 451 | 452 | server.on("/", Wx_home_page); 453 | server.on("/DST_plus", DST_plus); 454 | server.on("/DST_minus", DST_minus); 455 | server.on("/ALT_plus", ALT_plus); 456 | server.on("/ALT_minus", ALT_minus); 457 | server.on("/TEMP_plus", TEMP_plus); 458 | server.on("/TEMP_minus", TEMP_minus); 459 | server.on("/LIST_WIFI", LIST_WIFI); 460 | server.on("/RESET_VALUES", RESET_VALUES); // Define what happens when a client requests attention 461 | } 462 | 463 | void loop() { 464 | int remainingTimeBudget = ui.update(); 465 | update_time_and_data(); 466 | server.handleClient(); // Wait for a client to connect and when they do process their requests 467 | delay(remainingTimeBudget); 468 | } 469 | 470 | void update_time_and_data(){ 471 | while (!update_time()); 472 | if (reading_hour != last_reading_hour) { // If the hour has advanced, then shift readings left and record new values at array element [23] 473 | for (int i = 0; i < 23;i++){ 474 | reading[i].pressure = reading[i+1].pressure; 475 | reading[i].temperature = reading[i+1].temperature; 476 | reading[i].wx_state_1hr = reading[i+1].wx_state_1hr; 477 | reading[i].wx_state_3hr = reading[i+1].wx_state_3hr; 478 | } 479 | reading[23].pressure = read_pressure(); // Update time=now with current value of pressure 480 | reading[23].wx_state_1hr = current_wx; 481 | reading[23].wx_state_3hr = current_wx; 482 | last_reading_hour = reading_hour; 483 | hr_cnt++; 484 | wx_average_1hr = reading[22].wx_state_1hr + current_wx; // Used to predict 1-hour forecast extra text 485 | wx_average_3hr = 0; 486 | for (int i=23;i >= 21; i--){ // Used to predict 3-hour forecast extra text 487 | wx_average_3hr = wx_average_3hr + (int)reading[i].wx_state_3hr; // On average the last 3-hours of weather is used for the 'no change' forecast - e.g. more of the same? 488 | } 489 | } 490 | } 491 | 492 | bool update_time(){ 493 | time_t now = time(nullptr); 494 | struct tm *now_tm; 495 | int hour,min,second,day,month,year; 496 | now = time(NULL); 497 | now_tm = localtime(&now); 498 | hour = now_tm->tm_hour; 499 | min = now_tm->tm_min; 500 | int weekday = now_tm->tm_wday; 501 | String week_days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; 502 | String wday = week_days[weekday]; 503 | second = now_tm->tm_sec; 504 | day = now_tm->tm_mday; 505 | month = now_tm->tm_mon+1; 506 | year = now_tm->tm_year - 100;// YY only no CC element add 1900 for CC element too 507 | // Needs to be in the format HH:MM:SS Sat 05-07-17 508 | time_str = (hour<10?"0":"")+String(hour)+":"+(min<10?"0":"")+String(min)+":"+(second<10?"0":"")+String(second) 509 | +" "+wday+(day<10?" 0":" ")+String(day)+"-"+(month<10?"0":"")+String(month)+"-"+String(year); 510 | //Serial.println(time_str); 511 | reading_hour = hour; 512 | return true; 513 | } 514 | 515 | int StartWiFi(const char* ssid, const char* password){ 516 | int connAttempts = 0; 517 | Serial.println("\r\nConnecting to: "+String(ssid)); 518 | WiFi.begin(ssid, password); 519 | while (WiFi.status() != WL_CONNECTED ) { 520 | delay(500); 521 | Serial.print("."); 522 | if(connAttempts > 20) { 523 | Serial.println("*** Failed to start Wifi ***"); 524 | return false; 525 | } 526 | connAttempts++; 527 | } 528 | Serial.print("WiFi connected\r\nIP address: "); 529 | Serial.println(WiFi.localIP()); 530 | return true; 531 | } 532 | 533 | void Wx_home_page () { 534 | update_webpage(); 535 | server.send(200, "text/html", webpage); 536 | } 537 | 538 | void update_webpage(){ 539 | append_webpage_header(); 540 | webpage += ""; 541 | webpage += "

Time Zone, Pressure and Temperature Offset Settings

"; 542 | webpage += "[DST offset is : "; 543 | if (DST > -1 && DST < 0) webpage += "-"; 544 | webpage += String(int(DST)) + ":"; 545 | if (abs(round(60*(DST - int(DST)))) == 0) webpage += "00]"; else webpage += "30]"; 546 | webpage += "

Current Pressure is : "+String(Lpressure+alt_offset,1)+ " offset is : [" +String(alt_offset,1)+" hPa]
"; 547 | webpage += "
Current Temperature is : "+String(Ltemperature+temp_offset,1)+ "°C offset is [: " +String(temp_offset,1)+"°C]

"; 548 | webpage += "

[DST+]  [DST-]

"; 549 | webpage += "

[ALT+]  [ALT-]

"; 550 | webpage += "

[TEMP+]  [TEMP-]

"; 551 | webpage += "

[WIFI Network List]

"; 552 | webpage += "

Reset Weather Forecaster

"; 553 | webpage += "
"; 554 | webpage += ""; 555 | } 556 | 557 | void append_webpage_header() { 558 | // webpage is a global variable 559 | webpage = ""; // A blank string variable to hold the web page 560 | webpage += "Wx Forecaster"; 561 | webpage += ""; 570 | } 571 | 572 | void DST_plus() { 573 | start_EEPROM(); 574 | DST += 0.5; 575 | EEPROM.put(0,DST); 576 | delay(500); 577 | EEPROM.commit(); 578 | EEPROM.end(); 579 | configTime(0*3600, DST*3600, "pool.ntp.org"); // +1hour (1*60*60=3600=+1hour) ahead for DST in the UK 580 | Wx_home_page(); 581 | } 582 | 583 | void DST_minus() { 584 | start_EEPROM(); 585 | DST -= 0.5; 586 | EEPROM.put(0,DST); 587 | delay(500); 588 | EEPROM.commit(); 589 | EEPROM.end(); 590 | configTime(0*3600, DST*3600, "pool.ntp.org"); // +1hour (1*60*60=3600=+1hour) ahead for DST in the UK 591 | Wx_home_page(); 592 | } 593 | 594 | void ALT_plus() { 595 | start_EEPROM(); 596 | alt_offset += 0.1; 597 | EEPROM.put(0,alt_offset); 598 | delay(500); 599 | EEPROM.commit(); 600 | EEPROM.end(); 601 | Wx_home_page(); 602 | } 603 | 604 | void ALT_minus() { 605 | start_EEPROM(); 606 | alt_offset -= 0.1; 607 | EEPROM.put(0,alt_offset); 608 | delay(500); 609 | EEPROM.commit(); 610 | EEPROM.end(); 611 | Wx_home_page(); 612 | } 613 | 614 | void TEMP_plus() { 615 | start_EEPROM(); 616 | temp_offset += 0.1; 617 | EEPROM.put(0,alt_offset); 618 | delay(500); 619 | EEPROM.commit(); 620 | EEPROM.end(); 621 | Wx_home_page(); 622 | } 623 | 624 | void TEMP_minus() { 625 | start_EEPROM(); 626 | temp_offset -= 0.1; 627 | EEPROM.put(0,alt_offset); 628 | delay(500); 629 | EEPROM.commit(); 630 | EEPROM.end(); 631 | Wx_home_page(); 632 | } 633 | 634 | void RESET_VALUES() { 635 | start_EEPROM(); 636 | DST = 0; 637 | alt_offset = 0; 638 | temp_offset = 0; 639 | EEPROM.put(0,DST); 640 | delay(500); 641 | EEPROM.put(4,alt_offset); 642 | delay(500); 643 | EEPROM.put(8,temp_offset); 644 | delay(500); 645 | EEPROM.commit(); 646 | EEPROM.end(); 647 | Wx_home_page(); 648 | } 649 | 650 | void LIST_WIFI(){ 651 | append_webpage_header(); 652 | webpage += ""; 653 | webpage += "

Avialable Networks

"; 654 | int n = WiFi.scanNetworks(); 655 | Serial.println("scan done"); 656 | if (n == 0) { 657 | Serial.println("no networks found"); 658 | webpage += "

*** No Networkks Found ***

"; 659 | } 660 | else 661 | { 662 | Serial.println("scan start"); 663 | Serial.print(String(n)+" networks found"); 664 | webpage += ""; 665 | for (int i = 0; i < n; ++i){ 666 | webpage += ""; 669 | Serial.print(String(i + 1)+": "+WiFi.SSID(i)+" ("+WiFi.RSSI(i)+")"); 670 | Serial.println(); 671 | 672 | } 673 | webpage += "
No.SSIDRSSIEncrypted(*)
"+String(i+1)+"" + WiFi.SSID(i) + "(" + WiFi.RSSI(i) + ")"; 667 | webpage += (WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*"; 668 | webpage += "

[BACK]

"; 674 | webpage += "
"; 675 | webpage += ""; 676 | } 677 | server.send(200, "text/html", webpage); 678 | if (!StartWiFi(ssid,password)) Serial.println("Failed to start WiFi Service after 20 attempts");; 679 | configTime(0*3600, DST*3600, "pool.ntp.org"); // e.g for the UK +1hour (1*60*60=3600=+1hour) ahead for DST 680 | } 681 | 682 | 683 | void start_EEPROM(){ 684 | EEPROM.begin(128); 685 | delay(1000); 686 | } 687 | 688 | /* 689 | FRAME-3 description 690 | // This frame draws a graph of pressure (delata) change for the last 24-hours, see Annex* for more details 691 | // Draws a 'relative value' chart using reading[23] as the baseline 692 | // +8 | 693 | // +7 |-- 694 | // : 695 | // +1 |-- -- -- -- -- -- -- 696 | // 0 +-24+-18+-12+-8-+-3-+-2-+-1-+-0-+ 697 | // -1 | 698 | // -2 | 699 | // The 'reading' array holds values for Pressure, Temperature, Humidity and Wx State for the last 24-hours 700 | // [00][01][02][03][04][05][06][07][08][09][10][11][12][13][14][15][16][17][18][19][20][21][22][23] Values are shifted left <-- each hour 701 | // ^-23Hr ^-18Hr ^-12Hr ^-6Hr ^-3 ^-2 ^-1 ^0Hr 702 | // P ~ readings in each array position 703 | // T ~ readings in each array position 704 | // % ~ readings in each array position 705 | // Wx ~ readings in each array position 706 | 707 | // Forecast basics: 708 | // Look at the pressure change over3 hours 709 | // If pressure is descending, then a low pressure area is approaching 710 | // If pressure is ascending , then a low is passing or a high pressure is coming 711 | // When pressure is changing rapidly (>6hPa/3 hours), it will be windy (or potentially windy) 712 | 713 | // More detailed: 714 | // Pressure falling slowly (0.5 - 3 hPa in 3h): low is weak, dying or moving slowly. You might get some rain but typically no high winds. 715 | // Pressure falling moderately (3-6 hPa/3h): rapid movement or deepening low. Moderate winds and rain and a warm front. 716 | : the low is passing fast, the day after tomorrow will typically be fine. 717 | // Pressure falling fast (6-12 hPa/3h) : Storm conditions highly likely. 718 | // Pressure rises are connected with gradually drier weather 719 | 720 | */ 721 | 722 | 723 | -------------------------------------------------------------------------------- /ESP8266_Time_Services2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "time.h" 3 | 4 | String Local_Date_Time; 5 | 6 | const char* ssid = ""; 7 | const char* password = ""; 8 | 9 | void setup() { 10 | Serial.begin(115200); // For serial diagnostic prints 11 | // We start by connecting to a WiFi network 12 | Serial.println("Connecting to " + String(ssid)); 13 | /* Explicitly set the ESP8266 to be a WiFi-client, otherwise, it by default, 14 | would try to act as both a client and an access-point and could cause 15 | network-issues with your other WiFi-devices on your WiFi-network. */ 16 | WiFi.mode(WIFI_STA); 17 | WiFi.begin(ssid, password); 18 | while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");} 19 | configTime(0, 0, "0.pool.ntp.org"); 20 | // See https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv for your timezone 21 | setenv("TZ", "EST5EDT,M3.2.0,M11.1.0", 1); 22 | delay(1000); // Wait for time to start 23 | } 24 | 25 | void loop() { 26 | GetLocalTime(); // You must call this to get the current date-time 27 | Serial.println(Local_Date_Time); 28 | delay(5000); 29 | } 30 | 31 | String GetLocalTime() { 32 | time_t rawtime; 33 | struct tm * timeinfo; 34 | char buffer[80]; 35 | time (&rawtime); 36 | timeinfo = localtime (&rawtime); 37 | // See here for time format codes http://www.cplusplus.com/reference/ctime/strftime/ 38 | strftime (buffer,80,"%D %I:%M:%S %p",timeinfo); // US Format 01-31-19 17:59:22 PM 39 | //strftime (buffer,80,"%I:%M:%S %p",timeinfo); // US Format time only 17:59:22 PM 40 | //strftime (buffer,80,"Date is : %D",timeinfo); // US format date 'Date is: 01-31-19' 41 | //strftime (buffer,80,"%T",timeinfo); // 24-Hour time format 42 | //strftime (buffer,80,"Time is : %T",timeinfo); // 24-Hour time format 'Time is: 17:19:22' 43 | 44 | //Serial.println(buffer); 45 | Local_Date_Time = String(buffer); 46 | } 47 | -------------------------------------------------------------------------------- /GPSLoRa.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include "GPSLoRa.h" 5 | 6 | // registers 7 | #define REG_FIFO 0x00 8 | #define REG_OP_MODE 0x01 9 | #define REG_FRF_MSB 0x06 10 | #define REG_FRF_MID 0x07 11 | #define REG_FRF_LSB 0x08 12 | #define REG_PA_CONFIG 0x09 13 | #define REG_LNA 0x0c 14 | #define REG_FIFO_ADDR_PTR 0x0d 15 | #define REG_FIFO_TX_BASE_ADDR 0x0e 16 | #define REG_FIFO_RX_BASE_ADDR 0x0f 17 | #define REG_FIFO_RX_CURRENT_ADDR 0x10 18 | #define REG_IRQ_FLAGS 0x12 19 | #define REG_RX_NB_BYTES 0x13 20 | #define REG_PKT_SNR_VALUE 0x19 21 | #define REG_PKT_RSSI_VALUE 0x1a 22 | #define REG_MODEM_CONFIG_1 0x1d 23 | #define REG_MODEM_CONFIG_2 0x1e 24 | #define REG_PREAMBLE_MSB 0x20 25 | #define REG_PREAMBLE_LSB 0x21 26 | #define REG_PAYLOAD_LENGTH 0x22 27 | #define REG_MODEM_CONFIG_3 0x26 28 | #define REG_FREQ_ERROR_MSB 0x28 29 | #define REG_FREQ_ERROR_MID 0x29 30 | #define REG_FREQ_ERROR_LSB 0x2a 31 | #define REG_RSSI_WIDEBAND 0x2c 32 | #define REG_DETECTION_OPTIMIZE 0x31 33 | #define REG_DETECTION_THRESHOLD 0x37 34 | #define REG_SYNC_WORD 0x39 35 | #define REG_DIO_MAPPING_1 0x40 36 | #define REG_VERSION 0x42 37 | 38 | // modes 39 | #define MODE_LONG_RANGE_MODE 0x80 40 | #define MODE_SLEEP 0x00 41 | #define MODE_STDBY 0x01 42 | #define MODE_TX 0x03 43 | #define MODE_RX_CONTINUOUS 0x05 44 | #define MODE_RX_SINGLE 0x06 45 | 46 | // PA config 47 | #define PA_BOOST 0x80 48 | 49 | // IRQ masks 50 | #define IRQ_TX_DONE_MASK 0x08 51 | #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 52 | #define IRQ_RX_DONE_MASK 0x40 53 | 54 | #define MAX_PKT_LENGTH 255 55 | 56 | LoRaClass::LoRaClass() : 57 | _spiSettings(LORA_DEFAULT_SPI_FREQUENCY, MSBFIRST, SPI_MODE0), 58 | _spi(&LORA_DEFAULT_SPI), 59 | _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), 60 | _frequency(0), 61 | _packetIndex(0), 62 | _implicitHeaderMode(0), 63 | _onReceive(NULL) 64 | { 65 | // overide Stream timeout value 66 | setTimeout(0); 67 | } 68 | 69 | int LoRaClass::begin(long frequency) 70 | { 71 | #ifdef ARDUINO_SAMD_MKRWAN1300 72 | pinMode(LORA_IRQ_DUMB, OUTPUT); 73 | digitalWrite(LORA_IRQ_DUMB, LOW); 74 | 75 | // Hardware reset 76 | pinMode(LORA_BOOT0, OUTPUT); 77 | digitalWrite(LORA_BOOT0, LOW); 78 | 79 | pinMode(LORA_RESET, OUTPUT); 80 | digitalWrite(LORA_RESET, HIGH); 81 | delay(200); 82 | digitalWrite(LORA_RESET, LOW); 83 | delay(200); 84 | digitalWrite(LORA_RESET, HIGH); 85 | delay(50); 86 | #endif 87 | 88 | // setup pins 89 | pinMode(_ss, OUTPUT); 90 | // set SS high 91 | digitalWrite(_ss, HIGH); 92 | 93 | if (_reset != -1) { 94 | pinMode(_reset, OUTPUT); 95 | 96 | // perform reset 97 | digitalWrite(_reset, LOW); 98 | delay(10); 99 | digitalWrite(_reset, HIGH); 100 | delay(10); 101 | } 102 | 103 | // start SPI 104 | _spi->begin(); 105 | 106 | // check version 107 | uint8_t version = readRegister(REG_VERSION); 108 | if (version != 0x12) { 109 | return 0; 110 | } 111 | 112 | // put in sleep mode 113 | sleep(); 114 | 115 | // set frequency 116 | setFrequency(frequency); 117 | 118 | // set base addresses 119 | writeRegister(REG_FIFO_TX_BASE_ADDR, 0); 120 | writeRegister(REG_FIFO_RX_BASE_ADDR, 0); 121 | 122 | // set LNA boost 123 | writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); 124 | 125 | // set auto AGC 126 | writeRegister(REG_MODEM_CONFIG_3, 0x04); 127 | 128 | // set output power to 17 dBm 129 | setTxPower(17); 130 | 131 | // put in standby mode 132 | idle(); 133 | 134 | return 1; 135 | } 136 | 137 | void LoRaClass::end() 138 | { 139 | // put in sleep mode 140 | sleep(); 141 | 142 | // stop SPI 143 | _spi->end(); 144 | } 145 | 146 | int LoRaClass::beginPacket(int implicitHeader) 147 | { 148 | // put in standby mode 149 | idle(); 150 | 151 | if (implicitHeader) { 152 | implicitHeaderMode(); 153 | } else { 154 | explicitHeaderMode(); 155 | } 156 | 157 | // reset FIFO address and paload length 158 | writeRegister(REG_FIFO_ADDR_PTR, 0); 159 | writeRegister(REG_PAYLOAD_LENGTH, 0); 160 | 161 | return 1; 162 | } 163 | 164 | int LoRaClass::endPacket() 165 | { 166 | // put in TX mode 167 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); 168 | 169 | // wait for TX done 170 | while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { 171 | yield(); 172 | } 173 | 174 | // clear IRQ's 175 | writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); 176 | 177 | return 1; 178 | } 179 | 180 | int LoRaClass::parsePacket(int size) 181 | { 182 | int packetLength = 0; 183 | int irqFlags = readRegister(REG_IRQ_FLAGS); 184 | 185 | if (size > 0) { 186 | implicitHeaderMode(); 187 | 188 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 189 | } else { 190 | explicitHeaderMode(); 191 | } 192 | 193 | // clear IRQ's 194 | writeRegister(REG_IRQ_FLAGS, irqFlags); 195 | 196 | if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 197 | // received a packet 198 | _packetIndex = 0; 199 | 200 | // read packet length 201 | if (_implicitHeaderMode) { 202 | packetLength = readRegister(REG_PAYLOAD_LENGTH); 203 | } else { 204 | packetLength = readRegister(REG_RX_NB_BYTES); 205 | } 206 | 207 | // set FIFO address to current RX address 208 | writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); 209 | 210 | // put in standby mode 211 | idle(); 212 | } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { 213 | // not currently in RX mode 214 | 215 | // reset FIFO address 216 | writeRegister(REG_FIFO_ADDR_PTR, 0); 217 | 218 | // put in single RX mode 219 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); 220 | } 221 | 222 | return packetLength; 223 | } 224 | 225 | int LoRaClass::packetRssi() 226 | { 227 | return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157)); 228 | } 229 | 230 | float LoRaClass::packetSnr() 231 | { 232 | return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; 233 | } 234 | 235 | long LoRaClass::packetFrequencyError() 236 | { 237 | int32_t freqError = 0; 238 | freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); 239 | freqError <<= 8L; 240 | freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); 241 | freqError <<= 8L; 242 | freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); 243 | 244 | if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on 245 | freqError -= 524288; // B1000'0000'0000'0000'0000 246 | } 247 | 248 | const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) 249 | const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 250 | 251 | return static_cast(fError); 252 | } 253 | 254 | size_t LoRaClass::write(uint8_t byte) 255 | { 256 | return write(&byte, sizeof(byte)); 257 | } 258 | 259 | size_t LoRaClass::write(const uint8_t *buffer, size_t size) 260 | { 261 | int currentLength = readRegister(REG_PAYLOAD_LENGTH); 262 | 263 | // check size 264 | if ((currentLength + size) > MAX_PKT_LENGTH) { 265 | size = MAX_PKT_LENGTH - currentLength; 266 | } 267 | 268 | // write data 269 | for (size_t i = 0; i < size; i++) { 270 | writeRegister(REG_FIFO, buffer[i]); 271 | } 272 | 273 | // update length 274 | writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); 275 | 276 | return size; 277 | } 278 | 279 | int LoRaClass::available() 280 | { 281 | return (readRegister(REG_RX_NB_BYTES) - _packetIndex); 282 | } 283 | 284 | int LoRaClass::read() 285 | { 286 | if (!available()) { 287 | return -1; 288 | } 289 | 290 | _packetIndex++; 291 | 292 | return readRegister(REG_FIFO); 293 | } 294 | 295 | int LoRaClass::peek() 296 | { 297 | if (!available()) { 298 | return -1; 299 | } 300 | 301 | // store current FIFO address 302 | int currentAddress = readRegister(REG_FIFO_ADDR_PTR); 303 | 304 | // read 305 | uint8_t b = readRegister(REG_FIFO); 306 | 307 | // restore FIFO address 308 | writeRegister(REG_FIFO_ADDR_PTR, currentAddress); 309 | 310 | return b; 311 | } 312 | 313 | void LoRaClass::flush() 314 | { 315 | } 316 | 317 | #ifndef ARDUINO_SAMD_MKRWAN1300 318 | void LoRaClass::onReceive(void(*callback)(int)) 319 | { 320 | _onReceive = callback; 321 | 322 | if (callback) { 323 | pinMode(_dio0, INPUT); 324 | 325 | writeRegister(REG_DIO_MAPPING_1, 0x00); 326 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 327 | SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); 328 | #endif 329 | attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); 330 | } else { 331 | detachInterrupt(digitalPinToInterrupt(_dio0)); 332 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 333 | SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); 334 | #endif 335 | } 336 | } 337 | 338 | void LoRaClass::receive(int size) 339 | { 340 | if (size > 0) { 341 | implicitHeaderMode(); 342 | 343 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 344 | } else { 345 | explicitHeaderMode(); 346 | } 347 | 348 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); 349 | } 350 | #endif 351 | 352 | void LoRaClass::idle() 353 | { 354 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); 355 | } 356 | 357 | void LoRaClass::sleep() 358 | { 359 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); 360 | } 361 | 362 | void LoRaClass::setTxPower(int level, int outputPin) 363 | { 364 | if (PA_OUTPUT_RFO_PIN == outputPin) { 365 | // RFO 366 | if (level < 0) { 367 | level = 0; 368 | } else if (level > 14) { 369 | level = 14; 370 | } 371 | 372 | writeRegister(REG_PA_CONFIG, 0x70 | level); 373 | } else { 374 | // PA BOOST 375 | if (level < 2) { 376 | level = 2; 377 | } else if (level > 17) { 378 | level = 17; 379 | } 380 | 381 | writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); 382 | } 383 | } 384 | 385 | void LoRaClass::setFrequency(long frequency) 386 | { 387 | _frequency = frequency; 388 | 389 | uint64_t frf = ((uint64_t)frequency << 19) / 32000000; 390 | 391 | writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); 392 | writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); 393 | writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); 394 | } 395 | 396 | int LoRaClass::getSpreadingFactor() 397 | { 398 | return readRegister(REG_MODEM_CONFIG_2) >> 4; 399 | } 400 | 401 | void LoRaClass::setSpreadingFactor(int sf) 402 | { 403 | if (sf < 6) { 404 | sf = 6; 405 | } else if (sf > 12) { 406 | sf = 12; 407 | } 408 | 409 | if (sf == 6) { 410 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); 411 | writeRegister(REG_DETECTION_THRESHOLD, 0x0c); 412 | } else { 413 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); 414 | writeRegister(REG_DETECTION_THRESHOLD, 0x0a); 415 | } 416 | 417 | writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); 418 | setLdoFlag(); 419 | } 420 | 421 | long LoRaClass::getSignalBandwidth() 422 | { 423 | byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); 424 | switch (bw) { 425 | case 0: return 7.8E3; 426 | case 1: return 10.4E3; 427 | case 2: return 15.6E3; 428 | case 3: return 20.8E3; 429 | case 4: return 31.25E3; 430 | case 5: return 41.7E3; 431 | case 6: return 62.5E3; 432 | case 7: return 125E3; 433 | case 8: return 250E3; 434 | case 9: return 500E3; 435 | default: return 0; 436 | } 437 | } 438 | 439 | void LoRaClass::setSignalBandwidth(long sbw) 440 | { 441 | int bw; 442 | 443 | if (sbw <= 7.8E3) { 444 | bw = 0; 445 | } else if (sbw <= 10.4E3) { 446 | bw = 1; 447 | } else if (sbw <= 15.6E3) { 448 | bw = 2; 449 | } else if (sbw <= 20.8E3) { 450 | bw = 3; 451 | } else if (sbw <= 31.25E3) { 452 | bw = 4; 453 | } else if (sbw <= 41.7E3) { 454 | bw = 5; 455 | } else if (sbw <= 62.5E3) { 456 | bw = 6; 457 | } else if (sbw <= 125E3) { 458 | bw = 7; 459 | } else if (sbw <= 250E3) { 460 | bw = 8; 461 | } else /*if (sbw <= 250E3)*/ { 462 | bw = 9; 463 | } 464 | 465 | writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); 466 | setLdoFlag(); 467 | } 468 | 469 | void LoRaClass::setLdoFlag() 470 | { 471 | // Section 4.1.1.5 472 | long symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ; 473 | 474 | // Section 4.1.1.6 475 | boolean ldoOn = symbolDuration > 16; 476 | 477 | uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); 478 | bitWrite(config3, 3, ldoOn); 479 | writeRegister(REG_MODEM_CONFIG_3, config3); 480 | } 481 | 482 | void LoRaClass::setCodingRate4(int denominator) 483 | { 484 | if (denominator < 5) { 485 | denominator = 5; 486 | } else if (denominator > 8) { 487 | denominator = 8; 488 | } 489 | 490 | int cr = denominator - 4; 491 | 492 | writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); 493 | } 494 | 495 | void LoRaClass::setPreambleLength(long length) 496 | { 497 | writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); 498 | writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); 499 | } 500 | 501 | void LoRaClass::setSyncWord(int sw) 502 | { 503 | writeRegister(REG_SYNC_WORD, sw); 504 | } 505 | 506 | void LoRaClass::enableCrc() 507 | { 508 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); 509 | } 510 | 511 | void LoRaClass::disableCrc() 512 | { 513 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); 514 | } 515 | 516 | byte LoRaClass::random() 517 | { 518 | return readRegister(REG_RSSI_WIDEBAND); 519 | } 520 | 521 | void LoRaClass::setPins(int ss, int reset, int dio0) 522 | { 523 | _ss = ss; 524 | _reset = reset; 525 | _dio0 = dio0; 526 | } 527 | 528 | void LoRaClass::setSPI(SPIClass& spi) 529 | { 530 | _spi = &spi; 531 | } 532 | 533 | void LoRaClass::setSPIFrequency(uint32_t frequency) 534 | { 535 | _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); 536 | } 537 | 538 | void LoRaClass::dumpRegisters(Stream& out) 539 | { 540 | for (int i = 0; i < 128; i++) { 541 | out.print("0x"); 542 | out.print(i, HEX); 543 | out.print(": 0x"); 544 | out.println(readRegister(i), HEX); 545 | } 546 | } 547 | 548 | void LoRaClass::explicitHeaderMode() 549 | { 550 | _implicitHeaderMode = 0; 551 | 552 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); 553 | } 554 | 555 | void LoRaClass::implicitHeaderMode() 556 | { 557 | _implicitHeaderMode = 1; 558 | 559 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); 560 | } 561 | 562 | void LoRaClass::handleDio0Rise() 563 | { 564 | int irqFlags = readRegister(REG_IRQ_FLAGS); 565 | 566 | // clear IRQ's 567 | writeRegister(REG_IRQ_FLAGS, irqFlags); 568 | 569 | if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 570 | // received a packet 571 | _packetIndex = 0; 572 | 573 | // read packet length 574 | int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); 575 | 576 | // set FIFO address to current RX address 577 | writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); 578 | 579 | if (_onReceive) { 580 | _onReceive(packetLength); 581 | } 582 | 583 | // reset FIFO address 584 | writeRegister(REG_FIFO_ADDR_PTR, 0); 585 | } 586 | } 587 | 588 | uint8_t LoRaClass::readRegister(uint8_t address) 589 | { 590 | return singleTransfer(address & 0x7f, 0x00); 591 | } 592 | 593 | void LoRaClass::writeRegister(uint8_t address, uint8_t value) 594 | { 595 | singleTransfer(address | 0x80, value); 596 | } 597 | 598 | uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) 599 | { 600 | uint8_t response; 601 | 602 | digitalWrite(_ss, LOW); 603 | 604 | _spi->beginTransaction(_spiSettings); 605 | _spi->transfer(address); 606 | response = _spi->transfer(value); 607 | _spi->endTransaction(); 608 | 609 | digitalWrite(_ss, HIGH); 610 | 611 | return response; 612 | } 613 | 614 | void LoRaClass::onDio0Rise() 615 | { 616 | LoRa.handleDio0Rise(); 617 | } 618 | 619 | LoRaClass LoRa; 620 | -------------------------------------------------------------------------------- /GPSLoRa.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #ifndef GPSLORA_H 5 | #define GPSLORA_H 6 | 7 | #include 8 | #include 9 | 10 | #ifdef ARDUINO_SAMD_MKRWAN1300 11 | #define LORA_DEFAULT_SPI SPI1 12 | #define LORA_DEFAULT_SPI_FREQUENCY 250000 13 | #define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB 14 | #define LORA_DEFAULT_RESET_PIN -1 15 | #define LORA_DEFAULT_DIO0_PIN -1 16 | #else 17 | #define LORA_DEFAULT_SPI SPI 18 | #define LORA_DEFAULT_SPI_FREQUENCY 8E6 19 | #define LORA_DEFAULT_SS_PIN 10 20 | #define LORA_DEFAULT_RESET_PIN 9 21 | #define LORA_DEFAULT_DIO0_PIN 2 22 | #endif 23 | 24 | #define PA_OUTPUT_RFO_PIN 0 25 | #define PA_OUTPUT_PA_BOOST_PIN 1 26 | 27 | class LoRaClass : public Stream { 28 | public: 29 | LoRaClass(); 30 | 31 | int begin(long frequency); 32 | void end(); 33 | 34 | int beginPacket(int implicitHeader = false); 35 | int endPacket(); 36 | 37 | int parsePacket(int size = 0); 38 | int packetRssi(); 39 | float packetSnr(); 40 | long packetFrequencyError(); 41 | 42 | // from Print 43 | virtual size_t write(uint8_t byte); 44 | virtual size_t write(const uint8_t *buffer, size_t size); 45 | 46 | // from Stream 47 | virtual int available(); 48 | virtual int read(); 49 | virtual int peek(); 50 | virtual void flush(); 51 | 52 | #ifndef ARDUINO_SAMD_MKRWAN1300 53 | void onReceive(void(*callback)(int)); 54 | 55 | void receive(int size = 0); 56 | #endif 57 | void idle(); 58 | void sleep(); 59 | 60 | void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); 61 | void setFrequency(long frequency); 62 | void setSpreadingFactor(int sf); 63 | void setSignalBandwidth(long sbw); 64 | void setCodingRate4(int denominator); 65 | void setPreambleLength(long length); 66 | void setSyncWord(int sw); 67 | void enableCrc(); 68 | void disableCrc(); 69 | 70 | // deprecated 71 | void crc() { enableCrc(); } 72 | void noCrc() { disableCrc(); } 73 | 74 | byte random(); 75 | 76 | void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); 77 | void setSPI(SPIClass& spi); 78 | void setSPIFrequency(uint32_t frequency); 79 | 80 | void dumpRegisters(Stream& out); 81 | 82 | private: 83 | void explicitHeaderMode(); 84 | void implicitHeaderMode(); 85 | 86 | void handleDio0Rise(); 87 | 88 | int getSpreadingFactor(); 89 | long getSignalBandwidth(); 90 | 91 | void setLdoFlag(); 92 | 93 | uint8_t readRegister(uint8_t address); 94 | void writeRegister(uint8_t address, uint8_t value); 95 | uint8_t singleTransfer(uint8_t address, uint8_t value); 96 | 97 | static void onDio0Rise(); 98 | 99 | private: 100 | SPISettings _spiSettings; 101 | SPIClass* _spi; 102 | int _ss; 103 | int _reset; 104 | int _dio0; 105 | long _frequency; 106 | int _packetIndex; 107 | int _implicitHeaderMode; 108 | void (*_onReceive)(int); 109 | }; 110 | 111 | extern LoRaClass LoRa; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /Licence.txt: -------------------------------------------------------------------------------- 1 | This software, the ideas and concepts is Copyright (c) David Bird 2014 and beyond. 2 | 3 | All rights to this software are reserved. 4 | 5 | It is prohibited to redistribute or reproduce of any part or all of the software contents in any form other than the following: 6 | 7 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 8 | 9 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 10 | 11 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 12 | 13 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 14 | 15 | 5. You MUST include all of this copyright and permission notice ('as annotated') and this shall be included in all copies or substantial portions of the software and where the software use is visible to an end-user. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. 18 | 19 | FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | 21 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Marc_WU_Code.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | const char* ssid = ""; 6 | const char* password = ""; 7 | 8 | bool done = false; 9 | 10 | typedef struct WGAstronomy { 11 | String moonPctIlum; 12 | String moonAge; 13 | String moonPhase; 14 | String sunriseTime; 15 | String sunsetTime; 16 | String moonriseTime; 17 | String moonsetTime; 18 | } WGAstronomy; 19 | 20 | int moon_percent; 21 | 22 | WGAstronomy astronomyData; 23 | 24 | char bufferJson[128]; 25 | 26 | // setup 27 | void setup() { 28 | Serial.begin(115200); 29 | while (!Serial) continue; 30 | 31 | Serial.println(); 32 | Serial.println("Connecting to: " + String(ssid)); 33 | WiFi.begin(ssid, password); 34 | while (WiFi.status() != WL_CONNECTED) { 35 | delay(500); 36 | Serial.print(F(".")); 37 | } 38 | Serial.println(); 39 | Serial.println("WiFi connected at: " + WiFi.localIP().toString()); 40 | Serial.println(); 41 | } 42 | 43 | 44 | // loop 45 | void loop() { 46 | if (!done) { 47 | Serial.println(F("WUnderground")); 48 | String jsonStr = Request_WUnderground(); 49 | 50 | Serial.println(); 51 | Serial.println(F("reponse: ")); 52 | jsonStr.trim(); 53 | Serial.println(jsonStr); 54 | Serial.println(); 55 | 56 | DecodeJson(jsonStr); 57 | 58 | done = true; 59 | } 60 | } 61 | 62 | String Request_WUnderground() { 63 | bool ok = false; 64 | String respString = ""; 65 | 66 | if (WiFi.status() == WL_CONNECTED) { 67 | HTTPClient http; 68 | Serial.print("[HTTP] begin...\n"); 69 | 70 | // configure server and url 71 | http.begin("http://api.wunderground.com/api/7b20aec43affb7b6/astronomy/q/CANADA/MONTREAL.json"); 72 | //http.begin("http://newsapi.org/v2/everything?q=bitcoin&apiKey=2ee8e9e7ebea498b9ce519389e71aae8"); 73 | 74 | //http.begin("192.168.1.12", 80, "/test.html"); 75 | 76 | Serial.print("[HTTP] GET...n"); 77 | // start connection and send HTTP header 78 | int httpCode = http.GET(); 79 | if (httpCode > 0) { 80 | // HTTP header has been send and Server response header has been handled 81 | Serial.printf("[HTTP] GET... code: %dn", httpCode); 82 | 83 | // file found at server 84 | if (httpCode == HTTP_CODE_OK) { 85 | respString = http.getString(); 86 | ok = true; 87 | 88 | http.end(); 89 | } 90 | } 91 | else { 92 | ok = false; 93 | } 94 | } 95 | if (ok) { 96 | Serial.println(F("ok.")); 97 | return respString; 98 | } 99 | else { 100 | Serial.println(F("nothing.")); 101 | return ""; 102 | } 103 | } 104 | 105 | void DecodeJson(String jsonStr) { 106 | const size_t bufferSize = JSON_OBJECT_SIZE(1) + 8 * JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(9) + 440; 107 | DynamicJsonBuffer jsonBuffer(bufferSize); 108 | 109 | //const char* json = "{"response":{"version":"0.1","termsofService":"http://www.wunderground.com/weather/api/d/terms.html","features":{"astronomy":1}},"moon_phase":{"percentIlluminated":"96","ageOfMoon":"17","phaseofMoon":"Waning Gibbous","hemisphere":"North","current_time":{"hour":"23","minute":"12"},"sunrise":{"hour":"6","minute":"46"},"sunset":{"hour":"18","minute":"44"},"moonrise":{"hour":"19","minute":"54"},"moonset":{"hour":"8","minute":"05"}},"sun_phase":{"sunrise":{"hour":"6","minute":"46"},"sunset":{"hour":"18","minute":"44"}}}"; 110 | 111 | const int jsonSize = jsonStr.length(); 112 | char bufferJson[jsonSize]; 113 | jsonStr.toCharArray(bufferJson, jsonSize); 114 | 115 | JsonObject& root = jsonBuffer.parseObject(jsonStr); 116 | if (!root.success()) { 117 | Serial.println(F("json parse failed.")); 118 | } 119 | else { 120 | Serial.println(F("json parse ok.")); 121 | } 122 | 123 | // Once you have a response, you can use the JSON coder see here: https://arduinojson.org/v5/assistant/ 124 | // Paste the JSON respone into the left side, it produces the code for you 125 | 126 | JsonObject& response = root["response"]; 127 | const char* response_version = response["version"]; // "0.1" 128 | const char* response_termsofService = response["termsofService"]; // "http://www.wunderground.com/weather/api/d/terms.html" 129 | 130 | int response_features_astronomy = response["features"]["astronomy"]; // 1 131 | 132 | JsonObject& moon_phase = root["moon_phase"]; 133 | int moon_phase_percentIlluminated = moon_phase["percentIlluminated"]; // "95" 134 | int moon_phase_ageOfMoon = moon_phase["ageOfMoon"]; // "17" 135 | const char* moon_phase_phaseofMoon = moon_phase["phaseofMoon"]; // "Waning Gibbous" 136 | const char* moon_phase_hemisphere = moon_phase["hemisphere"]; // "North" 137 | 138 | int moon_phase_current_time_hour = moon_phase["current_time"]["hour"]; // "4" 139 | int moon_phase_current_time_minute = moon_phase["current_time"]["minute"]; // "38" 140 | 141 | int moon_phase_sunrise_hour = moon_phase["sunrise"]["hour"]; // "6" 142 | int moon_phase_sunrise_minute = moon_phase["sunrise"]["minute"]; // "47" 143 | 144 | int moon_phase_sunset_hour = moon_phase["sunset"]["hour"]; // "18" 145 | int moon_phase_sunset_minute = moon_phase["sunset"]["minute"]; // "42" 146 | 147 | int moon_phase_moonrise_hour = moon_phase["moonrise"]["hour"]; // "20" 148 | int moon_phase_moonrise_minute = moon_phase["moonrise"]["minute"]; // "21" 149 | 150 | int moon_phase_moonset_hour = moon_phase["moonset"]["hour"]; // "9" 151 | int moon_phase_moonset_minute = moon_phase["moonset"]["minute"]; // "11" 152 | 153 | int sun_phase_sunrise_hour = root["sun_phase"]["sunrise"]["hour"]; // "6" 154 | int sun_phase_sunrise_minute = root["sun_phase"]["sunrise"]["minute"]; // "47" 155 | 156 | int sun_phase_sunset_hour = root["sun_phase"]["sunset"]["hour"]; // "18" 157 | int sun_phase_sunset_minute = root["sun_phase"]["sunset"]["minute"]; // "42" 158 | //Now you have some local variables within this function scope that you need to assign to a global variable to use elsewhere 159 | moon_percent = moon_phase_percentIlluminated; 160 | Serial.println("Moon Illumination = " + String(moon_phase_percentIlluminated)); 161 | Serial.println("Moon age = " + String(moon_phase_ageOfMoon)); 162 | Serial.println("Moon phase = " + String(moon_phase_phaseofMoon)); 163 | // And so-on 164 | } 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | General Examples 3 | -------------------------------------------------------------------------------- /Relay_Control_Example_1.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int variable1, variable2, variable3, variable4, address; 4 | 5 | void setup() { 6 | Serial.begin(115200); 7 | Serial.println("Starting..."); 8 | EEPROM.begin(64); // Only need 4 x 4-bytes (16) for the 4 32-bit integers, could uses bytes 9 | 10 | pinMode(D5, OUTPUT); // e.g. Pin D5 on ESP8266 11 | pinMode(D6, OUTPUT); // e.g. Pin D6 on ESP8266 12 | pinMode(D7, OUTPUT); // e.g. Pin D7 on ESP8266 13 | pinMode(D8, OUTPUT); // e.g. Pin D8 on ESP8266 14 | 15 | variable1 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 16 | variable2 = 0; // All Relays OFF 0 = OFF 1 = ON is the assumption 17 | variable3 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 18 | variable4 = 0; // All Relays OFF 0 = OFF 1 = ON is the assumption 19 | Save_Data(); 20 | } 21 | 22 | void loop() { 23 | Read_Data(); 24 | if (variable1 == 1) digitalWrite(D5, HIGH); else digitalWrite(D5, LOW); 25 | if (variable1 == 1) digitalWrite(D6, HIGH); else digitalWrite(D6, LOW); 26 | if (variable1 == 1) digitalWrite(D7, HIGH); else digitalWrite(D7, LOW); 27 | if (variable1 == 1) digitalWrite(D8, HIGH); else digitalWrite(D8, LOW); 28 | 29 | Serial.printf("var1: %d, var2: %d, var3: %d, var4: %d", variable1, variable2, variable3, variable4); 30 | Serial.println(); 31 | delay(10000); // wait and then do it all again 32 | Save_Data(); 33 | variable1 = 0; // Clear the values to prove it's reading back from EEPROM 34 | variable2 = 0; // Clear the values to prove it's reading back from EEPROM 35 | variable3 = 0; // Clear the values to prove it's reading back from EEPROM 36 | variable4 = 0; // Clear the values to prove it's reading back from EEPROM 37 | } 38 | 39 | void Read_Data() { 40 | Serial.println("Reading datas"); 41 | address = 0; 42 | variable1 = EEPROM.read(address); 43 | address = address + 4; 44 | variable2 = EEPROM.read(address); 45 | address = address + 4; 46 | variable3 = EEPROM.read(address); 47 | address = address + 4; 48 | variable4 = EEPROM.read(address); 49 | } 50 | 51 | void Save_Data() { 52 | Serial.println("Saving datas"); 53 | address = 0; 54 | EEPROM.write(address, variable1); 55 | address = address + 4; 56 | EEPROM.write(address, variable2); 57 | address = address + 4; 58 | EEPROM.write(address, variable3); 59 | address = address + 4; 60 | EEPROM.write(address, variable4); 61 | address = address + 4; 62 | EEPROM.commit(); 63 | } 64 | 65 | -------------------------------------------------------------------------------- /Relay_Control_Example_2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | byte variable1, variable2, variable3, variable4; 4 | int address; 5 | 6 | void setup() { 7 | Serial.begin(115200); 8 | Serial.println("Starting..."); 9 | EEPROM.begin(8); // Only need 4 bytes for the 4 8-bit numbers 10 | 11 | pinMode(D5, OUTPUT); // e.g. Pin D5 on ESP8266 12 | pinMode(D6, OUTPUT); // e.g. Pin D6 on ESP8266 13 | pinMode(D7, OUTPUT); // e.g. Pin D7 on ESP8266 14 | pinMode(D8, OUTPUT); // e.g. Pin D8 on ESP8266 15 | 16 | variable1 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 17 | variable2 = 0; // All Relays OFF 0 = OFF 1 = ON is the assumption 18 | variable3 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 19 | variable4 = 0; // All Relays OFF 0 = OFF 1 = ON is the assumption 20 | Save_Data(); 21 | } 22 | 23 | void loop() { 24 | Read_Data(); 25 | if (variable1 == 1) digitalWrite(D5, HIGH); else digitalWrite(D5, LOW); 26 | if (variable1 == 1) digitalWrite(D6, HIGH); else digitalWrite(D6, LOW); 27 | if (variable1 == 1) digitalWrite(D7, HIGH); else digitalWrite(D7, LOW); 28 | if (variable1 == 1) digitalWrite(D8, HIGH); else digitalWrite(D8, LOW); 29 | 30 | Serial.printf("var1: %d, var2: %d, var3: %d, var4: %d", variable1, variable2, variable3, variable4); 31 | Serial.println(); 32 | Save_Data(); 33 | 34 | variable1 = 0; // Clear the values to prove it's reading back from EEPROM 35 | variable2 = 0; // Clear the values to prove it's reading back from EEPROM 36 | variable3 = 0; // Clear the values to prove it's reading back from EEPROM 37 | variable4 = 0; // Clear the values to prove it's reading back from EEPROM 38 | Serial.printf("var1: %d, var2: %d, var3: %d, var4: %d", variable1, variable2, variable3, variable4); 39 | Serial.println(); 40 | 41 | delay(10000); // wait and then do it all again 42 | } 43 | 44 | void Read_Data() { 45 | Serial.println("Reading datas"); 46 | address = 0; 47 | variable1 = EEPROM.read(address); 48 | address = address + 1; 49 | variable2 = EEPROM.read(address); 50 | address = address + 1; 51 | variable3 = EEPROM.read(address); 52 | address = address + 1; 53 | variable4 = EEPROM.read(address); 54 | } 55 | 56 | void Save_Data() { 57 | Serial.println("Saving datas"); 58 | address = 0; 59 | EEPROM.write(address, variable1); 60 | address = address + 1; 61 | EEPROM.write(address, variable2); 62 | address = address + 1; 63 | EEPROM.write(address, variable3); 64 | address = address + 1; 65 | EEPROM.write(address, variable4); 66 | address = address + 1; 67 | EEPROM.commit(); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /Relay_Example_SPIFFS_1.ino: -------------------------------------------------------------------------------- 1 | #include "FS.h"; 2 | 3 | int variable1, variable2, variable3, variable4; 4 | 5 | void setup() { 6 | Serial.begin(115200); 7 | Serial.println("Starting..."); 8 | bool ok = SPIFFS.begin(); 9 | if (ok) Serial.println("Spiffs started"); else Serial.println("SPIFFS.failed"); 10 | pinMode(D5, OUTPUT); // e.g. Pin D5 on ESP8266 11 | pinMode(D6, OUTPUT); // e.g. Pin D6 on ESP8266 12 | pinMode(D7, OUTPUT); // e.g. Pin D7 on ESP8266 13 | pinMode(D8, OUTPUT); // e.g. Pin D8 on ESP8266 14 | variable1 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 15 | variable2 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 16 | variable3 = 0; // All Relays OFF 0 = OFF 1 = ON is the assumption 17 | variable4 = 1; // All Relays OFF 0 = OFF 1 = ON is the assumption 18 | Save_Data(); 19 | } 20 | 21 | void loop() { 22 | Read_Data(); 23 | // Your application amends 'varable1..variable4' as required 24 | if (variable1 == 1) digitalWrite(D5, HIGH); else digitalWrite(D5, LOW); // Switch Relay1 ON if variable = 1 otherwise OFF 25 | if (variable2 == 1) digitalWrite(D6, HIGH); else digitalWrite(D6, LOW); // Switch Relay2 ON if variable = 1 otherwise OFF 26 | if (variable3 == 1) digitalWrite(D7, HIGH); else digitalWrite(D7, LOW); // Switch Relay3 ON if variable = 1 otherwise OFF 27 | if (variable4 == 1) digitalWrite(D8, HIGH); else digitalWrite(D8, LOW); // Switch Relay4 ON if variable = 1 otherwise OFF 28 | Serial.printf("var1: %d, var2: %d, var3: %d, var4: %d", variable1, variable2, variable3, variable4); 29 | Serial.println(); 30 | Save_Data(); 31 | delay(10000); // wait and then do it all again 32 | variable1 = 0; // Clear the values to prove it's reading back from EEPROM 33 | variable2 = 0; // Clear the values to prove it's reading back from EEPROM 34 | variable3 = 0; // Clear the values to prove it's reading back from EEPROM 35 | variable4 = 0; // Clear the values to prove it's reading back from EEPROM 36 | } 37 | 38 | void Read_Data(){ 39 | char filename [] = "datalog.txt"; // Assign a filename or use the format e.g. SD.open("datalog.txt",...); 40 | String line_input; 41 | File dataFile = SPIFFS.open(filename, "r"); // Open the file again, this time for reading 42 | if (!dataFile) Serial.println("file open failed"); // Check for errors 43 | while (dataFile.available()) { 44 | line_input = dataFile.readStringUntil('\n'); variable1 = line_input.toInt(); 45 | line_input = dataFile.readStringUntil('\n'); variable2 = line_input.toInt(); 46 | line_input = dataFile.readStringUntil('\n'); variable3 = line_input.toInt(); 47 | line_input = dataFile.readStringUntil('\n'); variable4 = line_input.toInt(); 48 | } 49 | dataFile.close(); // Close the file 50 | } 51 | 52 | void Save_Data(){ 53 | // Assign a file name e.g. 'names.dat' or 'data.txt' or 'data.dat' try to use the 8.3 file naming convention format could be 'data.d' 54 | char filename [] = "datalog.txt"; // Assign a filename or use the format e.g. SD.open("datalog.txt",...); 55 | if (SPIFFS.exists(filename)) SPIFFS.remove(filename); // First in this example check to see if a file already exists, if so delete it 56 | File dataFile = SPIFFS.open(filename, "a+"); // Open a file for reading and writing (appending) 57 | if (!dataFile) Serial.println("file open failed"); // Check for errors 58 | else { 59 | dataFile.println(variable1); // Write data to file 60 | dataFile.println(variable2); // Write data to file 61 | dataFile.println(variable3); // Write data to file 62 | dataFile.println(variable4); // Write data to file 63 | dataFile.close(); // Close the file 64 | } 65 | } 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ibrahim_code_v2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include //screen lib draw 3 | #include 4 | #include 5 | #include 6 | #define MINPRESSURE 200 // define variable for FSR 7 | #define MAXPRESSURE 1000 // define variable for FSR 8 | #define ir1 A9 // define the pin for IR 9 | #define model1 1080 // define model for IR 10 | #define BLACK 0x0000 11 | #define BLUE 0x001F 12 | #define RED 0xF800 13 | #define GREEN 0x07E0 14 | #define CYAN 0x07FF 15 | #define MAGENTA 0xF81F 16 | #define YELLOW 0xFFE0 17 | #define WHITE 0xFFFF 18 | 19 | QMC5883L compass; 20 | MCUFRIEND_kbv tft; 21 | 22 | const int XP = 6, XM = A2, YP = A1, YM = 7; //240x320 ID=0x9340 23 | 24 | const int TS_LEFT = 119, TS_RT = 919, TS_TOP = 72, TS_BOT = 926; 25 | int pixel_x, pixel_y; //Touch_getXY() updates global vars 26 | 27 | //TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); 28 | Adafruit_GFX_Button p1_btn, p2_btn, p3_btn, p4_btn, p5_btn; 29 | 30 | int pr, foot, dis1, dis7, rak; //variables 31 | 32 | void setup() { 33 | Serial.begin(9600); 34 | Wire.begin(); 35 | uint16_t ID = tft.readID(); //read screen id and store it in ID variable, unsigned take only positive. 36 | Serial.print("TFT ID = 0x"); 37 | Serial.println(ID, HEX); 38 | Serial.println("Calibrate for your Touch Panel"); 39 | if (ID == 0xD3D3) ID = 0x9486; //change id 40 | tft.begin(ID); 41 | tft.setRotation(0); //PORTRAIT 42 | tft.fillScreen(BLACK); 43 | compass.init(); 44 | compass.setSamplingRate(500); 45 | } 46 | 47 | void loop() { 48 | ww: 49 | foot = analogRead(A8); // this is force sensor resistor 50 | delay(100); 51 | if (foot > 500) { getkeblah(); } 52 | } 53 | 54 | void getkeblah() { 55 | ss: 56 | tft.fillScreen(BLACK); 57 | tft.setTextSize(5); 58 | tft.setTextColor(BLUE, BLACK); 59 | tft.setCursor(60, 120); 60 | int heading = compass.readHeading(); 61 | if (heading == 0) {} 62 | else { 63 | tft.print(heading); 64 | } 65 | delay(100); 66 | if ((285 <= heading) and (heading <= 300)) { 67 | return; 68 | } 69 | Draw_Compass(heading); 70 | goto ss; 71 | } 72 | 73 | void Draw_Compass(float angle){ 74 | Draw_Compass_Rose(); 75 | dx = (diameter * cos((angle-90)*3.14/180)) + centreX; // calculate X position 76 | dy = (diameter * sin((angle-90)*3.14/180)) + centreY; // calculate Y position 77 | arrow(last_dx,last_dy, centreX, centreY, 20, 20,BLACK); // Erase last arrow 78 | arrow(dx,dy, centreX, centreY, 20, 20,CYAN); // Draw arrow in new position 79 | last_dx = dx; 80 | last_dy = dy; 81 | //Print the angle on the LCD Screen 82 | tft.setCursor(0, 0); 83 | tft.print("Angle=" + String(angle,2)); 84 | tft.setCursor(20, 0); 85 | tft.print("Heading: "); 86 | if((bearing < 22.5) || (bearing > 337.5 )) tft.print("North"); 87 | if((bearing > 22.5) && (bearing < 67.5 )) tft.print("North-East"); 88 | if((bearing > 67.5) && (bearing < 112.5 )) tft.print("East"); 89 | if((bearing > 112.5) && (bearing < 157.5 )) tft.print("South-East"); 90 | if((bearing > 157.5) && (bearing < 202.5 )) tft.print("South"); 91 | if((bearing > 202.5) && (bearing < 247.5 )) tft.print("SOuth-West"); 92 | if((bearing > 247.5) && (bearing < 292.5 )) tft.print("West"); 93 | if((bearing > 292.5) && (bearing < 337.5 )) tft.print("North-West"); 94 | 95 | //Print the approximate direction 96 | Serial.print("\nYou are heading "); 97 | if((bearing > 337.5) || (bearing < 22.5)) Serial.print("North"); 98 | if((bearing > 22.5) && (bearing < 67.5 )) Serial.print("North-East"); 99 | if((bearing > 67.5) && (bearing < 112.5 )) Serial.print("East"); 100 | if((bearing > 112.5) && (bearing < 157.5 )) Serial.print("South-East"); 101 | if((bearing > 157.5) && (bearing < 202.5 )) Serial.print("South"); 102 | if((bearing > 202.5) && (bearing < 247.5 )) Serial.print("South-West"); 103 | if((bearing > 247.5) && (bearing < 292.5 )) Serial.print("West"); 104 | if((bearing > 292.5) && (bearing < 337.5 )) Serial.print("North-West"); 105 | delay(100); 106 | } 107 | 108 | void arrow(int x2, int y2, int x1, int y1, int alength, int awidth, int colour) { 109 | float distance; 110 | int dx, dy, x2o,y2o,x3,y3,x4,y4,k; 111 | distance = sqrt(pow((x1 - x2),2) + pow((y1 - y2), 2)); 112 | dx = x2 + (x1 - x2) * alength / distance; 113 | dy = y2 + (y1 - y2) * alength / distance; 114 | k = awidth / alength; 115 | x2o = x2 - dx; 116 | y2o = dy - y2; 117 | x3 = y2o * k + dx; 118 | y3 = x2o * k + dy; 119 | // 120 | x4 = dx - y2o * k; 121 | y4 = dy - x2o * k; 122 | tft.drawLine(x1, y1, x2, y2, colour); 123 | tft.drawLine(x1, y1, dx, dy, colour); 124 | tft.drawLine(x3, y3, x4, y4, colour); 125 | tft.drawLine(x3, y3, x2, y2, colour); 126 | tft.drawLine(x2, y2, x4, y4, colour); 127 | } 128 | 129 | void Draw_Compass_Rose() { 130 | int dxo, dyo, dxi, dyi; 131 | tft.drawCircle(centreX,centreY,diameter,YELLOW); // Draw compass circle 132 | for (float i = 0; i <360; i = i + 22.5) { 133 | dxo = diameter * cos((i-90)*3.14/180); 134 | dyo = diameter * sin((i-90)*3.14/180); 135 | dxi = dxo * 0.9; 136 | dyi = dyo * 0.9; 137 | tft.drawLine(dxi+centreX,dyi+centreY,dxo+centreX,dyo+centreY,YELLOW); 138 | } 139 | display_item((centreX-5),(centreY-85),"N",RED,2); 140 | display_item((centreX-5),(centreY+70),"S",RED,2); 141 | display_item((centreX+80),(centreY-5),"E",RED,2); 142 | display_item((centreX-85),(centreY-5),"W",RED,2); 143 | } 144 | --------------------------------------------------------------------------------