Over the Air Firmware

Introduction

MBED makes it very easy to update firmware on the device by plugging in the MBED and dragging over the binary file. However, sometimes it would be much more convenient to not have to plug in the MBED every time to update the firmware, such as if the device was in a hard to reach place.

The premise for this implementation of OTA firmware was the difficulty of updating many robots on the field during competition for my RoboCup robotics team. This implementation is somewhat specific in the hardware but if desired it could be adapted for other systems using the MBED.

The build system for our robot was too complex to use the MBED online compiler so our code and our build system can be found at: https://github.com/RoboJackets/robocup-firmware. It is currently being developed on the radio-protocol branch but will soon be merged into master once fully completed.

Hardware

This code was developed to run on an MBED LPC1768 connected to our custom PCB. The schematics for this PCB can be found at https://github.com/RoboJackets/robocup-pcb. The basics for what this board does is receive radio packets of how to control the robot by moving motors then send a reply with information about the robot. The competition is an autonomous robotics soccer league so there is a base computer computing strategies and where the robots should more then sending those commands over the radio to the robots.

/media/uploads/petersonev/control.png

The radio that is used with this system is called the Decawave radio. It is an ultra-wideband radio that allows for fast data transfer while communicating under the noise floor so very little interference. This radio protocol takes advantage of many features of Decawave radios but can also be adapted for other radios.

Code Structure

The major portions of the code that allow the radio to function are in CommLink, CommModule, and in the individual radio driver (in this case the Decawave driver). The packet structure can be found in rtp.hpp and how the packets are actually handled can be found in RadioProtocol.hpp.

CommLink and CommModule are used to abstract away portions of the radio protocol so it is easier to send and receive packets without having to directly interface with the radio drivers. It also allows us to more easily change radios in the future if we wish.

  • CommLink is used as a hardware abstraction layer for the radio driver so the Decawave class is derived from the CommLink class (which is derived from the SharedSPIDevice class). This allows us to call functions on CommLink and have the Decawave driver handle all the specifics of communicating with the radio.
  • CommModule is used to assist with packet handling and routing. It allows for various packet types to be sent and CommModule handles the specifics of how that information gets packed into the packet. It also allows us to attach callback functions for each of the various packet types so when a packet of that type is received it is easy to pass it into a function for processing that specific type of packet (such as having a callback for receiving a file).

The various structs that are used to form the specific radio packets can be found in rtp.hpp. For OTA firmware the packet structure is the multiple headers for MAC and higher layers then the entire payload is raw binary data for the sequence of the file defined in the header.

MAC header for Decawave

struct MACInfo {
    uint8_t seqNum;
    uint8_t ackRequest;
    uint8_t framePending;
    uint16_t srcAddr;
    uint16_t destPAN;
    uint16_t destAddr;
} __attribute__((packed));

The other important packet structure for OTA firmware is the packet to check the file after it has been sent. It contains the checksum for the file and the file size for verification of accurate file transfer, then it also has the file name and extension so if desired the file can be named once transferred

struct FileCheckMessage {
    uint32_t chkSum;
    uint32_t filesize;
    char fileName[8];
    char fileExt[3];
} __attribute__((packed));

Radio Protocol

Our radio protocol runs in three separate threads under MBED RTOS:

  • There is a radio receiving thread that is waiting for a signal from the radio interrupt pin. The Decawave asserts the interrupt pin high when it has received a radio packet. We have set up a number of filters in the Decawave configuration to reject all packets that are corrupted or incorrectly addressed so the MBED only has to use CPU time when there is a good radio packet waiting. This thread is configured in CommLink.
  • There is a packet receiving thread to handle all incoming packets that are in the receiving queue. These packets are placed there from the thread above and this allows for the MBED to receive packets in quick succession then process the queue of packets when there is time. This thread is configured in CommModule.
  • There is a packet transmitting thread so elsewhere in the program packets can be put in the transmit queue and sent when there is not something more critical happening. This thread is configured in CommModule.

