Migrate
BIN
Bitmaps/Clear.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
Bitmaps/Drizzle.png
Normal file
After Width: | Height: | Size: 382 B |
BIN
Bitmaps/Fog.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
Bitmaps/Hail.png
Normal file
After Width: | Height: | Size: 482 B |
BIN
Bitmaps/Rain.png
Normal file
After Width: | Height: | Size: 436 B |
BIN
Bitmaps/Snow.png
Normal file
After Width: | Height: | Size: 406 B |
BIN
Bitmaps/Thunderstorms.png
Normal file
After Width: | Height: | Size: 370 B |
207
D1_mini_weather_station.ino
Normal file
@ -0,0 +1,207 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <Wire.h>
|
||||
#include <NTPClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
#include "bitmaps.h"
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define OLED_RESET -1
|
||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||
WiFiClient client;
|
||||
|
||||
// set Wi-Fi SSID and password
|
||||
const char *ssid = "wifinetwork";
|
||||
const char *password = "12345678";
|
||||
|
||||
// Define NTP Client to get time
|
||||
WiFiUDP ntpUDP;
|
||||
NTPClient timeClient(ntpUDP, "pool.ntp.org");
|
||||
|
||||
// set location and API key
|
||||
const String Location = "Osijek";
|
||||
const String API_Key = "someapikey";
|
||||
int icon = 0;
|
||||
|
||||
void capitalizeFirstLetter(char* str) {
|
||||
if (str != nullptr && *str != '\0') {
|
||||
// Create a copy of the string
|
||||
char temp[48];
|
||||
strcpy(temp, str);
|
||||
|
||||
// Capitalize the first letter
|
||||
temp[0] = toupper(temp[0]);
|
||||
|
||||
// Copy the modified string back to the original pointer
|
||||
strcpy(str, temp);
|
||||
}
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
|
||||
delay(1000);
|
||||
display.clearDisplay(); // clear the display buffer
|
||||
display.display();
|
||||
display.setTextColor(WHITE, BLACK);
|
||||
display.setTextSize(1);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.disconnect();
|
||||
WiFi.begin(ssid, password);
|
||||
display.setCursor(0, 0);
|
||||
display.print("Connecting");
|
||||
display.display();
|
||||
int cnt = 0;
|
||||
while ( WiFi.status() != WL_CONNECTED )
|
||||
{
|
||||
if (cnt == 3) {
|
||||
cnt = 0;
|
||||
display.clearDisplay();
|
||||
display.setCursor(0, 0);
|
||||
display.print("Connecting");
|
||||
display.display();
|
||||
} else {
|
||||
display.print(".");
|
||||
display.display();
|
||||
cnt++;
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.persistent(true);
|
||||
display.print("\nconnected");
|
||||
display.display();
|
||||
timeClient.begin();
|
||||
delay(500);
|
||||
timeClient.setTimeOffset(3600);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
|
||||
timeClient.update();
|
||||
// PowerSave
|
||||
int currentHour = timeClient.getHours();
|
||||
if (currentHour >= 23 || currentHour < 6) {
|
||||
display.clearDisplay();
|
||||
display.display();
|
||||
display.ssd1306_command(SSD1306_DISPLAYOFF);
|
||||
delay(300000);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
display.ssd1306_command(SSD1306_DISPLAYON);
|
||||
}
|
||||
//Get a time structure
|
||||
time_t epochTime = timeClient.getEpochTime();
|
||||
struct tm *ptm = gmtime ((time_t *)&epochTime);
|
||||
int currentDay = ptm->tm_mday;
|
||||
int currentMonth = ptm->tm_mon + 1;
|
||||
int currentYear = ptm->tm_year + 1900;
|
||||
HTTPClient http; //Declare an object of class HTTPClient
|
||||
|
||||
// specify request destination
|
||||
http.begin(client, "http://api.openweathermap.org/data/2.5/weather?q=" + Location + "&APPID=" + API_Key); // !!
|
||||
int httpCode = http.GET(); // send the request
|
||||
if (httpCode > 0) {
|
||||
String payload = http.getString(); //Get the request response payload
|
||||
|
||||
DynamicJsonBuffer jsonBuffer(1024);
|
||||
|
||||
// Parse JSON object
|
||||
JsonObject& root = jsonBuffer.parseObject(payload);
|
||||
if (!root.success()) {
|
||||
return;
|
||||
}
|
||||
http.end(); //Close connection
|
||||
const char* state = root["weather"][0]["description"]; // get status as string
|
||||
capitalizeFirstLetter(const_cast<char*>(state)); // Capitalize the first letter
|
||||
float temp = (float)(root["main"]["temp"]) - 273.15; // get temperature in °C
|
||||
int humidity = root["main"]["humidity"]; // get humidity in %
|
||||
float pressure = (float)(root["main"]["pressure"]) / 1000; // get pressure in bar
|
||||
float wind_speed = root["wind"]["speed"]; // get wind speed in m/s
|
||||
int statusSize = 2;
|
||||
if (strlen(state) > 12) {
|
||||
statusSize = 1;
|
||||
}
|
||||
// print data
|
||||
display.clearDisplay(); // clear the display buffer
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, 0);
|
||||
display.printf("%d-%d-%d\r\n", currentYear, currentMonth, currentDay);
|
||||
display.setCursor(0, 9);
|
||||
display.printf("%s",Location);
|
||||
display.setTextSize(statusSize);
|
||||
display.setCursor(0, 21);
|
||||
display.printf("%s\r\n", state);
|
||||
display.setCursor(0, 39);
|
||||
display.setTextSize(1);
|
||||
display.printf(" %5.2f C %d%%\r\n", temp, humidity);
|
||||
display.drawRect(43, 39, 3, 3, WHITE); // put degree symbol ( ° )
|
||||
display.setCursor(0, 52);
|
||||
display.printf(" %.3fbar %.1fm/s \r\n", pressure, wind_speed);
|
||||
display.drawLine(0, 18, 127, 18, 1);
|
||||
display.drawLine(65, 18, 65, 0, 1);
|
||||
display.drawBitmap(0, 38, temperature_icon, 10, 10, WHITE);
|
||||
display.drawBitmap(74, 38, humidity_icon, 10, 10, WHITE);
|
||||
display.drawBitmap(0, 51, pressure_icon, 10, 10, WHITE);
|
||||
display.drawBitmap(74, 51, wind_icon, 10, 10, WHITE);
|
||||
display.display();
|
||||
}
|
||||
|
||||
// specify request destination
|
||||
http.begin(client, "http://api.openweathermap.org/data/2.5/forecast?q=" + Location + "&APPID=" + API_Key + "&cnt=3"); // !!
|
||||
httpCode = http.GET(); // send the request
|
||||
if (httpCode > 0) {
|
||||
String payload = http.getString(); //Get the request response payload
|
||||
DynamicJsonBuffer jsonBuffer(1024);
|
||||
|
||||
// Parse JSON object
|
||||
JsonObject& forecast = jsonBuffer.parseObject(payload);
|
||||
if (!forecast.success()) {
|
||||
return;
|
||||
}
|
||||
http.end(); //Close connection
|
||||
|
||||
// State id info
|
||||
// 200-232 Thunderstorm
|
||||
// 300-321 Drizzle
|
||||
// 500-531 Rain
|
||||
// 600-622 Snow
|
||||
// 701-781 Mist
|
||||
// 800 Clear
|
||||
// 801-804 Clouds
|
||||
int pos = 69;
|
||||
for (int day = 0; day <= 2; day++) {
|
||||
int state = forecast["list"][day]["weather"][0]["id"]; // get state id
|
||||
// Display bitmap based on state_id
|
||||
if (state >= 200 && state <= 232) {
|
||||
icon = 6; // Thunderstorm
|
||||
} else if (state >= 300 && state <= 321) {
|
||||
icon = 5; // Drizzle
|
||||
} else if (state >= 500 && state <= 531) {
|
||||
icon = 3; // Rain
|
||||
} else if (state >= 600 && state <= 622) {
|
||||
icon = 4; // Snow
|
||||
} else if (state >= 701 && state <= 781) {
|
||||
icon = 2; // Mist
|
||||
} else if (state == 800) {
|
||||
icon = 0; // Clear
|
||||
} else if (state >= 801 && state <= 804) {
|
||||
icon = 1; // Clouds
|
||||
} else {
|
||||
// Handle unknown state_id
|
||||
icon = 0; // Default to Clear icon
|
||||
}
|
||||
display.drawBitmap(pos, 0, bitmap_icons[icon], 16, 16, WHITE);
|
||||
pos = pos + 20;
|
||||
display.display();
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(1800000);
|
||||
}
|
71
bitmaps.h
Normal file
@ -0,0 +1,71 @@
|
||||
// 'clear', 16x16px
|
||||
const unsigned char bitmap_clear [] PROGMEM = {
|
||||
0x01, 0x80, 0x01, 0x80, 0x31, 0x8c, 0x38, 0x1c, 0x1f, 0xf8, 0x0f, 0xf0, 0x0c, 0x30, 0xec, 0x37,
|
||||
0xec, 0x37, 0x0c, 0x30, 0x0f, 0xf0, 0x1f, 0xf8, 0x38, 0x1c, 0x31, 0x8c, 0x01, 0x80, 0x01, 0x80
|
||||
};
|
||||
|
||||
// 'cloudy', 16x16px
|
||||
const unsigned char bitmap_cloudy [] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x03, 0xf8, 0x0f, 0x9e, 0x1f, 0xcf, 0x78, 0xf3,
|
||||
0xf0, 0x7b, 0xc0, 0x1f, 0xc0, 0x1f, 0xff, 0xf8, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
// 'fog', 16x16px
|
||||
const unsigned char bitmap_fog [] PROGMEM = {
|
||||
0x00, 0x00, 0x78, 0x1e, 0xff, 0xff, 0xef, 0xf7, 0x00, 0x00, 0xfe, 0x7f, 0xff, 0xff, 0x03, 0xc0,
|
||||
0x7c, 0x3e, 0xff, 0xff, 0xcf, 0xf3, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0x00, 0x00
|
||||
};
|
||||
|
||||
// 'rain', 16x16px
|
||||
const unsigned char bitmap_rain [] PROGMEM = {
|
||||
0x0f, 0x80, 0x1f, 0xc0, 0x38, 0xf8, 0x70, 0x7c, 0xf0, 0x0c, 0xe0, 0x0f, 0xc0, 0x07, 0xc0, 0x03,
|
||||
0xc0, 0x03, 0xff, 0xff, 0x7f, 0xfe, 0x66, 0x33, 0xe6, 0x73, 0xff, 0xff, 0xff, 0xfe, 0x73, 0x38
|
||||
};
|
||||
|
||||
// 'snow', 16x16px
|
||||
const unsigned char bitmap_snow [] PROGMEM = {
|
||||
0x01, 0x80, 0x07, 0xe0, 0x1f, 0xf8, 0x7b, 0xde, 0x79, 0x9e, 0x7d, 0xbe, 0x7f, 0xfe, 0x67, 0xe6,
|
||||
0x67, 0xe6, 0x7f, 0xfe, 0x7d, 0xbe, 0x79, 0x9e, 0x7b, 0xde, 0x1f, 0xf8, 0x07, 0xe0, 0x01, 0x80
|
||||
};
|
||||
|
||||
// 'thunderstorm', 16x16px
|
||||
const unsigned char bitmap_thunderstorm [] PROGMEM = {
|
||||
0x07, 0xf0, 0x06, 0x60, 0x0e, 0x60, 0x0c, 0xc0, 0x0c, 0xf8, 0x18, 0xfc, 0x18, 0x18, 0x38, 0x38,
|
||||
0x3f, 0xf0, 0x1f, 0xe0, 0x03, 0xe0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x07, 0x00
|
||||
};
|
||||
|
||||
// 'drizzle', 16x16px
|
||||
const unsigned char bitmap_drizzle [] PROGMEM = {
|
||||
0x00, 0x00, 0x06, 0x00, 0x36, 0xc0, 0x3f, 0x80, 0x1f, 0xf0, 0x7b, 0xb8, 0x1e, 0x18, 0x18, 0x0e,
|
||||
0x18, 0x06, 0x19, 0x32, 0x0f, 0xfe, 0x07, 0xfc, 0x01, 0xf0, 0x03, 0xf0, 0x00, 0xc0, 0x00, 0x00
|
||||
};
|
||||
|
||||
// Array of all bitmaps for convenience
|
||||
const unsigned char* bitmap_icons[7] = {
|
||||
bitmap_clear,
|
||||
bitmap_cloudy,
|
||||
bitmap_fog,
|
||||
bitmap_rain,
|
||||
bitmap_snow,
|
||||
bitmap_drizzle,
|
||||
bitmap_thunderstorm,
|
||||
};
|
||||
|
||||
const unsigned char temperature_icon [] PROGMEM = {
|
||||
0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x3f, 0x00,
|
||||
0x3f, 0x00, 0x1e, 0x00
|
||||
};
|
||||
|
||||
const unsigned char humidity_icon [] PROGMEM = {
|
||||
0x0c, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x3f, 0x00, 0x7f, 0x80, 0x7f, 0x80, 0x7f, 0x80, 0x7f, 0x80,
|
||||
0x3f, 0x00, 0x3f, 0x00
|
||||
};
|
||||
|
||||
const unsigned char wind_icon [] PROGMEM = {
|
||||
0x00, 0x00, 0x07, 0x00, 0x07, 0x80, 0xff, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x07, 0x80,
|
||||
0x07, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char pressure_icon [] PROGMEM = {
|
||||
0x1e, 0x00, 0x3f, 0x00, 0x73, 0x80, 0xe7, 0xc0, 0xce, 0xc0, 0xdc, 0xc0, 0xe9, 0xc0, 0x73, 0x80,
|
||||
0x3f, 0x00, 0x1e, 0x00
|
||||
};
|