FormLabs Form 1+ OpenFL API Connection

I had a longshot idea to revive the galvanometer control of a broken FormLabs Form 1+ resin laser 3D printer. It didn’t work and galvanometer remains broken. Because the printer also had a broken resin tray tilt motor and other smaller problems, I wasn’t trying to get it to print again. What I had in mind was to repurpose the optical core into a laser light show machine.

This was made possible because FormLabs opened up the Form 1/1+ for experimentation after they stopped supporting the hardware. Since they are no longer responsible if anything goes wrong, they freed the hardware for people to mess around with. This consisted of a special build of the PreForm software, which will flash a special (final?) edition of firmware. This firmware is willing to talk to a PC beyond accepting print jobs from PreForm. To make this communication easier, they released a Python library. All of these were posted on a GitHub repository under their “OpenFL” umbrella.

I really appreciate the fact FormLabs did this, exposing an API to control hardware independently of PreForm print jobs make it possible to do things completely outside the realm of printing. One of their examples turn the Z-axis stepper motor into a single-channel MIDI instrument making buzzy electromechanical music. The API also allows control of laser power and galvanometer position, which lead to my idea of turning the printer into a laser light show machine.

But first, I had to get it up and running. The first problem is that, as a seven-year-old Python library, it was written for Python 2 which is now discontinued. To create a backwards compatible Python environment, I used Miniconda to create a Python 2 environment called “openfl”

conda create --name openfl python=2

Following instructions in OpenFL repository I setup and installed Python dependencies. It allowed me to load OpenFL library but I immediately ran into an error.

Python 2.7.18 |Anaconda, Inc.| (default, Apr 23 2020, 17:26:54) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from OpenFL import Printer as P
>>> p = P.Printer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "OpenFL\", line 62, in __init__ = usb.core.find(idVendor=self.VID, idProduct=self.PID)
  File "C:\Users\me\miniconda3\envs\openfl\lib\site-packages\usb\", line 1297, in find
    raise NoBackendError('No backend available')
usb.core.NoBackendError: No backend available

This “No backend available” error came from pyusb library, complaining about a missing dependency outside of Python: we need a compatible USB driver installed. Solving this error required the following chain of events:

  1. Read up on PyUSB at
  2. Which point me to LibUSB at
  3. Which pointed me to its Wiki for running on Windows at
  4. Which recommended the Zadig utility at

Zadig offered several options for USB drivers, I tried them in the order recommended by LibUSB.

  1. WinUSB (v6.1.7600.16385): nope, still got “No backend available” error
  2. libusb-win32 (v1.2.6.0): looks good!
Python 2.7.18 |Anaconda, Inc.| (default, Apr 23 2020, 17:26:54) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from OpenFL import Printer as P
>>> p = P.Printer()
>>> p.state()

I’m in! Now to poke around and see what I can do with the laser.

FormLabs Form 1+ Electrical Failure Reproduced

I’ve taken apart a broken FormLabs Form 1+ laser resin 3D printer and laid out its entire electrical system on my workbench. Freed of most of its mechanical parts, it is a much more compact and easily explored layout. Ideal for me to try my long shot idea, which came to me when I started closely examining the damaged galvanometer control board and guessing at how it worked.

The hypothesis: root cause of failure may be a badly crimped connector, which is a common failure mode. It worked well enough to pass FormLabs quality assurance, but presented higher resistance than was desirable, wasting power as heat. Functionally it was probably fine, as this wire carried -24V which was converted to -15V DC by a voltage regulator. So if the extra resistance dropped a volt or two it would not have been noticed. But as time went on, this heat would have weakened and damaged things, raising resistance and temperature. Until one day things got hot enough to reach ignition temperature and started our electrical fire.

The examination: With the system powered up, I probed the front and confirmed -24V on the purple wire. Flipping the board over (an easy task now everything is laid out on workbench) I could access pins corresponding to the connector. I confirmed +24V and ground were as expected, and also confirmed only 0.6V DC instead of -24V DC on the final pin. To double-check, I also probed the input pin of L79 negative voltage regulator, and it also showed 0.6V DC. The -24V DC plane is not receiving -24V, consistent with the burned-out connector hypothesis.

The experiment: I cut the purple wire and soldered it directly to the connector pin on the back. If the connector was the only problem, this would bypass that connection and revive the galvanometer control board. If the connector is not the only problem, I have replicated the conditions leading to that electrical fire. I turned everything on and witnessed the following sequence:

  1. An orange glow at the base of the visibly burnt area. (Not in the connector.)
  2. Foul-smelling smoke from the new recreated fire.
  3. Heat melted solder and released the newly connected wire, breaking the connection and preventing any further damage.

The conclusion: I successfully replicated the conditions leading up to an electrical fire. Whatever went wrong on this board, it isn’t a bad connector. Or at least, not just a bad connector. This was an easy thing to try and I wanted to give it a shot. It would have been a great story if this had worked! I even had some ideas on what I would do with it.

FormLabs Form 1+ Internals on Workbench

I’ve been taking apart a broken FormLabs Form 1+ laser resin 3D printer because I wanted to learn as much as I can from a piece of hardware I probably wouldn’t have bought on my own. Also, it is taking up too much space. Once I separated the Z-axis subassembly from the rest of the printer chassis, I could place the entire electrical and electronics nervous system on my workbench. The printer wouldn’t print anything in this state, of course, but it thinks it could. I’m very amused to read “Ready to print” on the OLED status display.

My first experiment with this “benchtop printer” is to see what components are necessary to run through a print program. I knew some connections between components must have been open-loop, because the printer mainboard had no idea the galvanometer control board was fried nor that the resin tray tilt motor was damaged. What else is the mainboard unaware of? Here are my findings:

OK to disconnect:

  • GALVO X SIGNAL, GALVO X POWER, GALVO Y SIGNAL, and GALVO Y POWER. The galvanometers are a motor+sensor combination and requires closed-loop control with the galvanometer control board. However, the connection from printer mainboard to galvo control board is open loop.
  • TILT stepper motor is open-loop control, mainboard has no feedback on resin tray position.
  • LASER could be disconnected and still allow print program to run.
  • DISPLAY is one-way SPI communication.
  • LED wires for BUTTON is optional.

