Sensor Programming and Deployment: How To


#1

The following are my notes for adding new sensors into the OpenAg V2 environment. If you are just building a kit with supplied sensors, this information is not needed. The intended audience is experimenting with new sensors. For this implementation, I used:

Methodology Assumption:

My experience is with ‘test first’ development; get something working and don’t let it break. By making one change at a time you know what caused the problems and can more easily fix it. In this case it means starting with existing sensor sample code, modifying it in Arduino, and after the *.h and *.cpp files are working and tested, moving it over into the OpenAg environment.

##Design Assumptions:
Arduino does not control when a class constructor is called, so you cannot have a class constructor with parameters. Hence there is the ‘begin()’ method that initializes variables. Thus you may need:
A no parameter constructor and a begin method for the parameters
A standard constructor with parameters (for ROS [Robotic Operating System). ROS can handle the parameters (cf the json file ‘inputs’).
The begin() method replaces the Arduino 'setup()'
Existing sensor code is good for guidance on most issues, what I found to be a bit ‘thin’ was documentation on the firmware_module json file, getting code compiled and hooking through to the UI.

##OpenAg Assumptions
OpenAg has its own way of handling errors via the modlue superclass. I am still not sure what ROS does with this information.
Data returns from the ‘get_’ methods are via a ROS message type.

##Set Up:
Follow the OpenAg instructions for setting up the RaspberryPi. Use the NOOBS installer and include the GUI.

Configure the system for localization (it defaults to a British keyboard, check your ‘~’ key), set it to boot to the command line, and connect to the network.
For remote access and the UI you will need a either a static IP address or a DDNS service. For more details see: here

##Install Arduino

sudo apt-get install arduino

At this point I make a copy the SD card. It is always convenient to have a minimum OS on a card, saving the time of rebuilding and customizing. This is good for more than just OpenAg.

Install the Docker image and get familiar with the documentation
Docker
Fixtures
CLI

##Download the following libraries
Either download the zip to your Download directory and expand it to ~/sketchbook/libraries, or go into Arduino and tell it to add the zip to the library (Arduino/Sketch/Library/Add zip - let Arduino do the expansion). To keep Arduino happy, you may need to rename the directory to remove the ‘-master’ GitHub branch information from the filename.
Standard message definitions
OpenAg superclass for firmware_modules

##For each sensor:
Download sensor sketch, *.h and *.cpp files and put these in the sketchbook/libraries. These can usually be found on the Adafruit site, or other sensor vendors (ie: BMP085).

#Test:
run the stock arduino software for the sensor to check that the sensor is wired properly and the code works.

Copy the sensor code directory to a new name (what you want to call your OpenAg sensor code). It is best to keep the original directory for reference, or if you get into trouble and need to start code changes over again.

#Rename *.h and *.cpp file
Open the sample Arduino code, and save it as a new project (at the sketchbook directory level). This will be your test going forward.

Start modifying the *.h and *.cpp files to OpenAg standards
Add includes to files: h and *.io
#include <openag_module.h>

Add ROS includes, set std_msgs to the unit that is needed for the sensor
#include <ros.h> //this must be included, and be put first
#include <std_msgs/UInt16.h>

#Add OpenAg boilerplate code
void begin()
void update()

Add a getter for each output. Each value must be in a separate function (ie a temperature and humidity sensor will have two methods). OpenAg devines “magic names”, a specification that assumes the format of get and set methods. Data returns must start with “get_”
// bool get_(std_msgs:: &msg);
bool get_temperature(std_msgs::Float16 &msg);

The pattern I have seen in existing sensor code is to change the legacy ‘getter’ code to save the value to a private variable, then simply have the new getter set the message to the variable value (along with error flagging, etc).

#Subclass the sensor to the ROS; in the *.h file
class XXXXXXXXXXX : public Module //note: inheritance from OpenAg module
class openag_bpm180: public Module //note: inheritance from OpenAg module

It is assumed you are testing at each step along the way. At this point you should have an Arduino script that tests the new code and is returning values. Now you need to move the code to Github (from where OpenAg will retreive it). If you don’t have a git account, you need to make one
Follow the pattern of existing sensors

  1. Create a repository for the sensor project
  2. Include readme and license
  3. add *.h and *.cpp file to directory
  4. add a new file (“examples/project_name_test/readme”) this creates two directories and puts a readme in the lowest level
  5. add arduino script test file to the new directory

#Create json files for the sensor
See here for further documentation:
Existing files are in the sensor directories, and here:
You need to create:
module.json - this is basically the firmware_module_type information
library.json - more metadata, most of which is also in the firmware_module_type

Create a json file to load into the database for building brain code. I find it easiest to load each sensor separately. This could be put into the default.json file, but it made more work for me. The following is what I used for the barometer sensor: openag_bmp180.json:

> {
>   "firmware_module":[
>    {
>     "type": "bpm180",
>     "environment": "environment_1",
>     "_id": "bpm180_1",
>     "outputs": {
>         "Pressure": {"variable": "water_electrical_conductivity"},
>         "Temp": {"variable": "water_emperature"},
>         "Altitude": {"variable": "water_potential_hydrogen"}
>       }
>      }
>    ],
>   "firmware_module_type": [
>     {
>       "_id": "bpm180",
>       "repository": {
>         "type": "git",
>         "url": "https://github.com/webbhm/openag_bpm180.git"
>       },
>       "header_file": "openag_bpm180.h",
>       "class_name": "Barometer",
>       "description": "Driver for the BPM180 barometric (pressure) sensor",
>     "arguments":[
>         { 
>         "name": "bpm180_address",
>         "type": "int",
>         "default": 119
>         }
>       ],
>       "outputs": {
>         "Pressure": {"type": "std_msgs/Float32"},
>         "Temp": {"type": "std_msgs/Float32"},
>         "Altitude": {"type": "std_msgs/Float32"}
>       },
>       "dependencies": [
>         {"type": "git", "url": "https://github.com/OpenAgInitiative/openag_firmware_module.git"}
>       ]
>     }
>   ]
> }

There are two parts here (which could be loaded separately): firmware_module and firmware_module_type. I put them together, and store the file in github with the other code. The stuff in default.json should already be loaded to the database (check the firmware_module_type for confirmation).

#firmware_module
This describes an individual deployed sensor. It is possible to have multiple sensors of the same type (with different i2c addresses) by having different firmware_module records in the database. The following are the parts:
_id: unique id for this sensor. It is easiest to append a sequence number to the type.
type: reference to (same value as) the firmware_module_type id
environment: environment in which the sensor will run (should be “environment_1” for the brain)
arguments: values unique to this particular sensor, used by the constructor. If you have a default in the firmware_module_type, this is optional. This is a good place to put the i2c address for sensors that can have multiple addresses.
outputs: returned data values that will be tracked through rostopic. This is optional unless you want to override the variable names used in firmware_module_type. In this case you can see where I wildly changed the names to match existing rostopics, so these values would flow through and show up on the UI charting. Your code’s get method name (less the "get
") is the name, and the output rostopic is the variable.

#firmware_module_type
This is an ‘class’ description, used by the brain to retreive the C++ code and generate the Arduino code.
_id: identifier used for uniqueness in the database
repository: where the brain should go to get the *.h file and other code
header_file: name of your *.h file
class_name: name of your sensor class
description: whatever useful information you want to supply
arguments: metadata on the individual parameters of the constructor
outputs: name of the get methods, and the return message data type
dependencies: other code that needs to be included when building this sensor

#Move to Brain
Now you are ready to move this into the brain (assuming it has been installed and is running. To check that it is up and running, open up a command prompt (which you will be using for a bit) and run the following:

docker ps

You should see two processes returned, one for the brain, and one for the database. If they are not both up, try:

docker-compose restart

Move fixture file to Docker (assumes it is on the Raspberry Pi). If successful, there will be no return from this command.

docker cp your_fixture.json openagbraindockerrpi_brain_1:/home/pi/your_fixture.json

Assuming you json file is in the Adruino sketchbook:

docker cp ~/sketchbook/libraries/openag_bpm180/openag_bpm180.json openagbraindockerrpi_brain_1:/home/pi/openag_bpm180.json

Put the fixture (json) in database. Note: there is no “.json” at the end of the fixture name. You should get one line returned for each part loaded, or errors for syntax problems.

docker exec openagbraindockerrpi_brain_1 /home/pi/catkin_ws/devel/env.sh rosrun openag_brain load_fixture ~/your_fixture

docker exec openagbraindockerrpi_brain_1 /home/pi/catkin_ws/devel/env.sh rosrun openag_brain load_fixture ~/openag_bpm180

#Build - Flash to Adruino
Now you are ready to get into docker and build the code.
Get into Docker

docker exec -it openagbraindockerrpi_brain_1 bash

Activate ROS (Robotic Operating System)

source ~/catkin_ws/devel/setup.bash

To build the code run:

rosrun openag_brain flash

This may take a bit. There are two main processes, the generation of the code, and moving it to the Arduino (which must be plugged in!). You shouldn’t see any build errors if you have tested the code previously, though you may get some errors due to issues in the json files.
The move to Arduino has been more problematic for me (the portion running avrdude). Most of the complaints about missing files and such can be ignored, just restart the flash. Timeout problems are another issue. I had problems with the initial read going well without timeouts, and then starting to occur on the write. My solution: kill the process (Ctl-C) and move the USB cable to a different USP port (no reboot needed!). I went through a lot of agnoy getting past this issue

If successful, reboot your Raspberry so everything gets up and running.

#Check the results
Get back into Docker (see above) and look at the output.

List all the topics.

rostopic list

You should see the topics, which should include /sensor with your sensor and the output variables. To see the data, run:

rostopic echo -rostopic to output-

If you use the rostopic list of what is wired to the UI, you should soon see data coming out in the [UI.]((https://github.com/OpenAgInitiative/openag_ui/blob/master/openag-config.json) - see the section under “chart”:). Note: This is why I have “Pressure” charted as “water_electrical_conductivity”, this is quick-and-dirty to see output, this is not production.
Standard outputs are found in. I suspect if you want a new sensor output persisted, it needs to be added here, an in at least one other place. I have not dug into this aspect yet; but this is getting into another subject (rostopic connecting)


Openag_brain is not working
#2

Thank you for the great documentation @webbhm


#3

We need to move this to the wiki. :slight_smile: