An Event-Driven Daemon with CLI

Introduction

This article describes a program that makes building robots and other automation easier. The empty event-driven daemon (eedd) gives a nice abstraction (text over TCP) to the peripherals on your robot. It is empty in the sense that the daemon itself only provides the command line interface, leaving driver functionality of the daemon to a set of loadable shared object libraries or plug-ins. EEDD has several features that you might might find useful for your next project.
  - Command line tools to view and set driver parameters
  - Simple publish/subscribe mechanism for sensor data
  - All commands and data are printable ASCII
  - Modular user-space drivers (plug-ins) for easy development and debug
  - No dependencies (excluding libc)
  - Ready-to-run, event-driven daemon

An article describing how to write plug-ins is available here.

 

Controllers and Motors as Plug-ins
Introduction
Quick Start
High Level Application API
Sample Application
The EEDD API
Other EEDD Notes

 

Quick Start

If you have gcc installed and an open terminal window you can download, build, and test the empty daemon in about 60 seconds.
  (ssh to your target device if needed)
  git clone --depth=1 https://github.com/bob-linuxtoys/eedd.git
  cd eedd; make
  sudo make install
  # Start the empty daemon (background but not a daemon)
  export PATH=$PATH:/usr/local/bin
  eddaemon -ef &
  # Load the "hello, world" demo plug-in 
  edloadso hellodemo.so

  # Subscribe to the message service of hellodemo
  edcat hello_demo message     # "hello, world" once a second

  # Open a second terminal window, and subscribe again
  export PATH=$PATH:/usr/local/bin
  edcat hello_demo message     # "hello, world" once a second

  # Open a third terminal window
  # Change the print period to 2 seconds
  export PATH=$PATH:/usr/local/bin
  edset hello_demo period 2

  # Change the printed string to good-bye
  edset hello_demo messagetext good-bye

  # Display info about the system and about hellodemo
  edlist
  edlist hello_demo
The above shows how to start the empty daemon, how to load a plug-in, how to subscribe to a data stream, and how to change parameters while the program is running.

 

High Level Application API

The core of the empty daemon is pretty simple: when the daemon starts it opens a TCP port (default is 8870) to listen for incoming command connections. Once a connection is set up, the daemon parses newline terminated ASCII commands that are passed in. The recognized commands are:
   edloadso <plug-in.so>
   edget <name|ID#> <resource_name>
   edset <name|ID#> <resource_name> <value>
   edcat <name|ID#> <resource_name>
   edlist [name]

Each of the above has an equivalent bash command that behind the scenes opens a TCP connection and sends the command. (You can use telnet to connect directly to the empty daemon if you wish.) You'll find that having Bash commands to get or change plug-in parameters makes it easy to use a script file to drive your project or to help automate its testing.

The edloadso command starts everything by loading a shared object plug-in. Plug-ins are user-space device drivers that interface to the underlying hardware or software service. EEDD provide sample drivers that include interfaces to a game controller, to IRC (for robot team communication), and to a GPS receiver.

Each time a plug-in is loaded it is given a slot ID. Slots are numbered sequentially starting from zero. It is possible to load a plug-in twice. Since the two instances have the same name you'll want to use the slot ID to differentiate them in the get, set, and cat commands.

If you have a USB game controller you can test it by plugging it into your computer and starting the daemon as described above. Load the game controller plug-in with:

    edloadso gamepad.so

Resources are ASCII printable values stored in an instance of a plug-in. Resources can be read-only, read-write, or broadcast. You can view a readable resource's value with the edget command and set a read-write value with the edset command. Callbacks in the plug-in are invoked when you get or set a resource, or start a stream.

Although resources are presented as a static values, callbacks for writing or reading a resource can be used as a command to the system. For example, setting robot's motor mode to BRAKE would trigger sending the underlying commands to stop the motor.

For example, the game controller has a resource that tells the system where in /dev to find the device for the game controller. Tell EEDD where your game controller is located with a command like:

    edset gamepad device /dev/input/js0

The edcat command starts a stream of data. The TCP connection becomes dedicated to just that stream of data. As you write applications you'll find that you have a TCP connection for each sensor stream, and one more connection for get and set commands.

You can start a stream of game controller state information with the command:

    edcat gamepad state

The command edlist gives a list of the plug-ins that are loaded. It also shows the resources associated with each plug-in.

The edlist command doubles as the help system. If you give the edlist command followed by the name of a plug-in you'll get the help text for that plug-in. This makes writing applications easier since the meaning and syntax of a plug-in's resources are easy to see.

You can list the plug-ins in your system and get help for the game controller with the commands

    edlist
    edlist gamepad

Each of the get, set, list, and loadso commands return a backslash to indicate that the command has completed. If the command fails for any reason an error message is given. The error message is followed by a backslash to indicate that the command has completed.

Be sure your application reads the backslash from the command connection or the socket's receive buffer will fill and all communication over the socket will fail.

 

Sample Application

Let's say you want to use the game controller described above to control a small two wheeled robot. You're using an FPGA based robot controller that offers an EEDD plug-in that let's you set the mode and PWM pulse width for two H-bridges motor controllers.