Needs to be connected:

  • INTERLOCK door safety is something the mainboard definitely checks. It can be bypassed by taping a magnet onto the sensor. Could also try connecting the sensor wire (blue) to ground (black) but I have not tried this.
  • Switch wires for BUTTON is needed for a confirmation press before printing process begins.
  • Z LIMIT and Z MOTOR are required to run through Z-axis homing procedure.

Laying out all the components on a workbench in running condition also lets me try a long shot idea: providing alternate negative voltage path for galvanometer control.

FormLabs Form 1+ Z-Axis Assembly

I think I can find new useful homes for several components of a broken FormLabs Form 1+ laser resin 3D printer. After carefully removing its laser optical core, I proceed to attempt salvaging its Z-axis actuator.

The Z-axis motion in this printer is driven by a stepper motor turning an Acme thread leadscrew. This concept is pretty commonly found in FDM 3D printers as well, but closer inspection unveiled a higher quality design. The first hint was the limit switch at the top. Cheap FDM printers use a microswitch, this printer uses an optical interrupter. Eliminating the flexible spring in a microswitch makes this limit switch more precise in marking its location. The print platform is mounted on a ball bearing carriage traveling on a guide rail, again more precise than the typical FDM printer usage of sleeves traveling on rods.

And finally, I noticed a detail significant in its absence: there is no shaft coupler between motor and leadscrew. A leadscrew as motor output shaft eliminates all problems introduced by couplers. No set screws to back out, no errors in concentricity between the two shafts, etc. Markings on the motor says LDO-42STH34-L291E. We can find LDO Motor’s page for their LDO-42STH line of 42mm hybrid stepper motors, but this particular model number is not listed. Searching for similar items revealed several other LDO motors with a leadscrew output shaft, all at significantly higher cost than generic NEMA17 stepper motors + leadscrew + coupler. Looks like this particular FormLabs motor variant is an engineering tradeoff of higher parts cost for higher precision.

However, all this precision means I have to make a decision on salvaging these parts. The linear rail, optical interrupter limit switch, and stepper motor are all mounted to the printer chassis. The chassis is constructed from several sheets of stamped sheet metal, folded and riveted together for rigidity. Good for optical stability, bad for me. If I remove each component separately, their relative precision alignment would be lost. If I want to remove the Z-axis as an intact sub assembly from the printer chassis, I have to perform the irreversible act of drilling out some rivets. After some thought I decided on the latter option.

Drilling out rivet heads would generate a lot of metal shavings. So before I got started with that destructive act, I wanted to remove the main mirror and get it away from scratch-inducing shavings.

The back side of the mirror has been glued to a metal plate with two embedded threaded rods. Removing two nuts freed the mirror assembly.

With the mirror stored safely away, it’s time to make some chips.

Roughly half an hour later, I’ve freed the Z-axis subassembly from all other pieces of stamped and riveted sheet metal.

I thought about grabbing my angle grinder to cut off the bottom, as it is not strictly related to the Z-axis assembly. But this is where the laser optical subassembly was mounted, and it’s also where the mirror was mounted. There’s a chance these pieces of sheet metal may yet be useful. Besides, it’s only minimally more than keeping the Z-axis itself. Even with this bottom portion, this subassembly is a lot less bulky than keeping the rest of the printer chassis together. And small enough for me to lay out everything on my workbench.

FormLabs Form 1+ Optical Core

Too many things have gone wrong with this old FormLabs Form 1+ laser resin 3D printer for me to think I could return to precision printing duty. A broken resin tray tilt motor was just the latest discovery added to that pile. But it’s such an interesting machine to take apart, filled with precision components that I don’t know how to use but couldn’t bear to pitch in the garbage. Top among this list is this laser optical core.

A precision machined piece of aluminum is home to four important optical components: The laser itself in a black cylinder, emitting a beam to strike two mirrors. Each mirror adhered to the end of two orthogonally mounted galvanometers. After bouncing off these two rotating mirrors, the beam strikes a small front-surface mirror directing the beam into the large center cavity. In that cavity is a much larger main mirror, who is angled to deflect the beam up into the resin tray to precisely harden some resin for the print.

Before I removed the core, I ran the optical path one last time. In place of the resin tray, I have placed a sheet of normal white office printer paper. I had no concept of the power level of this laser and wanted to see what it would do. The answer: no hole, no fire, no smoke, not even a little brown singe mark. Just a bright dot. I suppose it is possible that this laser isn’t driven at maximum power, but I am comforted to know that my initial fear of rampant laser destruction was misplaced. However, I will continue to assume it is powerful enough to cause eye damage and deserving of respect.

When removing this optical core, take care not to just blindly loosen every visible fastener. The three most visible fasteners are actually holding the laser and galvanometers in place. The galvanometer fasteners can be loosened in order to rotate the galvanometer to center their range of rotation and thus center the beam. I don’t know if similarly rotating the laser module would accomplish anything.

To remove the optical core, we actually need to unscrew the two deep set fasteners in opposing corners. These fasteners provide the force to hold the optical core in place, their orthogonally opposite corners host two alignment pins to locate the optical core on the printer chassis main spine.

At the moment I don’t know the electrical requirements to driver any of these three components, but I will try to keep this fragile optical core intact in case I can return with more knowledge about how to make them run. I also want to keep the Z-axis assembly, which is thankfully more robust than delicate optical components.

FormLabs Form 1+ Resin Tank Tilt Mechanism Damaged

I’ve been exploring several subsystems of a broken FormLabs Form 1+ laser resin 3D printer. After a rather disappointing session playing with its serial interface console, I’ve exhausted the list of electronics to explore and moving on to mechanical systems. First on the list: the resin tank tilting mechanism. I believe this is done to help with the peeling the partial print away from PDMS layer, but maybe more importantly, redistribute resin across the tank between layers. Tilt has stopped working on this printer, and I wanted to look for anything that might explain why.

Looking at the top part I see a leadscrew mechanism, but it wasn’t immediately obvious how it worked. Everything I see here seems to be rigidly fastened to something. I couldn’t rotate anything by hand. What was supposed to rotate on this mechanism driven by a stepper motor?

Once I could see the lower half of this mechanism, I saw why it failed: this white plastic portion has separated from the rest of the motor, no longer held by stamped sheet metal claws all around its perimeter.

Visible from the bottom is another problem: on either side of this motor are small hooks on the resin tray carrier. Each of which has a corresponding slot in the chassis. But there’s an alignment problem: the hook on the left side of this picture (front of the printer) looks good, but the right side (rear of the printer) hook is no longer aligned with its slot. This misalignment makes it very difficult to raise the resin tray back into place, and that stress might have contributed to white motor bottom pop out.

I saw no obvious explanation for the misalignment. There were no signs of distortion on the printer chassis, nor do I see any obvious problems with the tilt hinge. It was a surprise to see the hinge itself, though. With all the precision parts I’ve seen inside this laser instrument, it was jarring to see something resembling a cheap piano hinge I could get at the local hardware store.

Once I removed the motor, I could read its sticker:

LDO MOTORS            20150320

20150320 is probably a date of manufacture, March 2015 is within range of Form 1+ production.

LDO-35BYZ-B01-12 led me to information page for LDO Motors company’s line of 35mm linear PM stepper motors. Where I learned the only rotating part is inside the motor enclosure, acting upon the leadscrew passing through motor centerline. This is very novel to me, as it means there’s nothing to rotate externally and risk tangling wires. This motor is designed so there is only a linear motion in the output shaft. Either the motor can remain still and impart a linear motion to the shaft, or the motor could move along a static shaft. A Form 1+ bolted motor to chassis and its threaded output shaft is attached to the end of the resin tray carrier. Unfortunately, something went wrong with the tray tilt mechanism, exceeding the designed forces of this motor and breaking it.

I pushed on the dislocated white portion and, to my mild surprise, it was willing to pop back into place. The motor resumed operating as a linear actuator. Of course, this part has now been weakened so it is no longer as capable as it was when new. If I should try to reuse this linear actuator in another project, I’ll have to use it where the physical pushes in the opposite direction and/or supplement its failed metal claws with an external brace.

Next step on my hardware tour: the optical core.

FormLabs Form 1+ Serial Console Disappointingly Uninformative

I had a lot of fun with the OLED dot matrix display from a broken FormLabs Form 1+ laser resin 3D printer. It exhibits a slight bit of burn-in from its career as printer status display, but after my exploration I’m confident I can put it to use. Now I wrap up my exploration of Form 1+ mainboard with the 6-pin header in the corner labeled CONSOLE.

Out of 6 pins, three were labeled G, T, and R. G was a good candidate for ground, and my meter confirmed it had continuity to the ground plane. T and R would be good candidates for “Transmit” and “Receive” wires of a serial port. I first connected it to my oscilloscope to confirm these are 3.3V signals, then I connected it to my FTDI USB to serial bridge to see what’s shown on that console. I received legible data when configured serial port as follows:

  • 115200 baud
  • 8 data bits
  • 1 stop bit
  • No party
  • No flow control

I had hoped I would see low level information on this serial link, starting with perhaps the firmware version number. Since it is labeled CONSOLE I had also hoped for an interactive command prompt to query status and maybe diagnostics commands. Sadly, that did not seem to be the case. Or if it is, it required magic keypresses that I don’t know. There was no response to anything I tried. (Escape, Control+C, the usual suspects.)

Here’s what I saw upon power-up, with the unique items highlighted.

Starting setup...
initializing USB
setting up user interface
setting up ui spi port
starting UI timer
powering up SD card
setting up laser...
initializing temperature sensor...
setting up motors
setting up SD card
setting up print block processor
setting up odometer
...setup complete.

After powerup I pressed front panel button for printer startup. I saw a very similar but slightly different set of text. The differences are highlighted:

Starting initialize.
setting up user interface
setting up ui spi port
starting UI timer
powering up SD card
setting up laser...
initializing temperature sensor...
setting up motors
setting up SD card
setting up print block processor
setting up odometer
...setup complete.
turning on power.
initialization done

Beyond that… nothing. Nothing while downloading a print job, nothing while it is printing. I had hoped this serial console would display a superset of status information shown on front panel OLED display, but this is more likely a development feature that’s severely limited when running customer facing firmware. Oh well, at least I looked. And now it’s time to take apart some mechanical bits.

FormLabs Form 1+ OLED Burn-In

I’ve been playing with the OLED dot matrix display from a broken FormLabs Form 1+ laser resin 3D printer. After I explored all the functional aspects and documented its pinout information, I thought I’d explore a side curiosity: is there any visible burn-in on this OLED panel?

A characteristic of OLED technology is that the longer an OLED element has been shining, the dimmer it becomes. Usage pattern is a huge part of whether OLED dimming becomes a problem. If an OLED screen constantly shows static information, certain pixels would be illuminated for disproportionate amount of time relative to their neighbors. In this scenario, constantly illuminated pixels age much faster than their neighbors, and their difference in brightness results in “burning in” the static image. For example, OLED panels used for watching television or movies have working fine for years. But those same panels would show burn-in after a few months if used as static display for information. Displaying 3D printer status is definitely towards the unfriendly side of the spectrum, which is why I was surprised when I learned FormLabs used an OLED here. Did they make a bad decision? Could I detect signs of burn-in on this Form 1+ display?

While running the Adafruit example sketch, I didn’t notice anything obvious. This would support the optimistic view that any burn-in is negligible. But since I had the SSD1305 API at hand, I added a bit of code for full-screen white which illuminated all pixels at max power. When I did this, I could see signs of burn-in, but it is subtle. It is certainly very difficult to capture in a photograph, as I’m also fighting OLED refresh rate at the same time as I’m trying to capture a low-contrast effect. The title image for this blog post shows an unmodified picture where the burn-in effect is barely visible.

This image has been modified to enhance contrast, making the burn-in a tiny bit more visible. For reference, the information shown by a Form 1+ while printing is in the format of:

Job: "[Filename]"
HH:MM:SS remaining
Layer X of Y

Unchanging text “Job”, “remaining” and “Layer” are visibly burned in. Remaining variable data end up in a more distributed blur. Pixels between lines of text are rarely illuminated, so those lightly used pixels show up brighter than the rest. Still don’t see it? Perhaps it would help to have a set of text overlaid in red:

The unknown variable is how long this printer operated before it retired. If this printer provided many years of service and this is all we see after years of constant display, then I would grudgingly admit burn-in is negligible. If this printer only printed a few jobs before dying, then this is horrible. With these pictures I can conclude OLED burn-in is real, but this incomplete data point is not enough to say whether OLED burn-in is a real problem of concern.

One thing I did notice, though: FormLabs chose an LCD module for their Form 2 printers’ front panel display. That might be the most telling sign. I have yet to see a Form 3 in person, but I would expect an LCD for that as well. Maybe an OLED status panel isn’t the greatest idea if it would eventually render the machine unusable. Perhaps there are other ways to obtain printer status information? Earlier I noticed what might be a serial port on the mainboard, so I’m going to look into that.

FormLabs Form 1+ OLED Pinout

I have a broken FormLabs Form 1+ laser resin 3D printer and I’m learning what I can from taking it apart. On its front panel is a small OLED dot-matrix display that I have been exploring. I have now successfully controlled that OLED module using an ESP8266 development board.

Confirming the speculation in this FormLabs forum thread, the OLED module is very similar to the Newhaven Display International NHD-2.23-12832UCB3. Both of their display areas are 2.23″ diagonal with 128×32 pixels of resolution. They both use a SSD1305 controller, but while Newhaven’s module provided a single row of 20 pins, FormLabs custom built their own circuit board connecting to the rest of the printer with a 10-wire IDC ribbon cable. Only 7 wires are actually used.

This OLED module is also very similar to Adafruit product #2675 Monochrome 2.3″ 128×32 OLED Graphic Display Module Kit but without Adafruit luxuries like 5V logic level shifter and power buffering capacitor.

This module only requires a power supply of 3.3V DC, because it has an onboard voltage boost converter to supply other voltages needed by OLED. All logic high signals are also 3.3V DC. Data communication is via SPI protocol with an additional command/data select input wire. When that wire low, the chip will interpret SPI traffic as commands and when high, SPI traffic will be sent to graphics frame buffer.

  1. Ground
  2. Vcc to supply 3.3V DC
  3. Command/Data select
  4. SPI Clock
  5. Reset (Active Low)
  6. SPI Data In (*)
  7. SPI Chip Select (Active Low)
  8. Unused
  9. Unused, but connected to mainboard I2C bus data line
  10. Unused, but connected to mainboard I2C bus clock line

(*) There is no SPI Data Out pin.

Now this pinout is documented, I will explore side curiosities like potential OLED burn-in.

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.

FormLabs Form 1+ OLED Screen Updates

It looks like the OLED dot matrix display in my broken FormLabs Form 1+ laser resin 3D printer uses a SSD1305 controller, based on communication traffic captured during its initial power-up sequence. Walking through that data while cross-referencing with the SSD1305 datasheet taught me a lot, and now I can apply this knowledge to examine logic analyzer traces from OLED reacting to other printer activities.

The Form 1+ doesn’t really have a power switch. When the 24VDC power supply is plugged in, it immediately starts running which includes the OLED power-up sequence I examined. But the user doesn’t see anything, because OLED display frame buffer has been filled with all black pixels. It’s not until they press the front panel button does the OLED start displaying visible pixels, and I have a logic analyzer trace of this startup sequence.

Visually, the startup sequence is a short animation of FormLabs text and butterfly logo. From off the bottom of the screen, it translates upwards until the text and logo is centered on screen for a brief second marking the end. From that point on, the screen is used to display up to four lines of text. Before this animation started, I see an initialization sequence identical to the power-up sequence: set all parameters and clear all eight memory pages to zero. After that, the animation starts running. When reading the SSD1305 datasheet, I saw it had a vertical scroll mode where bitmap in memory can be scrolled by changing the rendering start address. I thought that’s what the FormLabs animation used, but it wasn’t. Each frame of the animation is a full screen update sending four blocks of data for pages 0-3.

It appears memory pages 4-7 are not actively used for this application, which makes sense as the SSD1305 is designed for up to 132×64 pixels and we only have 128×32 on this OLED. However, those four pages of update data are transmitted in reverse order. Page 3 first, then 2, then 1, then 0. I wonder why? Hypothesis: This is to minimize visual artifacts. Imagine what happens if we update a memory page at the exact same time SSD1305 is displaying data from that page. We’d see a part of the old image mixed in with the updated image. Assuming the SSD1305 renders in increasing page order, sending data in the same increasing order means worst case unlucky timing will mess up all four pages. But if we update page in decreasing order, even the unluckiest timing scenario means only one out of four pages would be messed up.

Guesses or not, I feel like I have a pretty grasp of this OLED display module. Enough to try controlling it with my own code.

FormLabs Form 1+ OLED Control Consistent with SSD1305

I’ve been looking at various components of a broken FormLabs Form 1+ laser resin 3D printer, right now the focus is its front panel dot matrix OLED display. My first attempt at using a logic analyzer on its control signals told me which wires were active, but the actual data were gibberish. Fortunately, a second attempt retrieved sensical data waveforms. Someone on the FormLabs forums speculated this was an OLED display built around a SSD1305 controller, so now I will see if my captured data matches commands listed in SSD1305 documentation.

