ESP-WROOM-32 with ST7789 LCD (Wiring, Adafruit_GFX and Adafruit_ST77* Libraries, and Arduino Code)

April 17th, 2025 by Keith Neufeld

I’m wanting to play with a medium-resolution LCD screen attached to something that I can program from the Arduino IDE and I happen to have some “AITRIP”-branded ESP-WROOM-32 modules on hand. Connecting an ST7789-driven LCD to one of them involved filling in a number of gaps in the documentation I was able to find, so let me write it all down in one place in case it’s useful to someone else, including Future Keith.

ESP-WROOM-32 driving Waveshare 2.0-inch LCD

The LCD I bought is a Waveshare 2.0″ LCD using an ST7789V driver chip, a variant of a popular (the popular?) LCD driver.

Read the rest of this entry »

ESP32 Modules in Breadboards

April 14th, 2025 by Keith Neufeld

A frequently-heard refrain is that ESP32 modules are so inconvenient because with their 1.0″ spacing between header rows, in the standard installation, their footprint covers all but one horizontal row of holes on a breadboard; and kids these days like using fly-wire-style breadboard jumpers rather than trace-style breadboard jumpers (which would work underneath it).

This should be regarded as irritating rather than intractable. I present here two workarounds and leave the discovery of more as an exercise for the reader.

ESP32 module installed on modular breadboard system

Option 1, remove one power-strip row from a modular breadboard and dovetail-pin that breadboard onto another. Install the ESP module over the new “power gutter” and revel in the luxury of plenty of rows for connecting jumpers.

ESP32 module installed on modified breadboard system

Option 2, SAW THROUGH THAT and revel in the luxury of plenty of rows for connecting jumpers.

Footnote: Use a hacksaw. Its finer teeth cut without snagging like a wood-cutting saw’s teeth would do; I don’t want to contemplate the kind of workholding it would take for me to feel safe using a tablesaw or circular saw on this; and you would be so startled the moment a wood-cutting bandsaw’s teeth first engage in the edge of the plastic and the blade leaps toward you.

ESP32-WROOM-32 in Arduino IDE

April 14th, 2025 by Keith Neufeld

Back in 2021 I documented the process of installing and selecting support for a particular ESP8266 into the Arduino IDE, in case it would help anyone else and as a reference for the next time I needed to do it myself. Well, here I am back again doing the same to bootstrap myself on the ESP-WROOM-32 aka ESP32-WROOM-32.

ESP-WROOM-32 board

My refresher on the basics came from https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c, although he had to go through some steps I didn’t and vice-versa.

Adding Espressif URL to Arduino IDE board manager list

First, go to File / Preferences / Additional boards manager URLs and add
https://dl.espressif.com/dl/package_esp32_index.json to indicate another repository to search for board definitions.

Arduino IDE board manager

Then Tools / Board / Boards Manager... to pick an available board bundle, start typing esp32, and click to install esp32 by Espressif Systems .

I’m installing this on my Linux workstation so I had to give myself permission to access the USB port, which was:

sudo usermod -aG dialout neufeld

To make that group change take effect, I was only supposed to need to logout; but doing so added me to the entry in /etc/group but didn’t activate the group membership when I ran id or tried to access the device file. I had an OS update to apply anyway so I rebooted and all was well.

Picking ESP32 Dev Module from Arduino IDE board selection list

Back in the Arduino IDE, Select Other Board and Port, start typing esp32 dev into the board search, and pick ESP32 Dev Module when it popped up.

Arduino IDE board selection list with nothing picked

Weirdly, even with the USB serial port selected (and accessible), it complains that no boards were found — but it works.

Finally, File / Examples / 01.Basics / Blink to make sure I can compile and upload code to the correct board.

Defining LED_BUILTIN for Arduino Blink sketch on board that doesn't define it

Blink relies on an LED_BUILTIN macro that’s not defined in this Espressif board spec. This forum post suggested pin 2 for the built-in LED on this board and adding

#define LED_BUILTIN (2)

did the trick. Compile, upload, and I have a blinking blue LED next to the red power LED.

AoC 2023 D8P1: Traversing a Digraph

December 9th, 2023 by Keith Neufeld

Day 8 part 1 gives us a directed graph of nodes with links to two (hopefully other) nodes and a set of dance moves to perform through the graph; how many steps to get from AAA to ZZZ at the end of a dance pattern? (A lot more steps if you stray into DDD, EEE, or GGG.)

RL

AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)

All one need do is make a hash of the nodes with their branches, then dance through it.

Read the rest of this entry »

AoC 2023 D7P2: Pseudo-Poker Hands with Wildcards

December 9th, 2023 by Keith Neufeld

