How to use a 1.3 inch display in a weather station?
You hook up a 1.3 inch 240x240 ips display to a weather station by connecting the SPI interface to your microcontroller, mapping weather data to the screen’s pixel grid, and writing efficient code to refresh the display at a rate that doesn’t bog down sensor readings. The 1.3 inch display, typically driven by a ST7789 or SH1106 controller, offers a 240x240 resolution with a 16-bit color depth, which is more than enough to show temperature, humidity, pressure, wind speed, and a simple icon for conditions like sunny or rainy. Most weather station projects use an ESP32 or a Raspberry Pi Pico as the main controller because these chips have built-in Wi-Fi for fetching online weather data and enough GPIO pins to handle the SPI bus without extra hardware. The SPI communication runs at speeds up to 40 MHz, so you can push a full frame buffer of 115,200 bytes (240x240 pixels times 2 bytes per pixel for 16-bit color) in about 23 milliseconds, leaving plenty of time for the microcontroller to poll sensors like the BME280 or DHT22 every 2 to 5 seconds without lag.
For the physical connection, you need to wire the display’s CS, DC, MOSI, SCK, and RESET pins to the microcontroller’s SPI pins. On an ESP32, the default SPI pins are VSPI: MOSI on GPIO 23, MISO on GPIO 19, SCK on GPIO 18, and you assign CS to GPIO 5, DC to GPIO 2, and RESET to GPIO 4. The display runs on 3.3V logic, so you must power it from the 3.3V pin of the ESP32, not the 5V pin, or you risk frying the driver chip. The backlight pin, often labeled BL or LED, connects to a PWM-capable GPIO pin, like GPIO 16, so you can dim the display in low-light conditions to save power. A typical weather station running on battery power uses a PWM duty cycle of 10% to 20% during the night, dropping current draw from 20 mA to about 4 mA, which extends battery life from 12 hours to over 60 hours with a 2000 mAh LiPo pack.
The display’s 240x240 resolution means you have a square aspect ratio, which is perfect for a circular or square gauge layout. You can divide the screen into four quadrants: top-left for temperature, top-right for humidity, bottom-left for pressure, and bottom-right for wind speed or a time stamp. Each quadrant uses about 120x120 pixels, which gives you enough room for a 32-point font for the number and a 16-point font for the unit label. For example, a temperature reading of 23.5°C displays as “23.5” in a 32-point font, which takes up roughly 60x40 pixels, and the “°C” label in a 16-point font takes 20x20 pixels, so you have plenty of white space to avoid clutter. The color palette should be high-contrast: white text on a dark blue background for daytime readability, and a red-orange gradient for the temperature value to catch the eye. The display’s IPS technology gives you 178-degree viewing angles, so you can mount the weather station on a wall or a desk and still read the data from the side.
To fetch weather data, you can use a BME280 sensor for temperature, humidity, and pressure, which communicates over I2C at 400 kHz, and a wind speed sensor like a cup anemometer connected to a digital input pin with interrupt handling. The BME280 has a typical accuracy of ±0.5°C for temperature, ±3% for humidity, and ±1 hPa for pressure, and it samples at 1 Hz, which is fast enough for real-time updates. The ESP32 reads the sensor every 5 seconds, averages the last three readings to smooth out noise, and then updates the display buffer. The frame buffer is stored in the ESP32’s PSRAM if you have a module with 8 MB of PSRAM, or in the regular SRAM if you don’t. The buffer size is 115,200 bytes, which fits in the 520 KB SRAM of the ESP32, but you need to allocate it as a static array to avoid heap fragmentation. The update routine uses a double-buffer technique: you write the new data to a back buffer, then swap the buffer pointers, so the display never shows partial updates. This swap happens in the vertical blanking interval of the display, which is about 1.5 milliseconds at a 60 Hz refresh rate, so there’s no tearing.
For the software, you use the Adafruit ST7789 library or the TFT_eSPI library, which is optimized for ESP32 and includes a font rendering engine. The TFT_eSPI library allows you to use custom fonts, like a 7-segment style font for the numbers, which gives the weather station a retro digital clock look. The library also supports sprite operations, so you can pre-render static elements like the background grid and the unit labels, then only redraw the dynamic numbers. This cuts the frame update time from 23 ms to about 8 ms, because you skip the pixel writes for the background. The sensor data is formatted as strings using sprintf, which is faster than the Arduino String class, and you avoid floating-point math by using integer arithmetic: for temperature, you multiply the float by 10, then format as an integer with a decimal point inserted. For example, 23.45 becomes 2345, then you print “2345” with a dot after the second digit, which saves CPU cycles and reduces flash memory usage by about 2 KB compared to using dtostrf.
The weather station can also display a simple icon for the weather condition, like a sun for clear skies, a cloud for overcast, or a raindrop for rain. You store these icons as 32x32 pixel bitmaps in the program flash, using the PROGMEM directive. Each icon takes 2,048 bits (32x32x2 bytes for 16-bit color), so five icons use about 10 KB of flash, which is negligible on an ESP32 with 4 MB of flash. The icon is drawn at the center of the screen, overlapping the quadrants, but you can also place it in the top-center area if you use a 240x240 resolution. The icon selection logic is based on the weather code from an online API, like OpenWeatherMap, which returns a code like 800 for clear sky. You map the code to an icon index using a switch statement, and the update routine checks the code every 10 minutes to avoid excessive API calls. The API call uses the ESP32’s Wi-Fi and the ArduinoJson library to parse the JSON response, which takes about 1.5 seconds for a full update, including the HTTP request and JSON parsing. You run this on a separate core of the ESP32 using FreeRTOS tasks, so the display update and sensor reading run on core 0, and the Wi-Fi task runs on core 1, with a semaphore to protect the shared data structure.
Power management is critical for a weather station that runs on batteries. The ESP32 deep sleep mode draws only 10 µA, but you need to wake it up every 5 seconds to take a sensor reading and update the display. The wake-up time from deep sleep is about 150 ms, which includes the bootloader, the Wi-Fi initialization, and the sensor reading. To save power, you can use a timer wake-up from the RTC, and you keep the display off during sleep by pulling the backlight pin low. The display’s sleep mode is entered by sending a command over SPI, which drops its current draw from 20 mA to 5 µA. The total average current for a 5-second cycle is: 150 ms at 80 mA (active) plus 4.85 seconds at 10 µA (sleep), which gives an average of about 2.5 mA. With a 2000 mAh battery, you get about 800 hours of operation, or 33 days, which is acceptable for a home weather station. If you want longer battery life, you can increase the update interval to 30 seconds, which drops the average current to 0.5 mA and extends the runtime to 166 days.
The display’s physical mounting is also a factor. The 1.3 inch display module typically comes with a PCB that has mounting holes at the corners, spaced 2.54 mm apart, so you can screw it onto a 3D-printed enclosure. The enclosure should have a cutout for the display that’s 26.5 mm by 26.5 mm, which is the active area of the display. The bezel around the active area is about 1.5 mm, so the total module size is 30 mm by 30 mm. You can use a 3D-printed case with a snap-fit lid, with a vent for the BME280 sensor to get accurate humidity readings. The sensor should be placed at least 5 mm away from the display to avoid heat from the backlight affecting the temperature reading, because the backlight can raise the local temperature by 2°C to 3°C if it’s on continuously. You can mitigate this by using a PWM duty cycle of 50% during the day, which reduces the heat output by half, and by placing the sensor on a separate PCB that’s connected via a ribbon cable.
For the data visualization, you can add a trend indicator that shows whether the temperature is rising or falling. This is done by storing the last 10 readings in an array, then calculating the slope using a linear regression. The slope is displayed as an arrow pointing up or down, drawn using a 16x16 pixel bitmap. The arrow is placed next to the temperature value, and it updates every 5 seconds. The regression calculation uses integer math to avoid floating-point overhead: you sum the x and y values, then compute the slope as (n*sum_xy - sum_x*sum_y) / (n*sum_xx - sum_x*sum_x), where n is 10. The result is a fixed-point number with a scaling factor of 100, so a slope of 0.5°C per minute becomes 50. You then compare this to a threshold of 10 to decide if the arrow points up or down. This gives a responsive indicator that doesn’t flicker, because you only update the arrow when the slope changes by more than 5 units.
The display’s SPI interface is also compatible with the Raspberry Pi Pico, which uses the RP2040 chip. On the Pico, you use the PIO (Programmable I/O) to drive the SPI bus at 30 MHz, which is slower than the ESP32’s 40 MHz but still fast enough for a 240x240 display. The Pico has 264 KB of SRAM, which is enough for the frame buffer, but you need to be careful with memory allocation because the buffer takes 115 KB, leaving 149 KB for the rest of the program. The Pico’s I2C interface runs at 400 kHz for the BME280, and you can use the Pico’s built-in temperature sensor as a backup, but it’s less accurate at ±2°C. The Pico’s sleep mode draws 1.5 mA, which is higher than the ESP32’s 10 µA, so the Pico is better suited for a USB-powered weather station that sits on a desk, not a battery-powered one.
For the online weather data, you can use the OpenWeatherMap API, which gives you a free tier of 1,000 calls per day. The API returns a JSON object with temperature, humidity, pressure, wind speed, and weather condition code. The JSON parsing takes about 1.2 seconds on an ESP32 at 240 MHz, using the ArduinoJson library version 6. The library uses a dynamic memory allocation of about 2 KB for the JSON document, which you allocate on the heap and free after parsing. The parsed data is stored in a struct with fields for temperature (int16_t, scaled by 10), humidity (uint8_t), pressure (uint16_t), wind speed (uint16_t, scaled by 10), and weather code (uint16_t). The struct is shared between the Wi-Fi task and the display task using a mutex, so the display task reads the latest data without race conditions. The Wi-Fi task runs every 10 minutes, but you can also set it to run every 30 minutes to save API calls, because the weather doesn’t change that fast.
The display’s color depth of 16 bits means you have 65,536 colors, which is enough for a gradient background. You can create a gradient from dark blue at the top to light blue at the bottom, which gives a sky effect. The gradient is pre-computed in a lookup table stored in flash, with 240 entries for the 240 rows. Each entry is a 16-bit color value, and you use a for loop to write the gradient to the buffer during initialization. The gradient takes 480 bytes of flash, which is negligible. The text is drawn in white (0xFFFF) with a black shadow (0x0000) offset by 1 pixel to the right and down, which improves readability on the gradient background. The shadow is drawn first, then the text, so the text appears to float above the background.
The weather station can also display a time stamp from an NTP server. The ESP32 connects to the NTP server at startup and then updates the time every hour using the configTime function. The time is displayed in the bottom-right quadrant as “HH:MM” in a 24-hour format, using a 24-point font. The time update is done by the display task every second, but you only redraw the time digits, not the entire screen, to save processing. The time digits are drawn using a sprite that’s 40x24 pixels, which is the size of the “HH:MM” string. The sprite is updated every second by copying the new digit bitmaps from flash, then the sprite is pushed to the display at the bottom-right coordinates. This takes about 2 ms, compared to 23 ms for a full screen update, so the display task runs at 1 Hz for the time and 0.2 Hz for the weather data, which is smooth and responsive.
For the sensor calibration, you can use a known reference to offset the BME280 readings. The BME280 has a factory calibration that’s stored in the sensor’s registers, but you can apply a software offset if the readings are off by more than 1°C or 5% humidity. The offset is stored in the ESP32’s NVS (Non-Volatile Storage) and applied during the sensor read. The NVS uses 512 bytes of flash, and you can write the offset using a serial command interface. The offset is a float, but you store it as an integer scaled by 100, so 0.5°C becomes 50. The offset is added to the raw reading before formatting, and it’s persistent across reboots. This is useful if you place the weather station in a location that’s consistently warmer or cooler than the true ambient temperature, like near a window that gets direct sunlight.
The display’s refresh rate is 60 Hz, but you only update the buffer when new data is available, which is every 5 seconds for the sensor data and every 10 minutes for the online data. The display’s controller has a built-in frame buffer, so you don’t need to send the entire buffer every frame. You only send the changed pixels, which is a technique called partial update. The ST7789 controller supports partial update by setting a window with the CASET and RASET commands, then sending only the pixels in that window. For example, if the temperature changes from 23.5 to 23.6, you only update the 60x40 pixel area of the temperature number, which is 2,400 bytes instead of 115,200 bytes. This cuts the SPI transfer time from 23 ms to 0.5 ms, which reduces the CPU load and the power consumption. The partial update is implemented by comparing the old and new buffers, then calculating the bounding box of the changed pixels. The bounding box is typically small, like 10x10 pixels for a single digit change, so the update is very fast.
The weather station’s enclosure should be weather-resistant if you plan to use it outdoors. The display is not waterproof, so you need to seal the enclosure with an IP65 rating. The enclosure can be printed with PETG filament, which has a glass transition temperature of 80°C and is UV-resistant. The display’s front glass is covered with a 0.5 mm thick polycarbonate sheet that’s glued to the enclosure with silicone sealant. The polycarbonate sheet has a 90% transmittance, so the display’s brightness is reduced by 10%, but you can compensate by increasing the backlight PWM to 60% during the day. The sensor is placed in a separate compartment with a vent that’s covered with a hydrophobic mesh, which allows air to pass through but blocks water droplets. The vent is a 10 mm diameter hole with a mesh that has a 0.2 mm pore size, which is small enough to block rain but large enough for air flow.
The [1.3 inch 240x240 ips display](https://www.displaymodule.com/products/1-3-inch-ips-display-240x240-with-spi) is a solid choice for a weather station because it balances resolution, power consumption, and physical size. The 240x240 resolution gives you enough pixels for a clean layout, the IPS viewing angles ensure readability from any angle, and the SPI interface is fast enough for real-time updates. The display’s 16-bit color depth allows for a gradient background and high-contrast text, and the 30 mm x 30 mm module size fits into a compact enclosure. The power consumption of 20 mA with the backlight on is acceptable for a battery-powered station, and the deep sleep mode of the display and the microcontroller extends the runtime to weeks or months. The software stack is mature, with libraries like TFT_eSPI and Adafruit ST7789 that handle the low-level SPI communication and font rendering, so you can focus on the data visualization and the sensor integration. The weather station can be expanded with additional sensors, like a rain gauge or a UV sensor, by using the remaining GPIO pins on the ESP32 or the Pico. The display’s SPI bus can be shared with other SPI devices, like an SD card for data logging, by using a separate CS pin for each device. The data logging saves the sensor readings to a CSV file every hour, and you can download the file over Wi-Fi using a web server that runs on the ESP32. The web server also serves a real-time dashboard that mirrors the display, so you can check the weather data from your phone. The entire project is open-source, with schematics and code available on GitHub, so you can modify it to fit your specific needs.