This stream of data represents system powerup, a set of commands (channel 0 white line is low) sent just before the first batch of data (channel 1 white line is high). If I interpret these bytes in context of SSD1305 datasheet, I get the following:

  • 0xAE: Display OFF.
  • 0xD5 0x10: Set display clock divide ratio to 1:1 and oscillator frequency to 300kHz.
  • 0xA8 0x1F: Set multiplex ratio to 31. (0x1F)
  • 0xD3 0x00: Set display offset to zero.
  • 0x40: Set display start line to zero.
  • 0xAD: Master Configuration for external Vcc power supply.
  • 0x8E: ???
  • 0x20 0x02: Set memory addressing mode to 0x02 (Page Addressing Mode.)
  • 0xA0: Set segment remap to 0 (Column address 0 is SEG0)
  • 0xC8: Set COM output scan direction to 1 (Remapped mode. Scan from COM[N~1] to COM0)
  • 0xDA 0x12: Set COM pins hardware configuration. (Disable COM Left/Right remap, Alternative COM pin configuration.)
  • 0x91 0x3F 0x3F 0x3F 0x3F: Set current drive pulse width of BANK0, color A, B, and C all to maximum valid value of 0x3F (63 clocks).
  • 0x81 0xFF: Set contrast control for BANK0 to 0xFF. (256, which is maximum contrast.)
  • 0x82 0xFF: Set brightness for area color banks to 0xFF. (256, which is maximum brightness.)
  • 0xD9 0xD2: Set precharge period to 0xD2. (Phase 1 period of 0x2 clocks, phase 2 period of 0xD or 13)
  • 0xDB 0x08: Set VCOMH Deselect Level to 0x08, but 0x08 is not in the list of valid values?
  • 0xA4: Entire display ON to display RAM content.
  • 0xA6: Set Normal Display. (Instead of 0xA7 for Inverted.)

This looks like an entirely sensible chain of commands for initial startup, aside from the two gaps: command 0x8E and parameter 0x08 for command 0xDB. The datasheet I have is rev 1.9 dated May 2008, so it’s possible those commands were added later. Even though they didn’t quite line up with the datasheet, these matches are too close to have been a coincidence. I’m now convinced there is a SSD1305 (or very closely related derivative) controller inside this OLED module.

Three more commands round out the end of the startup sequence:

  • 0xB0: Set Page Start Address to 0.
  • 0x00: Set Lower Column Start address to 0.
  • 0x10: Set Higher Column Start address to 0.

After these are sent, the Command/Data line was raised signifying data transmission. A large number of zeros followed, then the C/D line was lowered in for another trio of commands:

  • 0xB1: Set Page Start Address to 1.
  • 0x00: Set Lower Column Start address to 0.
  • 0x10: Set Higher Column Start address to 0.

Followed by another large chunk of zeroes, and this repeated for all eight pages of memory. Documentation section 8.7 Graphic Display Data RAM (GDDRAM) gave a size of 132 x 64 bits divided into eight pages. By that math, there should be 132 bytes in each block of zeros, but I’m not going to count that by hand. There’s probably a way to count inside Saleae Logic software, but I went with a low-tech approach:

  1. Zoom into the trace so one large block of zeros span majority of my computer monitor.
  2. Using a ruler, I measured the physical width on screen of the first eight decoded bytes of 0x00: they are 33mm wide.
  3. 132 bytes / 8 bytes = 16. So if there are 132 bytes in the block of zeros, they should be (16 * 33mm =) 528mm wide.
  4. I measured the entire block, 525mm wide. Close enough!

Having learned this information about initial startup including clearing the screen memory, I can better interpret the data captured by the logic analyzer during my other test activities.

Second Try with FormLabs Form 1+ Display Board Signals

One lasting memory I have from the movie Apollo 13 is the line “is this an instrumentation problem or are we looking at real power loss?” When an instrument tells us something is wrong, it’s possible the problem is in the instrument and not in the system it is measuring. I thought of this when I looked over the initial set of logic analyzer traces of data sent to an OLED display module. The traces superficially resembled SPI, but with many traits inconsistent with SPI. Before I dive into a rabbit hole of trying to figure out strange data, I wanted to make sure it isn’t an instrumentation problem.

The first thought was sampling rate. I gathered information for 8 channels because I had an 8-channel logic analyzer. But there’s a tradeoff. Sampling frequency drops as number of channels go up. For the base model Saleae Logic 8 that I have, polling all 8 channels drops it down to 25MS/s (25 million samples per second.) This might not be fast enough, because SPI peripherals could go all the way up to a clock rate of 50MHz. My first round of probing found only three wires with interesting activity, so dropping sampling down to 3 channels let me increase sampling rate to 100MS/s. Which is the minimum requirement to capture a 50MHz signal, but I doubt this OLED is running that fast. If this is a SSD1305 controller, its datasheet (Table 13-4 Serial Interface Timing Characteristics) lists a clock cycle time minimum of 250ns which translates to a maximum clock speed of 4 MHz. I figured even if it isn’t a SSD1305, it likely operates at similar speeds.

The next step was to redo all physical connections. I disconnected all eight probes and reconnected to reseat just the three channels I care about. I switched to a different USB cable for the Saleae, and I plugged it into a different computer that had two advantages: (1) it had a faster processor, and (2) I could connect to an onboard USB port. (I didn’t need a USB hub.)

This second set of traces look more like the SPI signal I expected, so my problem was indeed instrumentation. But the white line (channel 0, display input pin 3) is still clearly not an SPI chip select signal, as data transmission occurs both at high and low levels. What might it be? Looking into the SSD1305 datasheet, I saw its SPI mode required an extra pin labeled D/C#. This is the Data/Command control pin telling the OLED how to interpret incoming SPI traffic. If this line is low, SPI traffic will be interpreted as commands. If this line is high, SPI traffic will be interpreted as data. This could explain what I see, but for final confirmation I will examine the data to see if it’s consistent with SSD1305 communication.

First Look at FormLabs Form 1+ Display Board Signals

