Adafruit SSD1305 Arduino Library on ESP8266

Thanks to Adafruit publishing an Arduino library for interfacing with SSD1305 display driver chip, I proved that it’s possible to control an OLED dot matrix display from a broken FormLabs Form 1+ laser resin 3D printer. But the process wasn’t seamless, I ran into several problems using this library:

  1. Failed to run on ESP32 Arduino Core due to watchdog timer reset.
  2. 4 pixel horizontal offset when set to 128×32 resolution.
  3. Sketch runs only once on Arduino Nano 33 BLE Sense, immediately after uploading.

Since Adafruit published the source code for this library, I thought I’d take a look to see if anything might explain any of these problems. For the first problem of watchdog reset on ESP32, I found a comment block where the author notes potential problems with watchdog timers. It sounds like an ESP8266 is a platform known to work, so I should try that.

  // ESP8266 needs a periodic yield() call to avoid watchdog reset.
  // With the limited size of SSD1305 displays, and the fast bitrate
  // being used (1 MHz or more), I think one yield() immediately before
  // a screen write and one immediately after should cover it.  But if
  // not, if this becomes a problem, yields() might be added in the
  // 32-byte transfer condition below.

While I’m setting up an ESP8266, I could also try to address the horizontal offset. It seems a column offset of four pixels were deliberately added for 32-pixel tall displays, something not done for 64-pixel tall displays.

  if (HEIGHT == 32) {
    page_offset = 4;
    column_offset = 4;
    if (!oled_commandList(init_128x32, sizeof(init_128x32))) {
      return false;
    }
  } else {
    // 128x64 high
    page_offset = 0;
    if (!oled_commandList(init_128x64, sizeof(init_128x64))) {
      return false;
    }
  }

There was no comment to explain why this line of code was here. My best guess is the relevant Adafruit product has internally wired its columns with four pixels of offset, so this code makes a shift to compensate. If I remove this line of code and rebuild, my OLED displays correctly.

As for the final problem of running just once (immediately after upload) on an Arduino Nano 33 BLE Sense, I don’t have any hypothesis. My ESP8266 happily restarted this test sketch whenever I pressed the reset button or power cycled the system. I’m going to chalk it up to a hardware-specific issue with the Arduino Nano 33 BLE Sense board. At the moment I have no knowledge (and probably no equipment and definitely no motivation) for more in-depth debugging of its nRF52840 chip or Arm Mbed OS.

Now I have this OLED working well with an ESP8266, a hardware platform I have on hand, I can confidently describe this display module’s pinout.

First Test with Adafruit SSD1305 Library

I feel I now have a good grasp on how I would repurpose the OLED dot matrix display from a broken FormLabs Form 1+ laser resin 3D printer. I felt I could have figured out enough to play back commands captured by my logic analyzer, interspersed with my own data, similar to how I controlled a salvaged I2C LCD. But this exploration was much easier because a user on FormLabs forums recognized the SSD1305-based display module. Thanks to that information, I had a datasheet to decipher the commands, and I could go searching to see if anyone has written code to interface with a SSD1305. Adafruit, because they are awesome, published an Arduino library to do exactly that.

Adafruit’s library was written to support several of their products that used an SSD1305, including product #2675 Monochrome 2.3″ 128×32 OLED Graphic Display Module Kit which looks very similar to the display in a Form 1+ except not on a FormLabs custom circuit board. Adafruit’s board has 20 pins in a single row, much like the Newhaven Display board but visibly more compact. Adafruit added level shifters for 5V microcontroller compatibility as well as an extra 220uF capacitor to help buffer power consumption.

Since the FormLabs custom board lacked such luxuries, I need to use a 3.3V Arduino-compatible microcontroller. The most convenient module at hand (because it was used in my most recent project) happened to be an ESP32. The ssd1305test example sketch of Adafruit’s library compiled and uploaded successfully but threw the ESP32 into a reset loop. I changed the Arduino IDE Serial Monitor baud rate to 115200 and saw this error message repeating endlessly every few seconds.

ets Jun  8 2016 00:22:57

rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13516
load:0x40080400,len:3604
entry 0x400805f0
SSD1305 OLED test

Three letters jumped out at me: WDT, the watchdog timer. Something in this example sketch is taking too long to do its thing, causing the system to believe it has locked up and needs a reset to recover. One unusual aspect of ssd1305test code is that all work live in setup() leaving an empty loop(). As an experiment, I moved majority of code to loop(), but that didn’t fix the problem. Something else is wrong but it’ll take more debugging.

To see if it’s the code or if it is the hardware, I pulled out a different 3.3V microcontroller: an Arduino Nano 33 BLE Sense. I chose this hardware because its default SPI communication pins are those already used in the sample sketch, making me optimistic it is a more suitable piece of hardware. The sketch ran without triggering its watchdog dimer, so there’s an ESP32 incompatibility somewhere in the Adafruit library. Once I saw the sketch was running, I connected the OLED and immediately saw the next problem: screen resolution. I see graphics, but only the lower half. To adjust, I changed the height dimension passed into the constructor from 64 to 32. (Second parameter.)

Adafruit_SSD1305 display(128, 32, &SPI, OLED_DC, OLED_RESET, OLED_CS, 7000000UL);

Most of the code gracefully adjusted to render at 32 pixel height, but there’s a visual glitch where pixels are horizontally offset: the entire image has shifted to the right by 4 pixels, and what’s supposed to be the rightmost 4 pixels are shown on the left edge instead.

The third problem I encountered is this sketch only runs once, immediately after successful uploading to the Nano 33 BLE Sense. If I press the reset button or perform a power cycle, the screen never shows anything again.

Graphics onscreen prove this OLED responds to an SSD1305 library, but this behavior warrants a closer look into library code.

LED Strobing to Fan Speed Signal

The reason I cared about power-on response time of a salvaged LED array is because I wanted to use it as a strobe light shining on a cooling fan pulsing once per revolution. Historically strobe lights used xenon bulbs for their fast response, as normal incandescent bulbs were too slow. This LED array used to be a battery-powered work light with no concern of reaction time, but LEDs are naturally faster than incandescent. Is it fast enough for the job? PC case fan specifications usually range from the hundreds to low thousands of RPM. Using 1200RPM as a convenient example, that means 1200/60 seconds per minute = 20 revolutions per second. Pulsing at 20Hz should be easy for any LED.