In order to attach a callback for file transfer for OTA firmware the following code is used:

m_commModule->setRxHandler(this, &RadioProtocol::rxFile, rtp::MessageType::FILE);

/media/uploads/petersonev/screen_shot_2017-12-13_at_05.56.17.png

This causes the following function to be called every time there is a packet recieved with the message type FILE. This can be relatively simple because much of the complexity has been abstracted away in CommLink, CommModule, and the Decawave driver. Because of limitations in the MBED interface's local file system all other binary files must be deleted and a binary with a different name must be loaded on. This is because the microcontroller communicated with the local file system over JTAG through the MBED interface and the MBED interface does not implement any kind of file data modification so it defaults to creating files in 2008 at the same time.

FILE *fp;
uint16_t fileSeqNum = 0;
void RadioProtocol::rxFile(rtp::Packet pkt) {
    if (pkt.header.seqNum == (fileSeqNum)) {
        if (fileSeqNum == 0) {
            FILE *fp2;
            if ((fp2 = fopen("/local/firm1.bin", "r")) == 0) {
              cleanupBin();
              fp = fopen("/local/firm1.bin", "wb");
            } else {
              cleanupBin();
              fp = fopen("/local/firm2.bin", "wb");
            }
            LOG(INFO, "File transfer opened");
        }
        fileSeqNum++;
        fwrite(pkt.payload.data(), 1, pkt.payload.size(), fp);
        if (pkt.macInfo.framePending != 1) {
            LOG(INFO, "File transfer closed");
            fileSeqNum = -1;
            fclose(fp);

            Thread::wait(3000);
            mbed_reset();
        }
    }

    rtp::Packet pkt2;
    pkt2.macInfo.destAddr = 0x0000;
    pkt2.macInfo.destPAN = rtp::BASE_PAN;
    pkt2.header.seqNum = fileSeqNum;
    pkt2.header.type = rtp::MessageType::PKT_ACK;
    pkt2.empty = false;

    m_commModule->send(std::move(pkt2));
}

This opens a file when the sequence number is zero to start and increments the sequence counter when the correct packet is received. It then sends an acknowledgment back indicating the the transfer for that segment of the file is complete. This allows the transfer to correct itself if there was a packet dropped because the base station will send back the next segment in the sequence according to the acknowledgment sent from the robot. Then when it is indicated that there is no frame pending, the transfer is over and the file can be closed and the MBED can be reset.

Base Station

The base station is what is connected to the computer and is simply an MBED, a Decawave, and a USB connector. The PCB can be found in the robocup-pcb repository. The base station is basically reading a file 100 bytes at a time and sending those 100 bytes to the robot. If it receives an acknowledgment that shows a packet has been dropped it readjusts where it is reading the file and resends the correct segment of the file. The base station waits for an acknowledgment from the robot or a set amount of time to ensure if the ack is lost the file transfer continues.

/media/uploads/petersonev/base_station.jpg

Problems and Future Work

The to main problems currently are the transfer speed and the issues with the MBED local file system:

  • The transfer speed is quite slow currently because for each packet the base station must wait for the robot to receive and process the packet then send an acknowledgment then have the base station receive the ack. There are many ways this can be improved, for example not requiring and ack every time, only have periodic acks so if there was a dropped or corrupted packet it can be corrected
  • The MBED microcontroller has a very limited of functions that can be run on the local file system through the MBED interface so all binary files have to be deleted to upload a new one from the microcontroller because the creation date is 2008. Since the MBED interface updates the firmware from the latest binary file it is very likely that there is a newer binary file on the MBED. There is very little that can be done to fix this without updating the MBED interface which is not going to happen.

There are many more features that can be added to this OTA firmware updater such as the ability to address multiple robots or have a reliable data transfer over a broadcast. This would allow for very quickly updating the entire fleet of robots but would be difficult because of packet timing for the various acknowledgements from robots at the same time.

Video

Video of uploading MBED blinky test program:


Please log in to post comments.