GitHub Actions changes to fight cryptocurrency miners

GitHub actions are a great CI/CD setup for every open-source project. Of course, as long, as the project is hosted on GitHub. It turned out, however, that cryptocurrency miners started to take advantage of that and propose abusive pull requests that were mining cryptos using GitHub computing power.

This changed somehow. Yes, if you really, really want to, you can propose a PR that will mine for you, but now, if you are a first time contributor, Actions will not run until someone approves it.

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” »

The State of the RC/FPV Hobby Q1 2021 – The results

A few months ago I aske the RC/FPV community a series of questions what they use and how they fly. Today, it is time to announce the results!
In total, I got answers from 1739 people. I will not lie: the number is like 10 times higher I expected it to be. So that you! I appreciate the effort!

Let's go through the question one by one, with a short commentary.

In the last 3 months I've been flying…

In the last 3 months I've been flying...

Continue reading “The State of the RC/FPV Hobby Q1 2021 – The results” »

Pirx Seven vs DJI FPV

Pirx Seven 7-inch FPV frame

I designed the Pirx Seven 7-inch FPV frame when the DJI Digital FPV System was something as a novelty. Truth to be told, back then I have not believed it will so popular as it is today. Turned out, I was wrong on that and the DJI Digital FPV System is incredibly popular. Luckily, the Pirx Seven turned out to be future-proof and it accepts both DJI Airt Unit and Caddx Vista without much effort.

The Pirx Seven allows you to:

  • Install Caddx Vista in the front section, right behind the FPV camera
  • Install Caddx Vista in the center section upside-down under the top plate
  • Install Caddx Vista in the rear section with a longer, 14cm coax wire
  • Install the DJI Air Unit in the center section upside-down under the top plate

For details on the Air Unit setup, please see the video below

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
How GPS works?

How GPS works? The short story of a time difference!

We take GPS and other GNSS systems (Galileo, Glonass, Beidou) for granted. They are there, they tell us where we are, and everything works "out of the box". However, all the GNSS/GPS system are fascinating examples of how amazing science and technology is! Do you know a GPS receiver can find its location only thanks to the delay of radiowaves and all GPS satellites emit actual date and time?

FPV meets corporation – DJI FPV Goggles V2 vs V1 and availability

Why can't I buy DJI goggles anymore

All of you who tried to buy FPV goggles in the last three months might have noticed that it was not a trivial endeavor. Starting from the last December, the DJI Goggles and Air Units and Caddx Vistas availability was limited. To some extent, it was due to increased demand just before Christmas and so some, because V1 goggles production ended a few months ago and what we were getting on the market were units produced probably in the Q2-Q3 of 2020.

DJI FPV goggles

Continue reading “FPV meets corporation – DJI FPV Goggles V2 vs V1 and availability” »