For the hardware side of controlling LED flashes, I used a 2N2222A transistor because I had a bulk bag of them. They are usually good for switching up to 0.8 Amps of current. I measured this LED array and it drew roughly 0.3 Amps at 11.3V, comfortably within limits. I just need to connect this transistor’s base to a microcontroller to toggle this light on and off. For this experiment I repurposed the board I had built for the first version of my bedstand fan project. I unsoldered the TMP36 sensor to free up space for 2N2222A and associated LED power wire connector.

This board also had the convenience of an already-connected fan tachometer wire. My earlier project used it for its original purpose of counting fan RPM, but now I will use those pulses to trigger a LED flash. Since timing is critical, I can’t just poll that signal wire and need a hardware interrupt instead. Within Arduino framework I could use attachInterrupt() for this purpose and run a small bit of code on every tachometer wire signal pulse. Using an ESP8266 for this job had an upside and a downside. The upside is that interrupts could be attached to any available GPIO pin, I’m not limited to specific pins like I would have been with an ATmega328P. The downside is that I have to use an architecture-specific keyword IRAM_ATTR to ensure this code lives in the correct part of memory, something not necessary for an ATmega328P.

Because it runs in a timing-critical state, ISR code is restricted in what it can call. ISR should do just what they absolutely need to do at that time, and exit allowing normal code to resume. So many time-related things like millis() and delay() won’t work as they normally would. Fortunately delayMicroseconds() can be used to control duration of each LED pulse, even though I’m not supposed to dawdle inside an ISR. Just for experiment’s sake, though, I’ll pause things just a bit. My understanding of documentation is as long as I keep the delay well under 1 millisecond (1000 microseconds) nothing else should be overly starved for CPU time. Which was enough for this quick experiment, because I started noticing motion blur if I keep the LED illuminated for more than ~750 microseconds. The ideal tradeoff between “too dim” and “motion blurred” seems to be around 250 microseconds for me. This tradeoff will be different for every different combination of fan, circuit, LED, and ambient light.

My minimalist Arduino sketch for this experiment (using delayMicroseconds() against best practices) is publicly available on GitHub, as fan_tach_led within my ESP8266Tests repository. Next step in this project is to move it over to ESPHome for bells and whistles.

Honda CD Spinner Demo

I am at a good stopping point for my exploration of a retired CD player control panel from a Honda Accord. At this point I can interact with all audio control button and knobs, and control what is displayed on its output LCD controlled by Sanyo LC75883 chip. My Arduino sketch toggles through several different modes when I press the “Mode” button (read via LC75883): Turn all segments on, or a “drawing mode” of turning on individual segments selected via the quadrature encoder knob. Very functional, but I wanted to put together something a little flashier before I move on to the next project.

After I figured out the Toyota tape deck, I made a simple Larson scanner. I could do another Larson scanner with this LCD, but I thought I could put together something more interesting. The central attention-getting element of this segmented LCD is a series of 24 segments arranged in a circle. I’ve never seen this LCD work in normal operation, but since it is a CD player, I assume it showed graphics that resembled a spinning CD. A quick test confirmed that I could show a spinning animation, so I will make that the centerpiece for a demo. The next question is what to do with remaining segments.

There are ten 7+ segment displays. Four to the left of the spinner, three above, and four to the right. Some of these have additional segments, likely to display specific subsets of the alphabet. That works for static text, but it rules out scrolling text.

There are two additional segments that appear to be 7-segment numeric display but are not. Above the spinner is apparently a clock, as there are two elements that pretend to be “1” of a 7-segment numeric display but is in fact a single segment. And far to the right is a number that appear to be a 7-segment display but is in fact only three segments and thus extremely limited.

I also have a few miscellaneous elements for describing CD operation. “DISC”, “TRACK” “RPT”, etc. Plus six numbered CDs for the six-disc changer. I haven’t thought of anything useful to do with those beyond a Larson scanner-like animation.

I settled on the following:

  • Focus on center spinning CD animation using its 24 segments.
  • All the individually controllable 7-segment displays will use their outer 6 segments and also display a spinning animation.
  • There will be 12 frames in the animation, which works out evenly for the 24-segment CD and 6-segment outer digit segments.
  • Most of the remaining segments will form a near-vertical section of 1 in 12 bands, they will animate left-and-right in a Larson scanner-like manner.

The animation cycle: CD and digits spins clockwise, and the bands will move in one direction. The motion will slow, then reverse and acceleration. Then it will decelerate and reverse again, and the loop repeats. It didn’t turn out quite as visually interesting as I had hoped, but I’m not going to invest in the time to make a better animation. It is sufficient to show off the fact I have full control all segments independent of this LCD’s original designed purpose.

Source code for this demo is part of the investigation project available on GitHub.

Honda CD Control Detours

After I finally found my mistake reading a Honda CD control panel’s input (I had left the reset pin floating) I think I have a pretty good handle on communicating with it. The CD audio side, at least, as I had no interest in figuring out the HVAC side. But before I wrap up with a summary and demo, this page describes two additional experiments for future reference.

External Quadrature Encoder

Before I realized my problem was a floating reset pin, I wired in an external quadrature encoder to determine if the problem might be with the Honda circuit board or if it was my code. There was an added bonus that this particular quadrature encoder was designed so that every detent would be high/high. I knew the problem of LCD blanking out was related to grounding various controls (buttons or this knob) to ground, so with its four transitions per detent, this knob would quickly blitz through the problematic states as a workaround.

Successful use of the external knob also meant I now know the LCD wasn’t blanking out due to something in my code, or even something in the Arduino as related to a quadrature encoder. The LCD would blank out if the onboard knob was in the wrong position, even if none of its wired connected to my Arduino. This observation was consistent with the actual cause of a floating reset pin. I removed this external knob once the reset pin was no longer left floating, making room for the next experiment.

Boost Converter for LCD Backlight

When I had illuminated the LCD backlight using my bench power supply, it indicated the backlight drew 0.2A at 14.4V ~= 3W. I thought that would be within reasonable range for a USB power bank, so I dug up a DC voltage boost converter (*) from a batch I had bought for an earlier project. I connected the voltage input to Arduino VIN pin and adjusted the converter to 14.4V open-circuit output voltage. But when I connected that output to the LCD backlight, voltage sagged and the USB power bank went into a continuous reset loop consistent with overload response.

I wasn’t sure if the overload was a startup issue, a transient issue, or a continuous power issue. As an experiment I soldered 220μF capacitors to both input and output. This did not change the behavior: the USB power bank still enters a continuous reset loop. I added a USB power meter (*) between my power bank and the Arduino and it said the circuit tried to draw 3 amps. Yikes! That explains the reset when I had expected only 0.6 amps (3 watts / 5 volts) to be drawn.

