LILYGO T5 4.7-inch E-paper ESP32 development board

One of the biggest advantages of ESP32 development boards (without even counting the speed, flash size, WiFi, Bluetooth, and two cores) is that they come in a variety of shapes and sizes: bare boards, with OLED, with color LCD, with LoRa chipset, with GPS modem, etc., etc. And finally, you can get them with an e-paper display.

LILYGO T5 4.7-inch E-paper ESP32 development board

Continue reading “LILYGO T5 4.7-inch E-paper ESP32 development board” »

How to generate PPM signal with ESP32 and Arduino

The PPM protocol for encoding Remote Control channel values is now a legacy. Still, it is widely accepted by different hardware and when tinkering with Arduino, remote control, and working on own accessories for flight controllers, PPM is still a valid option.

A few years ago I presented a code that allows generating PPM stream using Arduino and AVR hardware. That solution is very hardware-specific and works only with ATMega microcontrollers.

During my work of ESP32 DiyMotionController I stumbled on a problem: if ESP32 is not compatible with AVR when timers are a concern, how to generate a PPM stream on ESP32?

PPM signal on an oscilloscope

This is why I reserved one evening, powered up my oscilloscope (Rigol DS1054Z), and created the code you can see below.

    #define PPM_FRAME_LENGTH 22500
    #define PPM_PULSE_LENGTH 300
    #define PPM_CHANNELS 8
    #define DEFAULT_CHANNEL_VALUE 1500

    #define OUTPUT_PIN 14

    uint16_t channelValue[16] = {1500};

    hw_timer_t * timer = NULL;
    portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

    enum ppmState_e {
        PPM_STATE_IDLE,
        PPM_STATE_PULSE,
        PPM_STATE_FILL,
        PPM_STATE_SYNC
    };

    int getRcChannel_wrapper(uint8_t channel)
    {
        if (channel >= 0 && channel < 16)
        {
            return channelValue[channel];
        }
        else
        {
            return DEFAULT_CHANNEL_VALUE;
        }
    }

    void IRAM_ATTR onPpmTimer() {

        static uint8_t ppmState = PPM_STATE_IDLE;
        static uint8_t ppmChannel = 0;
        static uint8_t ppmOutput = LOW;
        static int usedFrameLength = 0;
        int currentChannelValue;

        portENTER_CRITICAL(&timerMux);

        if (ppmState == PPM_STATE_IDLE) {
            ppmState = PPM_STATE_PULSE;
            ppmChannel = 0;
            usedFrameLength = 0;
        }

        if (ppmState == PPM_STATE_PULSE) {
            ppmOutput = HIGH;
            usedFrameLength += PPM_PULSE_LENGTH;
            ppmState = PPM_STATE_FILL;

            timerAlarmWrite(timer, PPM_PULSE_LENGTH, true);
        } else if (ppmState == PPM_STATE_FILL) {
            ppmOutput = LOW;
            currentChannelValue = getRcChannel_wrapper(ppmChannel);

            ppmChannel++;
            ppmState = PPM_STATE_PULSE;

            if (ppmChannel > PPM_CHANNELS) {
                ppmChannel = 0;
                timerAlarmWrite(timer, PPM_FRAME_LENGTH - usedFrameLength, true);
                usedFrameLength = 0;
            } else {
                usedFrameLength += currentChannelValue - PPM_PULSE_LENGTH;
                timerAlarmWrite(timer, currentChannelValue - PPM_PULSE_LENGTH, true);
            }
        }
        portEXIT_CRITICAL(&timerMux);
        digitalWrite(OUTPUT_PIN, ppmOutput);
    }

    void setup()
    {
        pinMode(OUTPUT_PIN, OUTPUT);
        timer = timerBegin(0, 80, true);
        timerAttachInterrupt(timer, &onPpmTimer, true);
        timerAlarmWrite(timer, 12000, true);
        timerAlarmEnable(timer);
    }

    void loop()
    {
        /*
        Here you can modify the content of channelValue array and it will be automatically
        picked up by the code and outputted as PPM stream. For example:
        */
        channelValue[0] = 1750;
        channelValue[1] = 1350;
    }

the code uses one of ESP32 Timers/Alarm to generate PPM in the background. Logic happens inside of onPpmTimer handler function. Code rescheduled the timer alarm to trigger according to RC channel values. The given example allows to encode 8 channels in a PPM stream. It's fully asynchronous from the main loop.

ESP32

ESP32, Arduino and Timer/Alerts

Because of completely different architecture, ISR and Timer solutions known from other Arduino compatible platforms, especially AVR/ATmega does not work on ESP32. They don't. At all. If you would like to port any code that uses timers from AVR Arduino to ESP32 Arduino, you would have to rewrite them completely.