I’m working to understand the OLED dot matrix display from a broken FormLabs Form 1+ laser resin printer. It is hosted on a FormLabs custom circuit board and, after tracing through copper traces of that board, I have a candidate list of five wires for further investigation. When I went to attach my Saleae logic analyzer, I decided to attach probes to all eight unknown wires. (Out of ten wires total and I have identified two: the ground and 3.3VDC Vcc wires.) It wasn’t much extra effort and I was curious if anything was going on. I then captured traces for four activities:

  1. Power-on: when I plugged the 24VDC power supply into the printer.
  2. Startup: when I pressed the front panel button to start its logo animation, ending at the “lid is open” warning.
  3. Steady: Several seconds when the display stays at “lid is open” warning, with no updates.
  4. Ready/Open/Ready: Using a magnet, I toggled the display from “ready” to “cover open” and back to “ready” again.

Trace for “Steady” showed no activity at all. I had expected the system to refresh the display periodically regardless of update activity, but I just captured five seconds of silence. This is quite a contrast from the super chatty display from an AT&T CL84209 handset where I had 2569 messages within 10 seconds! Here I have nothing. Well, at least that was out of the way.

Trace for “Power-on” and “Startup” was interesting because it captured activity on two of the three wires that were unused. One looked like clock and another looked like data, so I asked Saleae Logic to treat them as I2C. They came back as valid I2C messages.

write to 0x48 ack data: 0x01 0x00 
write to 0x48 ack data: 0x00

Hypothesis: printer mainboard has an I2C peripheral bus and it’s been routed all the way to the OLED display module circuit board. I2C is not used by this particular display module, but the design gives FormLabs an option to switch to an I2C display module without changing the rest of printer hardware. In the meantime, a logic analyzer connected to the display module would see traffic on the I2C bus. By this hypothesis, such traffic is intended for other components instead of this display, so I’ll ignore it until/unless I discover a reason to revisit. [UPDATE: I found a NXP LM75B on the mainboard, an I2C temperature sensor that could answer to address 0x48.]

Back to the five wires of interest: three wires showed activity correlating with screen updates. I didn’t see any activity independent of screen update, so these wires might be a dedicated peripheral data line. If it is a peripheral bus, every other peripheral on the bus stayed quiet during my test set of activities. At first glance I thought this was SPI, but a closer look revealed behavior inconsistent with SPI.

  • Mainboard cable pin 3 — connected to logic analyzer channel 0 (white line) — showed infrequent level changes near the start of every activity. A good candidate for SPI “Chip Select” or Enable, except data transfers seem to happen both when it is low and when it is high. Which shouldn’t happen if it is indeed Enable.
  • Mainboard cable pin 4 — connected to logic analyzer channel 4 (yellow line) — shows regular level changes during every activity. A good candidate for SPI Clock, except the candidate data line changes within each “clock” pulse” which shouldn’t happen if it is indeed clock.
  • Mainboard cable pin 6 — connected to logic analyzer channel 5 (green line) — shows level changes at irregular bursts. A good candidate for SPI Data, except it pulses out of sync with “clock”.

If this is SPI, why does it look weird? If this is not SPI, what is it? I’ll have to check over my setup and try again.

FormLabs Form 1+ Display Board Routing

I’m working to understand the OLED dot matrix display from a broken FormLabs Form 1+ laser resin printer. Thanks to FormLabs user forums I have a lead on an OLED module that might be using the same OLED display. However, the OLED is hosted on a different circuit board. Publicly downloadable information exists for that board, so I will use it as a guide in my exploration.

On the printer mainboard, next to the DISPLAY label for this connector is a number 1. The closest pin appears to be system ground and the red wire in the ribbon cable. I will use that as the starting point for display input pin numbering.

Examining the front, I could see there’s a connection between pin 1 and majority of copper on this side of the circuit board, giving us a generous ground plane. The copper trace connecting to pin 2 is wider than any other on this board. It measures 3.26V DC when the system is powered up, making it the best candidate for power input. It feeds into the network of components mounted on this circuit board, which then has its own traces to the OLED. This is strongly suggestive of a power-related circuit. I measured those traces and found a few different higher voltages. Conclusion: these components implement a voltage boost converter.

Out of remaining eight wires in the ribbon cable, it seems like only five are used. Those five signals were routed together towards the OLED.

This diagram captures what I could determine by visually following traces of copper. On the other end of these traces is the sheet of yellow FPC. It has “1” printed on the right and “24” on the left, so I’ll happily use them as pin numbers. Using that system, I have a first draft for the OLED wires on that yellow FPC. Right to left in the above picture, they are: [UPDATE: added information from deciphered pinout.]

  1. NC (Not Connected)
  2. GND (Ground — connected to display input pin 1)
  3. GND
  4. NC (Only wire to visibly end inside the FPC.)
  5. 3.3V — connected to display input pin 2
  6. GND
  7. GND
  8. Display input pin 7 [UPDATE: SPI Chip Select (Active Low)]
  9. Display input pin 5 [UPDATE: Reset (Active Low)]
  10. Display input pin 3 [UPDATE: Command/Data Select]
  11. GND
  12. GND
  13. Display input pin 4 [UPDATE: SPI Clock]
  14. Display input pin 6 [UPDATE: SPI Data In (there is no SPI Data Out)]
  15. NC
  16. GND
  17. GND
  18. GND
  19. GND
  20. GND
  21. 9.0V supplied by boost converter
  22. 8.1V supplied by boost converter
  23. 12.6V supplied by boost converter
  24. NC

This explains the majority of wires going into this OLED module, leaving five unknowns that connected input IDC ribbon cable directly to OLED FPC. Those five wires will be the focus for further exploration and my Saleae logic analyzer will give me some insight as to what’s going on.

FormLabs Form 1+ Display Board Rev 01

Following wires on the mainboard in a broken FormLabs Form 1+ laser resin 3D printer, I found harnesses leading to several components mounted on the front panel. There is a sensor to detect if the lid is open. (Or more technically, if a magnet is nearby. A magnet is embedded in the lid.) There is also a button combined with embedded LED illumination. And finally, the dot matrix display.

