Observations on ESP_8_BIT Nintendo and Sega Colors

I have only scratched the surface of rossumur’s ESP_8_BIT project, but I got far enough for a quick test. I rendered the color palette available while in Nintendo emulation mode, and found that it roughly tracks with the palette shown on its corresponding Wikipedia page.

I say “roughly” because the colors seem a lot less vibrant than I would have expected from a video game. These colors are more pastel, or maybe like a watercolor painting. For whatever reason the color saturation wasn’t as high as I had expected. But judging color is a tricky thing, our vision system is not a precision instrument. What I needed was a reference point, and I found one with the OSD (on-screen display) for this TV’s menu system.

This menu is fairly primitive, using basic colors at full saturation. I can see the brilliant red I expected for Mario, alongside the brilliant green I expected for Luigi. These colors are more vibrant than what I got out of the Nintendo emulator palette. What causes this? I have no idea. NTSC colorburst math is voodoo magic to me, and rossumur’s description of how ESP_8_BIT performs it went high over my head. For the moment I choose to not worry about the saturation. I am thankful that I have colors at all as it is a far superior situation than earlier grayscale options.

At this point I felt confident enough to look at the Sega emulator code path, and was encouraged to see the table returned by its ntsc_palette() method had 256 entries instead of the 64 in the Nintendo path. Switching ESP_8_BIT into Sega mode, I can see a wider range of colors.

Not only are there four times the number of colors available, some of the saturation looks pretty good, too.

The red (partially truncated by overscan in the lower left) is much closer to the red seen on OSD, the yellow and blue looks pretty good, too. The green is still a bit weak, but I like what I see here. With its wider range of colors in its palette, I decided to focus on ESP_8_BIT Sega emulator code path from this point onwards.

Studying NES Section of ESP_8_BIT

I’m not sure why ESP_8_BIT Atari mode stopped working on my ESP32, but I wasn’t too bothered by it. My objective is to understand the NTSC color composite video signal generation code, which I assumed was shared by all three emulators of ESP_8_BIT so one should work as well as another. I switched the compile-time flag to Nintendo mode, saw that it started display on screen, and resumed my study.

The emu_loop() function indicated _lines was the frame buffer data passed from one of three emulators to the underlying video code. This was defined in video_out.h as uint8_t** and as its name implied, it is an array representing lines on screen. Each element of this array is a pointer to another array, storing uint8_t represent pixels on screen. This is a very flexible design that allowed variation in vertical and horizontal pixels, just by changing the size of one array or another. It also accommodates any extra data necessary for a line on screen, for example if there are padding requirements for byte alignment, without code changes. That said, ESP_8_BIT appears to use a fixed resolution of 256 pixels wide by 240 lines high: In video_out.h, video_init() always sets _active_lines to 240, and the for() loop inside blit() is hard-coded to 256.

Inside blit() I learned one of my assumptions was wrong. I thought video_out.h was common across all three emulators, but there were slight variations marked out by #ifdef sections. I confess I didn’t understand what exactly these different chunks of code did, but I could make two observations: (1) if I wanted to use this code in my own projects I only need one of the code paths, and (2) they have something to do with the color palette for each emulator.

With that lead, I started looking at the color palette. Each emulator has its own color palette, when running in NTSC mode (my target) it is returned by the aptly named ntsc_palette() function. This appears to be a fixed array which was interesting. 8-bit color was very limiting and one of the popular ways to get around this limitation was to use an optimized adaptive palette to give an impression of more colors than were actually available. Another reason I had expected to find variable palette was the technique of palette animation (a.k.a. color cycling) common in the 8-bit era. Neither technique would be possible with a fixed palette. On the upside, it simplifies my code comprehension task today so I can’t complain too much.

With some basic deductions in hand on how this code works, I wanted to do a quick exercise to test accuracy of my knowledge. I wrote a quick routine to fill the frame buffer with the entire range of 256 values, isolated into little blocks. This should give me a view of the range of colors available in the palette.

    for(int y = 0; y < 240; y++)
    {
      yMask = (((y>>4)<<4)&0xF0);
      for(int x = 0; x < 256; x++)
      {
        _lines[y][x] = ((x>>4)&0x0F) | yMask;
      }
    }

When I ran this in the Nintendo emulator mode, I got what appeared to be four repeats of the same set of color. Referencing back to Nintendo code path in video_out.h I can see it meshes. Color value was bitwise-AND with 0x3F meaning only the lower six bits of the color value are used. Cycling through 256 meant 64 colors repeated 4 times.

        case EMU_NES:
            mask = 0x3F;

I didn’t need four repeats of the same 64 colors, so I edited my test program a bit to leave just a single bar on screen. Now I can take a closer look at these colors generated by ESP_8_BIT.

ESP_8_BIT Atari Mode Mysteriously Stopped Working

I was interested in getting my hands on some code that could generate color NTSC composite video without esoteric hardware hacking, and rossumur’s ESP_8_BIT promised to do that. As soon as I had it loaded on my own ESP32 and turned on my old tube TV with composite input, I could see it delivered on that promise. Everything else in this project would just be icing on the cake.

Sadly I had a lot of trouble getting the rest of ESP_8_BIT to work for me, so there were precious little icing for me. The first problem I encountered was that I didn’t have audio. This was expected for the Atari logo screen, but I checked the ESP_8_BIT YouTube video and saw there was supposed to be sound when the robot came on screen. Mine stayed silent, darn.

