top of page

ESP32 Internet Weather with 3.5inch TFT Display


Here to learn how to make an IOT based internet weather station with TFT Display.

The ESP32 can access the internet and gets weather data from www.openweathermap.org that provide Free/Paid weather information for many cities over the world. In this project to show how to get weather data from the internet and print TFT display.


Components Required

ESP-32 Module (38Pin)

3.5 inch TFT LCD Display Module SPI Interface 320x480 with Touch Screen

Jumper Wires


Circuit Diagram



3.5 inch TFT LCD Display Module SPI Interface 320x480 with Touch Screen

This TFT display is big bright and colorful! 480×320 pixels with individual RGB pixel control, this has way more resolution than a black and white 128×64 display.


As a bonus, this display has a resistive touch screen attached to it already, so you can detect finger presses anywhere on the screen. This display has a controller built into it with RAM buffering so that almost no work is done by the microcontroller.


This 3.5-inch SPI Touch Screen Module is wrapped up into an easy-to-use breakout board, with SPI connections on one end. If you’re going with SPI mode, you can also take advantage of the onboard MicroSD card socket to display images.


The 3.5-inch display doesn't have a built-in level shifter, so it's advised to use only 3.3v. Using a node MCU would be more suitable cause it provides only 3.3v. if you are using a 5v microcontroller like the Arduino UNO, MEGA, Using a level shifter would give you the appropriate voltage needed to operate the LCD without damaging it.


DATA SHEET Download

3.5 inch TFT LCD SPI Module Manual Download



Open weather map

Internet weather station To get weather data, first we’ve to sign up for a free account in order to get an API key which is important in this project.

Feature

Access current weather data for any location including over 200,000 cities

  • Current weather is frequently updated based on global models and data from more than 40,000 weather stations

  • Data is available in JSON, XML, or HTML format

  • Available for Free and all other paid accounts

Once you sign in to your account (of course after the free registration), you’ll be directed to member area, go to API keys and you’ll find your API key as shown in the following image:

Replace CITY by with the city you want weather data for, COUNTRY_CODE with the country code for that city (for example uk for the United Kingdom, us for the USA …) and YOUR_API_KEY with your API key which is shown above and replace API key in the arduino code.



Installing the library

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.


Download TFT Library , we need to use this library for TFT touch display

Download Jpeg decoder Library , we need to use this library for image display

Download Json Library , we need to use this library for arduino

Download NTPClient Library , we need to use this library for server.


In your Arduino IDE, to install the libraries go to Sketch > Include Library > Add .ZIP library… and select the library you’ve just downloaded.


After Arduino IDE installed, there is no package to support ESP32-S2, we need to install the ESP32 package in Arduino IDE to continue.


Select “File>Preferences>settings>Additional Boards Manager URLs” to fill the link: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json



arduino code


#include "earth.h"

#include <SPI.h>

#include <TFT_eSPI.h> // Hardware-specific library

#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson.git

#include <NTPClient.h> //https://github.com/taranais/NTPClient


TFT_eSPI tft = TFT_eSPI();



// JPEG decoder library

#include <JPEGDecoder.h>


// Return the minimum of two values a and b

#define minimum(a,b) (((a) < (b)) ? (a) : (b))


// Include the sketch header file that contains the image stored as an array of bytes

// More than one image array could be stored in each header file.

#include "jpeg1.h"

// Count how many times the image is drawn for test purposes

uint32_t icount = 0;


#define TFT_GREY 0x5AEB

#define lightblue 0x01E9

#define darkred 0xA041

#define blue 0x5D9B

#include "Orbitron_Medium_20.h"

#include <WiFi.h>


#include <WiFiUdp.h>

#include <HTTPClient.h>


const int pwmFreq = 5000;

const int pwmResolution = 8;

const int pwmLedChannelTFT = 0;



const char* ssid = "TP-Link_3200"; // your SSID

const char* password = "95001121379884265554"; //Your Password


String town="Chennai";

String Country="IN";

const String endpoint = "http://api.openweathermap.org/data/2.5/weather?q="+town+","+Country+"&units=metric&APPID=";

const String key = "add82e4e24d449f3a522f06621a3aaeb"; //paste your API key here

String payload=""; //whole json

String tmp="" ; //temperatur

String hum="" ; //humidity


StaticJsonDocument<1000> doc;


// Define NTP Client to get time

WiFiUDP ntpUDP;

NTPClient timeClient(ntpUDP);


// Variables to save date and time

String formattedDate;

String dayStamp;

String timeStamp;


int backlight[5] = {10,30,60,120,220};

byte b=1;