When this printer boots up, a short logo animation is shown before proceeding to display text, indicating arbitrary dot matrix graphics capability and not restricted to alphanumeric character display of a built-in font. Which is great for flexibility but would also mean it is more complex to operate.

After removing four tiny hex screws I could pull the display circuit board free. (Promptly losing one of four plastic spacers between the circuit board and front panel.) Now I can confirm its blue color came directly from the display and not a tint imparted by front panel plastic window. We also see the circuit board is a FormLabs custom breakout board labeled with:


But what is that module hosted by the board? I couldn’t see any identifying markings as-is. Maybe there are some on the back? The display module is held in place by four twisted rear metal tabs that I could straighten out with pliers, freeing the module.

I had noticed the pixel illumination didn’t look like backlit LCD, and here we see it very clearly marked as an OLED unit. Nice! Printed on the back were the following information:


And my search for that text came up with… nothing. Quite disappointing! I went on the internet to see if anyone else has identified this OLED module but didn’t find anything definitive. What I found was “Possible OLED display swap/spare” on the FormLabs user forums, posted by someone before they even received their Form 1 printer. They speculated it might be an NHD-2.23-12832UCB3 by Newhaven Display, which has a SS1305 controller. Sadly, they never returned to FormLabs forum to confirm their speculation, so I guess I’ll have to try that myself.

FormLabs circuit board has 10 IO pins in two rows of five. Newhaven Display’s website shows a very different breakout circuit board of 20 IO pins in a single row. But this difference could be explained. The display has very flexible IO configuration options: “This self-illuminating module has 6800/8080 parallel, serial SPI or I2C interface compatibility.” A custom FormLabs circuit board would only need to accommodate the interface they actually use in the product, ignoring pins for other interface options.

The collection of components on the FormLabs board looks broadly similar to the Newhaven board, though missing a few parts that could likewise be explained by supporting just data one interface instead of several. I would feel better if there’s a match between information printed on the OLED module itself and what’s listed on Newhaven Display website. But I felt more confident after looking at the thin FPC ribbon cable connecting the OLED to the circuit board: They are both 24-pins and have a very similar layout, including a truncated pin 4. Furthermore, they are both printed with the text:

E308847 F-D 1 94V-0

If these were two different displays, they’re using the same FPC ribbon cable so there’s a chance they use the same control protocols. Newhaven Display website says they welcome custom orders so maybe PG-2832ALBM is a FormLabs custom derivative of 2832UCB3? Either way, it is a promising starting point for a deeper look.

More FormLabs Form 1+ Mainboard IO

I turned on a broken FormLabs Form 1+ resin laser printer without its rear panel so I could poke around. I started with cables (POWER, X SIGNAL, Y SIGNAL) between its mainboard and galvanometer control board, because that’s where things broke. But now I have a better understanding (and even a long-shot idea to try) I came back to measure voltages of wires on the remaining mainboard connectors.

The Z-axis linear motion actuator (Z MOTOR) is driven by a bipolar stepper motor with two phases and two wires for each phase, the four wires labeled A+/A-/B+/B-. The resin tray peeling actuator (TILT MOTOR) is physically a very different motor but driven in the same style. Z-axis limit switch appears to be an optical interrupter which can be more accurate than the physical contact microswitch typically used for limit switch duty in FDM printers. It has three wires. One end has continuity to ground, the other end measured +5V while the printer is powered up. Middle wire measured 0.06V when Z-axis is positioned at top, and 3.7V when Z-axis is lowered.

As part of laser eye protection, the printer stops printing if we open the lid. Detecting this condition is the job of a (likely Hall effect) sensor just under the lid along with a magnet embedded in the lid. The magnet sensor plugs into the connector labeled INTERLOCK on the mainboard, with three wires colored black, blue, and red.

Wire ColorVolts DC (No magnet)Volts DC (Magnet nearby)
Black0V (Ground)0V (Ground)
Blue11.18V DC0.02V DC
Red11.18V DC11.18V DC

Speaking of that laser, I had hoped to see a diode module with two wires positive and ground. (Example*) But LASER connector actually had four wires: red, black, blue, and yellow.

Wire ColorLaser StandbyLaser Illuminated
Red11.18V DC11.18V DC
Black8.55V DC6.6V DC
Blue5V DC3.3V DC
Yellow0V (Ground)0V (Ground)

I’m sure those voltages aren’t the whole picture so if I have the ambition to drive this laser module myself, I have more investigation ahead.

Next connector is BUTTON for a gorgeous illuminated metal button on the front panel. Four wires support this button, two for the LED and two for the switch. Red wire is LED+, gray wire is LED- (wired to ground on this circuit board.) Yellow is pulled up to 3.3V on this board and when pressed, yellow wire shorts to black which is wired to ground.

The final connector is a little more complex. It is a 10-pin (2×5) IDC ribbon cable for the front panel display. Probing that display will be a project all by itself.

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

Guesses on FormLabs Form 1+ Galvanometer Control Board

The laser galvanometers on this FormLabs Form 1+ resin laser printer no longer move, which rendered the printer useless. Probing voltage of wires leading to those galvanometers weren’t as enlightening as I had hoped but hinted that actuator position sensors were analog. Earlier probing of wires between printer mainboard and galvanometer control board, I also found mainboard commands were analog voltages.

With these analog inputs, I was confused by the fact I found no analog-to-digital converters among the list of ICs. The STM32 microcontroller has but a single ADC channel. And even if it had enough channels, I doubt they’d be fast or accurate enough. (Built-in ADC peripheral proved to be limited when I used a ESP32 to measure voltage, and when I used an ESP8266 to measure temperature.) I found many digital potentiometers and a two-channel SPI-controlled DAC for converting digital commands to analog voltage, but not the other way around.

I went online to look at other galvanometer control boards. Looking at this unit (*) and this unit (*) found a few commonalities. They have six wires leading to their galvanometers as well, hinting at a standardized system or at least a convention. I see they also want positive and negative supply voltages, which matches what I’ve seen. I also noticed they want different input voltage ranges, and their galvanometers don’t operate with the same mechanical ranges. (Movement in terms of degrees of rotation.) Dashing any hope of a direct replacement.

