Another ESP8266 Weather Station

by uiflorin in Circuits > Wireless

2100 Views, 10 Favorites, 0 Comments

Another ESP8266 Weather Station

IMG_20210113_174928.jpg

This is another weather station based on ESP8266 boards available almost everywhere. It grabs the weather data from a weather site (in this case OpenWeatherMap.org) based on an API key and then displays the available data. Bellow I will explain in short how to get the API key.

I started to look for such project and found this:

https://www.instructables.com/ESP8266-Weather-Widg...

https://www.instructables.com/ESP8266-Weather-Serv...

You should check these projects too, and learn the steps how to setup the Arduino IDE, load libraries and stuff. I will not go into details about setting up the IDE. I used two boards for this project, the ones that I have, presented in the next sections.

Get the Hardware

IMG_20210113_183755 (3).jpg
IMG_20210113_191640 (3).jpg
esp8266-wemos-d1-mini-pinout.png
d1-mini-esp8266-board-sh_fixled.jpg
Wemos_D1_mini_ESP8266.jpg

1) WEMOS D1 mini, the blue board. This board sells for about $7-$8 in Romania, ships in two days to your doorstep, no need to wait for a month to come from chinese websites. It's available for people in Romania at the following links, could be more shops, look for it:

https://ardushop.ro/ro/electronica/223-nodemcu-min...

https://cleste.ro/placa-dezvoltare-esp12-mini-v2.h...

Don't mind the names posted by the sellers, these blue boards are WEMOS D1 mini, that is the board I selected in the Arduino IDE to program. What is nice about this board is that it has the CH340 usb to TTL communicator on the back, no need for a dedicated programmer that you have to hook up, and a small (SOT23-5) 3.3V regulator, the usual AMS1117. Ofcorse, having the usb circuit makes it a bit more expensive, but then it's more convenient. I found also the schematic of this board, nicely laid out.

Get the Hardware, Second Variant

ESP8266-Witty-Cloud-Module.jpg
ESP8266-Witty-Cloud-Pinout.jpg
IMG_20210113_183755 (2).jpg

2) The second board I tried, because it was in the drawer, it's the black board, and I got it from here:
https://cleste.ro/placa-dezvoltare-nodemcu-esp-12f...

Again, don't mind the name NODEMCU ESP-12F, it's also found as Witty Cloud ESP8266, the hardware is the same as WEMOS board, but split into two boards. I pictured both boards together. The ESP8266 it's on the bottom of the picture and has 3.3V regulator to supply the ESP8266 board and not fry it with 5V. On top of the picture it's the shield board, that basically has the CH340 and the circuitry (two transistors) to pull the GPIO0 and RST as needed to program it. It has also two press buttons that basically do the same thing as the transistors, one connected to RST pin, the other to GPIO0. Ofcorse, as in the instructable projects linked above, one can try the bare ESP8266 boards, with the help of a programmer, but it will need a 3.3V regulator soldered somehow on a pcb. The shield of the NODEMCU ESP-12F it is a programmer that can be used with other boards that you might have. So if you get this board, you get a programmer too.

The pinout of the Witty Cloud is a bit different than Wemos D1 mini, as you can see in the pictures, but they both can be programmed with the same compiled code.

Get the Display

IMG_20210113_192433 (2).jpg

There is an OLED screen as a shield for WEMOS D1 mini board, to make it even more compact, but the resolution is only 64x48 pixels, so not that much space to display things on it. You can try this too, but things have to be changed arround in the libraries, select the geometry for this resolution and then change the code to display on the 64x48 geometry. It's not that complicated, one needs to understand basically where in the main loop to change the pixel coordinates to display things. Later on this.

https://ardushop.ro/ro/home/1470-d1-oled.html?sea....

The regular OLED display is 0.96inch with 128x64 pixels and this is used in the project. You will see how small is this too, but enough to get the data nicely readable. Get it here:

https://cleste.ro/ecra-oled-0-96-inch.html

I have two versions of this display, almost identical, but with a subtle difference, in case you want to pack things on a PCB. The color is a nice CYAN. There are also some bicolor versions of this display that have a horizontal band of yellow pixels on the pin side. They behave the same, just displaying yellow pixels on that region, so you can choose that region to be top or bottom of the display. Later on this.

The Connections

IMG_20210113_180450.jpg
esp8266-wemos-d1-mini-pinout.png
ESP8266-Witty-Cloud-Pinout.jpg

The connections are very simple. The display needs to get power, 5 V is fine, from the board pins 5V and GND seen in the schematic. Take a good look at the schematics.

- the Wemos D1 board has the 5V and GND on the right side, looking at the board with the antena on top. Connect these pins:

WEMOS D1 / Display

  1. 5V _______VCC
  2. GND _____GND
  3. SCL ______D1
  4. SDA ______D2

Witty Cloud / Display

  1. 5V _______VCC
  2. GND _____GND
  3. SCL _____GPIO5
  4. SDA _____GPIO4

