This is a DIY ESP32-based split-flap display, optimized for easy assembly at home in small quantities but able to be scaled up to large affordable displays.
Latest auto-generated (untested!) artifacts
Instructions: v2 assembly guide
Module dimensions:
Latest auto-generated (untested!) artifacts
Note
For small displays (up to 3 modules), you can skip the custom controller boards and use off-the-shelf ULN2003A driver modules plugged into an Arduino Uno. This is partially documented in the wiki but may require some additional tinkering to get it to work. Help wanted: if you'd like to help improve these instructions, please reach out in the Discord server, thanks!
The "Chainlink" electronics system is designed to support long chains of driver boards to control medium/large displays (up to 100+ split-flap modules). It's also designed to be easy and cheap to order pre-assembled or build yourself, especially in higher quantities, due to its simple BOM and surface-mount components.
To build a display, you'll need 3 different electronics:
Each module needs a hall-effect sensor for start-up calibration and fault monitoring.
Older sensors for the v0.7 and older laser-cut hardware can be found in the tagged sensor release.
These older sensors are not compatible with v2 laser-cut hardware.
New sensors for the v2 laser-cut hardware - these use surface mount components and are optimized for PCB assembly at JLCPCB. These new sensors are not compatible with v0.7 and older laser-cut hardware.
Packs of 6 sensors are available mostly-assembled in the Bezek Labs store, and come with the right-angle pin headers and magnets you'll need. Purchases support continued development of this project.
Latest auto-generated (untested!) artifacts
Key features:
Chainlink Driver boards are available mostly-assembled in the Bezek Labs store, and come with the additional connectors and ribbon cables you'll need. Purchases support continued development of this project.
More information on building and using Chainlink Drivers is available in the Chainlink Driver User Guide.
Or if you'd like to order these directly from a fab, this design is optimized for assembly at JLCPCB, and files are automatically generated for ordering assembled PCBs there. Or if you wish to assemble this board yourself instead of paying for assembly, you can view the interactive BOM/placement tool
Depending on available stock at JLCPCB, you may need to manually modify the BOM file to use alternative components, or regenerate the files
yourself using export_jlcpcb.py
and specifying one or more LCSC_ALT_*
field names to use a pre-selected alternative part number. See
the schematic for available pre-selected alternatives (check the symbol's properties/fields).
Latest auto-generated (untested!) artifacts
The Chainlink Buddy [T-Display] is a convenient way to connect a T-Display ESP32 board (recommended microcontroller) to a chain of Chainlink Drivers.
Key features:
Chainlink Buddy [T-Display] boards are available in the Bezek Labs store, and come with the additional connectors you'll need. Purchases support continued development of this project.
Latest auto-generated (untested!) artifacts
The Chainlink Buddy [Breadboard] makes it easy to connect a Chainlink Driver to a breadboard for prototyping. You could use 5 dupont wires and have a messy rats nest, or you could use a single ribbon cable and this slick breakout board.
Chainlink Buddy [Breadboard] boards are available in the Bezek Labs store, and come with the additional connectors you'll need. Purchases support continued development of this project.
Latest auto-generated (untested!) artifacts
For larger displays, you should take additional care to make the hardware more robust to potential faults. The Chainlink Base is an experimental (but unsupported) controller design that adds some additional functionality. This has been tested and appears to work, but is not recommended for general use.
The Chainlink Base PCB is an optional alternative to a Chainlink Buddy, designed for particularly large displays. It hosts the ESP32 and adds additional connectivity options (terminals for UART and RS485 serial) and power distribution (independently-monitored power channels for multiple "zones" of Driver boards).
Key features:
Latest auto-generated (untested!) artifacts
The Classic driver board is deprecated and unsupported.
The Classic controller board was designed to plug into an Arduino like a shield, and could control 4 stepper motors. Up to 3 driver boards could be chained together, for up to 12 modules controlled by a single Arduino.
The driver uses 2 MIC5842 low-side shift-register drivers, with built-in transient-suppression diodes, to control the motors, and a 74HC165 shift register to read from 4 hall-effect magnetic home position sensors. There are optional WS2812B RGB LEDs which can be used to indicate the status of each of the 4 channels.
If you'd like to print your own flaps, or cut custom vinyl letter stickers, the project includes a script to generate vector design files that is extremely configurable:
TODO: finish documenting this and render some example images...
If you'd like to share a single front face across multiple modules (rather than each module having its own front face), the repo includes a script to generate a combined front panel for laser-cutting or CNC milling/routing.
You can modify:
For CNC cutting, the script supports rendering a vector file optimized for thicker material (e.g. 6mm MDF) where only the bolt-holes will be through-cut. In this mode, the slots for the top/bottom enclosure pieces can be cut as ~4mm pockets so they aren't visible from the front face. The script automatically generates dog-bone shapes for these pocket cuts.
TODO: finish documenting this and render some example images...
The project also includes a number of optional 3D printed designs to make assembly easier. These include:
All of these designs are parametric and customizable within OpenSCAD. To print them, open up the relevant file in OpenSCAD and use File -> Export -> Export as STL
to render the design as an STL file for your slicer.
This is not likely to be useful unless you're planning to manufacture dozens to hundreds of Chainlink Driver boards, but the Chainlink Driver Tester is a complete testbed for Chainlink Driver boards as they come assembled by the PCBA fabricator.
This is currently under very active development.
Key features:
Latest auto-generated (untested!) artifacts
The driver firmware is written using PlatformIO with the Arduino framework and is available at firmware/
.
The firmware implements a closed-loop controller that accepts letters as input over USB serial and drives the stepper motors using a precomputed acceleration ramp for smooth control. The firmware automatically calibrates the spool position at startup, using the hall-effect magnetic sensor, and will automatically recalibrate itself if it ever detects that the spool position has gotten out of sync. If a commanded rotation is expected to bring the spool past the "home" position, it will confirm that the sensor is triggered neither too early nor too late; otherwise it will search for the "home" position to get in sync before continuing to the desired letter.
In order for a computer to communicate with the splitflap, it appears as a USB serial device.
However, usage of Arduino’s Serial
is strictly forbidden, and instead a logger
abstraction is provided for sending basic text debug logs. Other data is transferred in a structured way, described below.
This allows flexibility in the format of data transferred over serial, and in fact the splitflap provides 2 different serial modes that serve different purposes.
By default, it starts in “plaintext” mode, which is developer-friendly and you’re probably familiar with if you’ve opened a serial monitor with the splitflap connected:
{"type":"init", "num_modules":6}
However, this isn’t great for programmatically configuring or receiving updates from the splitflap, so instead the firmware offers a programmatic interface using a binary protocol based on Google’s Protobuf standard.
The protobuf-based binary serial mode is a compact and flexible way to transfer structured data from the host computer to the splitflap and vice-versa.
protobuf provides several benefits over other encoding mechanisms like JSON:
This is why the splitflap defaults to plaintext mode to make basic validation/debugging easier.
Protobuf messages are encoded to their binary wire format and a CRC32 checksum appended. Then that entire binary string is COBS encoded into a packet, and delimited/framed by 0 (NULL) bytes when sent over serial. This provides a basic packet-based interface with integrity checks (rather than the raw, stream-based interface of a serial connection).
The splitflap automatically switches to binary protobuf mode when it receives a 0 byte.
The display can be controlled by a computer connected to the ESP32 over USB serial. If you've built a display and want to test it out, check out the web-based demo here which will connect to your display using USB - no applications/installation necessary!
The firmware supports a plaintext serial mode (enabled by default) for ease of testing, and a protobuf-based binary mode used by the software libraries for enhanced programmatic control and feedback.
You can find example Typescript and Python libraries in the software/chainlink
folder.
Looking to make some modifications or play around with the design on your local machine? Jump right in! Note that all of the scripts and automation are developed for Ubuntu. Mac OS support is planned, but not currently implemented (but feel free to open a PR if you want to help!).
The main design file is 3d/splitflap.scad
You'll need a recent version of OpenSCAD (e.g. 2015-03), which may need to be installed through the PPA:
sudo add-apt-repository ppa:openscad/releases
In general, solid objects such as the enclosure sides or spool components are built from 2d primitives and then extruded to the appropriate thickness for 3d rendering, rather than using 3d primitives. This simplifies the design without losing expressiveness; the perpendicular laser cut beam doesn't allow for cuts that vary in the Z dimension anyway.
Note that while the design is parameterized and many values may be tweaked, there is currently no error checking for invalid parameters or combinations of parameters. Please take care to validate the design if you change any parameters. For instance, while most of the design will adjust to a changed num_modules
value, certain values may cause some elements to intersect with other elements or protrude beyond their expected dimensions.
The design can be rendered to 2d for laser cutting by running 3d/scripts/generate_2d.py [--panelize <number>]
, which outputs to 3d/build/laser_parts/combined.svg
. The optional --panelize
argument allows for rendering a panel of modules in a single SVG, for bulk laser-cutting.
Internally, the design uses a projection_renderer
module (3d/projection_renderer.scad
), which takes a list of child elements to render, and depending on the render_index
renders a single child at a time. It also adds material to each shape to account for the kerf that will be cut away by the laser.
The generate_2d.py
script interacts with the projection_renderer
module by first using it to determine the number of subcomponents to render, then runs OpenSCAD to export each component to an SVG file. It does some post-processing on the SVG output (notably adds "mm" to the document dimensions), and then combines all components into the single combined.svg
output.
Once the combined.svg
file is generated, you'll want to double-check there aren't any redundant cut lines that are shared by multiple adjacent pieces, to save time/cost when cutting. They should be detected automatically (and highlighted in red in the rendering above), but it doesn't hurt to double-check. In Inkscape, select the "Edit paths by nodes" tool and select an edge to delete - the endpoints should turn blue. Then click "Delete segment between two non-endpoint nodes", and repeat this for all other redundant cut lines.
The design can be rendered to a rotating 3d animated gif (seen above) by running 3d/scripts/generate_gif.py
, which outputs to 3d/build/animation/animation.gif
The generate_gif.py
script runs multiple OpenSCAD instances in parallel to render the design from 360 degrees to individual png frames, which are then combined into the final gif animation. As part of building the animation, generate_gif.py
renders the design with multiple configurations (opaque enclosure, see-through enclosure, no-enclosure and no flaps) by setting the render_enclosure
and render_flaps
variables.
The design can be rendered to a series of STL files (one per color used in the model) in order to be displayed in an interactive web-based 3d viewer. Similar to the projection_renderer
used to render individual components for laser-cutting, the ColoredStlExporter detects all the colors used in the model and renders them one-by-one to separate STL files, along with a manifest that maps each STL file to its RGB color. The STL files and manifest are loaded using three.js to display an interactive model on a web site using WebGL. See this blog post for more details on how the export and three.js renderer work: OpenSCAD Rendering Tricks, Part 3: Web viewer.
All of the electronics are developed using KiCad 5. Panelization is provided by KiKit and gerber/BOM generation is provided by KiBot.
The mechanical and electrical design renderings and links above are automatically updated on every commit with the latest rendering. See this blog post for more details on how that works: Automated KiCad, OpenSCAD rendering using Travis CI.
The PCB layout can be rendered to an svg or png (seen above) by running electronics/scripts/generate_svg.py file.kicad_pcb
.
This uses KiCad's Python scripting API
to render several layers to individual svg files, manipulates them to apply color and opacity settings, and then merges them to a single svg.
For additional details, see this blog post: Scripting KiCad Pcbnew exports.
For reviewing the design, a pdf packet with copper, silkscreen, and drill info can be produced by running electronics/scripts/generate_pdf.py file.kicad_pcb
.
Gerber files for fabrication can be exported by running electronics/scripts/generate_gerber.py file.kicad_pcb
.
This generates gerber files and an Excellon drill file with Seeed Studio's naming conventions and produces a .zip
which can be sent for fabrication.
EESchema isn't easily scriptable, so to export the schematic electronics/scripts/export_schematic.py
starts an X Virtual Frame Buffer (Xvfb) and open the eeschema
GUI within that virtual display, and then send a series of hardcoded key presses via xdotool
to interact with the GUI and click through the dialogs. This is very fragile but seems to work ok for now. For additional details, see this blog post: Using UI automation to export KiCad schematics.
I'd love to hear your thoughts and questions about this project, and happy to incorporate any feedback you might have into these designs! Please feel free (and encouraged) to open GitHub issues, email me directly, reach out on Twitter, and get involved in the open source development and let's keep chatting and building together!
This project is licensed under Apache v2 (see LICENSE.txt).
Copyright 2015-2024 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.