The motor_cntrl plug-in has a mode resource that indicates the H-bridge configuration. Mode is given as two space separated character in 'frbc' (forward, reverse, brake, and coast). The PWM widths are two floating point numbers in the range of 0.0 to 100.0. Spinning in place at a high speed could be accomplished with:
    edset motor_cntrl mode f r
    edset motor_cntrl pwm 100 100

The 'filter' resource in the gamepad plug-in let's you remove specific fields from the state data stream. After reading 'edlist gamepad' you've decided to us a filter of edffff so an edcat of the gamepad state resource gives a stream of data in the form:

    

Joystick values are in the range of -32768 to +32767. The joysticks are spring loaded to return to zero and you want -32768 to be full reverse, and +32767 to be full forward. A small range of dead space around zero would be nice.

The full Python program to implement gamepad control of the robot is given below:

#!/usr/bin/env python
import socket
import sys
''' This program opens two sockets to the eddaemon, one
    to listen for gamepad events and one to update the
    motors.  This code uses a blocking read but a select()
    implementation would work too.
'''

# Send a set command to the fpga and wait for the response prompt
def set_cmd(sock, set_str):
    sock.send(set_str)
    while True:
        retval = sock.recv(1)
        if retval == '\\':
            break


try:
    sock_cmd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_cmd.connect(('localhost', 8870))
    sock_gamepad = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_gamepad.connect(('localhost', 8870))
    set_cmd(sock_gamepad, 'edset gamepad filter edffff\n')
    sock_gamepad.send('edcat gamepad state\n')
    # loop forever getting gamepad joystick positions  
    while True:
        gamepad_state= sock_gamepad.recv(1024).split()
        # Gamepad analog output a value between -32767 and +32767.
        if (int(gamepad_state[1]) > 20)
            left_pwm = {0:.2f}".format(int(gamepad_state[1]) / 327.6)
            left_mode = "f"
        else if (int(gamepad_state[1]) < -20)
            left_pwm = {0:.2f}".format(-int(gamepad_state[1]) / 327.6)
            left_mode = "r"
        else
            left_pwm = "0.0"
            left_mode = "b"
        if (int(gamepad_state[2]) > 20)
            right_pwm = {0:.2f}".format(int(gamepad_state[2]) / 327.6)
            right_mode = "f"
        else if (int(gamepad_state[2]) < -20)
            right_pwm = {0:.2f}".format(-int(gamepad_state[2]) / 327.6)
            right_mode = "r"
        else
            right_pwm = "0.0"
            right_mode = "b"
        # Set the mode and speeds
        set_cmd(sock_cmd, 'edset motor_cntrl mode c c\n')
        set_cmd(sock_cmd, 'edset motor_cntrl pwm '+left_pwm+' '+right_pwm+'\n')
        set_cmd(sock_cmd, 'edset motor_cntrl mode '+left_mode+' '+right_mode+'\n')


except KeyboardInterrupt:
    # exit on Ctrl^C
    sock_cmd.close()
    sock_gamepad.close()
    sys.exit()

except socket.error:
    print "Couldn't connect to eddaemon"
    sys.exit()

Each of the EEDD protocol commands has a corresponding shell equivalent. You may find initial debugging is easier using shell scripts to exercise the hardware. For example, the following shell script lacks reverse but is otherwise equivalent to the above Python program.

#!/bin/bash

edset gamepad filter edffff
unbuffer edcat gamepad state |
while read tm l r
do
   modl="c"
   if (( $l >= 20 ))
   then
       motl=$(( $l / 328 ))
       modl="f"
   fi
   modr="c"
   if (( $r >= 20 ))
   then
       motl=$(( $r / 328 ))
       modr="f"
   fi
   edset ed_motor mode $modl $modr
   edset motor_cntrl pwm $motl $motr
   echo  $l $r  $motl $motr
done

Any Controller with Any Motor




 

Other EEDD Notes

 - EEDD is released under the GPLv2 and github is the official source code repository. If you would like to participate let me know and I'll give you commit access to the repository.
The repository is here: https://github.com/bob-linuxtoys/eedd.

 - Although the empty daemon is written in C the plug-ins can be written in any language that can produce a shared object file.

 - Plug-ins do not need to be released under the GPL. If you have driver code you would like to keep proprietary you can place it in a plug-in without any issues.

 - When you build EEDD you can give the commands a prefix other than 'ed'. Just change CPREFIX in the EEDD top level Makefile.

 - EEDD provides comes with three plug-ins that might be of use to robot builders. Each plug-in has a readme file that tells you how to use the plug-in and how to install any required hardware or software. The plug-in readme files are available here:
   - Game Controller
   - Team Communications using IRC
   - Text-to-Speech    - VL53L0X Time-of-flight distance sensor

 - EEDD has two permanent branches. One is for the Home Brew Automation FPGA based robot. The other is for the Demand Peripherals FPGA based robot. Their respective git branch names are "hba" and "dpi".