This little example demonstrate how simple it is to create a Servo using the build in LED PWM. ESP32 comes with 16 channels of PWM, so in setup we asscociate channel 0 with pin 14 and set pin 14 to OUTPUT. We also set channel 0 to 50Hz 16 bit PWM. The example will sweep 0-180 degrees using GPIO14 as signal.
#define SERVO_PIN 14
void SetServoPos(float pos)
uint32_t duty = (((pos/180.0)
*2000)/20000.0*65536.0) + 1634;
// convert 0-180 degrees to 0-65536
// set channel to pos
A servo signal is a 20ms pulse (50Hz) with the signal as a 500uS – 2500uS width to indicate servo angle.
ExpressIF implemented FreeRTOS with their Arduino IDE library, so by using this we get a lot for free. By default we run Wifi etc on core 0, while the classic Arduino loop() run’s on core 1. In my case I want to use core 1 for bit banging and that is straight forward to program in loop(). By using an ever loop I bit-bang with < 50yS accuracy which is very good.
The more tricky part is the communication tasks that we have to add to core 0. Firstly we need to deal with the Task Watchdog. Since core 0 is used for system tasks it also monitor it’s tasks and trigger the watchdog if any of them run forever, so we need to (1) create a loop, and (2) call delay within that loop.
The console print below show a loop w/counters in loop being monitored by a task in core 0.
Ca 526191 iteration a second and stats show that we max have 10uS delta with an average of 1.9uS between iterations. This is our bit-bang accuracy. These 10uS will probably be 50uS as we add content, but that is still 50-100Khz accuracy or 0.05 – 0.01ms if you like.
My alternative would be to attempt a 10,000 timer per sec timer interrupt that would give 0.1 ms accuracy and use ca 10% CPU load. I have not tried this on ESP32, but this technique have its advantage that you can use 80-90% of Core2 for something else.
It’s time to code up the ESP32 Utility Driver. I am not that found of Arduino IDE, but it works and I like the wire library concept and how it simplify things. A Servo port is a signal that send a 50Hz pulse. The technique I suggest is a bit-banger where we loop as fast as we can checking pulse length. We set up the 12 signals in a table, start them at the same time and then iterate as fast as we can closing them at the proper time to get a correct pulse length.
The alternative is to use an interrupt. This could work, but we would get a resolution of 1ms (1000/sec) or 0.1ms(10000/sec) max. Any interrupt faster than that would use to much CPU time. Another technique would be to bit-bang signal 1, then signal 2 etc. this is what the classic Arduino library does. The technique I suggest gives a much higher resolution as we iterate and check much faster that we can use interrupts.
This would have worked well with a single core, but as we have a dual core we can allocate this task to one core and I expect something like 10uS accuracy or 100Khz sampling if we use this as data sampler.
We have 21 IO ports to maintain in an iteration:
- 12 Servo or IO ports
- 2 (4) H-Bridge signals
- 7 PWM signals
ESP32 is perfect for this job as we can use 1 core for this purpose while the second core handle the Wifi and easyIPC that I will return to later.
This ESP32 based Utility driver is just awesome in what it can do. I have to make a Rev 1.1, but for most parts it worked “as is”
- 7 x PWM Signals 0,5A each.
- CH340G UART to USB.
- ESP32 WROOM.
- 12 x Servo or IO ports.
- 12V PSU Input.
- 2 x H-Bride for DC Motors.
I had 2 errors on the board. (1) I did not cross Rx/Tx correctly and (2) I overlooked some logic needed for the serial bootloader. The later forces me to use the boot jumper all the time, but I will fix that on Rev 1.1. The schematics below illustrate the bootloader fix:
This is copied from the reference diagram at expressif and indicate how DTR and RTS on the serial interface is used to automatically toogle ChipPU and Boot as we download new firmware. This should avoid the need to set the boot jumper and restart to trigger the serial bootloader protocol. I will see if I can test this on a vero board before ordering 1.1 rev of the PCB’s.
Finally received the PCB for the Train Control System and the Utility Driver. I decided to solder the Utility driver because that has the CG340G USB connection. Soldered the PSU & CG340 was straight up. I am actually impressed by CH340G as it always work and connect to Windows. Soldering ESP32 was straight forward, but first attempt to get it working failed.
A quick look at schematics and swapping RX/TX did however do the job. A trick is to use Boot first time you load from Arduino IDE and it also help having a working reference like a breakout board.
Finally the Wifi scan works from my utility driver. I will solder up the rest of the circuit and start programming servo’s tomorrow. My first usage of this will be on my 12 legged robot.
These two last pictures show the Train Control System. We can play with that later. Actually I need to get my act together and buy a test track. As said my ambitions are limited to a track around the x-mas tree – ok maybe 2,3,4 or 5 tracks 🙂
Not that many days since I ordered this, so was happy to see it. Caused me to work to 04:00 this morning and voila – Arduino IDE is running smooth. I am not sure I want to continue using Arduino IDE, but was nice to see that it works with ESP32 even if they still have things left.
To code a few lines and suddenly be on Wifi – cool! I also know that my 2 designs are in mail – exiting.
The power of Arduino is that you plug in the chip, start Arduino IDE, write a few lines and woops – it works. I will give many arguments for not using Arduino on more complex systems, but it is great for prototyping.
As for the Model Train Controller and Model Train Utility Driver I will support Arduino on them. My own model train plans are limited to a track around next years x-mas tree, but I think it’s a huge number of hobbyists out there that would benefit from these designs and enjoy a bit of coding themselves.
Early mornig fun. Just changed the driver to use USB (CH340G) and added a separate 12V supply and some jumpers to select power Source. It can now be driven from 12V or 5V and you can use 12V for PWM’s while using USB 5V for ESP32 etc to have some isolation. Also added pull-up for Boot/Reset and jumper to use them.
A bit more labels and TVS diodes on the left 12 x IO and we’re done. I must admit that this little fellow will see some usage.
My 12 legged robot comes to mine as this is a very small, compact alternative for this one. But, I need some experience with using ESP32 before I continue.
We have a lot of space on the Utility Driver, so I can afford to add some TVS diodes on the ports that go directly out. These are 3.3V and can only handle 6-12mA out – basically they can just about drive a Led with some care.
I also need to change UART0 pins + I would like to replace the RS485 port with an USB powered by CH340G – in which case the LM1117 can have a jumper selecting if it is powering 3.3V from USB or 12V. The advantage of this is that I get the Boot pin free.
I also need to add a pull-up on Reset and a way to Reset the ESP32.
And obviously – a power Led.
I found this schematics on esp32.com forum. I will add pull-up resistors on GPIO0 and EN as well as route them to the extension bus on the TCS. I also need to change pins so I use pin 34/35 as UART0.
UARTS can be assigned to any GPIO pins, but in this case I need to know where it is after Reset and how we boot firmware on a clean system.
As for the FTDI chip above – I apologize for this – You will NEVER see a FTDI chip on any of my designs!
This is a draft of the Utility Driver. Basically this is a ESP32 based driver board with 7 PWM lines, 2 H-Bridges, 12 IO lines and a RS-X connection. The board can be used as a wired/wireless utility board.
- ESP32 Wroom
- 7×2 PWM connectors
- 2 x H-Bridge Motor ports
- RS-X port w 12V Power inn.
- Jumper to select 3.3V or 12V on signal channels.
- 12 x Signal channels. Can be analogie in, Digital In/Out, Servo, I2S, I2C, UART, SPI, PWM or others.