void setup(void) {

Serial.begin(115200);

tft.begin();

tft.init();

tft.setRotation(0);

tft.setSwapBytes(true);///

tft.fillScreen(TFT_BLACK);

tft.setTextColor(TFT_WHITE,TFT_BLACK); tft.setTextSize(1);


ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution);

ledcAttachPin(TFT_BLACK, pwmLedChannelTFT);

ledcWrite(pwmLedChannelTFT, backlight[b]);



tft.print("Connecting to ");

tft.println(ssid);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {

delay(300);

tft.print(".");

}

tft.println("");

tft.println("WiFi connected.");

tft.println("IP address: ");

tft.println(WiFi.localIP());

delay(3000);

tft.setTextColor(TFT_WHITE,TFT_BLACK); tft.setTextSize(1);

tft.fillScreen(TFT_BLACK);

tft.setSwapBytes(true);

tft.setCursor(2, 392, 1);

tft.println(WiFi.localIP());

// tft.setCursor(80, 282, 2);

// tft.println("SEC:");

tft.setTextColor(TFT_WHITE,lightblue);

tft.setFreeFont(&Orbitron_Medium_20);

tft.setCursor(4, 300, 2);

tft.println("TEMP:");


tft.setCursor(4, 352, 2);

tft.println("HUM: ");

tft.setFreeFont(&Orbitron_Medium_20);

tft.setTextColor(TFT_WHITE,TFT_BLACK);


tft.setFreeFont(&Orbitron_Medium_20);

tft.setCursor(6, 82);

tft.println(town);

tft.setCursor(4, 420, 2);

// tft.setFreeFont(&Orbitron_Light_18);

tft.setTextColor(TFT_CYAN,TFT_BLACK);

tft.println("IOT BASED INTERNET");

tft.println("WEATHER DATA OUTSIDE");

tft.println("OPEN WEATHER");

// Initialize a NTPClient to get time

timeClient.begin();

// Set offset time in seconds to adjust for your timezone, for example:

// GMT +1 = 3600

// GMT +8 = 28800

// GMT -1 = -3600

// GMT 0 = 0

timeClient.setTimeOffset(19800); /*EDDITTTTTTTTTTTTTTTTTTTTTTTT */

getData();

drawArrayJpeg(Earth, sizeof(Earth), 160, 290);

delay(500);

}

int i=0;

String tt="";

int count=0;


void animation(){


// The image is 300 x 300 pixels so we do some sums to position image in the middle of the screen!

// Doing this by reading the image width and height from the jpeg info is left as an exercise!

int x = (tft.width() - 300) / 2 - 1;

int y = (tft.height() - 300) / 2 - 1;


drawArrayJpeg(Frames00, sizeof(Frames00), 0, 88);

drawArrayJpeg(Frames04, sizeof(Frames04), 0, 88);

drawArrayJpeg(Frames08, sizeof(Frames08), 0, 88);

drawArrayJpeg(Frames12, sizeof(Frames12), 0, 88);

drawArrayJpeg(Frames16, sizeof(Frames16), 0, 88);

drawArrayJpeg(Frames20, sizeof(Frames20), 0, 88);

drawArrayJpeg(Frames24, sizeof(Frames24), 0, 88);

drawArrayJpeg(Frames28, sizeof(Frames28), 0, 88);

drawArrayJpeg(Frames32, sizeof(Frames32), 0, 88);

drawArrayJpeg(Frames36, sizeof(Frames36), 0, 88);

drawArrayJpeg(Frames40, sizeof(Frames40), 0, 88);

drawArrayJpeg(Frames44, sizeof(Frames44), 0, 88);


}

void loop() {


if(count==0)

getData();

count++;

if(count>200)

count=0;

tft.fillRect(1,302,62,30,darkred);

tft.setFreeFont(&Orbitron_Light_32);

tft.setCursor(2, 329);

tft.println(tmp.substring(0,3));


tft.fillRect(1,356,88,30,darkred);

tft.setFreeFont(&Orbitron_Light_32);

tft.setCursor(2, 383);

tft.println(hum+"%");


tft.setTextColor(TFT_ORANGE,TFT_BLACK);

tft.setTextFont(2);

tft.setCursor(6, 44);

tft.println(dayStamp);

tft.setTextColor(TFT_WHITE,TFT_BLACK);


while(!timeClient.update()) {

timeClient.forceUpdate();

}

// The formattedDate comes with the following format:

// 2018-05-28T16:00:13Z

// We need to extract date and time

formattedDate = timeClient.getFormattedDate();

Serial.println(formattedDate);


int splitT = formattedDate.indexOf("T");

dayStamp = formattedDate.substring(0, splitT);

timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);

