ESP32S Animatronics Controller

Summary

A client needed a replacement for an aimatronics controller that was 20 years old for a display that was being refurbished. The client wanted a Nano or ESP32 implementation reusing the existing control and audio files. The control output was limited to 8 control signals and the client recommended using a self-contained MDFLY AU5016 card for audio playback.

Hardware Configuration

ESP32S Test Configuration

The hardware included an ESP32S NodeMCU module, an Adafruit microSD card breakout board+, a MDFLY AU5013/5016 audio card, and a HiLetgo 8-channel level translator. The AU5013/5016 card is a 5V only device and has a microSD slot for the audio files.

The AU5013/5016 is a fully integrated microSD/SDHC MP3/WAV/APE player module. It is controlled using start/stop protocol over a serial TTL level interface. The AU5016 also has digital signal control for several features. This module contains a high-performance DSP processor core and a high-quality stereo DAC that is capable of driving stereo earphone directly with an amplifier.

Also included in the breadboard test configuration was a MB102 breadboard power supply outputting both 5V and 3.3V, 10K pot for volume control, speaker, and 8 LEDs to represent the control output.

Implementation

The firmware was developed using the Arduino IDE (1.8.13) and use SPI (SD card interface), SD (SD card file system), HardwareSerial (AU5013/5016 com port), and FreeRTOS libraries. The firmware implemented two tasks, one task to control the output/audio and a second task for volume control. Each task was assigned to a core.

Two critical items in the implementation was the control playback at 30Hz and control of the audio card. The 30Hz playback used a hardware timer and interrupt handler for the output timing while the audio card control was implemented as a C++ object.

Timer

A timer object was created to generate an interrupt at the 30Hz rate. The interrupt handler incremented a counter (timeInt) while the main code that waits for the interrupt decrements the same counter. Using this method instead of a boolean allows for missed interrupts to be handled and the control loop to “catch-up” if necessary. The timer setup and interrupt control is greatly simplified for the ESP32 as shown in the code below.

hw_timer_t * timer = NULL;    // struct used for timer


// setup 30Hz timer values but don't enable interrupt
timer = timerBegin(1, 320, true);             // clock @ 250KHz, timer group 1
timerAttachInterrupt(timer, &onTimer, true);  // interrupt handler
timerAlarmWrite(timer, 8333, true);           // count and reload flag
timerInt = 0;    // non zero used to indicate when an interrupt has occurred


// enable timer interrupt
timerAlarmEnable(timer);


// disable interrupt
timerAlarmDisable(timer);
AU5016 Object

An object was created to interface with the AU5013/5016 audio card. The object implemented most of the audio card commands. The interface used the HardwareSerial library for the serial interface and a FreeRTOS binary semaphore to provided an interface MUTEX due to multiple tasks interfacing to the same card. The object interface is shown in the code segment below.

// AU5016 object class
class AU5016 {
  public:
    AU5016(int uartNum, int RXPin, int TXPin, int BusyPin); // AU5016 object
    int StartTrack(int track); // starts playing track, error return
    void Stop(void);           // stops tracking playing
    int SetVolume(int level);  // sets volume level, returns volume level
    int VolumeUp(void);        // increases volume level + 1, returns volume level
    int VolumeDown(void);      // decreases volume level -1, returns volume level
    void Mute(void);           // mutes output
    int PlayPause(void);       // pasues play track, un-pauses track, error return
    int SetEQ(int EQ);         // sets EQ, returns error
    int RepeatMode(int Mode);  // sets repeat mode, return error
    int TrackAfterPower(int Mode);  // sets power on play track, return error
    int SetBusyStatus(int Mode);    // sets busy output state, return error

  private:
    HardwareSerial *_AudioSerial;   // UART interface to AU5016
    SemaphoreHandle_t  _sema_v;     // multi task access control
    int _Busy;                      // busy pin
    int _audioSendCommand(int cmd); // send a command, return response
    int _audioGetResponse(void);    // get command response, return response
};

The constructor allows the users to select the UART was well as the pins used for the transmit, receive, and busy signals. Because the audio and control are time synchronized using third party software, when the controller is ready to set the first output word, the audio playback is started using the StartTrack method. What clock slip there is between the ESP32 and AU5013/5016 card is not noticeable during a ~2 minute show.

Leave a Reply