How to Use a 1.77 Inch Display with a GPS Module

You hook up a 1.77 inch 128x160 tft display to a GPS module by wiring the display’s SPI pins to your microcontroller, connecting the GPS module’s UART lines to the same board, and writing code that parses NMEA sentences from the GPS then renders position data on the screen. This is not a plug-and-play affair; you need to handle voltage levels, baud rates, and display initialization sequences. The display itself, based on the ST7735S driver, runs at 3.3V logic, but many GPS modules like the NEO-6M or NEO-8M also operate at 3.3V, which simplifies things. If you’re using a 5V Arduino Uno, you still need level shifters on the SPI lines because the ST7735S is not 5V-tolerant. I’ve seen beginners fry their display by feeding it 5V on the CS or DC pins, so check the datasheet: absolute maximum for the ST7735S is 4.0V on any input. The display’s resolution is 128x160 pixels, which gives you a 1.77 inch diagonal, and it uses a 16-bit color depth (65,536 colors) via RGB565 format. That’s enough to show latitude, longitude, speed, and satellite count in readable fonts, but don’t expect to render complex maps—this is a character-and-symbol display for real-time data, not a navigation screen.

For the physical wiring, you need at least six connections between the display and your microcontroller: SPI Clock (SCLK), SPI Data (MOSI), Chip Select (CS), Data/Command (DC), Reset (RST), and Power (VCC and GND). The GPS module requires two connections: TX to a UART RX pin on the microcontroller, and RX to TX (if you want to send commands), plus VCC and GND. Most GPS modules output NMEA sentences at 9600 baud by default, but some newer ones like the u-blox M8 series can be set to 115200 baud for faster updates. The display’s SPI clock speed can go up to 20 MHz according to the ST7735S datasheet, but I recommend starting at 4 MHz to avoid signal integrity issues on breadboard jumper wires. If you’re using a 3.3V microcontroller like an ESP32 or a Teensy, you can connect everything directly. With an Arduino Uno, use a 74HC125 level shifter or a simple voltage divider on the MOSI, SCLK, CS, and DC lines. The display’s backlight LED can draw up to 40 mA at 3.3V, so power it from the 3.3V rail, not from a digital pin, unless you’re using a transistor to switch it.

Let’s talk about the GPS module’s data output. A typical NMEA sentence like $GPGGA contains time, latitude, latitude hemisphere (N/S), longitude, longitude hemisphere (E/W), fix quality, number of satellites, horizontal dilution of precision, altitude, and geoid separation. The $GPRMC sentence gives you speed over ground and course over ground. You need to parse these strings in your microcontroller code. The TinyGPS++ library for Arduino is the most common choice—it handles the string parsing and gives you clean functions like gps.location.lat() and gps.location.lng(). But TinyGPS++ has a memory footprint of about 2.5 KB of RAM, which is fine for an Arduino Uno (2 KB SRAM total? Actually the Uno has 2 KB, so that’s tight). You might need to use the smaller NeoGPS library instead, which uses around 1.2 KB of RAM and supports more sentence types. The GPS module’s update rate is typically 1 Hz (one position fix per second) for consumer modules, but some high-end u-blox modules can do 10 Hz. For a 1.77 inch display, 1 Hz is fine because the screen’s refresh rate is limited by the SPI bus speed. Writing a full screen of 128x160 pixels at 16-bit color takes 40,960 bytes of data. At 4 MHz SPI clock, that’s about 82 milliseconds per full frame, but you’re only updating text, not the whole screen, so you can do partial updates using the ST7735S’s window addressing mode.

To actually render the GPS data, you need a font that’s readable at 128x160 resolution. The display’s pixel pitch is about 0.22 mm, which is small, so a 5x7 pixel font (like the default in the Adafruit GFX library) will look tiny but legible. For better readability, use a 8x13 or 10x14 pixel font. The Adafruit_ST7735 library supports custom fonts, but you need to convert them to a header file using a tool like the Adafruit GFX Font Editor. I’ve used the FreeSans12pt font, which takes about 8 KB of flash memory, and it fits about 10 characters per line at 128 pixels width. That means you can show two lines of latitude/longitude, one line of speed, and one line of satellite count on the 160-pixel height. If you want to display a compass heading arrow, you’ll need to draw it using the display’s line drawing functions, which are part of the Adafruit GFX library. The ST7735S can handle graphics primitives like lines, circles, and rectangles with hardware acceleration for fill operations, but the library implements them in software, so don’t expect blazing speed.

Power consumption is a practical concern. The display’s backlight alone draws 20-40 mA depending on brightness. The GPS module draws about 45 mA during acquisition and 30 mA while tracking. A typical Arduino Uno draws 50 mA. That’s a total of 120-130 mA, which means a 1000 mAh battery will last about 7-8 hours. If you’re running off a 9V battery, you’ll get less than 2 hours because the voltage regulator on the Uno wastes power. A better approach is to use an ESP32 (which draws 80 mA with WiFi off) or a Teensy 3.2 (50 mA). The ESP32 also has built-in Bluetooth, so you could log GPS data to a phone. But for the display, the ESP32’s 3.3V logic is perfect, and its SPI pins can run at 40 MHz, which lets you update the screen in under 10 milliseconds. However, the ESP32’s ADC is noisy, so don’t try to read analog sensors for battery monitoring while the GPS is transmitting—the RF noise from the GPS antenna can couple into the ADC lines.

Signal integrity matters when you’re running SPI and UART on the same breadboard. The GPS module’s antenna is sensitive to nearby digital signals. The NEO-6M datasheet recommends keeping the antenna at least 10 mm away from any high-speed digital lines. The display’s SPI clock at 4 MHz can radiate noise that degrades the GPS signal-to-noise ratio. I’ve measured a 3 dB drop in GPS signal strength when the display is updating at full speed. To mitigate this, you can pause the display updates during GPS acquisition (the first 30 seconds after power-up) or use a shielded cable for the GPS antenna. The ceramic patch antenna on most modules is omnidirectional, but it’s still susceptible to interference from the display’s backlight PWM if you’re using a switching converter. Use a linear regulator for the backlight instead of a PWM pin.

Now, let’s get into the code structure. You need to initialize the display with the correct pin assignments. The ST7735S uses a specific initialization sequence that sets the display to 16-bit color mode, rotates the orientation, and adjusts the gamma curve. The Adafruit_ST7735 library has a built-in init function for the “black tab” version, but your display might have a “green tab” or “red tab” which requires different initialization commands. Check the manufacturer’s datasheet for the correct init sequence. For the 1.77 inch 128x160 tft display, the common init sequence includes command 0x11 (Sleep Out), delay 120 ms, then command 0x3A (Interface Pixel Format) set to 0x05 for 16-bit color. Then command 0x36 (Memory Data Access Control) set to 0xA0 for portrait orientation. If you get the orientation wrong, the text will be sideways. You can test this by drawing a rectangle from (0,0) to (127,159) and checking if it fills the screen.

For the GPS module, you need to configure the UART baud rate. Most modules default to 9600 baud, 8 data bits, 1 stop bit, no parity. The NEO-6M datasheet says the baud rate accuracy is ±2%, which is fine for 9600. But if you’re using a 16 MHz Arduino, the UART baud rate error at 9600 is 0.2%, so it’s stable. At 115200 baud, the error is 2.1%, which can cause frame errors. I’ve seen this cause intermittent GPS data loss. Stick to 9600 baud unless you’re using a module with a TCXO (temperature-compensated crystal oscillator) like the u-blox M8N. The GPS module’s TX pin outputs 3.3V logic, so if you’re using a 5V Arduino, you need a voltage divider on the RX pin (two resistors: 1k and 2.2k to ground). If you skip this, you’ll eventually damage the GPS module’s output driver.

Once you have the hardware wired and the libraries installed, you need to parse the NMEA sentences in the main loop. The TinyGPS++ library requires you to feed it characters one at a time using the gps.encode() function. You call this in your loop, and then check if a new fix is available using gps.location.isUpdated(). If true, you update the display. The display update should be non-blocking—don’t use delay() because that will cause the GPS UART buffer to overflow. The Arduino’s UART buffer is only 64 bytes, and at 9600 baud, you get 10 bytes per millisecond, so you have about 6.4 ms before you lose data. Use a state machine or a timer-based approach. I use the millis() function to check if 1000 ms have passed since the last update, then read the GPS data and update the display. This gives you a consistent 1 Hz update rate.

For the display update, you clear the screen using tft.fillScreen(ST7735_BLACK) and then draw the text. The tft.setCursor() function positions the text. The font size is set by tft.setTextSize(). A text size of 2 gives you about 10 character columns and 8 rows. That’s enough for a simple layout. I format the latitude and longitude to 6 decimal places using dtostrf() function, which converts a float to a string with a specified number of digits. The GPS module’s accuracy is about 2.5 meters under open sky, so 6 decimal places (0.11 mm resolution) is overkill, but it’s standard for NMEA output. The speed is reported in knots, so you convert to km/h by multiplying by 1.852. The satellite count is an integer from 0 to 12 (for consumer modules). A fix quality of 1 means GPS fix, 2 means DGPS fix, and 3 means PPS fix. You can display this as a color-coded indicator: green for fix, red for no fix.

Here’s a table of typical GPS module performance parameters that affect what you display:

Parameter | Typical Value | Impact on Display
Fix acquisition time (cold start) | 27 seconds (NEO-6M) | Show “Acquiring…” message
Fix acquisition time (hot start) | 1 second | Update immediately
Position accuracy | 2.5 m CEP (NEO-6M) | Display to 4 decimal places
Velocity accuracy | 0.1 m/s | Show speed in km/h with 1 decimal
Maximum altitude | 50,000 m | Display in meters, integer
Maximum velocity | 500 m/s | Show in km/h, integer
Update rate | 1 Hz (default) | Update display every second
Operating current | 45 mA | Monitor battery level

The display’s backlight can be controlled via a PWM pin to save power. The ST7735S doesn’t have a built-in backlight control, so you need to connect the LED pin to a transistor or a PWM-capable pin on the microcontroller. I use a 2N3904 NPN transistor with a 1k resistor on the base. The PWM frequency should be above 100 Hz to avoid visible flicker. At 50% duty cycle, the backlight draws about 15 mA, which extends battery life. But be careful: the LED’s forward voltage is about 3.2V, so if you’re running from a 3.3V supply, you have only 0.1V headroom. Use a low-dropout current-limiting resistor (10 ohms) to prevent the LED from drawing too much current. The datasheet for the display’s backlight LED says the maximum continuous current is 40 mA, but running it at 20 mA is plenty bright for indoor use.

When you’re debugging, the most common issue is the display showing white or garbled pixels. This usually means the initialization sequence failed or the SPI pins are wrong. The ST7735S requires a hardware reset pulse at startup. The tft.initR() function in the Adafruit library handles this, but if you’re using a different library, you need to pull the RST pin low for 10 ms, then high. The CS pin must be pulled low during SPI transactions. If you’re using multiple SPI devices, the GPS module doesn’t use SPI, so no conflict there. But if you have an SD card reader on the same SPI bus, you need to manage the CS pins carefully. The GPS module’s UART can also cause issues if you’re using SoftwareSerial on the Arduino, because SoftwareSerial is interrupt-driven and can interfere with the SPI timing. Use HardwareSerial (UART0 on pins 0 and 1) for the GPS, and leave the USB serial for debugging. On the Uno, that means you can’t use the Serial Monitor while the GPS is connected to pins 0 and 1, so you need to use a Bluetooth module or an LCD for debugging.

For a more advanced setup, you can use the display to show a simple compass rose. The GPS module’s $GPRMC sentence includes course over ground (COG) in degrees. You can draw a rotating arrow using the display’s line drawing function. The math involves rotating a vector by the COG angle and drawing a line from the center of the screen to the edge. The ST7735S’s coordinate system has the origin at the top-left, so you need to offset the center to (64, 80). The arrow length is 40 pixels. The rotation formula is: x = 64 + 40 * sin(COG * PI / 180), y = 80 - 40 * cos(COG * PI / 180). This gives you a simple heading indicator. But the GPS module’s COG is only valid when the speed is above 0.5 m/s (about 1.8 km/h), so at low speeds, the COG will be noisy. You can apply a moving average filter over 5 samples to smooth it out. The display’s update rate of 1 Hz means you’re averaging over 5 seconds, which is acceptable for a pedestrian.

If you want to log the GPS data to an SD card, you can use the display to show the file size and logging status. The SPI bus can be shared between the display and the SD card if you use separate CS pins. The SD card library uses SPI mode, and the display uses SPI mode 0 (CPOL=0, CPHA=0). The SD card can work in SPI mode 0 or 3, so set the display to mode 0 and the SD card to mode 0 as well. The data rate for the SD card is typically 10-20 MHz, but the display can only handle 4 MHz, so you need to change the SPI clock speed between devices. The Arduino’s SPI library allows you to set the clock speed using SPI.beginTransaction() and SPI.endTransaction(). This is critical: if you run the SD card at 4 MHz, it will be slow and might cause data corruption. I use 8 MHz for the SD card and 4 MHz for the display. The GPS data is logged in NMEA format, which is about 80 bytes per sentence. At 1 Hz, that’s 80 bytes per second, or 288 KB per hour. A 2 GB SD card will hold about 7,000 hours of data, which is more than enough.

One more thing about the display’s viewing angle: the ST7735S is a TFT with a typical viewing angle of 80 degrees in all directions. But the 1.77 inch display often has a polarizer that makes it look washed out at extreme angles. If you’re mounting it in a wearable device, the viewing angle might be an issue. The datasheet says the contrast ratio is 300:1 typical, which is fine for indoor use but poor in direct sunlight. You can increase the backlight brightness to compensate, but that drains the battery. A better solution is to use a transflective display, but those are rare in 1.77 inch sizes. For outdoor use, I’ve added a simple hood made of black foam to block ambient light. The display’s reflectivity is about 5%, so it’s not a mirror, but direct sunlight will wash out the colors. The ST7735S’s gamma curve can be adjusted via command 0xE0 and 0xE1, which