The next step was to get an input device working. ESP_8_BIT included a Bluetooth HID stack to interface with Bluetooth peripherals. One of the options is a Bluetooth Classic (not BLE) keyboard, and I had a Microsoft Wedge Mobile Keyboard on hand for this test. This is a keyboard I knew worked for my Bluetooth-enabled computers as well as an Apple iPad, so I did not expect problems. Unfortunately after going through the ESP_8_BIT pairing procedure, my ESP32 went into an endless reboot loop. As an alternate to Bluetooth, ESP_8_BIT also supported some infrared controllers, but I didn’t have any compatible device on hand. So unless I’m willing to buy an IR controller or buy another Bluetooth keyboard, it appears that there will be no ESP_8_BIT gaming for me.

There was one other mystery. This initial test pass was made several weeks ago. Afterwards, I set down ESP_8_BIT to be explored later. I worked on a few other projects (including Micro Sawppy rover) in the meantime. When I returned to ESP_8_BIT, I could no longer run the Atari emulator. It now enters a failure reboot loop with an error of insufficient memory. Not by a lot, either: the largest available segment of memory was only a few hundred bytes short of what’s requested.

esp_8_bit

mounting spiffs (will take ~15 seconds if formatting for the first time)....
... mounted in 99 ms
frame_time:0 drawn:1 displayed:0 blit_ticks:0->0, isr time:0.00%
emu_task atari800 running on core 0 at 240000000mhz
MALLOC32 235820 free, 113792 biggest, allocating Screen_atari:92160
MALLOC32 allocation of Screen_atari:92160 3FFE4374
MALLOC32 143644 free, 64864 biggest, allocating MEMORY_mem:65540
MALLOC32 FAILED allocation of MEMORY_mem:65540!!!!####################
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4

I hadn’t touched the code, and the hardware was only changed in that I actually have it on a circuit board this time. So I’m at a loss what might have caused this difference. One hypothesis is that an underlying Arduino library had been updated and took a little more memory than it used to, leaving not quite enough for the Atari emulator. Another hypothesis is that my Micro Sawppy code left something behind on this ESP32, impacting behavior of ESP_8_BIT.

Without more data or knowledge I couldn’t track down the source of this problem. So with the Atari emulator out of commission, my study course moved on to the Nintendo emulator for me to gain that knowledge.

NTSC Color Composite Video From ESP_8_BIT by rossumur

I’ve buried my head in remote-control vehicles for a while now, most recently writing code for my own Sawppy rover and looking over code written by others. Rover fatigue is setting in, so I’m going to look at a different topic as a change of pace.

About a year and a half ago Emily and I had a study session examining composite video signals. Using an oscilloscope to compare and contrast between different signal sources. Our reference was a commercial video camera, and we compared them against outputs generated by various microcontroller libraries. Two that run on ATmega328 Arduino, and one running on ESP32.

One thing they all had in common was that they were all in grayscale. The extra signal required for color NTSC composite video were beyond the reach of stock hardware. People have done some very creative things with hacking on oscillators to change operating frequencies, but even with such hacking the capability was quite limited. We thought color would be out of reach except by using chips with dedicated hardware such as the Parallax Propeller chip.

I was so happy to learn we were wrong! GitHub user rossumur found a way to generate color NTSC composite video signal from an ESP32 on stock unmodified hardware. The clever video hack was done using, of all things, an audio peripheral called the Phase Locked Loop. This color video capability formed the basis of an 8-bit video game console project called ESP_8_BIT, and I had to take a closer look.

For most projects I find online, I spend time researching the code before I bother investing equipment. But this project had such minimal hardware requirements that I decided to build a test unit first. I didn’t even need a circuit board: the video pin can be wired straight through and the audio pin only required a resistor and a capacitor. A few wires on a 0.1″ pitch connector and composite video jacks salvaged from an old TV were all I needed to install those parts on an ESP32 dev board.

Compared to the effort of wiring things up to my ESP32 dev board, getting my composite video screen actually took more effort. I had an old tube TV gathering dust sitting under a lot of other stuff, and it took more time to dig it up than to perform my ESP32 wiring. But once everything is plugged in and turned on, I could see the Atari logo on screen. And most importantly: it was in color! I’m going to follow this rainbow to the pot of ESP32 coding gold at the end, even if this adventure had an inauspicious start.

Cleaning Up And Commenting Sawppy Rover ESP32 Code

The rover is now up and running on its own, independent of my home network. I felt this was a good time to pause feature development of Sawppy ESP32 control software. From an user experience viewpoint, this software is already in better shape than my SGVHAK rover software. Several of the biggest problems I’ve discovered since our local rovers started running have been solved. I feel pretty good about taking this rover out to play. More confident, in fact, than rovers running my earlier software.

Before I turn my attention elsewhere, though, I need to pay off some technical debt in the areas of code organization and code commenting. I know if I don’t do it now, it’ll never happen. Especially the comments! I’m very likely to forget if I don’t write them down now while they’re fresh in my mind. The code organization needed to be tamed because there were a lot of haphazard edits as I experimented and learned how to do things in two new environment simultaneously: ESP32 and JavaScript. The haphazard nature was not necessarily out of negligence but out of ignorance as I had no idea what I was doing. Another factor was that I copied and pasted a lot of Espressif sample code (especially in the WiFi area) and I had instances of direct copied text “EXAMPLE” that needed to be removed.

I also spent time improving code robustness with error handling code paths. And if errors are not handled, they should at least be reported. I have many unhappy memories trying to debug some problem that made no sense, and eventually traced it to a failed API call upstream whose error code was never reported. Here I found Espressif’s own ESP_LOG AND ESP_ERROR_CHECK macros to be quite handy. Some were carried over directly from sample code. I’ve already started using ESP_ERROR_CHECK during experimentation, because again I didn’t have robust error handling in place at the time but I wanted to know immediately if something fails.

