Heltec Vision Master T190: Getting the Display to Boot

Start here: haitch.diy

The Heltec Vision Master T190 is an ESP32-S3 board with a 1.9-inch colour TFT bonded to it and an SX1262 LoRa radio on the same PCB. On paper it is the obvious pick for a battery-powered device that needs a screen and long-range radio without you designing either. In practice, the first firmware most people write for it compiles cleanly, flashes successfully, and then does nothing at all except reboot roughly five times a second.

We put 22 builds through this board between 13 and 21 August 2026 and flashed a physical unit over USB. This is what we learned, including the two failure modes that cost the most time and produce the least useful error output.

Quick Answer

The T190 is a good board with a bring-up path that punishes assumptions. Three things will stop you before you see a single pixel:

Get those three right and the board is unremarkable in the good way: 14 of our 22 builds landed, using 4.3 to 4.9 percent of flash and 5.7 to 6.1 percent of RAM.

Who This Is For

If you are choosing between the T190 and a bare ESP32-S3 devkit with a separate panel, the tradeoff section near the end is the part to read.

What the Board Actually Is

The silicon readout from a physical unit over USB, not the datasheet:

That is an ESP32-S3R8: dual-core Xtensa LX7 at 240 MHz, 320 KB internal SRAM, 8 MB of octal PSRAM, and 16 MB of flash. The octal detail matters, because a QSPI PSRAM build configuration on octal silicon will not give you the PSRAM.

The display is an ST7789 controller driving a 170 x 320 panel, module marking N190-1732TBWPG01-C30, at 18-bit colour depth. In the orientation most people want it is a 320 x 170 landscape strip, which is a genuinely awkward aspect ratio for anything that expects a square-ish screen. Plan your layout around a letterbox, not a phone.

The Boot Loop That Compiles Clean

This is the one that will cost you an afternoon, so it is worth understanding rather than just copying the fix.

TFT_eSPI up to and including 2.5.43 defaults SPI_PORT to FSPI. On the original ESP32 that was a sane default. On ESP32-S3 under Arduino-ESP32, FSPI is defined as 0. The S3 SoC header then computes the peripheral base like this:

With i equal to zero, the whole expression collapses to 0. Every subsequent register access becomes a bare offset from address zero. SPI_CMD_REG becomes 0x00. SPI_USER_REG becomes 0x10. SPI_W0_REG becomes 0x98.

The first write TFT_eSPI performs to *_spi_user therefore stores to address 0x10, which is not mapped, and the core panics:

with EXCVADDR reading 0x10 — the offset, betraying exactly which register it was trying to reach. The panic handler reboots, setup() starts again, and you get an unbreakable loop at roughly 200 ms per cycle. Nothing you can do over serial will interrupt it, because the crash happens before any of your code runs.

The fix is one build flag:

The reason this is worth a section rather than a footnote is that every gate you would normally trust says the firmware is fine. Our own build came back in 9.3 seconds with zero errors and a single unrelated TOUCH_CS warning. The binary was valid, the flash succeeded, the hash verified. A compiler cannot see this, and neither can a linker. The only thing that catches it is knowing the ESP32-S3 has this trap, or watching the serial output on real hardware.

The Two Pins Nobody Documents Together

Once SPI works you can still end up staring at a black rectangle. The T190 gates its panel through two independent pins, and the vendor examples set both without calling attention to either.

| Signal | GPIO | Active level | What happens if you skip it |

|---|---|---|---|

| VTFT_CTRL panel power | 7 | Low | Controller never powers up; SPI writes go nowhere |

| TFT_BACKLIGHT | 17 | High | Panel renders correctly and is invisible |

Order matters. Assert panel power, wait for the rail to settle — we use 20 ms — then run the controller init, then raise the backlight. Bringing the backlight up first means the user watches the initialisation garbage paint across the screen.

The rest of the display bus, from Heltec's own tft1_9.ino reference sketch:

| Signal | GPIO |

|---|---|

| TFT_SCLK | 38 |

| TFT_MOSI | 48 |

| TFT_CS | 39 |

| TFT_RST | 40 |

| TFT_DC | 47 |

| TFT_MISO | not connected (-1) |

| TOUCH_CS | none (-1) |

Two more settings that are not obvious. The panel is BGR-ordered, so without -D TFT_RGB_ORDER=TFT_BGR your reds and blues swap and everything looks subtly wrong in a way that takes a while to name. And TFT_eSPI needs the pins bound explicitly on this board rather than inferred, which means calling

before tft.init().

Buttons: There Is Only One

The T190 has three things that look like buttons and one that you can actually use.

This trips up UI designs more often than it should. If your interface needs up, down, and select, you either wire external buttons, or you design a one-button gesture scheme around short press, long press, and double press. Decide that before you write the UI, not after.

What 22 Builds Looked Like

Between 2026-08-13T23:41Z and 2026-08-21T05:30Z we ran 22 builds against this board across six separate projects. Fourteen succeeded. Six failed. One was still compiling and one was queued when we took the snapshot.

The fourteen successes:

| Metric | Range |

|---|---|

| Flash used | 280,533 – 320,593 bytes (4.3% – 4.9%) |

| RAM used | 18,724 – 19,996 bytes (5.7% – 6.1%) |

| Compile time | 28.1 s – 289.3 s, median 65.0 s |

You have an enormous amount of headroom on this board. A display-driving pomodoro timer with fonts and a full UI refresh loop uses under 5 percent of the flash. Nothing about the T190 is going to constrain a typical application; the constraint is entirely in getting the peripherals up.

The six failures are more instructive than the successes, because they are the shapes you will hit too:

Two were a macro collision. Declaring pins as constants when TFT_eSPI has already defined them as macros:

The preprocessor substitutes 38 for TFT_SCLK and the compiler sees const int 38 = 38;. Repeated for TFT_CS, TFT_RST, TFT_DC, TFT_MOSI. If you are passing pins through build flags, do not also declare them in source.

Two were toolchain installs stalling at roughly 900 seconds on a cold cache, producing no binary at all. Not a code problem. Warm your PlatformIO cache before you time anything.

Two were our own hardware profile refusing to build, with The selected hardware profile requires build defines that are missing: TFT_RGB_ORDER=TFT_BGR. That is the gate working: a build that would have produced a colour-swapped panel got stopped before it burned a flash cycle.

Flashing It

Nothing exotic. Standard esptool over USB, no button-holding required on our unit:

Roughly 48 seconds end to end including the compile, about 2.2 seconds of that actually writing flash. Across five flashes of the same unit we saw 27.8 to 58.6 seconds, the variance being compile cache rather than transfer.

Should You Buy One

Yes, if you want a screen and LoRa on one board and you are shipping tens to low hundreds of units. Integrating an ST7789 panel and an SX1262 onto your own PCB is a real design and a real certification conversation. The T190 skips both.

No, if the 320 x 170 letterbox does not suit your UI, or you need more than one button without adding external hardware, or you are going to volume where the board's margin becomes your bill of materials problem.

Be aware that the T190 is niche enough that PlatformIO does not ship a board definition for it and the community answers you find will mostly be for other Heltec boards with different pinouts. Budget bring-up time accordingly. The board is fine; the ecosystem around it is thin.

Common Mistakes

How Haitch Fits

That last mistake is one we made ourselves, and it is why the T190 now has a first-class hardware profile in Haitch rather than being mapped onto a generic ESP32-S3 target.

When you ask Haitch for firmware for a named board, the generator resolves it against a profile carrying the real pin map, the required build defines, the PlatformIO target, and the constraints — including that there is exactly one usable application button. A request that would produce a colour-swapped panel or a missing SPI port define gets refused at generate time with the reason, which is what those two TFT_RGB_ORDER failures above were.

The broader point applies to any board: the expensive failures in embedded work are not the ones the compiler catches. They are the configurations that build perfectly and then behave wrongly on hardware, where the feedback loop is a flash cycle and a serial monitor. Encoding board truth once, and gating on it, is cheaper than discovering it once per project.

FAQ

Why 4d_systems_esp32s3_gen4_r8n16 and not a Heltec board?

PlatformIO has no T190 definition. The 4D Systems GEN4-ESP32 16MB entry describes the same ESP32-S3R8 silicon, 16 MB flash, and octal PSRAM configuration, so it produces a correct build. You are selecting silicon, not branding.

Does USE_HSPI_PORT or USE_FSPI_PORT matter?

Either resolves the crash, because both map to a peripheral index of 2 or greater and give a valid register base. We use USE_FSPI_PORT. If you have another SPI device on the bus, pick the one that does not collide with it.

Is the LoRa radio covered here?

No. Everything above is display bring-up on our own testing. We have not put the SX1262 through the same measurement, so we are not going to write about it as though we had.

Why 20 ms after panel power?

It is the settle time in Heltec's reference sketch and it worked reliably for us. It is a conservative number rather than a characterised one; if you are optimising boot time, measure your own rail.

How much flash headroom do I really have?

The partition layout we built against exposes 6,553,600 bytes to the application, and a full display UI used about 274 KB of it. Unless you are embedding large assets, flash will not be your limit.

References

1. Heltec Vision Master T190 product page

2. Heltec ESP32 library, VMT190 reference sketch

3. Bodmer TFT_eSPI library

4. Espressif ESP32-S3 Technical Reference Manual

5. PlatformIO Espressif 32 platform documentation

6. Haitch -- AI-native hardware design platform