As for the external supply, the 5V can be provided by the micro-usb port, or can be directly applied to the 5V pin.

The Code

The code is found here.

https://github.com/ThingPulse/esp8266-weather-stat...

Install the libraries as instructed, open the project in Arduino IDE, by clicking the file WeatherStationDemo.ino from the /examples folder.

First thing, get an API key from OpenWeatherMap.org, by following the instructions here:

https://docs.thingpulse.com/how-tos/openweathermap...

Then, once you have the key, you can take a look at the code. Enter the credentials to you wifi router, to allow the board to connect to the internet, and define the timezone and DST:

// WIFI
const char* WIFI_SSID = "Wifi_SSID";
const char* WIFI_PWD = "password"; 
//define timezone for fetching the correct time for your location
#define TZ        2       // (utc+) TZ in hours
#define DST_MN    0      // use 60mn for summer time in some countries

The display needs the addres specified in the code, is 0x3C by default, but there are displays that have 0x78 or 0x7A address. Usually the adress is specified in the documentation, or it's printed on the silk mask. In fact, the displays I used have 0x78-0x7A on the silk mask (switchable by a jumper resistor that can be soldered in two positions), but they work with 0x3C. I had few minutes of despair when nothing was displayed on the little OLED, thinking something is wrongly connected, or the code just deosn't run. Define the pins used for I2C bus, by default these are:

- D2 and D1 marked on WEMOS D1 mini, that correspond to GPIO5 and GPIO4 of ESP8266.

- GPIO5 and GPIO4 on the Witty CLoud board.

Is easy to follow the board traces, they go to the same pins of the ESP8266.

// Display Settings<br>const int I2C_DISPLAY_ADDRESS = 0x3c;
const int SDA_PIN = 4; //D2 or GPIO4;
const int SDC_PIN = 5; //D1 or GPIO5;

The API key should be copied here:

String OPEN_WEATHER_MAP_APP_ID = "XXXXXXXXXXXXXXXXXXXX";

There is one more thing you need to enter into the code, the city/location code from the OpenWeatherMap.org. Login on the site, search for your city, select it from the list in case there is more than one city with the same name, then once selected it will show you the weather for that location. Look into the browser address bar, it will look like this:

https://openweathermap.org/city/5128581

The number at the end is the city code (the one here is actually for New York) you need to enter into the program like in the example bellow. Also, select the language in which the API will provide the description of weather, several languages are supported and they are commented in the code. Select the maximum number of forecast days, the free API key provides maximum 4, so choose not more than 4. Choose also the data system, metric or imperial (metric=false means imperial).

String OPEN_WEATHER_MAP_LOCATION_ID = "5128581";
String OPEN_WEATHER_MAP_LANGUAGE = "en";
const uint8_t MAX_FORECASTS = 4;
const boolean IS_METRIC = true;

There is also possible to set the days and month in your language, but they have to be specified as text in the code, like this (here are in romanian language), Sunday being the first:

// Adjust according to your language

const String WDAY_NAMES[] = {"DUM", "LUN", "MAR", "MIE", "JOI", "VIN", "SAM"};
const String MONTH_NAMES[] = {"JAN", "FEB", "MAR", "APR", "MAI", "IUN", "IUL", "AUG", "SEP", "OCT", "NOV", "DEC"};

I discovered that the board creates its own hot spot, I tried to connect with the smartphone to it, but it doesn't respond, so I found on internet that it can be turned OFF by adding a Wifi.mode(WIFI.STA) command just before the WiFi.begin. This will reduce the power consumption from 70mA to 20mA, with ocasional jump to 70mA when it grabs data from the internet. Nice trick.

WiFi.mode(WIFI_STA); //turn off access point of esp8266
WiFi.begin(WIFI_SSID, WIFI_PWD);

The screen can be flipped, to show the image in any orientation you desire. Remember what I said about the displays with yellow band, this can be usefull to show the band at the top or at the bottom. The libraries are flipping the screen by default I think, because I remember from other projects that normal orientation of the display is with pins at top side. In the original code there is this function display.flipScreenVertically() that is placed too early in the code, because the ui.init() function will reintialise the whole thing. So, add it here, you will find the place in the main loop.

// Inital UI takes care of initalising the display too.

ui.init();
  display.flipScreenVertically();
  Serial.println("");

At this point everything is ready to go. The code should run.

Program the Board

Arduino_choose_board.jpg

Select the board and the options as in the picture.

- Board is WeMos D1 R1

- Upload speed = 921600. I tried higher, it doesn't work, but it's just few seconds to dowload the entire code to the flash memory

- CPU frequency = 80MHz

- Flash size= 4MB (File system 3MB, OTA 512 kB). Remember, when use OTA, the program size is half of what's available, because the new program needs space to be downloaded, then copied as actual program. It's not the case here, the OTA is not implemented.

- Port: click on it to choose the actual port on wich the device is seen. That's if you installed the driver for the CH340 chip and the windows OS is seeing it correctly on a virtual COM port.

