Showing posts with label Raspberry Pi. Show all posts
Showing posts with label Raspberry Pi. Show all posts

Saturday, September 23, 2017

Raspberry Pi and Alexa Mobile Robot (Part 1)

The story so far...




In previous posts we have described building a Raspberry Pi based telepresence robot. At the moment, the robot can be remotely controlled via a web site to which it also streams video from the Pi camera. We have also added an ultrasonic sensor on a PTZ mount to allow it to roam autonomously. The next step in the robots evolution is to add voice recognition and speech using Amazon Alexa.

pi-top PULSE




The Raspberry Pi doesn't come with a microphone or speaker. There are lots of ways that you can add this capability but we decided to use the pi-top PULSE. The PULSE includes:

  • RGB LED's - 7x7 grid, illuminated speaker, underside ambient HAT and pi-top Accessory compatible;
  • SPEAKER - 2W with I2S amplifier; and a 
  • MICROPHONE - 200Hz to 11KHz response Automatic Gain Control (ACG).

Not only can we use the speaker and microphone for interfacing with Alexa but we can show some emotional/behaviour state changes via the LED's.

Wearing Multiple HATs - The 1st Problem


Even though HATs (Hardware Attached on Top) are not intended to be stacked, you can stack up to 62 HATs and not have an address collision. This assumes you don't have conflicting pin usage and you have compatible stackable headers.

The best way to check HAT / stackable board compatibility is to map out what every pin is being used for.


The image above illustrates the pin usage for the robot. The key is as follows:

  • Orange Pins - Are general I/O pins used for the PTZ servo's and ultrasonic sensor.
  • Blue Pins - Motor Driver Board pin usage.
  • Yellow Pins - Pi Top PULSE HAT pin usage.
Thus we don't have any electrical conflicts and can move on to the mechanical interfacing issues.




To call something a HAT it must meet the HAT requirements. We are using the Seeed Motor Driver Board (shown above) which can't be called a HAT because it doesn't have a full size 40W GPIO connector or an ID EEPROM. This presents us with two problems:

  1. We need access to 6 of the 14 pins which are not extended through the Motor Board.
  2. Even if all 40 pins were extended, placing the PULSE on top of the Motor Board wouldn't allow access to the power and GPIO pins used for the servo PTZ control and ultrasonic sensor. 
To solve this issue, we need a GPIO expansion shield which provides 3 x 40 pin connections in parallel. We can then use a couple of male to female cables to connect our "HAT's". We will conclude this build in the next post (once the expansion shield has arrived).




Thursday, September 14, 2017

Flight Tracking (ADS-B) using the Raspberry Pi



In a previous post we looked at getting ADS-B tracking working on a pcDuino, to compare build difficulty with a Raspberry Pi, we built one of those as well. TL;DR version - it is much easier and you get the latest version of PiAware.

To get Dump1090 and PiAware working on a Raspberry Pi the easiest way is to just download a version of Raspbian with the software preloaded and then copy it to a SD card. Done. Full instructions are available on the FlightAware website.

We made things a bit harder for ourselves because we wanted to also try out the 4D Systems 2.4" touchscreen HAT. This requires its own drivers.

4DPi 24 HAT Display



The 4DPi-24-HAT is a 2.4" 320x240 Primary Display HAT for the Raspberry Pi, which plugs directly on top of a Raspberry Pi. It features an integrated Resistive Touch panel, enabling the 4DPi-24-HAT to function with the Raspberry Pi without the need for a mouse (theoretically). In practise the touch part of the screen is unusable. You will need a mouse to do anything useful.

Communication between the 4DPi-24-HAT and the Raspberry Pi is via the high speed 48Mhz SPI connection.

The HAT also features 5 push buttons, and a backlight. Supposedly this backlight can be configured as either On/Off or PWM controlled, selectable by an on board jumper, but only one position worked for us - see point 2 below.

The pushbuttons can be used in a python script (for example) but it isn't totally straight forward. Have a look at the data sheet for some examples.