Since this was my first nontrivial ESP32 project, I don’t think it’s a good idea for anyone to use and copy my amateurish code. But if they want to, they should be allowed to. Thus another bulk edit added the MIT license prefix to all of my source files. Some of them had pieces copied from Espressif example, but those were released to public domain so I understand there would be no licensing conflict.

While adding error handling code, though, I wasted an hour to a minor code edit because I didn’t have debug tools.

[Code for this project is publicly available on GitHub]

Sawppy Rover Independence with ESP32 Access Point

I wanted to make sure I had good visual indication of control client disconnect before the next task on my micro Sawppy rover ESP32 brain: create its own little WiFi network. This is something I configured the Raspberry Pi to do on SGVHAK rover and Sawppy V1. (Before upgrading Sawppy to use a commercial router with 5GHz capability and much longer range.)

All of my development to date have been done with the ESP32 logged on to my home network. This helps with my debugging, but if I take this rover away from home, I obviously won’t have my home network. I need to activate this ESP32’s ability to act as its own WiFi access point, a task I had expected to be more complex than setting up the ESP32 as a client (station mode) but I was wrong. The Espressif example code for software-based access point (softAP) mode was actually shorter than its station mode counterpart!

It’s shorter because it was missing a lot of the functionality I had expected to see. My previous experience with ESP32 acting as its own access point had been with products like Pixelblaze, but what I saw was actually a separate WiFi manager module built on top of ESP32’s softAP foundation. That’s where the sophisticated capabilities like captive portal configuration are implemented.