If the compilign fails, there are few things you can do. People not very familiar with IDE keep asking what's wrong with the code, why it doesn't work. Well, you need to figure that out for yourself, a lot of things cand be different on your system. Scroll on the debug window at the bottom of the IDE. Usually there is a message there saying what's wrong. It can be a missing library that contains functions called in the code and these functions are not understood, being seen as "not declared" or something. Be sure you have all the "include" libraries at the top.

Wrong sintax is also detected and logged with "expecting ... before/after", where the expected character is a coma or something. Lots of mistakes happen when trying to modify and add something new by mimicking existing code. Anyways, if you succed to upload the code, next you can try new things.

Change the Code to Suit Your Needs

Arduino_project.jpg
Arduino_project_add_file.jpg

The characteristic of this code is that it displays the parameters pages (or frames, as they are called in the code) for a time, then swipes them LEFT or RIGHT, with a timing that can be set with parameters in the main loop. What can you do:

1) create new pages. Here you set the number of frames and declare the frames as you wish. The number of pages is not fixed, it can be changed as you like in this section:

//declaring prototypes
void drawProgress(OLEDDisplay *display, int percentage, String label);
void updateData(OLEDDisplay *display);
void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawCurrentWeather2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawCurrentWeather3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex); void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state); void setReadyForWeatherUpdate(); // Add frames // this array keeps function pointers to all frames // frames are the single views that slide from right to left
FrameCallback frames[] = { drawDateTime, drawCurrentWeather, drawCurrentWeather2, drawCurrentWeather3, drawForecast };
int numberOfFrames = 5;

With bold I marked the added pages similar to the original one. Just name it differently, then place the name in the FrameCallback.

Here are the pages I created to show other parameters:

void drawCurrentWeather2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {

// this displays the weather icon starting at x=24 and y=0 pixels on the display
// centered horizontally relative to this position
// the origin is in the upper left corner
  display->setFont(Meteocons_Plain_21);
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->drawString(24 + x, 0 + y, currentWeather.iconMeteoCon);
  
//this displays the string "hum: " humidity " %", where the text delimited
//by commas is explicit, while the humidity read from the internet is a parameter
//called by the function currentWeather.humidity. All the parameters that can be called
//are found in OpenWeatherMapCurrent.cpp and OpenWeatherMapForecast.cpp. Add these together
//their .h files, as seen in the picture.
 
  display->setFont(ArialMT_Plain_10);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  String humidity = ("hum: ") + String(currentWeather.humidity) + (" %");
  display->drawString(2 + x, 38 + y, humidity);
  
  display->setFont(ArialMT_Plain_10);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  String pressure = String(currentWeather.pressure) + " mBar";
  display->drawString(0 + x, 26 + y, pressure);
  
  display->setFont(ArialMT_Plain_10);
  display->setTextAlignment(TEXT_ALIGN_RIGHT);
  String feel = ("Feels like");
  display->drawString(128 + x, 0 + y, feel); 

  display->setFont(ArialMT_Plain_24);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  String feelsLike = String(currentWeather.feelsLike, 1) + (IS_METRIC ? "°C" : "°F");
  display->drawString(60 + x, 14 + y, feelsLike);
  
}
void drawCurrentWeather3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {

  display->setFont(ArialMT_Plain_16);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  String windspeed = ("wind: ") + String(currentWeather.windSpeed) + (" m/s ");
  display->drawString(2 + x, 2 + y, windspeed);

  display->setFont(ArialMT_Plain_16);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  String winddir = (" dir: ") + String(currentWeather.windDeg) +("°");
  display->drawString(2 + x, 20 + y, winddir);

I leave you the pleasure of understanding, for the novices, what these things display. Experiment, call and display new data from the string already downloaded from the API.

Change the Timing for Visual Effects

IMG_20210113_174935.jpg
IMG_20210113_175255.jpg

Here is the section where you can change few things:

  ui.setTargetFPS(100); //this is the equivalent fps for frame transition<br>
  ui.setActiveSymbol(activeSymbole);
  ui.setInactiveSymbol(inactiveSymbole);

  // You can change this to
  // TOP, LEFT, BOTTOM, RIGHT
  ui.setIndicatorPosition(BOTTOM);

  // Defines where the first frame is located in the bar.
  ui.setIndicatorDirection(LEFT_RIGHT);

  // You can change the transition that is used
  // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN
  ui.setFrameAnimation(SLIDE_LEFT);

  ui.setFrames(frames, numberOfFrames);
  ui.setTimePerFrame(5000); //this is the time per each frame in miliseconds
  ui.setTimePerTransition(2000); //this is the transition time from one frame to another

The target fps sets the smoothness of the animation.

SetframeAnimation defines the swiping direction, change accordingly the way you like to swipe.

SetTimePerFrame (5000) sets the time in miliseconds that a frame is displayed, here is 5 seconds. SetTimePerTransition (2000) sets the time that multiplied with fps/100 is the transition time. Experiment with these numbers to get what you like.

Good luck! This a fun project even for a novice.