Plug and play UART devices (USB alike)

We released another fine quality embedded system module to add connection safety to all serial devices. So, our devices will be foolproofed to the point that most end users can handle the connection for the devices themselves.

Where does this started?

I was developing a user-facing product, and I wanted something like a USB device. I want to let people plug my device into their phones, and everything else is automagically working.

CH340 IC

UART is a cheap and easy choice, but the protocol itself is just a simple wire protocol and does not include anything on the application layer. In contrast, once users plug in a USB device into their host device, USB descriptors would do the heavy lifting for them; it will figure out which driver to use, the name of the device, and a pointer that leads to more information on the internet.

It would be great if UART devices have something like USB vendor id and device id so end users can plug the devices in, on their own, and not worrying about using the wrong software driver.

It turns out to be possible to add the same safety as VID/PID to any serial interface devices, including UART. You only need another layer of software. Luckily, Slime Systems already designed one, arguably one of the best, and it is potentially supported your hardware stack too!

In case you are impatient

Head directly to

But, if you are curious, please read on for more details.

My takes on the issue

  • Scoping the issue down to my current use case
    My use case is that I want to create add-on hardware for an android phone application. Something like adding some muscle parts to the existing brain.
  • About command and data (How everything is a command?)
    It is all about designs. To some extent, you can get away with only commands.
    For example, If you want to turns on a motor for 10 seconds, you can send 10 seconds as data over the wire and let the external devices handle the timing. However, you can also send start and stop commands instead, and manage the timing internally on the phone.
    For me, all my current use cases can utilize command only encodings. And I hope other people can do the same thing too.
    This is very important to the design as it reduces complexity, eliminates problematic overrun issues, and allows finite buffer size, which in turn let the software run on devices without a memory management unit.
    This approach does not always work, though. For instance, in the case of sending numeric values read by sensors. So be it, the objective of the module is not to please everyone, and that’s fine and natural. Different problems need different solutions.
  • Why the heck does USB VID and PID needs a centralized bookkeeper?
    If you want to have your USB devices, you need to acquire your own vendor id (VID) from USB-IF by paying them a sum of money. They are the ones who manage the list of all USB vendors.
    The technical aspect of it is that, if no one manages the list, people would choose the number randomly and collide with each other.
    But, the real reason is that no one, excepts some few sponsors, would pay USB-IF if they don’t technically need to acquire something from them.
    In fact, the specs can simply specify VID of 128-bit length to prevent collisions, but they don’t do so because the real issue lies ahead.
    In my case, no one would pay me for the open-source anyway, so, no practical concerns here; thus, I will use that 128-bit approaches in my design.
  • Combining what we have
    So far
    • Everything is a command
    • 128-bit random key instead of VID/PID managements

    So, I will just create the library that prefixes a single-byte command with a 128-bit constant. And that is exactly what I did.

  • But not that simple
    I want to make it works on every existing serial port, not just UART, every existing and future CPUs; more importantly, I wanted to abstract away the time complexity of serial devices as a bonus. So, I design it with the clean separation between firmware and software, with no vendor-specific code included. I also include a finite-size buffer to store the commands and execute them asynchronously without a blocking operation.

Usage example

After I use the libraries in a project, I realized that there is a use case that most device will potentially implements. It is the ability to ping.

pingpong

To do that you needs both the encoder and the decoder libraries.

First configure the encoder.

#include "haystack_command_encoder.h"
#include "uart1_transceiver.h"
#define OUTPUT_COMMAND_PING (0)
#define OUTPUT_COMMAND_PONG (1)
static const HaystackCommandEncoder_Config command_encoder_config = {
    // 128-bit key generated with cryptographically-secured method
    // to ensure collision resistance 
    .key = {0xe0, 0x9b, 0x9f, 0xa4, 0x4c, 0xc1, 0xaa, 0x99,
            0xdc, 0xa8, 0x7e, 0xfe, 0xc1, 0xb4, 0x88, 0x0c},
    .port_ready = &is_uart_ready,
    .port_flush = &uart_send,
};
static HaystackCommandEncoder_State command_encoder_state;
// These are things needed to be implemented for each hardware
// For this example, I used a firmware that also created by Slime Systems
// included from uart1_transceiver.h (more on that later)
static bool is_uart_ready() {
  return uart1_transceiver__is_ready();
}
static void uart_send(uint8_t command) {
  uart1_transceiver__send(command);
}

Then configure the decoder.

#include "haystack_command_decoder.h"
#define INPUT_COMMAND_PING (0)
#define INPUT_COMMAND_PONG (1)
static const HaystackCommandDecoder_Config command_decoder_config = {
    // Should be the same key as the encoder.
    // You can use a different key but AFAIK there is no apparent reason to do so.
    .key = {0xe0, 0x9b, 0x9f, 0xa4, 0x4c, 0xc1, 0xaa, 0x99,
            0xdc, 0xa8, 0x7e, 0xfe, 0xc1, 0xb4, 0x88, 0x0c},
    .command_received = &command_handler,
};
static HaystackCommandDecoder_State command_decoder_state;
static void command_handler(uint8_t command) {
  switch (command) {
    case INPUT_COMMAND_PING:
      // when we receive a PING we response with a PONG
      haystack_command_encoder__queue(
        &command_encoder_state,
        OUTPUT_COMMAND_PONG
      );
      break;
  }
}

The following step is not directly about this library but included for the sake of completeness. This is how I hook the decoder with the firmware of a serial port. (More about the firmware later)

#include "uart1_transceiver.h"
static const Uart1Transceiver_ReceiverConfig receiver_config = {
    .data_received = &data_handler,
    .overrun_detected = &uart_error_handler,
    .data_error_detected = &uart_error_handler,
};
static void data_handler(uint8_t data) {
  haystack_command_decoder__push(
    &command_decoder_config,
    &command_decoder_state,
    data
  );
}
static void uart_error_handler(void) {
  haystack_command_decoder__discard(
    &command_decoder_state
  );
}

And for the sake of completeness I also include main in this example. Even if it is not directly related to the libraries.

int main() {
  disableInterrupts();
  haystack_command_encoder__init(&command_encoder_state);
  haystack_command_decoder__init(&command_decoder_state);
  uart1_transceiver__setup_clock(0x1a, 0x01);
  uart1_transceiver__setup_transmitter();
  uart1_transceiver__setup_receiver(&receiver_config);
  enableInterrupts();
  for (;;) {
    haystack_command_decoder__execute_pending(
      &command_decoder_config,
      &command_decoder_state
    );
    haystack_command_encoder__poll(
      &command_encoder_config,
      &command_encoder_state
    );
  }
}

And as a bonus, since my embedded C style is still quite unorthodox, I also wrote an example project. The example includes every integration details and build instructions. Check it out at the GitHub repository.

Byproduct

In the process of design and creating these modules, I also created UART firmware for STM8S. You can also check it out at the STM8S UART1 Transceiver repository. Since it is something designed by Slime Systems, quality is something you can look forward to.

I should write up about firmware designs sometimes.
See you then.