I’ll revisit the LCD backlight power supply issue later, if I decide to reuse this LCD for something fun. At least this failed experiment let me know boost converter power draw is more complex than (Power) = (Voltage)*(Current). It is also another checkmark next to “I should learn how boost converters work” on my to-do list, I hope with such knowledge I could properly diagnose this failure to verify I understand the situation correctly.


(*) Disclosure: As an Amazon Associate I earn from qualifying purchases.

Reuse Honda CD Connector for Arduino

Once I got over the excitement of lighting up the bulbs and LEDs on a Honda CD control panel, I returned to the original CD player mainboard. The labels on its panel connector gave me the confidence to apply power and expect illumination instead of the smoke of fried electronics. But now I have the labels documented for future reference, I will remove the connector for my own use.

Desoldering was mostly smooth except for the trio of ground pins. They were connected to thick ground traces that dissipated a lot of heat, making it difficult to melt the solder. For the final ground pin, I saw the solder had melted and pulled hard on the connector. Unfortunately, it had not yet melted all the way through so my yank damaged the pin. Well, at least I still have two intact ground pins to work with.

I still don’t know the manufacturer or name for this connector, but it is very similar to the connector I saw on a Toyota tape deck doing the same job. Which meant it shared the pin pitch of 0.1″ (just like a perforated prototype board!) and two rows 0.1″ apart (just like a perforated prototype board!) staggered with a 0.05″ offset. (Just like… oh no! It isn’t.) For the Toyota tape deck project, I gave up on a circuit board and directly soldered wires to connect pins. But I had been thinking about the problem since then and I have an idea I want to try.

I took one of my perforated prototype boards, cut a groove down a row of holes, and snapped it off. This exposed a row of semicircular vias that I could solder to one of the two rows on this connector. For the other row, I would still have to solder to wires directly.

It’s not nearly as solid of a connection as a custom circuit board with the proper pin layout, but it is still far better than nothing. My modified prototype board left just enough space to accommodate an Arduino Nano.

Flipping this assembly over allowed me to solder wires between the Arduino and the salvaged connector.

It is a far more compact and less accident-prone solution than my previous breadboard mess.

Sadly, it did not work straight off the bat. I had to do some debugging to bring it to parity with my breadboard solution, but the debugging session also solved a standing mystery.


Arduino code and other information for this investigation is available on GitHub.

Honda CD LCD Driver Problems

I’ve got the control panel for a Honda Accord’s CD/HVAC and I think I’ve found the electrical connections to talk to the LC75883 LCD driver. The software side was based on my LC75853 test program, which needed a few modifications to fit this LC75883 chip. It can control more segments, so I have to send three CCB messages of 9 bytes each instead of three messages of 7 bytes. Other than that, these two chips both respond to the same CCB addresses: 0x42 to send LCD control bits, 0x43 to read button presses. And they read the same number of buttons so there’s no change necessary there.

I launched the program and… nothing, the screen stayed blank while the Arduino ran. I turned the knob one step to see if my quadrature decoder routine worked, and I saw confirmation on the Arduino serial monitor but I also saw the screen came to life. What’s going on?

I quickly determined that the screen would go blank if one of the quadrature encoder phases are held to ground. The screen also blanks out if I press the button, which grounds a different pin. There’s something wrong with the electrical side, but it wasn’t as simple as a short circuit connecting +5V rail to ground. For one thing, the meter found no continuity between VDD and VSS. And for another, the +5V line stayed up when these events happen, allowing the Arduino serial output to continue running. I suspect I would learn more if I could see the behavior of the LCD partial voltage supplies VDD1 and VDD2 perhaps those voltages collapsed for some reason? But the chip pins were too small for me to get to them, and those pins weren’t brought out to the data connector for me to connect that way.

I can work around this grounding mystery by not pressing the power button and turning the knob two detents at a time. But even then, I have another problem: I could not read buttons with the LC75883 chip. Every time I pushed a button on the circuit board, the LC75883 signals that there’s a key activity to report. My code would go through all the motions to read the 32-bit report, but all bits would be zero. Could the “always low” data line be related to the knob/button grounding problem? Possibly, but at the moment I don’t know how to find it. I just worked around it the best I could to generate a segment map.


It’s not great, but my code to play with a LC75883 is on GitHub.

CL84209 Base Station LCD Segment Map

I have generated a printable lookup chart for the character set of an unknown LCD controller. This chip is embedded inside a black blob along the top edge of an LCD I salvaged from the base station of an AT&T CL84209 cordless phone system. The character set dictates what is rendered in two lines of 15-character alphanumeric text, each character is a dot matrix 5 pixels wide and 7 pixels tall. Below these two lines is a set of custom segmented LCD that can be controlled with 16 bytes, but there appear to be far fewer than 16*8 = 128 segments to control. A segment map will tell us which segments correspond to which bits.

I don’t have the datasheet for this chip, so I don’t know how it numbered its segments. I decided to mostly follow the precedence set by Sanyo LC75853 by numbering both bits and bytes in least-significant-first order. The difference for this map is that while LC75853 started counting from one, I’m going to start counting from zero.

My first attempt at mapping out these segments toggled them on/off alongside the character set data. When displaying characters starting with 0x00, I turned on all the segments whose number has zeroth bit set. For the character set starting with 0x10, I turned on all the segments with the next bit set, etc. In theory I could look at the on/off pattern as it cycled through 0x00 to 0x70 and determine its binary number. I also printed out the bit pattern to Arduino serial console, which looks like this:

0 1 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101010 
1 10 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 11001100 
2 100 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 11110000 
3 1000 0 11111111 0 11111111 0 11111111 0 11111111 0 11111111 0 11111111 0 11111111 0 11111111 
4 10000 0 0 11111111 11111111 0 0 11111111 11111111 0 0 11111111 11111111 0 0 11111111 11111111 
5 100000 0 0 0 0 11111111 11111111 11111111 11111111 0 0 0 0 11111111 11111111 11111111 11111111 
6 1000000 0 0 0 0 0 0 0 0 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 
7 10000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

In theory this can work, in practice I quickly got tired flipping through images and decoding binary numbers by hand. That was a lot more work than having a quadrature encoder knob to interactively select segments to activate. I had avoided wiring in extra components out of laziness, but between manual binary decoding and soldering, I decided a bit of soldering was the easier path towards this segment map:

This map clearly showed this particular segment allocation used less than 5/8 of available segments. Starting from position zero, we would have five bits for five segments (0, 1, 2, 3, 4) then three bits that were unused. (5, 6, 7) Then five more segments (8, 9, 10, 11, 12) and three unused bits (13, 14, 15). This repeats until the final set of five (120, 121, 122, 123, 124) which were also unused.

I was surprised that segment 9 (“MSG#” title for message count) was a separate segment from 4, the rectangular border around message count. I had expected them to be a single segment.

I was also surprised and annoyed at segment 27, which lights up all three horizontal lines of the hour tens digit for the clock plus the lower left vertical segment. For a clock it made sense to restrict the tens digit to 1 or 2. But if so, why did they bother with segment 26 which isn’t useful for either of those digits? I had hoped maybe I could use it as a generic numeral display and not a clock, by leaving 65 and 66 (AM/PM) inactive. But segment 27 means I could only display 1, 2, 6, and 8. I have not yet thought of an interesting non-clock usage under those restrictions.

Obtaining a full segment map marks the end of investigating this base station LCD, I will now try to do the same for the handset LCD starting with its disassembly.


Source code for this investigation is publicly available on GitHub.

CL84209 Base Station LCD Character Set

After a look at the similarities and differences between two LCDs salvaged from an AT&T CL84209 cordless phone set, I was ready to start controlling one myself to get more data. Earlier probing indicated that I2C communication on the handset was 3.3V, so I’m going to use an ESP8266 because it is also a 3.3V part. The Arduino platform should be a good (enough) starting point, using its Wire library to recreate the I2C control messages I saw sent to the handset LCD.

After a quick test program to verify things worked as expected, I wanted to dump out the character set built inside this LCD controller. Since the character data is sent one byte per character, there are potentially 256 different characters in the font. Dealing with these powers-of-two numbers, I like to keep such information lined up with their hexadecimal values. The LCD can only print up to 15 characters per line, so I couldn’t print these in 16 characters batches. As a next best thing, I dropped down to 8 characters per line along with their starting hexadecimal value. A for() loop cycled through 16 such screens once per second, so I could see the entire character set cycle through.

This is one of the sixteen steps in the cycle. The first line shows characters from 0x70 to 0x77, and the second line from 0x78 to 0x7F. Using workbench lighting, I had trouble trying to photograph the screen without glare. So, I switched to backlighting the LCD using one of my salvaged LED backlights. I took a picture for each of the 16 sections with the intent to edit them together, but the slightly undersized backlight made it difficult. The center section of the screen is noticeably brighter than the edges, which makes it difficult to set a threshold to generate a nice monochrome reference chart. I had to consult Emily Velasco for some photo-editing tips. (I have now added “burn” & “dodge” to my photo editing toolbox.) After some very informative experimentation in GNU Image Manipulation Program I have a reference sheet suitable for printing. Using this chart, I could see that 0x7F is a solid block that activates all pixels of a 5×7 dot matrix, which I will use to as part of an “turn everything on” program to help map out its adjacent segmented region.


Source code for this investigation is publicly available on GitHub.

Larson Scanner Demo for Tape Deck LCD

I am happy with a sense of accomplishment after I deciphered all the information necessary to utilize this circuit board, formerly the faceplate for a salvaged car tape deck. I started this investigation when I found I could power it up under control of the original mainboard. Now, I can work with the LCD and read all knobs and buttons with an Arduino, independent of the original mainboard. My original intent was just to see if I could get to this point. I thought I would learn a lot whether I succeeded or failed trying to control this faceplate. I have gained knowledge and experience I didn’t have before, and a faceplate I can control.

Now what?

It feels like I should be able to build something nifty with this faceplate, there’s room to be creative in repurposing it. At the moment I don’t have any ideas that would creatively utilize the display and button/knob input, but I could build a simple demo. This LCD is wide and not very tall, so I thought I would make it into a simple Larson Scanner. (a.k.a. Cylon lights a.k.a. K.I.T.T. lights.)

First, I divided the LCD segments into 16 groups of roughly similar size. I started using my segment map to generate the corresponding bit patterns by hand but then decided I should make a software tool to do it instead. I’ve already written code to light up one segment at a time for generating the segment map, it took only a bit of hacking to make it into a drawing tool. I use the knob to move segments as I did before, but now I could press the knob to toggle the selected segment. Every time I pressed the knob, I print the corresponding bit pattern out to serial terminal in a format that I could copy into C source code.

I then added a different operating mode to my Arduino test program. Pressing the Volume knob would toggle between drawing mode and Larson Scanner mode. While in Larson Scanner mode, I would select two of those 16 groups based on scanner position, and bitwise OR them together into my display. This gives me a nice little demo that is completely unrelated to this LCD’s original purpose, and confidence I no longer need this tape deck’s original mainboard.


Source code for this demo is publicly available on GitHub.

Pinout of Tape Deck Faceplate (Toyota 86120-08010)

It is time to wrap up investigation into the workings of a tape deck faceplate, salvaged from the stock audio head unit of a 1998 Toyota Camry LE. I believe I’ve deciphered all the information necessary to reuse this faceplate independently from the rest of the tape deck. Summarized in this pinout report with links to more details.

The faceplate circuit board is largely built around a Sanyo LC75853N chip, which communicates via a Sanyo proprietary protocol called CCB (Computer Control Bus). An external microcontroller (I used an Arduino Nano in experiments to date) can dictate what is displayed on the LCD (see segment map here) and scan pressed/not-pressed state of buttons (see button map here).

Some faceplate components are independent of LC75853N:

From right-to-left, functionality I observed on these pins are:

LabelFunction
ACC5VPower supply for digital logic.
+5V relative to GND
LCD-DOCCB digital data out.
LC75853N address 0x43
Single 4-byte transmission to microcontroller.
LCD-DICCB digital data in.
Microcontroller to LC75853N address 0x42.
Three 7-byte transmissions to LC75853N.
LCD-CLKCCB clock signal.
Active-low generated by microcontroller.
LCD-CECCB enable.
Active-high generated by microcontroller.
CD-EJEEject button.
Normally open, shorts to GND when “Eject” button is pressed.
ILLIllumination power supply (positive).
+5V to +14.4V (~60mA) relative to ILL- for variable button backlight brightness.
LCD-BLLCD backlight power supply (positive).
+6V (~60mA) relative to BL-
VOL.CONVolume control potentiometer.
Voltage between ACC5V (full clockwise) and GND (full counterclockwise)
PULS-AAudio mode quadrature encoder knob – A
ACC5V or GND, will be opposite of B when at a detent.
A and B briefly identical during transition between detents.
PULS-BAudio mode quadrature encoder knob – B
ACC5V or GND, will be opposite of A when at a detent.
A and B briefly identical during transition between detents.
GNDDigital logic power (negative).
Relative to ACC5V
ILL-Illumination power supply (negative)
Relative to ILL
BL-LCD backlight power supply (negative)
Relative to LCD-BL
RESETUnknown. Observed 0V relative to GND.
LC75853N has no reset pin.
Seems OK to leave it unconnected/floating.
Unknown. Observed 0V relative to GND.
Seems OK to leave it unconnected/floating.

Source code for this investigation (and accompanying demo) is publicly available on GitHub.

Button Presses on Tape Deck Faceplate (Toyota 86120-08010)

I’ve mapped out all LCD segments on a Toyota 86120-08010 tape deck faceplate, allowing me to control what is displayed from an Arduino. Constrained by the nature of a segmented LCD, of course. These segments were customized for a tape deck, doing what it needs and no more. Any projects trying to repurpose it would have to get creative. This will be even more difficult than abstract Nyan Cat on a VCR VFD!

But I have more than just the LCD, I have the entire faceplate which gives me option for user interactivity. Two knobs which I still have, and buttons which are now gone but their electrical traces are still present. I can find something conductive to bridge these traces like the buttons used to do, or I can solder wires connecting to switches elsewhere. Either way, I needed to write code for my Arduino to read key scan data from the Sanyo LC75853N chip. Just like LCD segment control data, my goal is to emulate the original mainboard as closely as I can. I will be guided by the datasheet and what my logic analyzer captured.

Thanks to lessons learned from doing LCD segment control CCB communication, reading keyscan data over CCB was relatively straightforward and I could dump control data out to Arduino serial monitor.

I will follow the KD1-KD30 numbering system used in the datasheet timing diagram.

Button map for Toyota 86120-08010 faceplate. (Click to view full size)

This key map shows the KD number for almost all of the buttons on the faceplate, including the push action on both knobs. The lone exception is the “Eject” button, which has its own dedicated wire and is not part of LC75853N key matrix. These numbers may look odd at first glance, but they make sense once we look at how they would be wired to the LC75853N:

These fifteen buttons make use of KS3-6 and KI2-5, learning only KD14 unused in its matrix. If I wanted to add in a single button, I will try to find KS3 and KI4 traces on the faceplate to wire in KD14. If I want to add more buttons, I might need to solder directly to the unused IC pins KI1, KS1, and KS2 as I wouldn’t expect any traces for those unused pins on the faceplate circuit board.

Feeling good that I’ve figured out the input & output of this faceplate, it’s time to wrap it all up.


Source code for this investigation is publicly available on GitHub.

Segmented LCD on Tape Deck Faceplate (Toyota 86120-08010)

Thanks to well-labeled connectors and an online datasheet, I can write Arduino code to control faceplate LCD of the stock tape deck audio unit from a 1998 Toyota Camry LE. (86120-08010). However, knowledge of the digital wiring doesn’t tell me anything about the physical location and shape of each segment in the LCD. I will build a segment map with the help of a knob already on the faceplate. The Sanyo LC75853N chip could control up to 126 segments. I edited my Arduino program to turn on all of them, so I could take this picture to see where segments even existed.

It reflected the custom nature of a segmented LCD. Some of these digits would only ever display numbers, so they had the standard 7 segments for numeric display. Others have a secondary use to display letters for a few audio settings, and those digits had more than 7 segments. But they can’t display arbitrary letters, only exactly what was needed and no more.

With the full set of available segments in hand, I changed the program to turn on just one segment at a time interactively selected via the “Audio Mode” knob. Since each segment is a single bit, I could print out my control bits to Arduino serial monitor to see current active segment’s programmatic address.

I’m using the same numbering system as used in the datasheet, D1 to D126. From there, I generated this segment map:

LCD segment map for 86120-08010. Click to view full size.

When I first started my Arduino program, I saw nothing for the first few turns of the dial. I thought perhaps my program was faulty, but a few turns later I saw my first result for D13. From there on it was relatively straightforward, working from bottom-to-top and right-to-left. Some notes:

  • D24 is missing, a mysterious gap.
  • There were a few segments that I had thought were separate but were actually the same segment. For example, two visually distinct segments were actually just D54. This was disappointing, because it restricted the number of letters we could show. (Example: a good looking “M” would be possible, but a good looking “N” wouldn’t.)
  • Along the same lines, some numeric segments looked like separate segments purely for the sake of preserving the 7-segment aesthetic. The three segments D101, D102, and D105 together could display either “1” or “2” but visually looked like 6 segments instead of 3.
  • The leftmost large numeric digit puzzled me. It could only display 1, which is fine. But given that, why are the two segments D91 and D92 individually addressable? I can’t think of a reason why we’d want to display only the top or bottom half of a “1”.

Here is a table from the LC75853N datasheet mapping segment numbers to pins. I colored in the segments that were seen and a clear pattern emerged: This LCD allocation avoided using the first four pins (S1-S4) leaving the option of using them as general-purpose output wires. (P1-P4) At the end, stopping at D105 meant they didn’t have to wire up the final seven output pins. (The final two S41 and S42 would have had to been reallocated from key scan duty KS1 and KS2, if used.)

None of this explains why D24 is missing. The answer to this mystery must lie elsewhere.

Now that I know what is on this LCD available to display, maybe I can think of a creative way to reuse it. While I think that over, I’ll proceed to work through how to read input from this faceplate.


Source code for this investigation is publicly available on GitHub.

Reading Faceplate “Audio Mode” Knob

Once I got a retired faceplate’s LCD up and running, I realized I was wrong about its backlight circuitry. Now that it’s been sorted out, attention returns to the LCD. I want to map out all the segments, which means I need to get some way to interactive select individual segments. Recently I’ve used ESPHome’s network capabilities for interactivity, but this project uses an Arduino Nano for its 5V operating voltage. I can wire up my own physical control, but the faceplate already had some on board. Earlier probing established Power/Volume knob is a potentiometer, and Audio Mode knob is a quadrature encoder with detent.

The encoder is perfect for selecting individual segments. I can write code to activate one segment at a time. When the knob is turned one way, I can move to adjacent segments in one direction. When the knob is turned the other way, segment selection can follow suit. However, this knob does have one twist relative to my prior experience: Each detent on this encoder is actually two steps. When the knob is at rest (at a detent) the A and B pins are always different. (High/low or low/high). Intermediate values where A and B pins are the same (high/high or low/low) occur between detents.

This places timing demands for reading that knob. For encoders where each detent is a single step change and turned by human hands, polling once every hundred millisecond or so would be fast enough. However, since this knob can flash through a step very quickly between detents, I need to make sure those steps do not get lost.

There are many quadrature encoder libraries available for the Arduino platform. I selected this one by Paul Stoffregen, who I know as the brains behind the Teensy line of products. When working with interrupt-capable pins, this library will set up hardware monitoring of any changes in encoder state. This makes it very unlikely for encoder steps to get lost. According to Arduino documentation for attachInterrupt(), all ATmega328-based Arduino boards (including the Arduino Nano I’m using) have two interrupt-capable pins: 2 and 3. Using those pins resulted in reliable reading of knob position for mapping out segments of this LCD.


Source code for this investigation is publicly available on GitHub.

Successful Arduino Test of LC75853N Control

Using a Saleae Logic 8 Analyzer, I’ve examined the communication protocol between the mainboard and faceplate of a car tape deck. These signals match expectations of a Sanyo LC75853N LCD controller which uses Sanyo’s proprietary CCB (Computer Control Bus) protocol. CCB has some resemblance to SPI and I2C but is neither, though close enough to SPI for me to use the SPI analyzer mode on a Saleae analyzer.

But “close enough” won’t be good enough for the next step: take an Arduino Nano and write code to talk to the LCD controller via CCB, copying the data waveform behavior I saw as closely as I can. Arduino has a library for SPI that assumes control of the enable pin, which has different behavior under CCB so that would not work here. I investigated using the shiftIn() and shiftOut() routines, which is part of the standard Arduino library. They are software implementations of a clocked serial data transfer routine, but unfortunately their clock signal behavior is different from what I saw of CCB under the logic analyzer. (Active-low vs. active-high.) In order to emulate behavior of the tape deck mainboard, I would have to write my own software implementation of CCB serial data transfer.

On the hardware side, I could no longer avoid soldering to small surface-mount connector pins on the back of the faceplate. I started simple by soldering the four data communication wires: LCD-DO, LCD-DI, LCD-CLK, and LCD-CE. Probing the circuit board with my meter, the only alternative soldering points were directly to the LC75853N, and those pins are even smaller. However, I found alternatives for ACC5V and GND: those were directly connected to the volume control potentiometer, which has nice big through-hole pins for me to solder to. I soldered these wires to a small prototype board with header pins, which then plugged into a breadboard alongside my Arduino Nano.

As a “Hello World” for CCB, I wrote code to replicate the control signals as closely as I could. I won’t try to replicate the exact timing of every pulse captured by my logic analyzer because (1) Arduino doesn’t make that level of control easy and (2) the CBB spec has no explicit requirement for precise timing anyway. However, I aim to make sure relationship between every clock, data, and enable pin high/low transition is preserved. I can verify this by capturing my Arduino output and compared the output to what I captured from the tape deck mainboard, look for where they are different, and fix differences over several iterations. Finally, I was satisfied the data waveforms look the same (minus the timing caveat above) and connected the faceplate.

These are almost the same LCD segments that are visible when I captured the data communication between mainboard and faceplate.

The only difference I see is “ST” in the upper right, which lights up when the FM tuner has a good enough signal to obtain stereo audio. Since this tape deck didn’t have an antenna attached, “ST” blinks on and off. Apparently, I had taken this picture when “ST” was on, and the recorded control signal I played back on an Arduino was when it was off. This is close enough to call my first test a success.

The other visible difference was the backlight: illuminated when I captured the data control message, but dark when I played it back. I had hoped the backlight was under LC75853N control somehow, but it looks like those LEDs are actually separately controlled.


Source code for this investigation is publicly available on GitHub.

Programming Mr Robot Badge Mk. 2 with Arduino

This particular Mr. Robot Badge Mk. 2 was deemed a defective unit with several dark LEDs. I used it as a practice subject for working with surface-mounted electronic devices, bringing two LEDs back into running order though one of them is the wrong color. Is the badge fully repaired? I couldn’t quite tell. The default firmware is big on blinking and flashing patterns, making it difficult to determine if a specific LED is functioning or not. What I needed was a test pattern, something as simple as illuminate all of the LEDs to see if they come up. Fortunately, there was a URL right on the badge that took me to a GitHub repository with sample code and instructions. It used Arduino framework to generate code for this ESP8266, and that’s something I’ve worked with. I think we’re in business.

On the hardware side, I soldered sockets to the unpopulated programmer header and then created a programming cable to connect to my FTDI serial adapter (*). For the software, I cloned the “Starter Pack” repository, followed installation directions, and encountered a build failure:

Arduino\libraries\Brzo_I2C\src\brzo_i2c.c: In function 'brzo_i2c_write':
Arduino\libraries\Brzo_I2C\src\brzo_i2c.c:72:2: error: cannot find a register in class 'RL_REGS' while reloading 'asm'
   72 |  asm volatile (
      |  ^~~
Arduino\libraries\Brzo_I2C\src\brzo_i2c.c:72:2: error: 'asm' operand has impossible constraints
exit status 1
Error compiling for board Generic ESP8266 Module.

This looks like issue #44 in the Brzo library, unfixed at time of this writing. Hmm, this is a problem. Reading the code some more, I learned Brzo is used to create I2C communication routines with the IS31FL3741 driver chip controlling the LED array. Aha, there’s a relatively easy solution. Since the time the badge was created, Adafruit has released a product using the same LED driver chip and corresponding software libraries to go with it. I could remove this custom I2C communication code using Brzo and replace it with Adafruit’s library.

Most of the conversion was straightforward except for the LED pixel coordinate lookup. The IS31Fl3741 chip treats the LEDs as a linear array, and something had to translate the linear index to their X,Y coordinates. The badge example code has a lookup table mapping linear index to X,Y coordinates. To use Adafruit library’s frame buffer, I needed the reverse: a table that converts X,Y coordinates to linear index. I started typing it up by hand before deciding that was stupid: this is the kind of task we use computers for. So I wrote this piece of quick-and-dirty code to cycle through the existing lookup table and print the information back out organized by X,Y coordinates.


      for(uint8_t x = 0; x < 18; x++)
      {
        for(uint8_t y = 0; y < 18; y++)
        {
          reverseLookup[x][y]=-1;
        }
      }
      for(uint8_t i = 0; i < PAGE_0_SZ; i++)
      {
        reverseLookup[page0LUT[i].x][page0LUT[i].y] = i;
      }
      for(uint16_t i = 0; i < PAGE_1_SZ; i++)
      {
        // Unused locations were marked with (-1, -1) but x and y are
        // declared as unsigned which means -1 is actually 0xFF so
        // instead of checking for >0 we check for <18
        if(page1LUT[i].x < 18 && page1LUT[i].y < 18)
        {
          reverseLookup[page1LUT[i].x][page1LUT[i].y] = i+PAGE_0_SZ;
        }
      }
      for(uint8_t y = 0; y < 18; y++)
      {
        Serial.print("{");
        for(uint8_t x = 0; x < 18; x++)
        {
          Serial.print(reverseLookup[x][y]);
          if (x<17)
          {
            Serial.print(",");
          }
        }
        if (y<17)
        {
          Serial.println("},");
        }
        else
        {
          Serial.println("}");
        }
      }

This gave me the numbers I needed in Arduino serial monitor, and I could copy it from there. Some spacing adjustment to make things a little more readable, and we have this:

const uint16_t Lookup[ARRAY_HEIGHT][ARRAY_WIDTH] = {
  {  17,  47,  77, 107, 137, 167, 197, 227, 257,  18,  48,  78, 108, 138, 168, 198, 228, 258},
  {  16,  46,  76, 106, 136, 166, 196, 226, 256,  19,  49,  79, 109, 139, 169, 199, 229, 259},
  {  15,  45,  75, 105, 135, 165, 195, 225, 255,  20,  50,  80, 110, 140, 170, 200, 230, 260},
  {  14,  44,  74, 104, 134, 164, 194, 224, 254,  21,  51,  81, 111, 141, 171, 201, 231, 261},
  {  13,  43,  73, 103, 133, 163, 193, 223, 253,  22,  52,  82, 112, 142, 172, 202, 232, 262},
  {  12,  42,  72, 102, 132, 162, 192, 222, 252,  23,  53,  83, 113, 143, 173, 203, 233, 263},
  {  11,  41,  71, 101, 131, 161, 191, 221, 251,  24,  54,  84, 114, 144, 174, 204, 234, 264},
  {  10,  40,  70, 100, 130, 160, 190, 220, 250,  25,  55,  85, 115, 145, 175, 205, 235, 265},
  {   9,  39,  69,  99, 129, 159, 189, 219, 249,  26,  56,  86, 116, 146, 176, 206, 236, 266},
  {   8,  38,  68,  98, 128, 158, 188, 218, 248,  27,  57,  87, 117, 147, 177, 207, 237, 267},
  {   7,  37,  67,  97, 127, 157, 187, 217, 247,  28,  58,  88, 118, 148, 178, 208, 238, 268},
  {   6,  36,  66,  96, 126, 156, 186, 216, 246,  29,  59,  89, 119, 149, 179, 209, 239, 269},
  {   5,  35,  65,  95, 125, 155, 185, 215, 245, 270, 279, 288, 297, 306, 315, 324, 333, 342},
  {   4,  34,  64,  94, 124, 154, 184, 214, 244, 271, 280, 289, 298, 307, 316, 325, 334, 343},
  {   3,  33,  63,  93, 123, 153, 183, 213, 243, 272, 281, 290, 299, 308, 317, 326, 335, 344},
  {   2,  32,  62,  92, 122, 152, 182, 212, 242, 273, 282, 291, 300, 309, 318, 327, 336, 345},
  {   1,  31,  61,  91, 121, 151, 181, 211, 241, 274, 283, 292, 301, 310, 319, 328, 337, 346},
  {   0,  30,  60,  90, 120, 150, 180, 210, 240, 275, 284, 293, 302, 311, 320, 329, 338, 347}
};

This lookup table got Mr. Robot Badge Mk. 2 up and running without Brzo library! I could write that simple “turn on all LED” test I wanted.

The test exposed two more problematic LEDs. One of them was intermittent: I tapped it and it illuminated for this picture. If it starts flickering again, I’ll give it a dab of solder to see if that helps. The other one is dark and stayed dark through (unscientific) tapping and (scientific) LED test equipment. It looks like I need to find two more surface-mount red LEDs to fully repair this array.

In case anybody else wants to play with Mr. Robot Badge and runs into the same problem, I have collected my changes and updated the README installation instructions in pull request #3 of the badge code sample repository. If that PR rejected, clone my fork directly.


(*) Disclosure: As an Amazon Associate I earn from qualifying purchases.

Window Shopping LovyanGFX

One part of having an open-source project is that anyone can offer their contribution for others to use in the future. Most of them were help that I was grateful to accept, such as people filling gaps in my Sawppy documentation. But occasionally, a proposed contribution unexpectedly pops out of left field and I needed to do some homework before I could even understand what’s going on. This was the case for pull request #30 on my ESP_8_BIT_composite Arduino library for generating color composite video signals from an ESP32. The author “riraosan” says it merged LovyanGFX and my library, to which I thought “Uh… what’s that?”

A web search found https://github.com/lovyan03/LovyanGFX which is a graphics library for embedded controllers, including ESP32. But also many others that ESP_8_BIT_composite does not support. While the API mimics AdafruitGFX, this library adds features like sprite support and palette manipulation. It looks like a pretty nifty library! Based on the README of that repository, the author’s primary language is Japanese and they are a big fan of M5Stack modules. So in addition to the software technical merits, LovyanGFX has extra appeal to native Japanese speakers who are playing with M5Stack modules. Roughly two dozen display modules were listed, but I don’t think I have any of them on hand to play with LovyanGFX myself.

Given this information and riraosan’s Instagram post, I guess the goal was to add ESP_8_BIT composite video signal generation as another supported output display for LovyanGFX. So I started digging into how the library was architected to enable support for different displays. I found that each supported display unit has corresponding files in the src/lgfx/v1/panel subdirectory. Each of which has a class that derives from the Panel_Device base class, which implements the IPanel interface. So if we want to add a composite video output capability to this library, that’s the code I expected to see. With this newfound knowledge, I returned to my pull request to see how it was handled. I saw nothing of what I expected. No IPanel implementation, no Panel_Device derived class. That work is in the contributor’s fork of LovyanGFX. The pull request for me has merely the minimal changes needed to ESP_8_BIT_composite to be used in that fork.

Since those changes are for a specialized usage independent of the main intention of my library, I’m not inclined to incorporate such changes. I suggested to riraosan that they fork the code and create a new LovyanGFX-focused library (removing AdafruitGFX support components) and it appears that will be the direction going forward. Whatever else happens, I now know about LovyanGFX and that knowledge would not have happened without a helpful contributor. I am thankful for that!

Initial Lessons on ESP8266 Arduino Sketch for InfluxDB

Dipping my toes in building a data monitoring system, I have an ESP8266 Arduino sketch that reads its analog input pin, converts it to original input voltage, and log that information to InfluxDB. Despite the simplicity of the sketch, I’ve already learned some very valuable lessons.

The Good

The Arduino libraries do a very good job of recovering from problems on their own, taking care of it so my Arduino sketch does not. The ESP8266 Arduino WiFi library can reconnect lost WiFi as long as I call WiFiMulti.run() periodically. And the InfluxDB library doesn’t need me to do anything special at all. I call InfluxDbClient.writePoint() whenever I want to write data. If the connection was lost since initial connection, it will be re-established and the data point written with no extra work on my part. I’ve had this sketch up and running as I’ve taken the InfluxDB docker container offline to upgrade to newer versions, or performed firmware updates WiFI access point which takes wireless offline for a few minutes. This sketch recovered and resume logging data, no sweat.

The Bad

ESP8266 ADC (analog-to-digital converter) is pretty darned noisy when asked to measure within a narrow range of its full range of voltages as I am. The full range is 0-22V, and I’m typically only within a narrow band between 12-14V. I tried taking multiple measurements and averaging them, but that didn’t seem to help very much.

This noisiness also made it hard to calibrate readings against voltage values as measured by my multi-meter. It’s not difficult to take a meter reading and calculation a conversion factor for an ADC reading taken at the same time. But if the ADC value can drift even as the actual voltage is held steady, the conversion factor is unreliable. Even worse, since the conversion is done in my Arduino sketch, every time I want to refine this value, I had to hook up a computer and re-upload my Arduino sketch.

Since I expect to add more data sources to this system, I also expected to query by source to see data returned by each. For this first iteration, I tagged points with the MAC address of my ESP8266. This is a pretty good guarantee of uniqueness, but it is not very human-friendly. Or at least not to me, as I’m not in the habit of memorizing MAC addresses of my devices.

The Ugly

As typical of Arduino sketches, this sketch is running loop() as fast as it could. Functionally speaking, this is fine. But it means the ESP8266 is always in a state of high power draw, with the WiFi stack always active and the CPU running as fast as it could. When the objective is merely to record measurements every minute or so, I could be far more energy efficient here.

Addressing these issues (and much more I’m sure) will be topic of future iterations. In the meantime, I have some data points and I want to graph them.


[Source code for this project is publicly accessible on GitHub]

Setting Up ESP8266 Arduino Sketch for InfluxDB

I think I’ve got the hardware side sorted out for building an ESP8266 that monitors voltage output of a solar panel, so it’s time to dive into the software side. I want to log this data into InfluxDB as a learning exercise, and the list of client libraries included a pointer to a GitHub repository for an InfluxDB client library for ESP8266 and ESP32 running on their Arduino core.

Arduino is not exactly the most fully featured development environment, so I’ve been doing my Arduino development using PlatformIO plugin of Visual Studio Code. However, this is the first time I’ve had to manually add a third-party library and it’s not the same as Arduino’s Library Manager so I had to go online for a little help like this forum thread. I learned I should go to https://registry.platformio.org/ and search for the desired library. Searching on “InfluxDB” resulted in several results, one of which is the same client library I found earlier but now with instructions on how to install into PlatformIO.

After compiling a simple test sketch, my serial output monitor returned gibberish. The key here is that baud rate must match between my platformio.ini configuration file:

monitor_speed = 115200

And my serial output initialization code in setup() function of my sketch:

  Serial.begin(115200);

Another configuration issue concern information necessary to connect to my home WiFi and my InfluxDB server. This little sketch needs that information to run, but I don’t want to include them in my source code since I intended to upload this to GitHub in a publicly accessible repository. I found a solution on StackOverflow: put my secret information in a separate secrets.h file. After I committed a basic version without any actual information, use the command git update-index --skip-worktree secrets.h to remove it from further Git activity. After that point, I could edit secrets.h and Git would not care to upload that information leaving my local secrets local, which is what I want.

Once all of these setup details were taken care of, I could dive into code and learn some valuable lessons out of the experience.


[Source code for this project is publicly accessible on GitHub]

Cat and Galactic Squid

Emily Velasco whipped up some cool test patterns to help me diagnose problems with my port of AnimatedGIF Arduino library example, rendering to my ESP_8_BIT_composite color video out library. But that wasn’t where she first noticed a problem. That honor went to the new animated GIF she created upon my request for something nifty to demonstrate my library.

This started when I copied an example from the AnimatedGIF library for the port. After I added the code to copy between my double buffers to keep them consistent, I saw it was a short clip of Homer Simpson from The Simpsons TV show. While the legal department of Fox is unlikely to devote resources to prosecute authors of an Arduino library, I was not willing to take the risk. Another popular animated GIF is Nyan Cat, which I had used for an earlier project. But despite its online spread, there is actual legal ownership associated with the rainbow-pooping pop tart cat. Complete with lawsuits enforcing that right and, yes, an NFT. Bah.

I wanted to stay far away from any legal uncertainties. So I asked Emily if she would be willing to create something just for this demo as an alternate to Homer Simpson and Nyan Cat. For the inspirational subject, I suggested a picture she posted of her cat sleeping on her giant squid pillow.

A few messages back and forth later, Emily created Cat and Giant Squid complete with a backstory of an intergalactic adventuring duo.

Here they are on an seamlessly looping background, flying off to their next adventure. Emily has released this art under the CC BY-SA (Creative Commons Attribution-ShareAlike) 4.0 license. And I have happily incorporated it into ESP_8_BIT_composite library as an example of how to show animated GIFs on an analog TV. When I showed the first draft, she noticed a visual artifact that I eventually diagnosed to missing X-axis offsets. After I fixed that, the animation played beautifully on my TV. Caveat: the title image of this post is hampered by the fact it’s hard to capture a CRT on camera.