They also have 6 or 7 potentiometers on board for tuning, and this was my “A-ha!” moment. I saw similar potentiometers on the teardown of a Form 1, but such manual tuning potentiometers are missing here. This is what the STM32 and those digital potentiometers/SPI DAC are for: instead of manual tuning control with potentiometers, the upgraded Form 1+ galvanometer control board uses a STM32 to manage those parameters using those SPI-controlled digital potentiometers. The high-speed control loop is still an entirely analog process built out of those quad-pack op-amp chips.

Looking at this system with new knowledge, I see a tempting possibility. When I turn on the printer, I see a few brief LED flashes on the galvanometer control board as if STM32 booted up. Probing the 3.3V regulator (component U5) I saw it received +24VDC input and output the expected +3.3V DC for digital logic. Looking at the burned-out power connector, I saw the drama happened with the purple wire which I now know carried -24VDC. Perhaps this damage meant the board no longer has negative voltage and that’s why analog control system stopped working. What if I soldered the purple wire to some other location on the -24VDC plane? Say the input leg on one of two L79 negative voltage regulators? There’s a good chance it’ll only recreate the electrical fire, because I have not fixed the root cause of whatever caused that fire. But I see a slim chance rerouting -24VDC will get the galvanometers running again.

While I contemplate this potentially destructive experiment, I will look at other parts of this printer starting with the rest of those mainboard connectors.

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

FormLabs Form 1+ Laser Galvanometer Voltages

I’m learning what I can from a broken FormLabs Form 1+ laser resin 3D printer, and it was instructive to probe voltage of wires between its (apparently functioning) mainboard and its (definitely toasted) galvanometer control board. Since I had my tools ready, I thought I’d take a look at voltages of wires leading to those galvanometers themselves. Even though they’re not moving anymore I had little to lose and hoped to learn something.

There weren’t many markings on these galvanometers. There are two stickers on both. On a flat end is a sticker with “ct-pass” probably for quality assurance during production. On the side of the cylinder is another sticker. One read “1411-0765 X” and the other “1411-0985 Y” Given that the X and Y axis are labeled, these are probably not part numbers. But what else would they be? Maybe something from the calibration process? I might see more markings if I remove these galvanometers from their aluminum mount, but I didn’t want to do that just yet. In the remote chance I could get things working, removing the galvanometers would ruin their factory direction calibration because I wouldn’t be able to put them back exactly in the same place.

Electrically speaking, from galvanometer Wikipedia page I knew a coil of wire to be involved, and there should be some way to read current position for closed-loop control. Examining the thin circuit board hosting the connector, I see the white and red wires on one end routed to one side together, a candidate for coil wires. Remaining four wires were routed together in a different direction, and I guess they are involved with position sensing in some way. Perhaps a high-resolution quadrature encoder? Or perhaps the four wires communicate position digitally via I2C or SPI? I did see a lot of chips on the control board communicate using SPI.

Turning the system on, I measured the following standby voltage values:

Wire ColorVolts DC

Not terribly informative by themselves, sadly. I didn’t want to probe these voltages while the laser is shining, out of concern for my eyeballs. But I was curious to test my hypothesis of position sensing. Using fine-tipped tweezers, I rotated the output shaft while watching voltage values. The yellow wire showed a tiny change that correlated to shaft position: from -0.014V to -0.078V. The adjacent red wire showed a larger range, from 0.044V to -0.235V, also correlated to shaft position. Both of these are tiny changes in voltage, but the fact there’s a repeatable correlation of voltage rules out quadrature encoder or digital data communication. This is an analog position sensor of some type. If it were like potentiometers I’ve dealt with, then at one end of the range of motion either yellow or red wire should get up to +1.328V provided by a white wire. But it didn’t, so something else is going on. These observations aren’t enough for me to fully understand workings of this board, but enough for me to formulate a few guesses.

FormLabs Form 1+ Laser Galvanometer Control

Despite some concerns, I’ve decided to poke around inside a broken FormLabs Form 1+ laser resin 3D printer while it is running. I think it’ll tell me more about how it was supposed to work, because I didn’t learn very much from just a roster of ICs. But it still wouldn’t tell me everything, because some of its current behavior would be as intended while other behavior would be wrong because the printer is broken. And as my only example of the species, it might be hard to tell which is which.

Before I powered it up, I probed around for all the ground wires. Since the printer is powered by 24V DC delivered through a standard barrel jack, it was least easy to probe for continuity to ground. There was even an exposed pad on the main processor board labeled GND. Knowing which wire is ground for each connector established a baseline for voltage measurements once I turned the printer on.

The first surprise was on the GALVO POWER connector. It had three wires colored red, black, and purple. Black was known to be ground. Red was expected to be +24V DC, which was confirmed. I didn’t know what to expect for purple, and it turned out to be -24V DC. Negative voltage! Something I rarely see but it would be consistent with the presence of a ST Micro L79 negative voltage regulator delivering -15V. Which is a mirror match for the L78 positive regulator taking +24V DC and delivering +15V DC. Separately there is an On Semi NCP1117 delivering 3.3V for digital logic.

As for GALVO SIGNAL, it had three wires colored black, white, and red. Again I knew black was ground from earlier probing. Once the system was powered on, I saw white wire voltage stayed pretty consistent at 2.5V DC. The red wire was where interesting things happened: It varied between 0V to 5V relative to ground (or -2.5V to +2.5V relative to white wire) while this printer went through the motion of printing, blissfully unaware its galvanometer control system was fried. Plotting X and Y galvo signals against each other on my oscilloscope’s XY mode, it sure looks like a laser draw path. That is, as long as we ignore that diagonal noise I blame on my beginner level oscilloscope skill. Anyway, noise aside, it shows this 0-5V (Or -2.5 to +2.5V) analog voltage is how mainboard commands galvo position. This scorched control board couldn’t translate those commands into galvanometer movement, but I wanted to see if anything is getting to those galvanometers at all.