This isn’t a big hinderance at the moment. I might not have automatic forwarding with captive portal, but it’s easy enough to remember to type in the default address for the ESP32 on its own network. (http://192.168.4.1) On the server side, I had to subscribe to a different event to start the HTTP server. In station mode, IP_EVENT_STA_GOT_IP signals that we’re connected to the access point and it has assigned an IP address. This doesn’t apply when the ESP32 is itself the access point. So instead I listen for a control client to connect with WIFI_EVENT_AP_STACONNECTED, and launch the HTTP server at that point. The two events are not analogous, but are close enough for the purpose micro rover control. Now it can roam without requiring my home WiFi network.

Sometime in the future I’ll investigate integrating one of those WiFi manager modules so Sawppy rover users can have the captive portal and all of those nice features. A quick round of web searching found this one by Tony Pottier, with evidence of several more out in circulation. These might be nice features to add later, but right now I should clean up the mess I have made.

[Code for this project is publicly accessible on GitHub.]

Micro Sawppy Beta 3 Running With HTML Control

After I established websocket communication between an ESP32 server and a phone browser JavaScript client, I needed to translate that data into my rover’s joystick command message modeled after ROS joy_msg. For HTML control, I decided to send data as JSON. This is not the most bandwidth-efficient format. In theory I could encode everything into binary with two signed 8-bit integers. One byte for speed and one byte for steering is all I really need. However I have ambition for more complex communication in the future, thus JSON’s tolerance for extra fields has merit from the perspective of forward compatibility.

Of course, that meant I had to parse my simple JSON on the ESP32 server. The first rule of writing my own parser is: DON’T. It’s a recurring theme in software development: every time someone thinks “Oh I can just whip up a quick parser and it’ll be fine” they have been wrong. Fortunately Espressif packaged the open source cJSON library in ESP-IDF and it is as easy as adding #include "cJSON.h" to pull it into my project. Using it to extract steering and speed input data from my JSON, I could translate that into a joy_msg data structure for posting to the joystick queue. And the little rover is up and running on HTML control!

The biggest advantage of using ESP32’s WiFi capability to turn my old Nokia Lumia 920 phone into a handheld controller is cost. Most people I know already have a touchscreen phone with a browser, many of whom actually own several with older retired phones gathering dust in a drawer somewhere. Certainly I do! And yeah I also have a Spektrum ground radio transmitter/receiver combo gathering dust, but that is far less typical.

Of course, there are tradeoffs. A real radio control transmitter unit has highly sensitive inputs and tactile feedback. It’s much easier to control my rover precisely when I have a large physical wheel to turn and a centering spring providing resistance depending on how far off center I am. I have some ideas on how to update the browser interface to make control more precise, but a touchscreen will never have the spring-loaded feedback. Having used a RC transmitter a few days before bringing up my HTML touch pad, I can really feel the difference in control precision. I understand why a lot of Sawppy rover builders went through the effort of adapting their RC gear to their rovers. It’s a tradeoff between cost and performance, and I want to leave both options open so each builder can decide what’s best for themselves. But that doesn’t mean I shouldn’t try to improve my HTML control precision.

[Code for this project is publicly available on GitHub.]

Shiny New ESP32 WebSocket Support

My ESP32 development board is now configured to act as HTTP server sending static files stored in SPIFFS. But my browser scripts trying to talk to the server via websocket would fail, so it’s time to implement that feature. When I did my initial research for technologies I could use in this project, there was a brief panic because I found a section in Espressif documentation for for websocket client, but there wasn’t a counterpart for websocket server! How can this be? A little more digging found that websocket server was part of the HTTP Server component and I felt confident enough to proceed.

That confident might have been a little misplaced. I wouldn’t have felt as confident if I had known websocket support was added very recently. According to GitHub it was introduced to ESP-IDF main branch on November 12, 2020. As a new feature, it was not enabled by default. I had to turn it on in my project with the CONFIG_HTTPD_WS_SUPPORT parameter, which was something I could update using the menuconfig tool.

Side note: since menuconfig was designed to run in a command prompt, using it from inside Visual Studio Code PlatformIO extension is a little different due to input handling of arrow keys. The workaround is that J and K can be used in place of up/down arrow keys.

Attempting to duplicate what I had running in my Node.js placeholder counterpart, I found one limitation with ESP32 websocket support: Using Node.js I could bind HTTP and WS protocols both to root URI, but this ESP32 stack requires a different URI for different protocols. This shouldn’t be a big deal to accommodate in my project but it does mean the ESP32 can’t be expected to emulate arbitrary websocket server configurations.

I also stumbled across a problem in example code, which calls httpd_ws_recv_frame with zero to get length, then allocating the appropriate sized buffer before calling it again to actually fetch data. Unfortunately it doesn’t work for me, the call fails with ESP_ERR_INVALID_SIZE instead of returning filled structure with length. My workaround is to reuse the same large buffer I allocated to serve HTML. I think I’ve tracked this down to a version mismatch. The “call with zero to get length” feature was only added recently. Apparently my version of ESP-IDF isn’t recent enough to have that feature in order to run the example code. Ah, the joys of running close to the leading edge. Still, teething problems of young APIs aside, it was enough to get my rover up and running.

[Code for this project is publicly available on GitHub.]

Notes on ESP32 RMT Peripheral For Receiving RC PWM

I want my micro Sawppy rover’s ESP32 brain to be capable of accepting control input from multiple sources. One of them will be my Spektrum SR300 radio control receiver. I examined its output control signal with a Saleae Logic 8 to make sure I understood what I will be working with, and the next step is to figure out how to interpret those signals with an ESP32.

With no shortage of hardware peripherals, including multiple options to generate PWM signals for servo control, I was confident an ESP had something to accept input. A bit of research quickly pointed me to its RMT peripheral, which was primarily designed to interface with infrared remote controls like our living room TV. But right in the documentation it also said “Due to flexibility of RMT module, the driver can also be used to generate or receive many other types of signals” and it looks like interpreting RC PWM is one of those “many other types of signals.”

Espressif pointed to several examples, but most of them only demonstrated how to send signals. Only one demonstrated how to interpret received signals, and it was for infrared remote control. I went out to the internet community of ESP32 users to find an example interpreting RC PWM signals, and I first came across this Reddit question, which was answered by [Justin Ong] with “I’ve done it and here’s my code.” I love forum threads with answers like this.

I didn’t try to download and run this code, but it was a valuable resource for me to cross-reference against Espressif documentation. From this I understood how RMT is used to sample the input signal at a particular rate, looking for rising and falling edges and reporting the time that has transpired between those edges. Once all the pieces came together in my head I understood why it is the perfect tool for interpreting pulse-width signals for hobby servo control.

After reading this example I think I have a good grasp of RMT configuration parameters and how the code interpreted the measured time durations. The part I’m still fuzzy about is the ISR (interrupt service routine.) This is the code that actually responds to an rising or falling edge event, and retrieves data from RMT peripheral hardware registers for later processing elsewhere. There were several pieces of bitwise manipulation code that I struggled to follow due to its tie with ESP32 hardware. This is not easy for people to grasp, the code comments referenced this one out of many forum threads from people confused about what they are expected to do. I see this as a warning flag that it’s very easy to make mistakes writing a RMT ISR, so I was glad to learn that I didn’t have to.

Micro Sawppy Beta 3 USB Access Port

When my little rover Micro Sawppy Beta 3 (MSB3) is running about, its brain built from an ESP32 dev module will be running from battery power. But when I’m updating its software via USB, I have to disconnect the power line to ensure it stays separate from my computer’s USB power line. I originally intended this disconnect to be done by removing a jumper. But after the jumper proved to be too buried to be easily removed, I made a quick hack to change it into a wire. Next step: make the USB port itself easier to access, because it is very much buried in there and hard to access.

I built MSB3’s body before knowing exactly what this circuit board will look like. My original intent is to make the bottom of the tray removable, held only by a few clips. Once those clips were removed I could drop the tray and access the USB port.

Unfortunately, this meant the circuit board is dangling by all the wheel motor and steering servo connections, which meant I’m constantly running the risk of accidentally pulling some wires. Either partially, resulting in an annoying intermittent connection. Or completely, resulting in a loss of functionality or possibly worse with power flowing where it isn’t supposed to. I didn’t like that risk before, and I’m certainly not happy about it now. What I want is a way to access the micro USB port without risking any unplanned wire removal, and that port is sitting behind this unbroken plastic face.

Since this is merely a prototype, there’s no reason why that plastic face must remain unbroken. So I pulled out my drill and started drilling. Starting with a small hole to create a pilot and verify I have the correct location, and gradually working my way towards larger diameter drill bits.

One of the larger drill bits caught and split the case along a printed layer line, but I could still keep things together with my clips so there is no functional problem with the new cracks. It is merely a bit of cosmetic embarrassment, which I can tolerate for the benefit of easy access to my ESP32 dev kit’s micro USB port. There are more things I would like to have in a micro Sawppy rover control board, but this is enough for me to continue working.

Provision for Micro Sawppy Voltage Monitoring

Recent experience iterating through Micro Sawppy prototypes made it clear I underestimated the task of designing a power supply scheme to fit all of my objectives. My blind spot came from the fact Sawppy V1 was up and running with a very simple scheme for power. I had a two-cell lithium-polymer battery pack, and almost all the components on board were happy to take power directly from that battery and perform their own internal voltage regulation. The only exception was the Raspberry Pi 3 on board, to which I attached a MP1584 buck converter to supply a consistent five volts. It was very little effort to get Sawppy V1 working, so I had the misconception power schemes are easy! They are not.

In order to meet my cost objectives for Micro Sawppy, I switched to different components. These simpler components were far more particular about their power supply, so I had to take on more of the power considerations that were previously a feature built in to more expensive parts. We are now at the point where I think I need to pull the rover’s ESP32 brain into the discussion. The motivation here are the six TT gearboxes and their corresponding motors, officially rated for operation up to 6 volts but can tolerate brief periods above that.

In order to stay below that maximum, the ESP32 can limit its maximum motor control PWM duty cycle sent to the DRV8833 motor control ICs. In an ideal world, if I had a 7.4V power supply, I should be OK as long as I limit PWM duty cycle to no more than (6 / 7.4) = 81%. But this voltage value would change as the battery depletes. When the battery is fully charged at 8.4V, 81% would delivery too much power. And as it approaches depletion of 7V, 81% would be too low to obtain 6V output. What I really want is for the PWM duty cycle to be dynamically adjusted based on battery voltage.

Conveniently, the current pin assignment for ESP32 dev kit still has one input pin open and available for use. So I soldered a pair of resistors to that pin. An 1 MOhm resistor to the battery voltage pane, and a 100 KOhm resistor to ground. This gives me an 11:1 voltage divider which I should be able to read with one of ESP32’s ADC (analog-to-digital conversion) peripherals. This provision will still need corresponding software work before it’ll do anything useful. But if it doesn’t work, it should be pretty easy to clip those resistors off.

The primary objective for voltage monitoring is to dynamically adjust PWM duty cycle in order to maintain rover performance as the battery discharges. Secondary objective is to let the ESP32 send out a low battery alert if the battery is low. Sawppy V1 used an external battery voltage alarm (*) but if I can incorporate that feature into ESP32 software it’ll cut down on parts cost. At the very least, I would like to put the rover into limp mode if the battery voltage drops below a threshold, which would be a feature missing from Sawppy V1.

I expect that battery voltage drop would make the motors unreliable well before it makes the ESP32 unreliable, but as my breadboard test showed, that is still possible. So another potential work item on the to-do list is to enable ESP32 brownout detection capability and recognize when battery voltage is dragged down by rover motors.

Configurable Micro Sawppy Servo Power Supply

My first soldered control board for Micro Sawppy was a huge mess of wires, most of which were related to power distribution. I am confident future revisions will improve, partly from experience and partly from features that I won’t need anymore. One of them is visible in the center of the title picture: a jumper by a MP1584 buck converter voltage regulator. It allowed me to switch between powering the servo with that buck converter and powering the servo directly from battery input power.

Earlier I had determined that four AA alkaline batteries had the right voltage for SG90 micro servos, but that voltage would sag significantly under the load of TT gearmotors driving six wheels. Adding another battery would destroy the servo when the system is unloaded. I knew there were a lot of power losses on my earlier breadboard-based prototype. I thought a soldered board would be a more accurate test. But while it may have made some difference, it was not enough to help Micro Sawppy run reliably on four AA batteries.

If Micro Sawppy is to be powered by alkaline batteries and still avoid using a voltage regulator for servos, I would have to move upscale to higher amperage C or D batteries. I also contemplated the idea of trying one of those large rectangular 6V lantern batteries but they all share the problem of availability. Alkaline C, D, and lantern batteries were once commonplace, but they aren’t very common anymore. I had a few NiMH (nickel-metal hydride) rechargeable batteries in AA form factor, who have the ambition to replace C and D batteries with a few adapter sleeves. I tested them and also found they could not sustain the required amperage under heavy draw.

I could also have multiple four-AA banks in parallel, or have separate power sources: one bank of four AA batteries just for steering servos, driving the remainder of the rover on some other power source. This complexity feels extremely inelegant and I can’t yet think of any reason why this path would be better than conceding that I will need a voltage regulator for steering servos.

So I moved the jumper to the other position, and started using a MP1584 buck converter breakout board set to produce 5.4V. This is between the valid range of 4.8V and 6V, and it is two volts under the nominal 7.4V of 2-cell LiPo I’m using to test this circuit. This two volt margin should be enough for MP1584 buck converter to work.

I used a MP1584 breakout board here because I had leftovers from a multipack (*) I bought for earlier projects, but I’m not confident they are the right device for the job. The datasheet claims it can sustain two amps of output with occasional spikes to maximum of 3 amps. Four SG90 micro servos would usually stay well under that limit, but their power consumption can spike occasionally making capacity planning unpredictable. At the very minimum I should put an electrolytic capacitor to buffer its output, and experimentation will tell me if I need more than that. I might also try to monitor the input voltage level.


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

Power Distribution Complicates First Soldered Prototype Circuit

One of my ESP32 dev kit modules was too wide to be breadboard-friendly. But it had reduced pin count, making for a smaller overall footprint on a circuit board so I used it as the centerpiece for a perforated board prototype. This Micro Sawppy control board has the functionality of my breadboard prototype plus a few enhancements.

When I set out to plan my ESP32 control pin assignments, I laid them out roughly in the same physical relationship as they would be on a rover, hoping this would make control wire routing easier. I’m happy to report that this planning paid off, the motor control signal wires were very straightforward to route. For the servo control signals I reused wires from micro servos I negligently destroyed, and for DC motor control signal pairs I pulled solid core wires out of a CAT5E cable which was conveniently in already-twisted together pairs.

What I did not plan out was power distribution, which was something I knew was a challenge yet I had no plan beyond “maybe soldering would help”. Well, the lack of planning really showed in the utter mess of wiring that resulted, as more than half of the wires visible are for power distribution of one context or another. Which wasn’t helped by the fact I used thicker 22AWG wires for power distribution taking up far more space than low-amperage control signal wires. I definitely need to put some thought into power distribution for the next control board revision.

In the middle of the title image is a yellow jumper. When it is in place, the ESP32’s VIN pin is connected to this board’s battery power, letting it run standalone. This jumper must be removed before I upload new software to my ESP32. If not removed and the battery is not connected, the rover will try to run on power from the computer’s USB port which will not work possibly destroying the USB port. Or if the battery is connected, it will send battery voltage into the computer’s USB port which is also a bad thing. Unfortunately this setup doesn’t have the physical exclusion design in my solid-core wire breadboard version. I am scared of forgetting this jumper and want to bring physical exclusion back in a future revision.

I have another jumper on this soldered prototype board, but it serves a completely different purpose.

Another ESP32 Dev Kit Layout

After destroying a few SG90 micro servos and admitting I should have known better, I think the breadboard prototype circuit board has fulfilled its mission and it’s time to move on. My next iteration of Micro Sawppy rover control circuit will be a perforated prototype board with soldered connections. Having soldered connections will help give me more reliable connections and also reduce loss of electrical power from thin wires, which may or may not help with my power supply problems.

For my breadboard prototype I chose one of the ESP32 development modules that were narrow enough to fit well on a breadboard, leaving room on either side for jumper wires. (Left module on title image.) The narrow width meant there was no room left for pin labels, but I found a paper template that I could use to help with pin identification.

Now that I am building a soldered circuit, I no longer need to use the narrow module. So I pulled out a different development module I had bought(*) for exploring ESP32 development. (Right module on title image.) This one is wider and longer, giving us several advantages including screw mounting holes in each corner. Even thought it was longer, we have fewer pins to worry about, because this design didn’t bother to bring out the six pins corresponding to this ESP32 module’s built-in flash memory. We couldn’t use those pins in our design anyway so it was no huge loss. Conveniently, those flash memory pins were divided three on each side, so their removal still left the layout symmetric. A fourth pin was dropped from each side. On one side, an extraneous ground pin and on the other, pin zero was omitted. This is technically an available I/O pin but using it is tricky. It is a part of ESP32 startup process, the pin pulled to ground by the BOOT button. I had considered it a “pin of last resort” and avoided allocating it in my allocation scheme, so its subtraction from this module was fine for my use. All of the remaining IO pins are brought out on this module, and it even maintained the same relative ordering of those pins making it familiar to use. Helped by the fact that its more generous width also allowed onboard labels on these pins, eliminating the need for a paper template.

On board the module, I saw a very similar set of components. Two buttons “EN” and “BOOT” surround the micro USB port. There is a chip to handle USB serial translation, and an AMS1117-3.3 handles voltage regulation. I think I see a second LED which was absent from Espressif’s reference design but present in a few other dev kits I’ve used. But I also see one oddball: it appears a capacitor next to the EN button is at an unusual angle. When I see a surface mount component at a non-orthogonal angle, my first thought is a pick-and-place or soldering error. But this capacitor seems to be bridging two real pads and not dangling off into space, so it’s probably not an error. It might be some sort of a hack to address some problem discovered after the rest of the board was laid out, I’m not sure. As long as the module works I guess it really doesn’t matter. As a test I flashed my ESP32 test program and saw the second LED start blinking. Good enough to get to work.


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

Initial Thoughts on Micro Sawppy Rover Battery Power

Wiring up all the motorized components on my cardboard rover testbed exposed a few minor problems that were easily fixed, but exposed battery power as a headache I’ll have to deal with. Sawppy Rover V1 and Micro Sawppy Beta 1 both used lithium-polymer battery packs from the world of remote-control hobbies. They are lightweight and powerful, but they are also sensitive to mechanical damage and occasionally react very badly to abuse. I have ambition to make Micro Sawppy a small beginner-friendly rover design, so I didn’t want people to be intimidated by all the dire warning surrounding proper LiPo care and feeding. Also, while lithium battery costs have dramatically dropped over the past years, they are still relatively expensive and I wanted to keep Micro Sawppy affordable.

Which was why for my cardboard rover testbed, I started with a pack of four AA alkaline batteries. Alkaline AAs are common, inexpensive, and tolerant of abuse. Four of them in series deliver a maximum of six volts, aligning with the maximum rated voltage for TT gearmotors and SG90 servos I used on Micro Sawppy. Which is why a four-pack of AA is a very popular way to power small robot kits using those components. The difference is that they usually only have a pair of TT gearmotors, whereas Micro Sawppy has six. They also have fewer than four SG90 micro servos like those handling corner steering here. With all of these components, my cardboard rover has proved to be too much work for four AAs. Dragging its output voltage below four volts and triggering a brownout reset of the ESP32 brain.

My next idea on a way out of this predicament is to use a commodity USB power bank. They produce nominal 5V for charging USB devices. They are usually built around lithium batteries, but they are a self-enclosed device with a durable shell and protective circuitry. Making them a beginner-friendly safe way to incorporate lithium battery power. Officially USB1 only allowed devices to draw half an amp at five volts, which is not nearly enough for a rover. But newer USB power banks advertise higher power levels, some of them communicate using a secret handshake to indicate this capability. I had a few different power banks on hand, advertising maximum output of up to 2A. Unfortunately none of those were able to sustain power delivery for my cardboard box rover, either.

All of these power sources advertised power ranging from 5V but none of them could sustain that under the load of Micro Sawppy motors. I then returned to the idea of using alkaline AA batteries and thought… what if I added one battery, for five AA batteries in series? The answer is: some micro servos get very unhappy and quit.

Cardboard Rover Testbed Wheels Turning And Steering

Once I figured out how to handle power for ESP32 on my cardboard box rover testbed, I started connecting the rest of the components. Since I had motor control PWM code ready to go, I started by connecting the DRV8833 breakout boards one by one to the power rail. Which for this experiment was a battery holder for four AA alkaline batteries that had a power switch built in. (*)

I wanted to start with 4 AAs because alkaline batteries are commonplace and more importantly, tolerant of abuse compared to sensitive lithium ion batteries. I’m not squeamish about working with lithium battery packs personally, but I am squeamish about using them on a rover design I want to advertise as beginner-friendly. Four fresh AA batteries in series would deliver 6V, which is coincidentally the maximum spec voltage for both SG90 micro servos and TT gearmotors. Thus a four-pack of AA is a common power supply for little robot kits using similar components.

Here I am thankful for the LED indicating power supply, which was not part of the DRV8833 data sheet but added as a bonus on the particular breakout boards I had bought. Each module was powered-on to verify the power LED illuminated, then I turned it off to install one wheel. After I turned it back on to verify one wheel is working correctly, I would turn it off and connect the other wheel for verification. Testing parts one step at a time ensured that whenever something went wrong (and they did) I would know where to start looking.

As I connected more motors, I noticed that the DRV8833 power LED would visibly dim when the wheels start turning. To monitor voltage, I wired in a compact voltmeter(*) originally purchased to monitor battery voltage on Sawppy V1. I saw the voltage would sag from nearly 6V to just under 5V. More of a drop than I had expected, but with all six wheel drive motors in place I thought I was in good shape. It turned out I severely underestimated the peak power draw for four corner steering SG90 micro servos. When those little servos are trying to reach a new position ASAP, they seemed to draw more than the TT gear boxes despite their physically smaller motors. During regular operation my four-pack of AA would struggle to stay above 4V, and if the steering servo encounters any obstruction, it would drop below 4V causing the ESP32 to brown out and reset. This is obviously Not Good and I will need to put more thinking into power supply.


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

Cardboard Box Rover Testbed ESP32 Power Supply

Power distribution is a concern in any electronics project, but a breadboard ESP32 circuit mounted on a cardboard box rover stand-in has an additional twist: USB. When a USB cable is plugged in to my ESP32 dev kit to upload new firmware and serial monitoring, it is also connected to the computer’s +5V power rail. But when my rover is out and about on its own, that USB connection will be absent and ESP32 will have to draw power from the onboard battery.

I initially thought I would have to install a 5-volt regulator on my rover to power the ESP32, much as how I had been using MP1584 buck converters to power Raspberry Pi 3 for earlier projects such as Sawppy V1. Then I remembered the ESP32 is a 3.3V part so there must already be a voltage regulator of some sort on the dev kit. Looking on the board and confirmed on the schematic, there is a AMS1117-3.3 LDO (low drop-out) regulator on duty. These regulators are happy to accept voltage up to 15V and down to as low as 1.3V over the output voltage, which in this case is 3.3 + 1.3 = 4.6V.

Looking over the schematic that HiLetgo bundled with this ESP32 dev kit, I see the onboard USB serial interface chip also runs on the 3.3V regulator output. Another thing this “Vin” pin is connected directly to is the power LED, which has a 1k current-limiting resistor so it can tolerate higher than 5V of input.

So that leaves the USB cable’s +5V line. If I have battery power voltage on that VIN pin, I don’t want that voltage to feed into my computer’s USB port. I need to make sure this ESP32 can connect to USB or battery power but never both at the same time. My solution is to use a length of solid-core wire to bridge battery power input to the Vin pin, and shaping the wire so it blocks the USB port. This way, I can’t connect this wire while the USB plug is in use.

In order to use USB power, I have to disconnect the VIN wire and swing it out of the way like a gate. In order to make sure that battery voltage wire doesn’t touch anything inconvenient while it is out of the way, I stabbed the exposed solid core wire into cardboard to tie up that literal loose end.

None of the remaining components on the rover testbed will need to receive software updates over USB, so this is the only place where I had to worry about switching between two power sources. Everything else will be wired directly to battery power.

Cardboard Box Rover Testbed Breadboard

Writing down ESP32 pin assignments in a spreadsheet is a good to plan things out ahead of time, but it isn’t a robot until wires are connected and circuits completed. I’ll test the plan on my cardboard box rover testbed. When I cut out the holes for all those motors and servos, I left a space open in the middle for circuitry. Since this is a testbed to prove out a plan, I want the flexibility to easily test things incrementally as I build things piece by piece. It would also be nice to easily fix problems I will undoubtedly discover during testing. Both of these desires pointed to a breadboard and jumper wire instead of a more permanent soldered solution.

In my collection of ESP32 development kits, there is a very important differentiation for this task: some of these modules are 0.1″ wider than the others. This difference is critically important for a breadboard prototype: the narrower modules leave one breadboard hole exposed on either side for jumper wiring, in contrast the wider module would completely block all holes on one side. To use wider modules on a breadboard, all the wires have to run underneath the module making them much less experimentation-friendly than their thinner counterparts. For this particular project I’m using an ESP32 dev module I purchased from this Amazon link (*) but there’s no guarantee the vendor wouldn’t change things up in the future.

One problem with the narrower module is that its compact layout left no space for labels on the visible side of the board. Rather than laboriously looking up charts and counting pins every time a wire connection is to be made, some clever people have designed paper templates to help with the task. I found one that matched my ESP32 dev kit at this link. Checking against my dev kit’s schematic, I see it is functionally correct though there is an inconsequential cosmetic typo of GIOP instead of GPIO. Printing out the PDF at 100% scale will match size with the real thing, which I then put between the breadboard and my ESP32 as I pushed the ESP32 dev kit in to the breadboard. Metal pins have no problem punching through that paper, and the paper is not conductive enough to cause problems with the circuit.

Having a pinout reference immediately available cuts down on frustration and mistakes, and I liked the idea so much I wanted them for my DRV8833 breakout boards as well. Because those breakout boards also had visible pin labels on their downward-facing side of the board. I didn’t have the skill to draw up a nice template using graphics design software, but using a sheet of paper I could create a hand-written reference.


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

Assigning Pins for Sawppy Rover ESP32

As part of exploring MCPWM duty cycles, I had to make decisions on ESP32 pin assignments. This is how my ESP32 DevKit will communicate with my DC motor driver and steering servos. Three DRV8833 motor driver IC breakout boards for Micro Sawppy rover’s six TT gear motors driving wheels, plus four corner steering servos. Together they require sixteen PWM pins which is within an ESP32’s capability.

I’m very thankful for this compilation of ESP32 pinout from Random Nerd Tutorials, which served as a starting point and immediately crossed off many off-limit pins like those tied to ESP32 flash memory access. I also eliminated the primary serial transmit & receive pins from consideration as I wanted to keep them available for serial monitoring.

The next pins I wanted to keep clear were the two hardware I2C pins. I want to be build a baseline ESP32-controlled rover with as few external support chips as possible. But if I can leave the I2C pins free, that leaves the door open for add-ons and expansions. Additional PWM pins can be added with something like the popular PCA9685 chip. (*) And additional digital input/output pins can be added via PCF8574 (*) or similar chips. In fact, if I get to a point where I desire specific ESP32 peripherals (capacitive touch?) I can offload all wheel driving and steering duties to an external PWM chip.

I wanted to leave hardware SPI pins open for the same reason, but SPI was a luxury I could not afford. That would mean leaving four more pins unused and that’s more than I can spare for my baseline design. If SPI becomes important, I’ll need to offload some wheel control to external PWM chips. Another luxury I had to leave behind are the JTAG debug pins, which also demanded four pins I could not spare unless I offload PWM to external components.

But even though I could use those JTAG pins for general output, some care had to be taken because there will be some spurious signals on those pins. Upon power-up, before my code can run to configure those pins for PWM output, these pins will have signals corresponding to JTAG or other system use. In practice I expect this to mean my rover will twitch a little bit at startup, so I assigned those pins to steering servo signal duty. I rather that my rover steering servo twitch on powerup instead of a drive wheel. I don’t want the rover to drive itself forward or back uncontrolled, no matter how briefly.

After all of the above concerns are factored in, the final one is the physical relationship of pins. I wanted to keep related pins close together, such as the A and B lines for motor control. This is a little tricky to do on the Random Nerd Tutorials chart, because it is sorted by GPIO number instead of their physical position. I used Excel to create a chart that maps the physical location of those pins. Then I assigned their use so they roughly correlate to their positions on the rover, with the DevKit USB port pointing towards back. In reality the wiring won’t be that straightforward because the motor control lines will have to go through a DRV8833 first, but it might help me during debugging.

The assignments are listed on the outer-most columns of the chart. The first letter is one of (F)ront, (M)id, or (B)ack. The second letter is (L)eft or (R)ight. The third letter is (A) and (B) for DRV8833 motor control, or (S) for steering servo. This particular set of assignments leaves the I2C SDA & SCL pins free. Unencumbered GPIO23 is left free and clear for anything. The four input-only pins 34, 35, 36, and 39 are still available. And as a last resort, we still have the restricted-use GPIO0 pin. With this plan for pin assignments in hand, I proceed to turn the plan into reality.


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

Interactive MCPWM Duty Cycle Explorer

I started exploring TT gearmotor response to different duty cycles by editing MCPWM parameters in source code, compile with those new parameters, and uploading the results to my ESP32 motor control test bench board. It took only three cycles of this process before it was painfully clear I needed a better way. So I copied an excerpt (the joystick and MCPWM portions) of my ESP32 Sawppy control software out to a separate test program in order to interactively explore TT gearbox behavior under different duty cycles.

This test program only dynamically varies the duty cycle, not the PWM frequency. I’m running my program at a PWM frequency of 20 kHz. There may be an annoying whine audible to dogs and newborns, but it’s comfortably above my not-spring-chicken range of hearing. A brief test established that these TT gear motors behave differently at lower PWM frequencies. The most consequential effect is that I could turn them at a lower minimum speed. Still not rock-climbing slow, but there’s definitely an effect. I don’t have enough electrical engineering background to understand why I have more torque with lower PWM frequency, so I’ll have to come back to this topic later to find an explanation for my observation. In the meantime I’m insistent on 20 kHz PWM because I don’t like to hear the whining.

In my test program I didn’t have a real need for analog control, so the joystick was effectively turned into a direction pad. Pushing one direction increments duty cycle, pushing the opposite decrements it. The orthogonal axis adjusts the increment rate, so I can adjust in duty cycle increments of 10%, 1%, and 0.1%. In order to test startup power, I could press down on the stick to trigger its associated button. This button toggles motor power on and off. This program is only useful when connected to serial monitor because the increment rate and the current duty cycle are displayed via text sent over serial link. I plan to use this tool again once Micro Sawppy is rolling on its own wheels, in order to update speed-to-duty cycle mapping to reflect realistic loads on the wheels rather than unloaded spinning in the air.

In theory it should be possible to have this tool as a part of Sawppy ESP32 control code instead of a separate project. I would have to set up some sort of conditional compilation mechanism so I could toggle whether to compile standard Sawppy control or this duty cycle explorer. However, I don’t see any particular motivation to perform this work today so until I see a benefit, I’ll leave that integration task on the to-do list for the future. With some working PWM duty cycles in hand I can start putting them to work with ESP32 GPIO pin assignments.


[ This test program is publicly available on GitHub. ]