There are a few tricks to getting this display to work with the Raspberry Pi:

  1. Make sure you download the latest drivers. The link in the instructions which came with my HAT was for an older version (which didn't work with the Raspberry Pi 3).
  2. Set the backlight link to ON/OFF not PWM. Mine came with PWM selected but it wouldn't work in this configuration. A definite trap for young players! It took me a while to track this down.
  3. You will need a couple of PCB stand offs. Get ones with a M2.5 thread and the body should be 11mm in length. These are not provided (but should be).

Optimising the Display




The 4DPi-24-HAT has a 320x240 resolution, so expectations need to be realistic about what can be usefully displayed. 

Raspbian has not been optimised to run on a display with this resolution, so there are some menus and applications which will not display correctly, or fit on the screen.

This can be helped somewhat, by setting up the display appearances, and setting fonts and
menus to be smaller. You will need to use a USB mouse and keyboard to perform this set up.

Raspberry Menu -> Preferences -> Appearance
Settings
go to Menu Bar tab
Select Small

go to System tab
click on font (Robo Light)
drag window, to see font size, select 8
(close window [x])

Click on File Manager in Menu Bar
Go to Edit, select Preferences
Select Display
Choose smallest icon sizes for all icon types
Click on "Size of Large Icons", press tab 6 times (as
the OK button is not visible), Press enter

Right click on menu bar (just left from pi menu,
near bottom edge of menu bar), to get a pop-up
menu.

Choose Add/Remove Panel Items
Remove unwanted items (eg Bluetooth)
(close window [x])

Raspberry Menu->Shutdown->Reboot

Installing Dump1090 and PiAware


To install Dump1090 and PiAware on an exisiting Raspbian installation, follow these instructions on FlightAware. It is very straight forward. The best way is to ssh in and paste the commands into terminal. Once you have plugged the USB SDR in and rebooted you should be up and running. 

We used the same USB DVB-T TV Tuner RTL2832U + R820T as our SDR. You can get these very cheaply on E-Bay.



Notice in the screen shot below that we are running the latest version of PiAware (unlike the older version we were using on the pcDuino).


Auto Load PiAware on Boot


We wanted the Raspberry Pi to auto load the PiAware web site for our ADS-B station every time the system is rebooted. To do this, first make a copy of the LXDE autostart file:

$ cp /etc/xdg/lxsession/LXDE-pi/autostart /home/pi/.config/lxsession/LXDE-pi/autostart
Then edit it:

sudo nano /home/pi/.config/lxsession/LXDE-pi/autostart
And add the following:

@xscreensaver -no-splash 
@xset s off
@xset -dpms
@xset s noblank
@chromium-browser --incognito --kiosk http://localhost:8080
Save the file, reboot and you should be good.

Comparison with pcDuino


As expected it is a lot easier (trivial in fact) to get PiAware to work on the Raspberry Pi than the pcDuino. The biggest issues we had were with the 4D Systems 2.4" Display HAT - which I don't think add much value in this instance.

Friday, August 18, 2017

Flight Tracking (ADS-B) using the pcDuino

Introduction





Interested in what planes are flying over your house? Want to know how high they are and how fast they are going? Want to build your own ADS-B ground station that can be installed anywhere and receive real-time data directly from airplanes on your computer. Then this project may be for you.

As mentioned in a previous post Diyode Magazine and Jaycar Electronics very kindly gave me a pcDuino to have a play with. Having got the 5" LCD display working with the pcDuino, I wanted to try using it as part of a project that has been on my "to do" list for some time, namely a plane tracker.

There are lots of tutorials on how to do this with a Raspberry Pi, but I think this may be the first time it has been done on the pcDuino. Everything installs pretty much as it would on the Raspberry Pi but of course the pcDuino uses an Ubuntu variant and most Pi's use Debian (or Raspbian to be precise). This means that there is a little bit more work to get things operational.



Credit to RubyDucky for providing the basis for the install technique on Ubuntu. I will note where I found differences installing on the pcDuino and the workarounds required.

ADS-B


Our plan is to track planes using ADS-B. So what is this?

ADS-B is a system in which electronic equipment onboard an aircraft automatically broadcasts the precise location of the aircraft via a digital data link. The data can be used by other aircraft and air traffic control to show the aircraft’s position and altitude on display screens without the need for radar.

The system involves an aircraft with ADS-B determining its position using GPS. A suitable transmitter then broadcasts that position at rapid intervals, along with identity, altitude, velocity and other data. Dedicated ADS-B grounds stations receive the broadcasts and relay the information to air traffic control for precise tracking of the aircraft.

Automatic – Requires no pilot input or external interrogation.

Dependant – Depends on accurate position and velocity data from the aircraft’s navigation system (eg. GPS).

Surveillance – Provides aircraft position, altitude, velocity, and other surveillance data to facilities that require the information.

Broadcast – Information is continually broadcast for monitoring by appropriately equipped ground stations or aircraft.

ADS-B data is broadcast every half-second on a 1090MHz, digital data link. The ability of a ground station to receive a signal depends on altitude, distance from the site and obstructing terrain. The maximum range of each ground station can exceed 250 nautical miles. [Credit: Air Services Australia]

Don't expect a 250 nm range with our setup. I have found the planes need to be pretty much overhead and the aerial near the window, although I do live in a valley. The signal is not designed to penetrate buildings and is pretty much line of sight. Refer to the antenna section below if you want to improve reception.

The Software Defined Radio (SDR) Receiver






The RTL2832U-based SDR receiver is designed and marketed for DVB-T reception. However, it’s possible to get raw samples from the device, rather than just a demodulated DVB signal. This means that wireless systems can then be implemented in software. I used the USB DVB-T TV Tuner RTL2832U + R820T from Wiltronics (shown above).

The RTL2832U chip is generally paired with a tuner IC and in the case of the USB receiver from Wiltronics, it’s an R820T, which enables reception from 24MHz to 1,850MHz.

Installation of the hardware is simple just plug the SDR receiver into a USB port. As there is only one USB port on the pcDuino you will probably need a hub so that you can also plug in the keyboard and mouse required for setup. I had a powered hub but it doesn't seem to need it, I think the pcDuino supplies enough current for an unpowered USB hub to work. Don't plug in the SDR receiver until instructed below.

Antenna


The antenna which comes with the USB Tuner is 100 mm in length. This works ok but is not optimised for receiving signals with a frequency of 1090 MHz. If you are having problems with reception, you could purchase the FlightAware 1090MHz antenna, or there are plenty of home brew designs online.

Software Installation


The first step is to download some packages and libraries which are required for dump1090 and piaware. Fire up the pcDuino, open LXTerminal and type the following.

$ sudo apt-get install git cmake libboost-all-dev libusb-1.0-0-dev python-scitools portaudio19-dev -y
$ sudo apt-get install tcl8.5-dev tclx8.4-dev itcl3-dev tcl-tls tcllib automake cmake telnet git gcc make

RubyDucky included tcl-tclreadline in the installation list above, but I received an "unable to locate package" error when I tried installing it. Omitting the package didn't appear to cause any subsequent issues.

RTL_SDR


RTL-SDR is a very cheap software defined radio that uses a DVB-T TV tuner dongle based on the RTL2832U chipset. Three Linux hackers found that the signal I/O data could be accessed directly, which allowed the DVB-T TV tuner to be converted into a wideband software defined radio via a new software driver. This means that a cheap TV tuner USB dongle (like the one shown above) which uses the RTL2832U chip can be used as a computer based radio scanner. This opens up all sorts of opportunities. For other project ideas have a look at the RTL-SDR website.

Download RTL-SDR (assuming you're in ~/):

$ sudo git clone git://git.osmocom.org/rtl-sdr.git

Install RTL-SDR:

$ cd rtl-sdr/
$ sudo mkdir build
$ cd build
$ sudo cmake ../
$ sudo make
$ sudo make install
$ cd ~
$ sudo cp ~/rtl-sdr/rtl-sdr.rules /etc/udev/rules.d/
$ sudo ldconfig

The kernel that comes with Ubuntu already contains a DVB driver, which we don't want to use. To stop the conflicting Linux DVB-T driver from loading, we need to blacklist it.

$ cd /etc/modprobe.d/
$ sudo nano blacklist.conf

Then add the following line at the end of the file. You can either use the existing blacklist.conf file or create a new file, as long as it is in this directory and ends in .conf.

blacklist dvb_usb_rtl28xxu

At this point you can plug in your receiver dongle and reboot. Rebooting will allow our blacklisting to take effect.

Aviation transponder interrogation modes


You may see references to s-mode when reading about ADS-B plane tracking and sometimes s-mode is used interchangeably with ADS-B (which is not strictly correct). We will take a small detour at this point to explain what this is and how it relates to ADS-B. A transponder is a piece of kit on a plane to help air traffic controllers identify a particular aircraft's position and altitude on a radar screen. This helps them maintain separation and prevent collisions. There are 3 civilian transponder modes, called A, C and S.

Mode A - Following an interrogation request, the transponder broadcasts the configured transponder code (or "squawk code").  A separate type of response called "Ident" can be initiated from the airplane by pressing a button on the transponder control panel.

Mode C - Will send a pressure altitude response when interrogated.

Mode S - (Select) is to avoid over interrogation of the transponder (if there are many radars in busy areas) and to allow automatic collision avoidance. Mode S transponders are compatible with Mode A and Mode C. A Mode S transponder is required to implement ADS-B, but there are other ways to implement ADS-B.

In Australia, CASA is moving towards most aircraft requiring a Mode S transponder which is ADS-B capable, but this will take years to come into effect (particularly for VFR aircraft). All of the big jets certainly have it.


Dump1090_mr




Dump1090 is a Mode S decoder specifically designed for RTLSDR devices. It provides robust decoding of weak messages, and an embedded HTTP server that displays the currently detected aircraft on Google Maps. The 1090 refers to the 1090MHz frequency that the signals are broadcast on.

Dump1090_mr is a FlightAware fork, of Malcolm Robb's fork, of Salvatore Sanfilippo's dump1090 program. FlightAware uses it as an important element of PiAware (a Debian package for forwarding ADS-B data to FlightAware - more on this later). This is the version that we will download and install. Note that dump1090_mr is no longer available on the FlightAware GitHub repository but there is a copy here, which we will use. To build and install dump1090 and faup1090 and configure the system to start them automatically whenever the system boots, change to the ~/ directory and type:

$ git clone https://github.com/CTassisF/dump1090_mr.git
$ cd dump1090_mr/
$ make
$ make -f makefaup1090 all
$ sudo make -f makefaup1090 full-install
You can now test whether dump1090_mr is operational. This should show you a list of detected planes (see screenshot above).

$ dump1090 --interactive

Dump1090 will also show the position of detected planes on Google Maps. Open the Chromium browser and point it at http://127.0.0.1:8080 to see the display on your pcDuino.

You can also view this page from other computers on your LAN. To do this you will need the IP address of your pcDuino (type hostname -I on the command line). On the other computer you can then access the page via that IP address. So for example, my pcDuino IP address was 192.168.0.7 to view the dump1090 page I enter: http://192.168.0.7:8080 into the browser address bar.



FlightAware and PiAware


This next part is optional but there are benefits to being part of a world wide network of ADS-B base stations.

FlightAware is the world's largest flight tracking data company. Head over to their site and register so that you can add your ADS-B base station results to the worldwide collection of other operators. Make a note of your user name and password.

PiAware is a FlightAware client program that runs on a Raspberry Pi (or pcDuino!) to securely transmit dump1090 ADS-B and Mode S data to FlightAware.

I had some trouble getting PiAware to work on the pcDuino. The instructions provided by RubyDucky didn't work for me. Instead I used Piaware Debian Package Builder. The instructions below work for version 3.5.1 of PiAware.

To create the PiAware package, change to the ~/ directory and type:

$ sudo apt-get install build-essential debhelper librtlsdr-dev libusb-1.0-0-dev pkg-config tcl8.5-dev autoconf python3-dev python-virtualenv libz-dev git tclx8.4 tcllib tcl-tls itcl3
$ git clone https://github.com/flightaware/piaware_builder.git
$ cd ~/piaware_builder
$ ./sensible-build.sh wheezy
$ cd ~/piaware_builder/package-wheezy
$ dpkg-buildpackage -b
To install the PiAware package:

sudo dpkg -i ~/piaware_builder/piaware_3.5.1_armhf.deb

Reboot and then you can check that everything is working as it should by typing:

sudo piaware-status

This should display something like the screenshot below. Note that you no longer need to add your user name and password to the PiAware configuration file.



To link your new pcDuino ADS-B base station to your FlightAware account, log in to the FlightAware website and then go to the page to claim a new station.



FlightAware works out which base station is yours based on your IP address, so the computer you are using to claim the station needs to be on the same LAN as your base station and the station needs to be transmitting data to FlightAware. Wait 15 minutes or so after booting the pcDuino to make sure that FlightAware is receiving your data before trying to claim your station.

As a thank you from FlightAware, users sending ADS-B data receive the following:

  • Live data on flightaware.com (subject to standard data processing delay of up to two minutes)
  • Access to up-to-the-second live data received by the local device (accessible from the stats page with a local network connection)
  • Data from local device highlighted on FlightAware track logs
  • Detailed statistics on site performance
  • A free Enterprise Account (USD89.95/mo value)

So, there you have it, plane tracking on a pcDuino using Dump1090 and PiAware.

Monday, April 24, 2017

Node Red Dashboard for Raspberry Pi

What is Node Red?




Node-RED is a programming tool for wiring together hardware devices, APIs and online services. It was developed as a visual programming tool for the Internet of Things. It also allows you to produce and publish a funky web based dashboard with one click.


Node-RED includes a browser-based editor that makes it easy to wire together flows using the selection of nodes in the side menu. Flows can be then be deployed to run in a single-click. JavaScript functions can be created within the editor to customise the messages passed between nodes. A built-in library allows you to save useful functions, templates or flows for re-use.

The light-weight runtime is built on Node.js, taking full advantage of its event-driven, non-blocking model. This makes it ideal to run on low-cost hardware such as the Raspberry Pi as well as in the cloud.

Nodes can be anything from a timer to trigger events to a Raspberry Pi GPIO output used to turn on a LED (or salt lamp in our example). With over 225,000 modules in Node's package repository, it is easy to extend the range of nodes to add new capabilities. As we will demonstrate there are packages available for the Raspberry Pi and Sense Hat. The flows created in Node-RED are stored using JSON.

Node-RED was developed by IBM and in 2016, they contributed Node-RED as an open source JS Foundation project.

The Himalayan Salt Lamp Project




My wife likes salt lamps. Salt lamps allegedly remove dust, pollen, cigarette smoke, and other contaminants from the air. How effective this is I don't know and it is really irrelevant, as I said my wife likes them! Salt is very hygroscopic, that is it absorbs water - this is the basis of the claimed health benefits, the salt also absorbs any foreign particles the water may be carrying. The water then evaporates when the lamp is switched on leaving the contaminants behind entrapped in the salt.

Salt is so hygroscopic that it readily dissolves in the water it absorbs: this property is called deliquescence. It is of course a problem if your expensive Himalayan salt lamp dissolves into a puddle of salty water, especially if it is connected to 240VAC. In our house this melting process starts at relative humidities above 70%.

The solution is to turn your lamp on if humidity gets above 70%. This seemed like a good excuse to introduce the start of our home automation hub and learn about node_RED. Turning a lamp on and off based on humidity and time (lamp goes on at 5pm and off at 10pm) is trivial using Python so we wont cover that. What we will look at is manually controlling the lamp via our node-RED dashboard and other associated data we display.

Node-RED and the Raspberry Pi




If you are running Raspbian Jessie on your Pi then you should already have node-RED installed. Before starting the node-RED server it is worth installing a few packages that you will need. Type the following at the CLI:

sudo apt-get update
sudo apt-get install npm
cd $HOME/.node-red
npm install node-red-dashboard
npm install node-red-node-snmp
npm install node-red-contrib-os

Node-RED is started by running the following command in the terminal:

node-red-start
Once started, you use a browser (either on the Pi or remotely) to build your applications and configure your dashboard. I used my Macbook Air, to do this point your browser at the ip address of your Pi:1880. If you do it on your Pi, the URL would be 127.0.0.1:1880 or localhost:1880. The associated dashboard URL is <IP Address>:1880/ui. So for example my Raspberry Pi dashboard is at http://192.168.0.18:1880/ui.

Most of the Raspberry Pi information charted in the dashboard shown above is from the node-red-contrib-os package. For example information on the SD Card is from the Drives node. You use this node to query the hard drives. Values for size, used and available are expressed in KB (1024 bytes). Value for capacity is a number between 0 and 1. Capacity*100 is also known as percentage used.

Some of the flows are shown below. The first step is to drag across a timer which you can use to poll the Drive node. Our timer sends a timestamp every minute.

Connect the timer to a Drive node and it will start pumping out messages with the size, used, available and capacity values for every drive on your target system. You can use a Debug node to see messages being sent out by any node. This is very useful in debugging your flows. On the Raspberry Pi there will be a few different file systems on your SD Card so you have to be specific about which area you want information about.


You can add a Function node to include custom JavaScript to process the messages passed between the nodes. The JavaScript used to extract the various Drive information that I use is shown below. The topic variable is used as the name for charts with multiple inputs.

var msg1,msg2,msg3;

if (msg.payload.filesystem === '/dev/root') {

    msg1 = { payload: msg.payload.used };
    msg2 = { payload: msg.payload.available };
    msg3 = { payload: msg.payload.capacity * 100 };

    msg1.topic = "used"
    msg2.topic = "available"
    msg3.topic = "capacity"

}

return [ msg1, msg2, msg3 ];

CPU Temperature




To display CPU temperature we use a different technique. On the Raspberry Pi you can display the current CPU temperature by typing:

/opt/vc/bin/vcgencmd measure_temp
You can use an Exec node to run OS commands. So connect our same timer node to an Exec node and input the command above. We then have to do a bit of processing to extract the temperature as a number. Use another function node with the following code.

msg.payload = msg.payload.replace("temp=","").replace("'C\n","");

return msg;



There are also nodes available for the Sense Hat. You need to use functions similar to those above to extract the various sensor data values.


Controlling GPIO using the Dashboard



Manual control of a GPIO is fairly straight forward. The one trick is that the Switch node outputs true/false and the Raspberry Pi GPIO out node expects a 1/0 input. So we include another Function node to mediate. The relevant code is:

msg.payload = msg.payload ? 1 : 0;

return msg;

Of course our Raspberry Pi outputs 3.3VDC which wont turn on a 240VAC lamp so we use a PowerSwitch Tail kit as an intermediary.





Friday, March 17, 2017

HC-SR04 Ultrasonic Sensor Python Class for Raspberry Pi

The HC-SR04




The HC - SR04 ultrasonic ranging module provides 2cm - 400cm non-contact
measurement, with ranging accuracy up to 3mm. The module includes ultrasonic transmitters, receiver and control circuitry. The time difference between transmission and reception of ultrasonic signals is calculated. Using the speed of sound and ‘Speed = Distance/Time‘ equation, the distance between the source and target can be easily calculated.

Credit to Vivek and his article on the same subject for the diagrams.




Wiring the HC-SR04 to a Raspberry Pi


The module has 4 pins:

  • VCC - 5V Supply
  • TRIG - Trigger Pulse Input
  • ECHO - Echo Pulse Output
  • GND - 0V Ground 

Wiring is straight forward with one exception, note that the sensor operates at 5V not the 3.3V of the Raspberry Pi. Connecting the ECHO pulse pin directly to the Raspberry Pi would be a BAD idea and could damage the Pi. We need to use a voltage divider or a logic level converter module to drop the logic level from the HC-SR04 to a maximum of 3.3V. Current draw for the sensor is 15 mA.

As we have a spare logic level converter, we will use that. Connections for the logic converter are shown below.


For the voltage divider option: Vout = Vin x R2/(R1+R2) = 5 x 10000/(4700 + 10000) = 3.4V






Python Class for the HC-SR04 Ultrasonic Sensor



To utilise the HC-SR04:

  1. Provide a trigger signal to TRIG input, it requires a HIGH signal of at least 10μS duration.
  2. This enables the module to transmit eight 40KHz ultrasonic bursts.
  3. If there is an obstacle in-front of the module, it will reflect those ultrasonic waves
  4. If the signal comes back, the ECHO output of the module will be HIGH for a duration of time taken for sending and receiving ultrasonic signals. The pulse width ranges from 150μS to 25mS depending upon the distance of the obstacle from the sensor and it will be about 38ms if there is no obstacle.
  5. Obstacle distance = (high level time × velocity of sound (343.21 m/s at sea level and 20°C) / 2
  6. Allow at least 60 ms between measurements.





Time taken by the pulse is actually for return travel of the ultrasonic signals. Therefore Time is taken as Time/2.

Distance = Speed * Time/2

Speed of sound at sea level = 343.21 m/s or 34321 cm/s

Thus, Distance = 17160.5 * Time (unit cm).

As we are using the ultrasonic sensor with our Raspberry Pi robot, we have created a python class that can be easily imported and used. Note the calibration function which can be used to help correct for things like altitude and temperature.

We have included a simple low pass filter function which is equivalent to an exponentially weighted moving average. This is useful for smoothing the distance values returned from the sensor. The higher the value of beta, the greater the smoothing.

#!/usr/bin/python
# RS_UltraSonic.py - Ultrasonic Distance Sensor Class for the Raspberry Pi 
#
# 15 March 2017 - 1.0 Original Issue
#
# Reefwing Software
# Simplified BSD Licence - see bottom of file.

import RPi.GPIO as GPIO
import os, signal

from time import sleep, time

# Private Attributes
__CALIBRATE      = "1"
__TEST           = "2"
__FILTER         = "3"
__QUIT           = "q"

class UltraSonic():
    # Ultrasonic sensor class 
    
    def __init__(self, TRIG, ECHO, offset = 0.5):
        # Create a new sensor instance
        self.TRIG = TRIG
        self.ECHO = ECHO
        self.offset = offset                             # Sensor calibration factor
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.TRIG, GPIO.OUT)                  # Set pin as GPIO output
        GPIO.setup(self.ECHO, GPIO.IN)                   # Set pin as GPIO input

    def __str__(self):
        # Return string representation of sensor
        return "Ultrasonic Sensor: TRIG - {0}, ECHO - {1}, Offset: {2} cm".format(self.TRIG, self.ECHO, self.offset)

    def ping(self):
        # Get distance measurement
        GPIO.output(self.TRIG, GPIO.LOW)                 # Set TRIG LOW
        sleep(0.1)                                       # Min gap between measurements        
        # Create 10 us pulse on TRIG
        GPIO.output(self.TRIG, GPIO.HIGH)                # Set TRIG HIGH
        sleep(0.00001)                                   # Delay 10 us
        GPIO.output(self.TRIG, GPIO.LOW)                 # Set TRIG LOW
        # Measure return echo pulse duration
        while GPIO.input(self.ECHO) == GPIO.LOW:         # Wait until ECHO is LOW
            pulse_start = time()                         # Save pulse start time

        while GPIO.input(self.ECHO) == GPIO.HIGH:        # Wait until ECHO is HIGH
            pulse_end = time()                           # Save pulse end time

        pulse_duration = pulse_end - pulse_start 
        # Distance = 17160.5 * Time (unit cm) at sea level and 20C
        distance = pulse_duration * 17160.5              # Calculate distance
        distance = round(distance, 2)                    # Round to two decimal points

        if distance > 2 and distance < 400:              # Check distance is in sensor range
            distance = distance + self.offset
            print("Distance: ", distance," cm")
        else:
            distance = 0
            print("No obstacle")                         # Nothing detected by sensor
        return distance

    def calibrate(self):
        # Calibrate sensor distance measurement
        while True:
            self.ping()
            response = input("Enter Offset (q = quit): ")
            if response == __QUIT:
                break;
            sensor.offset = float(response)
            print(sensor)
            
    @staticmethod
    def low_pass_filter(value, previous_value, beta):
        # Simple infinite-impulse-response (IIR) single-pole low-pass filter.
        # ß = discrete-time smoothing parameter (determines smoothness). 0 < ß < 1
        # LPF: Y(n) = (1-ß)*Y(n-1) + (ß*X(n))) = Y(n-1) - (ß*(Y(n-1)-X(n)))
        smooth_value = previous_value - (beta * (previous_value - value))
        return smooth_value
        

def main():
    sensor = UltraSonic(8, 7)       # create a new sensor instance on GPIO pins 7 & 8
    print(sensor)

    def endProcess(signum = None, frame = None):
        # Called on process termination. 
        if signum is not None:
            SIGNAL_NAMES_DICT = dict((getattr(signal, n), n) for n in dir(signal) if n.startswith('SIG') and '_' not in n )
            print("signal {} received by process with PID {}".format(SIGNAL_NAMES_DICT[signum], os.getpid()))
        print("\n-- Terminating program --")
        print("Cleaning up GPIO...")
        GPIO.cleanup()
        print("Done.")
        exit(0)

    # Assign handler for process exit
    signal.signal(signal.SIGTERM, endProcess)
    signal.signal(signal.SIGINT, endProcess)
    signal.signal(signal.SIGHUP, endProcess)
    signal.signal(signal.SIGQUIT, endProcess)

    while True:
        action = input("\nSelect Action - (1) Calibrate, (2) Test, or (3) Filter: ")

        if action == __CALIBRATE:
            sensor.calibrate()
        elif action == __FILTER:
            beta = input("Enter Beta 0 < ß < 1 (q = quit): ")
            filtered_value = 0
            if beta == __QUIT:
                break;
            while True:
                filtered_value = sensor.low_pass_filter(sensor.ping(), filtered_value, float(beta))
                filtered_value = round(filtered_value, 2)
                print("Filtered: ", filtered_value, " cm")
        else:
            sensor.ping()

if __name__ == "__main__":
    # execute only if run as a script
    main()

## Copyright (c) 2017, Reefwing Software
## All rights reserved.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are met:
##
## 1. Redistributions of source code must retain the above copyright notice, this
##   list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright notice,
##   this list of conditions and the following disclaimer in the documentation
##   and/or other materials provided with the distribution.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
## ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



Wednesday, March 15, 2017

Controlling the Raspberry Pi via a web browser

Web Controlled Robot





Now that we can stream video to a web page it would be nice to be able to remotely control our robot. To do this we will us the Raspberry Pi to run a web server that serves the page used to control the robot. Once we have this up and running you will be able to drive your robot around using a browser on your laptop via WiFi on your LAN.

As shown in the previous post, you can use the python command print(server) to see what URL you need to point your browser at to see the video and control your robot. The way the controls work is as follows:
  1. Typing the address of your Pi served page (e.g. http://192.168.0.9:8082) into your browser will send a web request to the python program running the server, in our case RS_Server.py.
  2. RS_Server responds with the contents of index.html. Your browser renders this HTML and it appears in your browser.
  3. The broadcasting of video data is handled by the broadcast thread object in RS_Sever. The BroadcastThread class implements a background thread which continually reads encoded MPEG1 data from the background FFmpeg process started by the BroadcastOutput class and broadcasts it to all connected websockets. More detail on this can be found at pistreaming if you are interested. Basically the camera is continually taking photos, converting them to MPEG's and sending them at the frame rate to a canvas in your browser.
  4. You will see below that we have modified the index.html file to display a number of buttons to control our robot. Pressing one of these buttons will send a GET request to the server running on your Pi with a parameter of "command" and the value of the button pressed. We then handle the request by passing on the appropriate command to our MotorControl class. To do this we will need to bring together RS_Server and RS_MotorControl in our new RS_Robot class.

Modifying index.html



The index.html file provided by pistreaming just creates a canvas in which to display our streaming video. To this we will add a table with 9 command control buttons for our robot. You could get away with only 5 (Forward, Back, Left, Right and Stop) but looking ahead we know we will also need 4 more (speed increase, speed decrease, auto and manual). Auto and Manual will toggle between autonomous control and remote control (i.e. via the browser). Associated with each button is a JavaScript script that will send the appropriate command when the button is clicked.

In addition to controlling your robot via the on screen buttons you can use the keyboard. We have mapped the following functionality:

Up Arrow      = Forward
Down Arrow = Back
Left Arrow    = Left
Right Arrow  = Right
Space             = Stop
-                     = Decrease Speed
+                    = Increase Speed
m                   = Manual
a                    = Autonomous

You can modify the index.html to map whatever keybindings you want. Be aware that the keycode returned by different browsers isn't always consistent. You can use the JavaScript Event KeyCode Test Page to find out what key code your browser returns for different keys.

The manual and auto modes don't do anything at this stage. 

The modified index.html file is shown below.

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=${WIDTH}, initial-scale=1"/>
    <title>Alexa M</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>

    <style>
        .controls {
            width: 150px;
            font-size: 22pt;
            text-align: center;
            padding: 15px;
            background-color: green;
            color: white;
        }
    </style>

    <style type="text/css">
            body {
                background: ${BGCOLOR};
                text-align: center;
                margin-top: 2%;
            }
            #videoCanvas {
                // Always stretch the canvas to 640x480, regardless of its internal size.
                width: ${WIDTH}px;
                height: ${HEIGHT}px;
            }
    </style>

    <script>
    function sendCommand(command)
    {
        $.get('/', {command: command});
    }
    
    function keyPress(event)
    {
        keyCode = event.keyCode;
        
        switch (keyCode) {
            case 38:                // up arrow
                sendCommand('f');
                break;
            case 37:                // left arrow
                sendCommand('l');
                break;
            case 32:                // space
                sendCommand('s');
                break;
            case 39:                // right arrow
                sendCommand('r');
                break;
            case 40:                // down arrow
                sendCommand('b');
                break;
            case 109:               // - = decrease speed
            case 189:
                sendCommand('-');
                break;
            case 107:
            case 187:
                sendCommand('+');   // + = increase speed
                break;
            case 77: 
                sendCommand('m');   // m = manual (remote control)
                break;
            case 65:
                sendCommand('a');   // a = autonomous
                break;
            default: return;        // allow other keys to be handled
        }
        
        // prevent default action (eg. page moving up/down with arrow keys)
        event.preventDefault();
    }
    $(document).keydown(keyPress);
    </script>
</head>

<body>

    <h1><FONT color=white>Alexa M</h1>

    <!-- The Canvas size specified here is the "initial" internal resolution. jsmpeg will
        change this internal resolution to whatever the source provides. The size the
        canvas is displayed on the website is dictated by the CSS style.
    -->
    <canvas id="videoCanvas" width="${WIDTH}" height="${HEIGHT}">
        <p>
            Please use a browser that supports the Canvas Element, like
            <a href="http://www.google.com/chrome">Chrome</a>,
            <a href="http://www.mozilla.com/firefox/">Firefox</a>,
            <a href="http://www.apple.com/safari/">Safari</a> or Internet Explorer 10
        </p>
    </canvas>
    <script type="text/javascript" src="jsmpg.js"></script>
    <script type="text/javascript">
        // Show loading notice
        var canvas = document.getElementById('videoCanvas');
        var ctx = canvas.getContext('2d');
        ctx.fillStyle = '${COLOR}';
        ctx.fillText('Loading...', canvas.width/2-30, canvas.height/3);
        // Setup the WebSocket connection and start the player
        var client = new WebSocket('ws://${ADDRESS}/');
        var player = new jsmpeg(client, {canvas:canvas});
    </script>

    <table align="center">
    <tr><td  class="controls" onClick="sendCommand('-');">-</td>
        <td  class="controls" onClick="sendCommand('f');">Forward</td>
        <td  class="controls" onClick="sendCommand('+');">+</td>
    </tr>
    <tr><td  class="controls" onClick="sendCommand('l');">Left</td>
        <td  class="controls" onClick="sendCommand('s');">Stop</td>
        <td  class="controls" onClick="sendCommand('r');">Right</td>
    </tr>
    <tr><td  class="controls" onClick="sendCommand('m');">Manual</td>
        <td  class="controls" onClick="sendCommand('b');">Back</td>
        <td  class="controls" onClick="sendCommand('a');">Auto</td>
    </tr>
    </table>

</body>
</html>

Python Robot Class


As Alexa M continues to evolve, so too will this robot class. For now we can keep things pretty simple. In addition to creating a robot class we have updated the motor control, servo and server classes. Rather than reproduce all the code, we will provide links to our Gist Repository where you can download the latest versions. For completeness, I will also provide links to the HTML and JavaScript library that you will need. All these files need to be in the same directory.

  1. RS_Robot.py version 1.0 - Run this script on your Pi to create a telepresence rover.
  2. RS_Server.py version 1.1 - Updated to include command parsing.
  3. RS_MotorControl.py version 1.1 - New motor control methods.
  4. RS_Servo.py version version 1.2 - License added.
  5. index.html version 1.0 - The file shown in the previous section.
  6. jsmpg.js - Dominic Szablewski's Javascript-based MPEG1 decoder.
That completes the remote control and video streaming portion of the design. We hope you have as much fun driving around your robot as we do. Next up we will look at battery monitoring and autonomous control of the robot.