However, timers and alarms with espressif ESP32 Arduino core are simple. Much simpler than AVR equivalents! No more writing registers with magical numbers.

The basic code for Arduino ESP32 Timer looks like this:

hw_timer_t * timer = NULL;

void IRAM_ATTR onTimerHandler() {
    //This code will be executed every 1000 ticks, 1ms
}

void setup()
{
    // Prescaler 80 is for 80MHz clock. One tick of the timer is 1us
    timer = timerBegin(0, 80, true); 

    //Set the handler for the timer
    timerAttachInterrupt(timer, &onTimerHandler, true); 

    // How often handler should be triggered? 1000 means every 1000 ticks, 1ms
    timerAlarmWrite(timer, 1000, true); 

    //And enable the timer
    timerAlarmEnable(timer); 
}

void loop() {
    //Here you can do whatever you want
}

You have to bear in mind that:

  1. timer = timerBegin(0, 80, true); If you run ESP32 with a different clock, you have to modify the prescaler. 80 is valid for 80MHz clock
  2. timerAlarmWrite(timer, 1000, true); the last true sets alarm to autorepeat. If you set it false, handler will be triggered only once and you will have to manually set it to enabled inside of the handler
  3. If you want to synchronize data/variable between the ISR/timer handler and the main loop, variable has to be declared as volatile
  4. To make it better, you should also use semaphores for data synchronization. Then, the code would look like this:
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
volatile int counter = 0;

void IRAM_ATTR onTimerHandler() {
    portENTER_CRITICAL(&mux);
    //This code will be executed every 1000 ticks, 1ms
    counter++;
    portEXIT_CRITICAL(&mux);
}

void loop() {
    portENTER_CRITICAL(&mux);
    counter = counter + 5;
    portEXIT_CRITICAL(&mux);
}

For more information refer to Espressif ESP32 documentation.

DIY Motion Controller

DIY Drone Gesture Control – DIY Motion Controller

Together with the DJI FPV Drone we, RC and FPV hobby enthusiasts, got a new accessory: DJI Motion Controller.

DJI Motion Controller

It does a pretty obvious thing that we know for years from the Nintendo Wii controller: with this device, you get gesture control over your FPV Drone. You tilt it left, drone turns left. You tilt it back, drone gains altitude. You press the index finger "trigger" and the drone goes forward! There are some problems though. First of all, it works only with the DJI FPV Drone. And second of all, it cost an extra $200!

What if, I could make a similar device that works with any drone or airplane? To be honest, I was thinking about a device like that for a while now, and DJI Motion Controller was just a final argument to sit down for a few days and make an Open Source version of a Motion Controller that can be connected to any OpenTX radio as a Trainer Slave and use gestures to fly!