tft.setFreeFont(&Orbitron_Light_32);

String current=timeStamp.substring(0,5);

if(current!=tt)

{

tft.fillRect(3,8,120,30,TFT_BLACK);

tft.setCursor(5, 34);

tft.println(timeStamp.substring(0,5));

tt=timeStamp.substring(0,5);

}

delay(80);

animation();


}



void getData()

{

tft.fillRect(1,170,64,20,TFT_BLACK);

tft.fillRect(1,210,64,20,TFT_BLACK);

if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status

HTTPClient http;

http.begin(endpoint + key); //Specify the URL

int httpCode = http.GET(); //Make the request

if (httpCode > 0) { //Check for the returning code

payload = http.getString();

// Serial.println(httpCode);

Serial.println(payload);

}

else {

Serial.println("Error on HTTP request");

}

http.end(); //Free the resources

}

char inp[1000];

payload.toCharArray(inp,1000);

deserializeJson(doc,inp);

String tmp2 = doc["main"]["temp"];

String hum2 = doc["main"]["humidity"];

String town2 = doc["name"];

tmp=tmp2;

hum=hum2;

Serial.println("Temperature"+String(tmp));

Serial.println("Humidity"+hum);

Serial.println(town);

}



//####################################################################################################

// Draw a JPEG on the TFT pulled from a program memory array

//####################################################################################################

void drawArrayJpeg(const uint8_t arrayname[], uint32_t array_size, int xpos, int ypos) {


int x = xpos;

int y = ypos;


JpegDec.decodeArray(arrayname, array_size);

renderJPEG(x, y);

Serial.println("#########################");

}


//####################################################################################################

// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit

//####################################################################################################

// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not

// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.

void renderJPEG(int xpos, int ypos) {


// retrieve infomration about the image

uint16_t *pImg;

uint16_t mcu_w = JpegDec.MCUWidth;

uint16_t mcu_h = JpegDec.MCUHeight;

uint32_t max_x = JpegDec.width;

uint32_t max_y = JpegDec.height;


// Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)

// Typically these MCUs are 16x16 pixel blocks

// Determine the width and height of the right and bottom edge image blocks

uint32_t min_w = minimum(mcu_w, max_x % mcu_w);

uint32_t min_h = minimum(mcu_h, max_y % mcu_h);


// save the current image block size

uint32_t win_w = mcu_w;

uint32_t win_h = mcu_h;


// record the current time so we can measure how long it takes to draw an image

uint32_t drawTime = millis();


// save the coordinate of the right and bottom edges to assist image cropping

// to the screen size

max_x += xpos;

max_y += ypos;


// read each MCU block until there are no more

while (JpegDec.read()) {

// save a pointer to the image block

pImg = JpegDec.pImage ;


// calculate where the image block should be drawn on the screen

int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU

int mcu_y = JpegDec.MCUy * mcu_h + ypos;


// check if the image block size needs to be changed for the right edge

if (mcu_x + mcu_w <= max_x) win_w = mcu_w;

else win_w = min_w;


// check if the image block size needs to be changed for the bottom edge

if (mcu_y + mcu_h <= max_y) win_h = mcu_h;

else win_h = min_h;


// copy pixels into a contiguous block

if (win_w != mcu_w)

{

uint16_t *cImg;

int p = 0;

cImg = pImg + win_w;

for (int h = 1; h < win_h; h++)

{

p += mcu_w;

for (int w = 0; w < win_w; w++)

{

*cImg = *(pImg + w + p);

cImg++;

}

}

}


// calculate how many pixels must be drawn

uint32_t mcu_pixels = win_w * win_h;


tft.startWrite();


// draw image MCU block only if it will fit on the screen

if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height())

{


// Now set a MCU bounding window on the TFT to push pixels into (x, y, x + width - 1, y + height - 1)

tft.setAddrWindow(mcu_x, mcu_y, win_w, win_h);


// Write all MCU pixels to the TFT window

while (mcu_pixels--) {

// Push each pixel to the TFT MCU area

tft.pushColor(*pImg++);

}


}

else if ( (mcu_y + win_h) >= tft.height()) JpegDec.abort(); // Image has run off bottom of screen so abort decoding


tft.endWrite();

}


// calculate how long it took to draw the image

drawTime = millis() - drawTime;


// print the results to the serial port

Serial.print(F( "Total render time was : ")); Serial.print(drawTime); Serial.println(F(" ms"));

Serial.println(F(""));

}


After a successful upload, open the Serial Monitor at a baud rate of 115200. Press the “EN/RST” button on the ESP32 board.


Demo:



bottom of page