
Hello I’m Ryan!
On its face, this is a course on how to build this thing — a WijiBoard, made from a small computer, motors, and some 3D prints, that can be controlled via a web browser on your phone.
First, let’s address the elephant in the room: I know how to spell Ouija, but this is Wiji. It’s a common misspelling (SEO BAYBEEE!). It's how most people would sound out the pronunciation of Ouija, and it’s a play on the acronym "WiFi," which is how it works. Also, Ouija is a protected intellectual property, this is distinctly different. That one "works" with spirits & Ether (and Copyright lawyers). This one works with WiFi (and Internet Memes).
Throughout this course, we’re going to be talking about how I outlined, designed, prototyped, and built this device. The intention is not just to instruct you on how to build this specific device, but to introduce you to the skills and ways of thinking that I used so you can use them for your own projects as well. The goal is to give insight into the real-world interplay of the design process — the parts that can’t be Googled. This isn’t a tutorial on how to use software like Fusion 360 or KiCad, but a guide on how to leverage those tools to build a product.
Quick Note on Abstraction
I didn't know where to put this section, so I'll put it right at the top.
TL;DR: Abstraction helps us manage complexity by focusing on the most relevant aspects of a system. In this course, we’ll navigate through different levels of abstraction to ensure a comprehensive yet comprehensible understanding of the topics at hand.
Abstraction allows us to interact with systems without fully understanding how they work. It lets us specialize and focus on relevant tasks without getting bogged down in the details. A classic example is driving a car. To turn a car on, you put the key in the ignition, turn it, operate the pedals and steering wheel, and you end up at your destination. The mechanical functions of the car are abstracted away; they're a black box in the background, functioning as intended. This lets you focus on the higher-level task of driving the car instead of the air-fuel mixture of the engine, the stresses of the metallurgy in the chassis, or the voltage of the electronics charging your phone.
Abstraction in This Course
When designing something new, having a less abstract view of how the components function allows you to troubleshoot more effectively. You can work inside the black box instead of just interfacing with the inputs and outputs. Balancing your available focus to avoid getting lost in the weeds is essential.
All of this is to say, in this course, I’m going to abstract some things and dive deeper into others. This is a subjective call, and I encourage feedback. I’ll lean towards one or two layers down: “When you turn the key, the starter motor engages, which spins the engine and engages the fuel pump and electrical system, kickstarting the internal combustion cycle.” Instead of, “When you turn the key, the key has grooves that interface with pins in the ignition system. Once the correct key is verified mechanically, the key turns, moving past a switch that engages the electrical system. Finally, the key reaches the 'start' position, sending current to the coils of a small DC motor with a spring-loaded clutch. This motor, upon spinning up, pushes a gear forward into a large gear on the crankshaft, which starts the engine…”
While the detailed explanation is interesting, it’s typically not necessary for understanding the basics or diagnosing most car issues. Everything has infinite complexity if you look hard enough. The more realistic a model of a system, the harder it is to work with. I’ll strive to provide a reasonably deep level of insight without overdoing it. But this is subjective, so please provide feedback if you have thoughts!
Project Origin and Outcome
This project started as a joke, but it had the perfect combination of whimsy and technical challenge to really hook me. It ended up being a massive learning experience, requiring hours of research through forums and videos. My goal for this course is to distill that learning process into something useful and concise for you.
My hope with this project, and with future ones, is to provide a full overview of the entire project — warts and all. Instead of focusing on just bits and pieces, I want to show how various disciplines come together to create something complete. I’ll explain not only what I did, but why I did it, so it’s useful for anyone building their own projects.
I find that I learn best when I have context and something "finished" to work towards, and I hope to provide that same sense of completion in this course.
This project definitely hit a few dead ends, which made it take longer than expected (as usual), but the final result, in my opinion, is worth it.
Course Structure and Content
In the next section, I’ll go into more detail on those dead ends and the lessons learned along the way. After that, I’ll walk you step-by-step through the final design so you can feel comfortable working on or with it.
To accommodate different learning styles, I’ll present the course material as a mix of text and video. Both formats will cover the same information, but because video takes longer to produce, consider text as the default source of information.
I’ve broken the text into sections based on the different disciplines involved in the project, mirroring the structure of the main video. I’ve also done my best to provide a top-down overview of all the necessary background information. Often, the small details skipped in other tutorials can become major roadblocks when learning something new, and I want to avoid that here.
PLEASE(!) if something isn’t clear, or if you need more examples to understand a concept, don’t hesitate to reach out! If you’re struggling with it, there’s a good chance someone else is too.
Dead Ends
In this section, I’ll discuss the project's dead ends — the parts of the project that, in retrospect, could have been skipped and led to significant amounts of "lost" time. It’s important to remember, however, that this time isn’t truly lost; mistakes are a crucial part of gaining experience. The process of moving past mistakes helps build intuition and instincts about what will and won’t work, contributing to good progress on the project of your development.
Etch-A-Sketch Idea
The single largest dead end in this project was my initial idea of trying to make it work like an Etch-a-Sketch. It was one of the first concepts I had, and I ran with it.
How it works:
An Etch-A-Sketch works by stringing two independent cables around a tortuous path and back to two knobs that act as a system input. My original intent was to replace these knobs with servos and use that as the method of moving the planchet. I figured, "This system has stood the test of time."
The fatal flaws in this idea were twofold:
Torque Requirements
The torque required to move the pointer is really high. In the Etch-A-Sketch, the input knobs are geared down, so you have to twist them several full rotations to move the pointer across the screen. I can buy high-torque servos to overcome this, but they have a finite range. If I gear up the output to increase the range, I reduce their precision and torque. It’s a solvable problem, but it’s not a great use of a servo.
Cable Direction Changes (Capstan Equation)
There are about 16 changes in the direction of the cables to make this work. The Capstan Equation says that the more a cable wraps around a post, the harder it is to pull — friction parasitically steals energy. This equation is cumulative (all the changes in direction add up) and exponential. For example, if one wrap around a post provides 1 lb of resistance, two wraps could give 12 lbs of resistance, depending on the coefficient of friction.
The solution is to use bearings, which roll and don’t transmit torque, avoiding the Capstan Equation. However, they’re precision parts, and 16 bearings quickly add up in cost.
Early Design Oversight
I could have done this initial mental assessment of the limitations of this idea right when I came up with it, but I was too focused on feeling like I was making progress rather than actually making progress.
While patience and iteration might have made this version work, 16 bearings and multiple days of printing were not feasible for a reproducible design. It would have been prone to mechanical failure, and I probably would have only ever made one.
Additionally, the issue with gearing up the servos would be present with any design. Most servos (with some exceptions) move up to 270 degrees. Converting that rotating motion into linear motion introduces its own set of challenges.
Servo Gear Issue
Imagine attaching a wheel to the front of the servo and wrapping a string around it. Rotating the servo will either take up or pay out some amount of string, and the circumference of the wheel determines this.
For the WijiBoard, which is 11 x 14 inches with the thinnest part being 0.75 inches, I’d need a wheel of about 4.5 inches to "move" that much (C = 2πD). Since the servos are lying on their side, I’d need to add a 90-degree drive to change the input axis. This is exactly what I attempted: I created a 0.75-inch wheel on the servo, then geared it up to a 4.5-inch wheel in the plane of the Etch-A-Sketch mechanism. This resulted in a 6:1 gear ratio.
You can see the servos on their sides in the image below, with a small wheel attached and a larger wheel nearby.
Problems with Gearing and Direction Changes
The issue with this setup is twofold:
A. Direction Changes: Every time the motion changes direction, some of the energy is lost. You can follow the highlighted cable in the image to see how many changes of direction are involved.
B. Servo Gearing: Gearing up the servo reduces both torque and accuracy. If I ask the servo to move the small wheel 10 degrees, the larger wheel will move 60 degrees, magnifying any positional error by 6x — which is, simply put, bad.
Torque and Efficiency Loss
Together, these factors are problematic from a torque perspective. If I choose a 20 kg/cm servo (considered very strong for its size), the output torque is immediately cut by 6x to 3 kg/cm.
If we assume one axis is in focus and we’re using 8 of the 16 bearings, each bearing has an efficiency of about 95%. This means the torque is reduced by another 95%^8, bringing our powerful 20 kg/cm servo down to a piddly 2 kg/cm.
Considering that this is the stall torque, and you generally want to run servos at about one-third of their stall torque to avoid damage, we’re far away from a functional system.
Key Takeaway
I knew all of these factors before setting pen to paper, yet I spent time developing something that was doomed from the beginning. The lesson here is to be critical of early design decisions. Optimizing and designing something destined to fail is not fun.
Now, onto the project...
High-Level Overview
At the beginning of each section, I’ll provide a high-level overview to help you understand where we are in the process. For this project, the mechanical design ended up being relatively simple because most of the complexity is handled by the software.
Mechanical Design Breakdown
The system features two arms, each with a forearm, elbow, and upper arm, all controlled by motors hidden inside a box. A large magnet at the end of the arms moves the planchette on top of the box, giving the illusion of being controlled by the ether.
I designed custom parts for the arms and motor/electronics holder, which are 3D printed. I used Fusion360, a CAD program, to create and modify the designs, ensuring they fit together seamlessly.
The goal was to create a reliable system that could be economically reproduced. When aiming to sell a product, especially in any sort of quantity, it’s essential to consider both the time required for assembly and the cost of raw materials.
Originally, I planned to create a 3D-printed housing for the electronics and mechanical components, with a laser-cut Baltic Birch top for the Ouija board surface. However, this process proved to be slow and impractical for any scale production:
Laser cutting took approximately 40 minutes per piece.
3D printing the housing required over 20 hours of printing and assembly time.
Laser cutting also posed a fire risk and produced low-contrast results, making the board hard to read.
A New Approach
After researching alternatives, I discovered wooden canvas boxes used by artists, available at craft stores or online. These provided a high-quality, ready-made wooden frame and surface, eliminating the need for extensive 3D printing and laser cutting.
I decided to screen print the Ouija board surface onto the lid, which:
Significantly reduced production time.
Provided better quality, as silkscreen inks give great contrast.
Opened new possibilities for custom designs, allowing me to collaborate with partner artists or create other "decals" beyond WijiBoards.
CAD Model Overview
The CAD model itself ended up being pretty straightforward. The first step is always to model the known pieces—things you’ve purchased and are already defined, or physical constraints you might have.
For this project, I modeled the USB battery and the two stepper motors, then roughed in a fake circuit board and the rough dimensions of the sanded plywood canvas box. Once those are in the model, you can start crafting the custom components that interface with these known pieces.
The Initial Design
The original Etch-A-Sketch design involved a significant number of bearings, motion ratios, and discrete components, including a torturous path of cable to allow it all to work. The model itself works but is a bit fiddly, and since I ultimately didn’t go with this design for economical and accuracy reasons, I’ll skip it here. Let me know if you'd like more detail on this.
5 Bar Linkage Model
I began working on the 5 bar linkage model, knowing that the linkage lengths would be critical and needed to fit into the newly sourced wooden box. Figuring out the lengths was a glorified game of guess-and-check.
I first imported a screenshot of a standard Ouija board to act as an underlay and sketched lines on top.
In this phase, CAD acts as a digital prototype—I could have done this with popsicle sticks and thumbtacks instead. My goal here is to have two arm lengths that won’t crash into each other, the motors, or the box, but can still reach all the individual letters of the Ouija board. There are many "correct" linkage lengths; I chose one that felt comfortable to move forward with and settled on 85mm and 110mm.
Mechanical Considerations
One important mechanical consideration was to keep the board as thin as possible. To achieve this, the motor needed to be the thickest part of the mechanical assembly. This meant sourcing a very thin battery pack and ensuring nothing was above or below the motor.
Identifying design constraints like this upfront can help keep you true to the design intent when you start working through individual challenges.
3D Printing and Components
I chose to 3D print all of the mechanism components to meet my custom design requirements. The functionality I needed included:
Holding the battery
Holding both motors in position
Holding the custom circuit board
Locating the components inside the wooden canvas box
Forming a simple 5 bar linkage
Creating two links: one to connect to the motor and another to connect the first link and each other (similar to holding your arms out—link 1 is from your shoulders to elbows, link 2 is from elbows to hands)
Forming the planchette and holding magnets
Creating a magnet holder to move the planchette through the wooden box with magnets
The CAD model is available if you’d like to explore the final design files. If you have specific CAD or design questions, feel free to reach out.
Assembly
With all the parts being simple 3D prints, assembly is easy. Some parts are pressed together (such as magnets and pins in the Link1-Link2 joint), while the main body is glued to the WijiBoard base.
That’s it!
Now, onto the next step: wrangling the electrical pixies into compliance!
The electrical design for this project required a custom PCB due to the combination of components and the limited space within the box. I used KICAD, a free software, to create the necessary files for PCB manufacturing.
The process involved several key steps:
Component Selection: Choosing the right components for the project.
Prototyping: Testing with a prototype made from development boards.
Schematic Creation: Designing the electrical schematic for the system.
PCB Layout Design: Arranging the components on the PCB within the space constraints.
Verification & Testing: Verifying and testing the final PCB to ensure functionality.
Design Challenges
The original plan to use RC hobby servos was abandoned due to power issues (see Dead Ends section). Instead, stepper motors were chosen, which required a more complex electrical design to manage their operation effectively.
The first step in electrical design is selecting the major components based on what you hope to do with the circuit board. For this project, I knew I wanted to:
Connect to a person's phone via WiFi/Bluetooth
Move stepper motors
Power the system via battery
Each of these functions typically requires an Integrated Circuit (IC) — the little black shapes on top of a circuit board. Each IC has a unique, dedicated function. These are often called "Jelly Bean components", meaning they’re easy to source and swap out, as many manufacturers make chips that perform identical functions.
Task Breakdown
Let’s take a look at the individual tasks we’re trying to accomplish:
Phone Connectivity (WiFi/Bluetooth)
To connect to a phone, we can use Bluetooth or WiFi. The most popular controllers for this are:
ESP32
ESP8266
Other options include Arduino Uno WiFi, Raspberry Pi, Adafruit Feather M0 WiFi, etc.
I usually pick an option with a lot of documentation and support, as this makes it easier to find help when needed, whether for installation or troubleshooting through forums, Reddit, etc.
I ultimately decided to go with the ESP32 for its mass adoption (providing plenty of support) while also being inexpensive. Any of the options could have worked, but the ESP32 struck a good balance.
Stepper Motors
For the stepper motors, I’m using the 28byj-48 stepper motor. I chose this motor because:
It's thin, which suits the design constraints.
It’s inexpensive (less than $1 if bought in bulk).
The motor comes with its own driver boards (ULN2003), which we’ll use for prototyping. It's always best to move the stepper motors using drivers specifically made for the motors, as these components handle the necessary power requirements.
Power Source (USB-C/Battery)
I want to give the user two options for power:
USB-C for a plug-in option, perfect for a display piece.
Battery for portability and "on the go" use.
USB-C is fairly standard to implement. While I’ll skip it for the breadboard prototype, I plan to add this functionality when it comes time for the PCB design.
If you can, it’s best to create a breadboard prototype before committing time and effort to engineering a custom solution. A breadboard is a convenient way to create temporary connections and experiment with different circuit designs. It allows you to connect modules together to create a fast and cheap prototype of your finished project, avoiding the time and resources needed for a custom solution.
The modules for this simple prototype can be purchased in low quantities and reused for various projects, making them an economical way to discover mistakes. For this prototype, I can buy an ESP32 development board that’s ready to go; the stepper motors already come with their own driver board; and I can hand-wire a USB-C connector to pull power from the battery. Once I have all these parts, I can use jumper wires and a solderless breadboard to build test circuits.
Testing and Debugging
Typically, I’ll look for a tutorial or demo set of code to get the basic functions working. This lets me confirm that everything is wired correctly. If the motors don’t move with basic code, adding more complex code on top won’t fix it.
WiFi Testing
First, I test the WiFi capabilities. I’m jumping ahead a bit, but I essentially follow the WiFi Access Point Example code from Espressif (the makers of ESP32)
Using Known Working Code
There are features later in the code I’ll implement, and using the manufacturer’s "demo" code is a great place to start. It’s known working code, so if the device doesn’t function with it, you know there’s troubleshooting to be done before mixing in custom code.
Stepper Motor Integration
For the steppers, I’m using the 28byj-48 stepper motor. There are many people online who have explained in detail how these motors work and how to use them with the ULN2003 driver.
How Stepper Motors Work
At a high level, all motors consist of a rotor and a stator. The rotor rotates, and the stator stays in place. Sometimes, the rotor is made of magnets and the stator is made of coils, or vice versa. The core principle is that applying a current to a coil of wires generates a magnetic field, and depending on the flow of electricity, this field can either push or pull against other magnetic fields, typically from permanent magnets.
Most conventional motors are designed to spin continuously by switching the coils on and off, which “pushes” and “pulls” the rotor to spin. Stepper motors, however, are designed to hold a position. Instead of missing the magnetic field, they align and generate force when the electrically induced magnetic field lines up with the magnetic field of the magnets. The motor then “steps” through each position, allowing precise control of its movement.
Analogy for Stepper Motors
A useful analogy: Imagine asking someone blindfolded to sprint to exactly 50 feet versus asking someone to measure out 50 feet by heel-to-toe walking. The sprinter might go faster but would be less accurate, while the person walking would be more accurate but slower. Similarly, a stepper motor steps along, and by keeping track of the electrical pulses, you can know its exact position.
USB-C Power and Battery Testing
The default ESP32 development module can be powered via USB. While USB-C can be tricky to implement, I’m just using it as a convenient power source in legacy mode for this project, which is easier to manage.
The battery is harder to test with the development board. For the final electrical design, I’ll be creating my own circuit, so I need to prevent the battery voltage from traveling back into the USB if someone plugs in both the battery and the USB. I also need to ensure the voltage regulator I pick can handle the voltages I plan to use. The ESP32 Dev module has this built in, but I won’t have that luxury when designing my custom board.
Finalizing the Prototype
If I encounter issues with any of these, I can troubleshoot and swap out parts until I achieve the basic functionality. With the ability to connect to a phone, move the steppers, and power the system via battery, we have everything needed to advance the circuit board design and software.
In the real world, the design and purchase of custom PCBs happen in conjunction with software development. For clarity, I’m discussing electrical design in isolation here, but know that while I’m designing the PCBs, I’m also actively thinking about how I’ll program the system and test code on the breadboard. PCBs can be expensive, so it’s important to catch simple mistakes and challenges before spending money to fix them.
The first step in designing a custom circuit board is to create a schematic. This is a block representation of each of the components that will be placed on the board. The schematic serves as a "recipe" for the final design, providing the simplest yet complete view of how the circuit works.
For our design, we’re using:
The ESP32-S3 module
ULN2003 boards to drive the stepper motors
A USB-C connector for powering and programming the system
Looking up the ESP32-S3 datasheet, we can see the recommended design for reference.
Power Requirements
We need to supply 3.3V power to the ESP32. Since USB provides 5V and we’ll also be dealing with battery power, which varies with use, we need a voltage regulator to convert the power down to 3.3V. Additionally, we need a few capacitors and buttons to interface with the device and stabilize the power.
By looking up a common 5V to 3.3V regulator, we can find its datasheet and identify the necessary passive components, like resistors and capacitors. This is as simple as visiting a vendor like Mouser and searching for a 3.3V voltage regulator.
Component Selection Strategy
A useful trick is to visit suppliers such as LCSC or Mouser and look for parts that are inexpensive and well-stocked. If a part is widely used in many products, there’s a good chance there are plenty of resources available to help you integrate it into your design. You always want parts that are:
Well-tested
Inexpensive
Have a community of support for troubleshooting.
Stepper Motor Driver Selection
While researching the ULN2003 driver chips for the stepper motors, I came across the DRV777 chips. These are more power-efficient and serve as a drop-in replacement for the ULN2003. This means all the same code works, but the DRV777 wastes less energy, making it a major improvement.
Looking at the DRV777 datasheet, I found a unipolar motor example to confirm this was a good use case. We’ll copy this reference design into our final schematic.
USB-C Connector
The USB-C spec sheet is extensive and can get quite complicated in some sections. For this project, we’ll use USB-C in legacy mode, allowing us to sidestep the complexity and pull 5V from any USB device using just two resistors. This is a common practice that simplifies the design.
In addition to pulling power to drive the motors and the ESP32-S3 microcontroller, we also want to use USB-C to program the microcontroller.
The ESP32-S3 natively supports USB; we just need to follow the correct reference design from the datasheet.
This requires connecting two of the pins from the USB-C connector to the GPIO pins (General Purpose Input/Output) and defining them as USB input when the board is first connected. These GPIO pins are versatile, capable of performing many functions, and are configured with code. Some pins, however, have specific functions like voltage input, ground, or reset.
Combining the Components
At this point, we have small "islands of functionality". We’ve copied some best practice examples for each component into our schematic, but now we need to make them work together. This is done with netlists—lines that represent the wires connecting components.
The USB-C accepts power from the outside world and sends 5V power to the DRV777 drivers to power the motor.
The 5V power also goes to the 3.3V regulator to power the ESP32.
The DRV777 receives input from the ESP32 via 4 data lines (step commands), each controlling a coil. These signals allow the motor to know its position by tracking the pulses sent.
Since we’re powering a motor, we need to send higher current than the microcontroller can handle. The DRV777 accepts low-power signals from the ESP32 and turns on a higher-power switch. It’s like pressing a button to open a door—the microcontroller can press the button, but not open the door on its own. These high-power outputs will eventually go to the motors, which will be soldered onto a jumper hole once we have a PCB.
Finally, the ESP32 requires two physical buttons to assist with programming—one to reset the device and the other to indicate that we’re uploading new code. These are taken directly from the ESP32 datasheet.
Creating a Full Schematic
Now, we have a fully interconnected view of all the components and how they interact. I usually take notes and screengrabs during research to avoid diving into a formal PCB design before I’m sure about which parts I want. Once the schematic is solidified, I’ll move the design into the computer.
Moving to KiCAD
KiCAD has a schematic editor that allows you to take all this information and place it into a digital schematic. This information is parametric, easy to edit, and automatically links to the PCB layout program. Taking the design parameters and transferring them into a digital schematic involves looking up components on vendor websites, downloading relevant data, and connecting the components with nets (which will become wires). It’s like a game of matching reference designs from various manufacturers.
PCB Design
Once all the footprints are selected, the schematic can be imported into the PCB editor (part of the KiCAD suite). The schematic editor outputs data, and the PCB editor uses this data to help you begin placing components on the board.
Once I’ve made a breadboard prototype, it’s time to start designing the custom circuit board. I prefer to use the free program KiCAD.
KiCAD is a suite of programs that work together to create the necessary files for custom PCBs. At a high level, a circuit board is a collection of components that perform specific tasks. Typically, there is a microcontroller (or multiple) that gets programmed by the designer and is connected to other "dumb" components—called peripherals—to accomplish a specific goal.
You can think of the microcontroller as the “brain” of the system, and the peripherals as the way it interacts with the outside world. For our project, we’ve already selected an ESP32, a popular and inexpensive microcontroller. We will place this on a circuit board along with the necessary peripherals and program it to perform the required tasks
Choosing the ESP32-S3
ESP32 microcontrollers come in several different versions, each with different features but built on the same base architecture. Some have more memory, others have different antenna configurations to improve reception with external antennas.
For this project, we’ll use the ESP32-S3 variety because:
It has a good blend of memory.
It supports USB natively—previous versions required a dedicated peripheral to handle USB communication, but this one does it by default.
It’s readily available and strikes a good balance between features and complexity.
Understanding Datasheets
Every component you use comes with a datasheet. These documents provide extensive details on how each component performs under "normal" operating conditions.
Datasheets also show examples of correctly configured chips in use. These reference designs serve as a guide for PCB designers, offering examples of the “correct” way to implement a component on a circuit board.
Passive Components
For any given chip, there are usually several passive components that are recommended to be placed near the physical component. These are typically:
Resistors
Capacitors
Inductors
They come in a variety of sizes and materials, and the datasheet for each important chip will provide insights into which parts are necessary for proper operation.
The PCB editor operates similarly to any graphics editing software, where everything works in vectors like lines, arcs, circles, and rectangles. Each layer within the program has a specific function, and you edit the PCB design by selecting the layer you want to influence and drawing your intended design. There are many tools within the program to make common actions easier.
Starting the PCB Design
Importing Components We start by importing all of the components selected in the previous step. This will show up as a cluster of footprints grouped together. This grouping is tightly packed and shows roughly the smallest PCB you can expect to make.
Drawing the Outline Next, we draw the outline of the board. If the board needs to interface with the outside world (as it does in our case with the WIji board), you can import a file and use it as the board’s outline. This represents the shape that your final PCB will take.
Placing Components Start roughly placing the components where you think they should go. We can adjust them later to ensure everything fits nicely before we move on to routing. Special attention must be paid to the 3.3V regulator, as its associated passive components need to be placed as close as possible to the chip. This information and examples are available in the part's datasheet.
Exporting a STEP File Once we’ve placed the ESP32, DRV777, power regulator, and passive components, it’s time to export a STEP file. This allows us to check that all the parts make sense in the physical space before investing too much time in routing the design. After importing the STEP file and verifying that nothing is crashing or causing issues, we can move on to routing.
Routing the PCB
Routing is satisfying and feels like solving a puzzle that leads to a working PCB design. While placing the components, you may notice thin white lines connecting the various components. These are rat’s nest wires, representing the connections made in the schematic. Each rat’s nest represents a wire that needs to be routed without touching other wires.
Double-sided PCB: To accomplish this, we can use both sides of the PCB—top and bottom. For more complex designs, you can make thicker PCBs with more copper layers inside them, but for a simple PCB like this, two layers will be enough.
Ground Plane: For now, we’ll ignore all ground connections because we can use a ground plane (or ground flood plane). This plane covers a large portion of the board with copper connected to the electrical ground, allowing every chip on the board to be connected to ground. This reduces the work of connecting to ground, which is a very common requirement.
Routing Data Lines
I like to start with the most important and sensitive connections—the data lines. Routing the data lines is as simple as clicking along a path that connects the two endpoints of the rat’s nest labels. As you populate the board with traces, feel free to rotate and fine-tune the placement of components to make routing easier.
Crossing Wires: When you reach a point where two wires need to cross, you can use vias. Vias allow a connection to jump from the front of the circuit board to the back, letting you "cross" wires without electrical contact.
Once you’ve finished routing all of the connections between the components, the PCB design is nearly complete. Since this is a low-speed, low-power board, considerations like timing and power dissipation are not as important.
Exporting Gerber Files
When the PCB design is finished, it’s time to export the files and upload them to a PCB manufacturer. All PCB manufacturers require Gerber files, which are a series of files in a .zip format that fully define the design. Once you have the Gerber files, you can upload the zip file to various PCB manufacturing websites and get an instant quote.
I chose a service that also offers assembly—this means I could specify the parts I planned to use, and the manufacturer would even place the components on the board and solder them! While this step isn’t necessary, it’s certainly convenient.
Testing the PCB and System Integration
Once you receive your purchased boards, it's time to test and begin integrating your mechanical system, electrical system, and software design. For this design, the testing process is very similar to the initial breadboard prototyping.
Testing Steps
Power Test
The first step is to check if plugging the board in powers the LED as expected.
ESP32 Detection
Next, confirm that the ESP32 is properly connected and detected by a computer.
Simple Code Check
Test to see if the ESP32 accepts simple code, such as:
Modifying an LED.
Printing something like "hello world" to the serial console.
Web Server Test
Verify that the ESP32 can host and support a web server.
Stepper Motor Control
Finally, check if the stepper motors can move in predictable ways.
Importance of Initial Testing
None of this code will make it into the final software design, but it’s crucial to test and verify that these individual parts are functioning before increasing the complexity of the design and incorporating multiple interdependent features.
Final Integration
Once the basic functionality of each component is verified, it’s time to start the final integration of all systems!
To easily connect to a user's phone, I create a simple WiFi network that the user can connect to. When they join the network, they can open a web browser and navigate to Wijiboard.local. Here, they’ll see a stylized keyboard on the screen. When a key is pressed, the Wijiboard "magically" moves to the selected key and back.
To achieve this functionality, we need to use several programming languages. It may sound like a lot, but stick with it—we're going to abstract much of the complexity. Understanding the proper names helps make intimidating concepts feel more impressive than scary!
Here’s a breakdown of the tasks and languages involved
C++
Generates the WiFi network and server.
HTML
Creates the webpage that users view in their browser.
CSS
Handles the visual styling of the webpage.
JavaScript
Detects when a user has pressed a button on the webpage.
WebSockets
Communicates the button press back to the server.
C++ (again)
Runs the main program that takes user input.
Performs trigonometry to determine motor angles.
Moves the motors to the required location, keeping track of their positions.
Connecting to a Phone via WiFi
As mentioned above, I want to create software that users can easily use with their phones without requiring them to download anything. While Bluetooth and WiFi are common ways to interface wirelessly with a phone, Bluetooth typically requires an app to be downloaded. Therefore, I chose to use WiFi.
With WiFi, I had two options:
Connect to the outside internet
Create a local network
I opted for a local network only. This eliminates the need to know or store other people’s WiFi information (which would be a security nightmare) and allows the board to be used anywhere without needing to ask for a WiFi password, which would be a buzzkill.
Why WiFi?
I know the ESP32 has enough computational power to handle the necessary inverse kinematics for the 5-bar linkage and can move the steppers. So, as long as I can connect it to a phone and have a user communicate wirelessly, that should be sufficient.
WiFi Network Setup
WiFi is a wireless communication protocol that allows local networks to be formed and provides a framework for devices to exchange data. In simple terms, it allows computers to "talk" to each other in an organized way.
Though WiFi is most commonly used to access the internet, for this project, we’ll use the phone’s web browser to communicate with the ESP32 through a familiar interface. Remember, WiFi does not equal the internet—it’s simply a network of computers talking to each other. Similarly, a server is just a program, and in this project, the ESP32 will handle both the server and the other functionalities, such as motor control.
To create the network, the ESP32 needs to announce that it is available to communicate by acting as an access point. Once set up, the ESP32 will create a new wireless network that can be detected by the phone, allowing the two devices to "talk" to one another.
Communication Between Devices
Once the devices are connected, we need to get them to speak the same language. When computers are on the same network (in this case, WiFi), they communicate in packets—small chunks of information that are pieced together by the recipient.
Using HTTP and TCP (standard internet protocols), we don’t need to worry about the details of how the packets get to their destination; we can be reasonably confident that they will. Since we’re using WiFi, we’ll continue with this standard approach and set up a webpage for communication.
Hosting a Webpage on the ESP32
Since the ESP32 acts as the access point for the network, it also makes sense for the ESP32 to serve as the webserver that hosts the webpage.
HTML, CSS, JavaScript
Here’s a quick overview of how the internet works:
The client (internet user) sends requests to the server, which provides the information for the client to display. The client uses a web browser to interpret and display this information, which is done through several different languages:HTML: Describes the content of the page and its structure.
CSS: Defines the style, layout, color, and overall look of the webpage.
JavaScript: Allows for complex motions, listens for user interactions (events), and can dynamically update both HTML and CSS based on user input.
Handling User Input
With JavaScript, we can create dynamic, modern-feeling interactions on the webpage. In our case, we’ll present the user with a keyboard made from HTML and CSS, and JavaScript will listen for button presses on the keyboard.
When a button is pressed, JavaScript will record the input and send it back to the ESP32 using WebSockets. WebSockets allow real-time, two-way communication, making it easy to handle multiple users connected to the server.
Determining Letter Positions
Once the button press is captured and sent to the ESP32, we need to translate that into motor movements. First, we determine where on the board the selected letter is located by using a simple grid with X and Y coordinates (in millimeters). Each letter’s position is stored as a unique point on the grid, which becomes the target for the end effector (planchette).
We’ll place the (0,0) point at the same horizontal axis as the motors, dead center between them, simplifying the math.
Inverse Kinematics and Motor Control
The next step is moving the motors to the correct position. Since the motors are steppers, we can control their precise angles. The challenge is correlating the target location with the motor positions.
Instead of manually determining the motor angles for each letter, we use inverse kinematics to create equations that mimic the mechanism. These equations take the desired position as input and output the necessary motor angles.
Moving the Motors
Once the motor angles are calculated, we use them to move the motors. Stepper motors move in predefined steps (our motors spin 0.17 degrees per step). By comparing the target angle with the current angle, we determine how many steps are required. Libraries help with this process, keeping track of the motor’s position and managing the electrical pulses needed to move the motors accurately.
Program Flow
This flow diagram shows how the program will work. Each module handles specific inputs and outputs, and we can work on each module individually. By treating each module as a black box, we keep the code easy to read, modify, and maintain.
Now, onto the code!
Before diving into the code that performs each of the functions discussed in the flow chart, it's helpful to have some background on how computers work. Modern microcontrollers are often described as "rocks we've squeezed some lightning into, carved some runes onto, and are basically magic"—or at least, that’s how it can feel.
Working code looks identical to code that does nothing, and having a deeper understanding of how things work under the hood can help troubleshoot potential issues in what can sometimes feel like an opaque problem space.
Crash Course in Computer Science
Originally, I had written a huge section on the history of computing—from mechanical relays through logic gates, digital logic, compilers, and how an IDE fits into the equation. Then I discovered the Crash Course Computer Science series from PBS, which contains everything I wanted to convey. I highly recommend you watch their series and support their work, as they did an excellent job!
If any of their content is confusing, feel free to reach out, and I'll happily chime in!
How Computers Work: The Short Version
Here’s a crash course of the Crash Course:
Computers understand 1's and 0's—this is called machine code or binary.
Different computer manufacturers have equipment that "speaks" different flavors of binary (e.g., binary written for Intel won’t run on an AMD machine).
Programming languages were developed to abstract the 1's and 0's, allowing programmers to focus on higher-level tasks.
There are several steps involved in going from human-readable code to binary code:
Pre-processing: Arranges your code to prepare it for compilation, combining all functions and definitions into a single document.
Compiling: Converts your high-level code into assembly code—a more primitive language that can still be translated into machine code.
Assembling: Translates assembly code into machine code (binary).
Linking: Attaches libraries and external functions to your code, allowing you to use prewritten chunks of tested code.
Loading: Directly translates the code into an executable binary file (1's and 0's), loading it onto the target device.
IDEs and Why We Use Them
Each of these steps (pre-processing, compiling, assembling, linking, and loading) involves separate tools, and managing all of this manually would be a huge hassle. To simplify the process, we use something called an IDE (Integrated Development Environment).
An IDE bundles all of the tools (pre-processor, compiler, assembler, linker/loader) and provides quality-of-life features like syntax highlighting, code suggestions, and error checking.
You could theoretically develop code in Notepad or Microsoft Word and manually handle each step, but nobody has time for that.
Our IDE of Choice: VS Code
For this project, we’ll use Visual Studio Code (VSCode). VSCode is a text editor similar to Notepad or Word but with many features tailored for programmers, like syntax highlighting (pretty hacker colors) and integrations for other useful tools.
We’ll use a plugin for VSCode called PlatformIO, which acts as our IDE. PlatformIO manages the entire compilation chain, allowing us to go directly from writing C++ code to running it on the ESP32 without worrying about the intermediate steps. Plus, it's free!
Writing the Code
Now that we have an IDE, we can start writing code and uploading it to the microcontroller.
Create a New Project in VSCode and select the microcontroller we’re using—the ESP32-S3.
PlatformIO will set up the appropriate environment, look up the assembly language for the ESP32, and create blank files for us to get started.
Project Structure Overview
Here’s a breakdown of the project structure that PlatformIO creates:
platformio.ini:This special file tells the IDE which system you’re working with, where to find the libraries, and how to treat the board you’re using.
.gitignore:Specifies which files to ignore when uploading to Git (a standard way to manage software projects). Sometimes, you don’t want to save everything, and this file allows you to specify what to exclude.
src Folder:Contains the Main.cpp file. This file runs when the ESP32 boots up and is where we’ll write the main function loop.
lib Folder:Holds all custom libraries. Best practice is to encapsulate functions into libraries, typically consisting of a .cpp file and a .h file (header file). The header file connects the libraries to the main function.
Data Folder:Contains the SPIFFS file system for server website files (HTML, CSS, JS), similar to having a USB stick plugged into the ESP32, allowing access to files from within the code.
.vscode Folder:Contains workspace settings for VSCode. If you want to change VSCode’s behavior, you edit the files in this folder.
.pio Folder:Stores all the built project files, including the actual binary loaded onto the controller and code from imported libraries like those from GitHub.
Dive deep into the world of product design and development with Build Your Own Interactive WijiBoard! This course offers a comprehensive, hands-on approach to creating a fully functional WijiBoard from scratch. Throughout the course, you’ll explore all the essential skills that go into interactive product design, covering mechanical design, electronics, software development, and motion control.
Starting with an overview of the product development process, you’ll learn to plan and design custom parts in CAD software, modeling components that bring your project to life. From there, we’ll dive into the electrical side of things, where you’ll gain practical knowledge in circuit theory, PCB design, and soldering techniques. By learning to create custom circuitry, you’ll be able to confidently power and control your WijiBoard’s unique features.
On the software side, you’ll get hands-on experience coding in C++, JavaScript, HTML, and CSS to program the WijiBoard’s embedded system, adding functionality, responsiveness, and interaction. Lastly, we’ll delve into kinematics, covering the basics of motion planning to ensure your WijiBoard moves with precision and smoothness.
Perfect for beginners and intermediate learners, this course provides the foundational skills and creative inspiration needed to tackle future projects. By the end, you’ll have a fully operational WijiBoard and a toolkit of versatile skills to apply to any product development journey. Join us, and let’s bring your ideas to life!