The DIY Motion Controller is based on ESP32 board with MPU6050 gyroscope and few extra elements like buttons, analog joystick, etc. It can be connected to any OpenTX radio (I'm using Radiomaster TX16S) that allows accepting SBUS signal on trainer port.

Drone Gesture Control Joystick

The exact list of part is:

Drone Gesture Control Joystick

As you can see in the video it works! However, it's not the end of the project as I intend to improve it in at least a few ways:

  1. The wired connection to the OpenTX radio is suboptimal. Next step is to develop a wireless mode to simplify usage
  2. There is still no way to arm/disarm the drone, so at least one switch will have to be added
  3. The analog Arduino joystick is too big and not precise enough. I will try to replace it wit PSP or similar joystick for altitude and yaw control

Drone Gesture Control Joystick

The source code with all the required details is available on GitHub. You will need an access to a 3D Printer to print the joystick, but STL file is available over there as well.

ESP32 and multiple I2C buses

One of the advantages of the ESP32 microcontrollers over the competitions is dual-core architecture and two I2C buses.

Yes, the I2C bus allows connecting multiple slave devices to single pair of SCL SDA wires. As long as slave device addresses are unique, everything will work just fine: OLED display, LM75 temperature sensor, MPU6050 gyroscope. However, one has to remember that one of the devices can be polled at a time. If the bus is used by, for example, MPU6050, parallel communication with SSD1306 OLED display is not possible.

Here comes the advantage of having multiple I2C buses. Not only you can have more devices of the same type and avoid I2C address conflicts, but you can also take full advantage of 2 ESP32 cores and communicate with multiple devices at the same time.

ESP32 with OLED

In the following example, I will show how to use multiple I2C buses of ESP32 using Espressif core for Arduino.

#include <Wire.h>

//Set pins for I2C1
#define I2C1_SDA_PIN 21
#define I2C1_SCL_PIN 22

//Set pins for I2C2
#define I2C2_SDA_PIN 0
#define I2C2_SCL_PIN 23

TwoWire I2C1 = TwoWire(0); //I2C1 bus
TwoWire I2C2 = TwoWire(1); //I2C2 bus

void setup()
{
    // Init buses
    I2C1.begin(I2C1_SDA_PIN, I2C1_SCL_PIN, 100000); // Start I2C1 on pins 21 and 22
    I2C2.begin(I2C2_SDA_PIN, I2C2_SCL_PIN, 100000); // Start I2C2 on pins 0 and 23
}

void loop() 
{
    //Talk to device connected to I2C1
    I2C1.beginTransmission(0x3c);
    I2C1.write(0x40);
    I2C1.endTransmission();

    //Talk to device connected to I2C2
    I2C2.beginTransmission(0x30);
    I2C2.write(0x45);
    I2C2.endTransmission();
}

Two remarks:

  1. ESP32 use matrix IO that allows mapping any pin to any function
  2. GPIO 34, 35, 36 and 39 are INPUT ONY ports and can not be used as SCL or SDA
  3. Some Arduino compatible libraries do not allow to set I2C bus to use always use I2C1. Refer to the library documentation for details on how to choose I2C bus

Radio modules FS1000A and XY-MK-5V – pros and cons

When looking for radio modules for your next Arduino project, you might have come across the couple named: FS1000A and XY-MK-5V. At first glance, they might look like the next best thing, but it’s not that simple.

FS1000A and XY-MK-5V

Before you start hacking anything with FS1000A and XY-MK-5V, read the following pros and cons:

Pros:

  • simple – to send a signal you do not need much. Only to power them up and set LOW or HIGH on the data pin
  • cheap – true, they are not expensive

Cons:

  • pretty much everything else, including the same things that are their pros…
  • too simple – they are just too simple and offer no other functions than transmitting a wave through the void. No CRC, no packets, so SPI or serial, no bidirectional communication
    everything has to be done in the software, including encoding and CRC.
  • transmitting and receiving devices have to use the same libraries and support on some platforms like Raspberry Pi might be problematic at best
  • the frequency can not be changed!
  • no frequency hopping or spread spectrum
  • you have to get your own antennas
  • range is poor at best and depends on things like voltage of the transmitter
  • build quality varies a lot and you can not be sure that TX and RX are really tuned to the same frequency

Verdict? At leat be very careful when choosing hardware for your next Arduino RF project…

How to connect GPS to ESP32

Thanks to a very versatile Input/Output matrix, it is quite simple to connect NMEA GPS modules to ESP32 MCUs. Not only ESP32 boards have 3 serial ports you can choose from, they can be assigned to almost any pin you want.

ESP32 with GPS and OLED display

In this example we will connect a popular Ublox NEO-M8N like Beitian BN-880 or BN-220 to a ESP32 development board and output current position on a USB serial port using Arduino IDE and TinyGPS++ library. Let’s begin

Continue reading “How to connect GPS to ESP32” »

Espressif ESP32

ESP32, Arduino and EEPROM memory

ESP32 MCUs can be a great replacement for popular ATmega328 Arduino boards, but they are somewhat different. Even if ESP32 Espressif Arduino core is used, some of the libraries will not work out of the box.

Just like EEPROM will not work without changes. Mainly because ESP32 does not have EEPROM memory. An external flash is used instead.

Good thing, ESP32 EEPROM library distributed together with Espressif ESP32 Arduino core solves this problem in a quite nice way. All you have to do is to call EEPROM.begin with a size of emulated storage size in bytes as an argument. From there, everything goes as before.

ESP32 and GPS with a help of TinyGPS++ library

One of the first projects I did with ESP32 development boards is a simple GPS tracker. OK, it's not really a tracker since it does not store the position anywhere, more like distance meter with a UBLOX Neo-8M Beitian BN-880 GPS unit and small SSD1306 OLED display.

This ESP32 GPS Thingy as I call it uses one button to store current position and then report straight line distance, speed and altitude compared to "Home Point". GPS communication is handled by TinyGPS++ library.

Oh, one the best things about ESP32 is that you can map ports to almost any pin you want. It's not like on ATmega328 where UART and I2C are always the same pins. Here you can choose them. How nice is that?

Code is available on GitHub.

Espressif ESP32

Getting started with ESP32 development boards and Arduino

However fond of good old Arduinos based on ATmega328 and ATmega32u4 we might be, no one can now say they are state of the art. Sure, they might be the first choice to do something cheap and simple, but compared to most more modern designs, they are just too old and too weak. Slow, little flash memory, little RAM, no built-in connectivity: no Bluetooth or WiFi.

When a few years ago ESP8266 started to appear, everything changed. The tinkers finally got something small and cheap with WiFi. And then, the world has changed, since ESP32 it the market.

Geekcreit ESP32 development board

Continue reading “Getting started with ESP32 development boards and Arduino” »