Part 2 redefines J from jack to joker, making jokers wildcards when determining type of hand but the lowest value when comparing individual cards. This requires very little modification to the part 1 program:

my $cardlist = "AKQT98765432J";

Change the card sort order;

my $jokers = grep { $_ eq "J" } @cards;

count the jokers;

++ $tally{$_} foreach grep { $_ ne "J" } @cards;

omit the jokers when counting cards for type of hand;

$ofakind[0] += $jokers;

and in this poker variant, simply add the count of jokers to the count of the most-frequent card when determining type of hand.

Read the rest of this entry »

AoC 2023 D7P1: Pseudo-Poker Hands

December 9th, 2023 by Keith Neufeld

Day 7 introduces a card game with hand values loosely based on poker: 5 of a kind, 4 of a kind, full house, 3 of a kind, 2 pair, 2 of a kind, nuffin’. A tie on the type of hand is resolved by the rank of the cards in order dealt (not in order of rank), with ordering AKQJT98765432. Given these rules, rank your hands, then multiply the ordinal value of the resulting list by a unique coefficient for that hand.

32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483

This sort is going to require a fair number of comparisons, each involving rank of types of hands and each potentially involving rank of cards. These comparisons are computationally-intensive enough that I’m leery about executing them during each comparison of the sort, so I calculated and cached them up front.

Read the rest of this entry »

AoC 2023 D6P1, P2: Quadratic Formula or Count

December 6th, 2023 by Keith Neufeld

Day 6 problem 1 asks us to consider a series of toy boat races. The longer you have the boat on the charger, the faster it’ll travel during the remaining units of time. With how many different integer charge times can you beat the record distance in that race?

The distance traveled is (ignoring units) tcharge ( ttotal – tcharge); so the answer to the problem can be found directly by using your favorite quadratic solver on tcharge2 – ttotal tcharge + drecord = 0, which will have zero, one, or two real solutions. If it has zero solutions, one solution that’s non-integer, or two non-integer solutions between two consecutive integers, then there are zero integer charge times that beat the record. Otherwise count the number of integers from the floor of the lower solution plus one to the ceiling of the upper minus one. (That sounds weird but math it out — it ensures not merely tying the record but beating it.)

Anyone who remembers algebra and has dignity and self-respect would use this trivial approach.

I wrote a program to count winning charge times by iteration.

Read the rest of this entry »

AoC 2023 D5P2: Interval Intersections

December 6th, 2023 by Keith Neufeld

Day 5 part 2 reveals that the seed list does not actually denote individual seed values; it is pairs of numbers denoting ranges of seeds. Run each range through the translations and find the lowest value in any resulting range.

After the way I wrote the first program, this made me feel like Obi-Wan just told me to go home and rethink my life. I could enumerate each range of seeds and run them all through the translations … for certain values of “could” that include more processing power, electricity, and time than I have remaining in my years on this planet.

It was obvious that I was going to have to treat ranges as data structures, intersect them with ranges in translation rules, translate them accordingly, and be prepared to split seed ranges that overlapped translation ranges to apply the translation to only a portion of the input range. Afflicted with a serious case of I don’t wanna, I pretended to be too busy with other things to get around to writing this yesterday.

But early this morning I cured my case of I don’t wanna in the way I’ve learned to cure any case related to programming: Write the utilitarian loops that do the boring work of the program; and once they’re done, there’s so little of the program left to write that I’m ready to go ahead and do it.

20231207 edit: I omitted handling one way that intervals can intersect and it was accidental that my program handles that case correctly. More below.

Read the rest of this entry »

AoC 2023 D5P1: Integer Ranges

December 6th, 2023 by Keith Neufeld

Advent of Code day 5 has us processing ranges of integers. Given seed values and lists of translations in the form dest src length, find the lowest value after applying all the translations.

seeds: 79 14 55 13

seed-to-soil map:
50 98 2
52 50 48

soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15

...

I went over the top on this problem, loading all of the translations into memory as though they were important, then iterated through seeds in my outer loop and translations looping inside that on each seed.

Read the rest of this entry »

AoC 2023 D4P2: Caching Coefficients of Future List Elements

December 4th, 2023 by Keith Neufeld

Day 4 part 2 asks us to find the number of winning entries on each line and use that to duplicate the succeeding n lines; and a duplicated line with winning entries multi-duplicates its succeeding lines; all with a promise not to overflow the end of the input list; and then count the total number of instances that occurred.

It would be vaguely entertaining to implement this using a queue of the coefficients of upcoming lines, or using recursion; but I chose simply to build an array of the multipliers that I prepopulate for lines I haven’t seen yet.

Read the rest of this entry »