Skip to content

Commit 5c3ebf3

Browse files
committed
Added extended weather station demo with WifiManager
1 parent 6f7bf91 commit 5c3ebf3

File tree

4 files changed

+1748
-0
lines changed

4 files changed

+1748
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
.DS_Store
3+
4+
.DS_Store
5+
6+
examples/.DS_Store
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
/**The MIT License (MIT)
2+
3+
Copyright (c) 2016 by Daniel Eichhorn
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
23+
See more at http://blog.squix.org
24+
*/
25+
26+
#include <ESP8266WiFi.h>
27+
#include <Ticker.h>
28+
#include <JsonListener.h>
29+
#include <ArduinoOTA.h>
30+
#include <ESP8266mDNS.h>
31+
32+
#include <DNSServer.h>
33+
#include <ESP8266WebServer.h>
34+
#include <WiFiManager.h>
35+
36+
#include "SSD1306Wire.h"
37+
#include "OLEDDisplayUi.h"
38+
#include "Wire.h"
39+
#include "WundergroundClient.h"
40+
#include "WeatherStationFonts.h"
41+
#include "WeatherStationImages.h"
42+
#include "TimeClient.h"
43+
#include "ThingspeakClient.h"
44+
45+
46+
/***************************
47+
* Begin Settings
48+
**************************/
49+
// Please read http://blog.squix.org/weatherstation-getting-code-adapting-it
50+
// for setup instructions.
51+
// In addition to the "normal" libraries you also need to install the WifiManager fro tzapu
52+
53+
#define HOSTNAME "ESP8266-OTA-"
54+
55+
// Setup
56+
const int UPDATE_INTERVAL_SECS = 10 * 60; // Update every 10 minutes
57+
58+
// Display Settings
59+
const int I2C_DISPLAY_ADDRESS = 0x3c;
60+
const int SDA_PIN = 0;
61+
const int SDC_PIN = 2;
62+
63+
// TimeClient settings
64+
const float UTC_OFFSET = 2;
65+
66+
// Wunderground Settings
67+
const boolean IS_METRIC = true;
68+
const String WUNDERGRROUND_API_KEY = "API_KEY";
69+
const String WUNDERGRROUND_LANGUAGE = "EN";
70+
const String WUNDERGROUND_COUNTRY = "CH";
71+
const String WUNDERGROUND_CITY = "Zurich";
72+
73+
//Thingspeak Settings
74+
const String THINGSPEAK_CHANNEL_ID = "67284";
75+
const String THINGSPEAK_API_READ_KEY = "L2VIW20QVNZJBLAK";
76+
77+
// Initialize the oled display for address 0x3c
78+
// sda-pin=14 and sdc-pin=12
79+
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);
80+
OLEDDisplayUi ui( &display );
81+
82+
/***************************
83+
* End Settings
84+
**************************/
85+
86+
TimeClient timeClient(UTC_OFFSET);
87+
88+
// Set to false, if you prefere imperial/inches, Fahrenheit
89+
WundergroundClient wunderground(IS_METRIC);
90+
91+
ThingspeakClient thingspeak;
92+
93+
// flag changed in the ticker function every 10 minutes
94+
bool readyForWeatherUpdate = false;
95+
96+
String lastUpdate = "--";
97+
98+
Ticker ticker;
99+
100+
//declaring prototypes
101+
void configModeCallback (WiFiManager *myWiFiManager);
102+
void drawProgress(OLEDDisplay *display, int percentage, String label);
103+
void drawOtaProgress(unsigned int, unsigned int);
104+
void updateData(OLEDDisplay *display);
105+
void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
106+
void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
107+
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
108+
void drawThingspeak(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
109+
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex);
110+
void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
111+
void setReadyForWeatherUpdate();
112+
int8_t getWifiQuality();
113+
114+
115+
// Add frames
116+
// this array keeps function pointers to all frames
117+
// frames are the single views that slide from right to left
118+
FrameCallback frames[] = { drawDateTime, drawCurrentWeather, drawForecast, drawThingspeak };
119+
int numberOfFrames = 4;
120+
121+
OverlayCallback overlays[] = { drawHeaderOverlay };
122+
int numberOfOverlays = 1;
123+
124+
void setup() {
125+
// Turn On VCC
126+
//pinMode(D4, OUTPUT);
127+
//digitalWrite(D4, HIGH);
128+
Serial.begin(115200);
129+
130+
// initialize dispaly
131+
display.init();
132+
display.clear();
133+
display.display();
134+
135+
//display.flipScreenVertically();
136+
display.setFont(ArialMT_Plain_10);
137+
display.setTextAlignment(TEXT_ALIGN_CENTER);
138+
display.setContrast(255);
139+
140+
//WiFiManager
141+
//Local intialization. Once its business is done, there is no need to keep it around
142+
WiFiManager wifiManager;
143+
// Uncomment for testing wifi manager
144+
//wifiManager.resetSettings();
145+
wifiManager.setAPCallback(configModeCallback);
146+
147+
//or use this for auto generated name ESP + ChipID
148+
wifiManager.autoConnect();
149+
150+
//Manual Wifi
151+
//WiFi.begin(WIFI_SSID, WIFI_PWD);
152+
String hostname(HOSTNAME);
153+
hostname += String(ESP.getChipId(), HEX);
154+
WiFi.hostname(hostname);
155+
156+
157+
int counter = 0;
158+
while (WiFi.status() != WL_CONNECTED) {
159+
delay(500);
160+
Serial.print(".");
161+
display.clear();
162+
display.drawString(64, 10, "Connecting to WiFi");
163+
display.drawXbm(46, 30, 8, 8, counter % 3 == 0 ? activeSymbol : inactiveSymbol);
164+
display.drawXbm(60, 30, 8, 8, counter % 3 == 1 ? activeSymbol : inactiveSymbol);
165+
display.drawXbm(74, 30, 8, 8, counter % 3 == 2 ? activeSymbol : inactiveSymbol);
166+
display.display();
167+
168+
counter++;
169+
}
170+
171+
ui.setTargetFPS(30);
172+
173+
//Hack until disableIndicator works:
174+
//Set an empty symbol
175+
ui.setActiveSymbol(emptySymbol);
176+
ui.setInactiveSymbol(emptySymbol);
177+
178+
ui.disableIndicator();
179+
180+
// You can change the transition that is used
181+
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN
182+
ui.setFrameAnimation(SLIDE_LEFT);
183+
184+
ui.setFrames(frames, numberOfFrames);
185+
186+
ui.setOverlays(overlays, numberOfOverlays);
187+
188+
// Inital UI takes care of initalising the display too.
189+
ui.init();
190+
191+
// Setup OTA
192+
Serial.println("Hostname: " + hostname);
193+
ArduinoOTA.setHostname((const char *)hostname.c_str());
194+
ArduinoOTA.onProgress(drawOtaProgress);
195+
ArduinoOTA.begin();
196+
197+
updateData(&display);
198+
199+
ticker.attach(UPDATE_INTERVAL_SECS, setReadyForWeatherUpdate);
200+
201+
}
202+
203+
void loop() {
204+
205+
if (readyForWeatherUpdate && ui.getUiState()->frameState == FIXED) {
206+
updateData(&display);
207+
}
208+
209+
int remainingTimeBudget = ui.update();
210+
211+
if (remainingTimeBudget > 0) {
212+
// You can do some work here
213+
// Don't do stuff if you are below your
214+
// time budget.
215+
ArduinoOTA.handle();
216+
delay(remainingTimeBudget);
217+
}
218+
219+
220+
}
221+
222+
void configModeCallback (WiFiManager *myWiFiManager) {
223+
Serial.println("Entered config mode");
224+
Serial.println(WiFi.softAPIP());
225+
//if you used auto generated SSID, print it
226+
Serial.println(myWiFiManager->getConfigPortalSSID());
227+
display.clear();
228+
display.setTextAlignment(TEXT_ALIGN_CENTER);
229+
display.setFont(ArialMT_Plain_10);
230+
display.drawString(64, 10, "Wifi Manager");
231+
display.drawString(64, 20, "Please connect to AP");
232+
display.drawString(64, 30, myWiFiManager->getConfigPortalSSID());
233+
display.drawString(64, 40, "To setup Wifi Configuration");
234+
display.display();
235+
}
236+
237+
void drawProgress(OLEDDisplay *display, int percentage, String label) {
238+
display->clear();
239+
display->setTextAlignment(TEXT_ALIGN_CENTER);
240+
display->setFont(ArialMT_Plain_10);
241+
display->drawString(64, 10, label);
242+
display->drawProgressBar(2, 28, 124, 10, percentage);
243+
display->display();
244+
}
245+
246+
void drawOtaProgress(unsigned int progress, unsigned int total) {
247+
display.clear();
248+
display.setTextAlignment(TEXT_ALIGN_CENTER);
249+
display.setFont(ArialMT_Plain_10);
250+
display.drawString(64, 10, "OTA Update");
251+
display.drawProgressBar(2, 28, 124, 10, progress / (total / 100));
252+
display.display();
253+
}
254+
255+
void updateData(OLEDDisplay *display) {
256+
drawProgress(display, 10, "Updating time...");
257+
timeClient.updateTime();
258+
drawProgress(display, 30, "Updating conditions...");
259+
wunderground.updateConditions(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
260+
drawProgress(display, 50, "Updating forecasts...");
261+
wunderground.updateForecast(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
262+
drawProgress(display, 80, "Updating thingspeak...");
263+
thingspeak.getLastChannelItem(THINGSPEAK_CHANNEL_ID, THINGSPEAK_API_READ_KEY);
264+
lastUpdate = timeClient.getFormattedTime();
265+
readyForWeatherUpdate = false;
266+
drawProgress(display, 100, "Done...");
267+
delay(1000);
268+
}
269+
270+
271+
272+
void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
273+
display->setTextAlignment(TEXT_ALIGN_CENTER);
274+
display->setFont(ArialMT_Plain_10);
275+
String date = wunderground.getDate();
276+
int textWidth = display->getStringWidth(date);
277+
display->drawString(64 + x, 5 + y, date);
278+
display->setFont(ArialMT_Plain_24);
279+
String time = timeClient.getFormattedTime();
280+
textWidth = display->getStringWidth(time);
281+
display->drawString(64 + x, 15 + y, time);
282+
display->setTextAlignment(TEXT_ALIGN_LEFT);
283+
}
284+
285+
void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
286+
display->setFont(ArialMT_Plain_10);
287+
display->setTextAlignment(TEXT_ALIGN_LEFT);
288+
display->drawString(60 + x, 5 + y, wunderground.getWeatherText());
289+
290+
display->setFont(ArialMT_Plain_24);
291+
String temp = wunderground.getCurrentTemp() + "°C";
292+
display->drawString(60 + x, 15 + y, temp);
293+
int tempWidth = display->getStringWidth(temp);
294+
295+
display->setFont(Meteocons_Plain_42);
296+
String weatherIcon = wunderground.getTodayIcon();
297+
int weatherIconWidth = display->getStringWidth(weatherIcon);
298+
display->drawString(32 + x - weatherIconWidth / 2, 05 + y, weatherIcon);
299+
}
300+
301+
302+
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
303+
drawForecastDetails(display, x, y, 0);
304+
drawForecastDetails(display, x + 44, y, 2);
305+
drawForecastDetails(display, x + 88, y, 4);
306+
}
307+
308+
void drawThingspeak(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
309+
display->setTextAlignment(TEXT_ALIGN_CENTER);
310+
display->setFont(ArialMT_Plain_10);
311+
display->drawString(64 + x, 0 + y, "Outdoor");
312+
display->setFont(ArialMT_Plain_16);
313+
display->drawString(64 + x, 10 + y, thingspeak.getFieldValue(0) + "°C");
314+
display->drawString(64 + x, 30 + y, thingspeak.getFieldValue(1) + "%");
315+
}
316+
317+
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex) {
318+
display->setTextAlignment(TEXT_ALIGN_CENTER);
319+
display->setFont(ArialMT_Plain_10);
320+
String day = wunderground.getForecastTitle(dayIndex).substring(0, 3);
321+
day.toUpperCase();
322+
display->drawString(x + 20, y, day);
323+
324+
display->setFont(Meteocons_Plain_21);
325+
display->drawString(x + 20, y + 12, wunderground.getForecastIcon(dayIndex));
326+
327+
display->setFont(ArialMT_Plain_10);
328+
display->drawString(x + 20, y + 34, wunderground.getForecastLowTemp(dayIndex) + "|" + wunderground.getForecastHighTemp(dayIndex));
329+
display->setTextAlignment(TEXT_ALIGN_LEFT);
330+
}
331+
332+
void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
333+
display->setColor(WHITE);
334+
display->setFont(ArialMT_Plain_10);
335+
display->setTextAlignment(TEXT_ALIGN_LEFT);
336+
display->drawString(0, 54, String(state->currentFrame + 1) + "/" + String(numberOfFrames));
337+
338+
String time = timeClient.getFormattedTime().substring(0, 5);
339+
display->setTextAlignment(TEXT_ALIGN_CENTER);
340+
display->drawString(38, 54, time);
341+
342+
display->setTextAlignment(TEXT_ALIGN_CENTER);
343+
String temp = wunderground.getCurrentTemp() + "°C";
344+
display->drawString(90, 54, temp);
345+
346+
int8_t quality = getWifiQuality();
347+
for (int8_t i = 0; i < 4; i++) {
348+
for (int8_t j = 0; j < 2 * (i + 1); j++) {
349+
if (quality > i * 25 || j == 0) {
350+
display->setPixel(120 + 2 * i, 63 - j);
351+
}
352+
}
353+
}
354+
355+
356+
display->setTextAlignment(TEXT_ALIGN_CENTER);
357+
display->setFont(Meteocons_Plain_10);
358+
String weatherIcon = wunderground.getTodayIcon();
359+
int weatherIconWidth = display->getStringWidth(weatherIcon);
360+
display->drawString(64, 55, weatherIcon);
361+
362+
display->drawHorizontalLine(0, 52, 128);
363+
364+
}
365+
366+
// converts the dBm to a range between 0 and 100%
367+
int8_t getWifiQuality() {
368+
int32_t dbm = WiFi.RSSI();
369+
if(dbm <= -100) {
370+
return 0;
371+
} else if(dbm >= -50) {
372+
return 100;
373+
} else {
374+
return 2 * (dbm + 100);
375+
}
376+
}
377+
378+
void setReadyForWeatherUpdate() {
379+
Serial.println("Setting readyForUpdate to true");
380+
readyForWeatherUpdate = true;
381+
}

0 commit comments

Comments
 (0)