blog

Microbit wrist holder

posted Jun 16, 2017, 5:06 AM by Matt Oppenheim   [ updated Jun 19, 2017, 9:03 AM ]

This post shows you how to convert a £3 iPod 7 arm band into a wrist or arm holder for a BBC Microbit board and battery. The photo shows the creation with a Microbit and battery pack being modeled by the local electronic's wear supermodel.

I am hoping to use the Microbit as assistive technology as a communications aid. The Microbit on the wrist detects a gesture and then communicates this to a second Microbit which is connected to communications device. This second Microbit says 'hey, there's been a gesture!' to the communications device, so acts as a switch. This enables the wearer to control software through hand motion. More details of this work are here

I need a wrist holder for the Microbit as I am not allowed to just superglue it on to other people according to the University Ethics Committee. I couldn't find anything suitable for sale, so broke out my sewing kit and made one from an armband aimed at one of the many iPod incarnations. With the Microbit on it, I am reminded of the Power Glove from 1989. Who wouldn't want one of those? The tagline for the Power Glove was 'it's so bad', which kind of fits.

A friend pointed out that the holder could be used to strap the Microbit on the leg or any other suitable appendage. For instance, you could log accelerometer and temperature data during sports. Use a second Microbit connected to a laptop to wirelessly log the data from the Microbit attached to your body.

I started with a 'King of Flash New iPod Nano 7th Generation Premium Water Resistant Armband Case' from eBay for the princely sum of £3.

Remove the plastic iPod holder bit with a few minutes of cutting with a suitably sharp instrument.


You need to sew back one of the plastic belt reinforcements on to the first of the two belt slots.


You can cut off the neoprene with the second slot. If you are using the modified arm band to attach to the wrist, then you can also trim down the length of the belt as it is sized to be long enough to go around a biceps. Even my weedy biceps are much larger than my wrist. Sew on some elastic loops to hold the Microbit board and the battery holder. I bought a 1m piece of 3mm black elastic from eBay to do this. 

To get the belt to stick to itself and close the holder, I used some sticky back velcro of the 'hook' gender. I stuck two pieces back to back to form a velcro 'gender bender'. This sticks to the belt part of the velcro on the holder, next to where the board and battery will sit. 




The assembled wrist holder with the Microbit and battery pack is shown below. I use a AAA battery holder with a switch on it that I also bought from eBay for about £2 instead of the 'always on' battery pack that came with the Microbit. I buy a lot of things from eBay.


If you are worried about the battery pack flying off after a violent wrist motion, you can double secure it with some sticky back velcro between the back of the battery pack and the neoprene of the wrist mount.

There you have it. How to make your own Microbit and battery pack wrist/arm/leg holder. 'It's so bad!'









Installing linux mint 18.1 onto a Lenovo 260 with an encrypted home drive

posted Apr 18, 2017, 8:21 AM by Matt Oppenheim   [ updated Apr 19, 2017, 6:32 AM ]

The simplest way I found to install Linux Mint 18.1 on to my Lenovo 260 with an encrypted home drive and a separate installation partition is to install the system using the simplest options, then afterwards encrypt your home drive and shrink down the installation partition using gparted. The rest of this post is how I failed to do this several times. Which is undoubtedly due to my lack of linux wisdom.

I tried and failed to install Linux Mint 18.1 on to custom partitions for my root, home and swap. The system would not boot after I completed the installation. I could not install grub to the /mnt partition to fix this. I tried some stackoverflow solutions with no joy.

So I did a simple install, clicking on the option to encrypt the home folder. Then I used gparted on the installation USB stick to shrink down the partition. However, my swap space was also encrypted, which I understand increases security. Every time I booted I was presented with message asking for a non-existent password to mount the encrypted swap space. No real issue, I just hit enter and carried on to the regular login screen. Then I tried updating the system. For each update I had to press enter to mount the encrypted swap space. A little tedious. So I went on stack overflow, found a 'fix' and rendered the system unbootable. This was getting a little tedious.

So I again installed Linux Mint 18.1 from my USB stick. This time I chose the vanilla, easiest options, no encryption. I used the instructions here to encrypt my home drive. I used the installation stick to run gparted and shrink down the partition. So now I have Linux Mint installed on a partition and an encrypted home drive.

Simple. Hind sight always is.

how to configure the accelerometer range on the microbit using micropython

posted Apr 13, 2017, 10:01 AM by Matt Oppenheim   [ updated Apr 13, 2017, 11:00 AM ]

This article details how to set the range of sensitivity on the accelerometer on the microbit board using micropython and the i2c interface. I am using v1.7.9 of micropython for the microbit, the mu editor and linux mint v17.

After listening to Joe Finney talk about his role in developing the microbit board I realised I could use it for some of my hand gesture assistive technology work. The accelerometer on the microbit board is an MMA8653FC, data sheet here. There are programming notes for this chip here. The default range for this chip is +/-2g. This can be reconfigured to be +/-4g or +/-8g. For some of the students I work with on gesture recognition I need the higher ranges. So I entered the world of microbit i2c programming. I chose the micropython platform as python is always the 'second best choice' for any programming application. Actually, I'm a fan of using C for embedded hardware, but in this case using micropython looked to be fastest way of getting a solution. I used the simple mu editor. Long story short, it's all about syntax. Thanks go to fizban for his example microbit code to interface a microbit with an lcd display using i2c. After reading this code I fixed the mistake(s) I'd been making. The documentation for the i2c microbit micropython is here.

Here's my working code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
''' microbit i2c communications with onboard accelerometer '''
from microbit import *

ACCELEROMETER = 0x1d
ACC_2G = [0x0e, 0x00]
ACC_4G = [0x0e, 0x01]
ACC_8G = [0x0e, 0x02]
CTRL_REG1_STANDBY = [0x2a, 0x00]
CTRL_REG_1_ACTIVE = [0x2a, 0x01]
PL_THS_REG = [0x14]  # returns b'\x84'
PL_BF_ZCOMP = [0x13]  # returns b'\44' = 'D'
WHO_AM_I = [0x0d]  # returns 0x5a=b'Z'
XYZ_DATA_CFG = [0x0e]


def command(c):
    ''' send command to accelerometer '''
    i2c.write(ACCELEROMETER, bytearray(c))


def i2c_read_acc(register):
    ''' read accelerometer register '''
    i2c.write(ACCELEROMETER, bytearray(register), repeat=True)
    read_byte = i2c.read(ACCELEROMETER, 1)
    print('read: {}'.format(read_byte))


def main_text():
    ''' send accelerometer data as a string '''
    print('starting main')
    counter = 0
    while True:
        x = accelerometer.get_x()
        y = accelerometer.get_y()
        z = accelerometer.get_z()
        counter = counter + 1
        print('{} {} {} {}'.format(counter, x, y, z))
        sleep(250)


print("sending i2c commands...")
print('reading PL_BF_ZCOMP :')
print(i2c_read_acc(PL_BF_ZCOMP))
print('reading WHO_AM_I')
print(i2c_read_acc(WHO_AM_I))
# check the initial accelerometer range
print('reading XYZ_DATA_CFG:')
print(i2c_read_acc(XYZ_DATA_CFG))
# change the accelerometer range
command(CTRL_REG1_STANDBY)
command(ACC_4G)
command(CTRL_REG_1_ACTIVE)
print('commands sent')
# check the accelerometer range
print('reading XYZ_DATA_CFG:')
print(i2c_read_acc(XYZ_DATA_CFG))
display.show(Image.MEH)
# main_text()

output:

reading PL_BF_ZCOMP :

read: b'D'

None

reading WHO_AM_I

read: b'Z'

None

reading XYZ_DATA_CFG:

read: b'\x00'

None

commands sent

reading XYZ_DATA_CFG:

read: b'\x01'

None


The onboard accelerometer has an i2c address of 0x1d. There is a good article on how to scan for and verify this address here. I set the variable ACCELEROMETER to be this value in line 4 so that I could refer to it throughout the code without having to remember the hex value. Too many hex values flying around - I'd be bound to make a mistake if I didn't give them names.

To send a command over i2c, as shown in line 18 of the example code, you need to address the target then send the commands as a bytearray. In this case the target is the accelerometer. Typically we send two bytes to the accelerometer. The first specifies the register we want to change, the second the value we want to write to this register. For example, to set the accelerometer's range of sensitivity, we need to set the value of the register called XYZ_DATA_CFG to the value that corresponds with the range we are after. The address of this register is 0x0e. To set the +/4G range, we want to set this register to be 0x01. Now the variable I set in line 6 should make sense. Look in the data sheet linked above for more details. Before we can change this register we have to set CTRL_REG1 to be inactive by writing 0x00 to it. After changing the XYZ_DATA_CFG register we have to set CTRL_REG1 to be active again by writing  0x01 to it. This is detailed in the accelerometer application notes which I linked at the start of this article. 

If you uncomment the last line, then the raw accelerometer values will stream out. The last column are the values for the z-axis of the accelerometer. Lay the board flat on the table. With the default +/-2g range you will see the z-axis values being around +1024 or -1024 depending on if the board is face up or down. This corresponds to +/-1g on the +/-2g range. Now that the board is set to +/-4g, the values for +/-1 g will be +/-512. The maximum and minimum value for the accelerometer stays as +/-2048, but it is now spread over +/-4g. Similarly, if you go crazy and set the range to be +/-8g, then you will see +/-256 for the z-axis value from the accelerometer for the board laying flat. As you would expect, you have to wave the board harder to get it to max out when you set the sensitivity to the higher ranges compared with the default +/-2g range.

So what about the PL_BF_ZCOMP and WHO_AM_I registers that I read from in lines 43 and 45? These are two read only directories. Reading the values stored in these is a sanity check that the chip is turned on and I have working code. I read the XYZ_DATA_CFG before and after setting it to verify that the sensitivity range has been set. Read up on these registers in the data sheet.

Look at line 23. The repeat=True flag has to be set. This clears the 'message end' flag in the write command. The default for this flag is False, which means that the i2c write command has a 'message end' flag at the end of it, which terminates the operation. As we want to read from the chip in line 24, we need to not set the 'message end' flag. Otherwise you will just read 0xff. Can you guess why? The data line is held high for i2c, so if there is nothing coming out of the chip you are trying to read from, you just read a bunch of '1s'. Line 24 means 'read 1 byte from the device with address ACCELEROMETER'. 

Where I initially came unstuck was by sending data as individual bytes, using e.g. b'\x0e' followed by b'\x02' to try and change the XYZ_DATA_CFG register. This looks to be valid for the Adafruit implementation of micropython, but I couldn't get it work.

parsing and unpacking python3 serial data containing double backslashes

posted Apr 11, 2017, 9:23 AM by Matt Oppenheim   [ updated Apr 12, 2017, 5:40 AM ]

I lost a day of my life figuring out how to parse serial data sent as bytes from the BBC Microbit using micropython. The problem is that the data byte string appears with double backslash characters instead of single backslashes when read in over a serial interface.
Actual data:
b'ST\\x00\\x00\\x00\\xe0\\xeaE\\x00\\x00HB\\x00\\x00`\\xc3\\x00\\x00\\x10C\\x00\\x00t\\xc4EN'
What I wanted as data:
b'ST\x00\x00\x00\xe0\xeaE\x00\x00HB\x00\x00`\xc3\x00\x00\x10C\x00\x00t\xc4EN'
So how to convert from one misformed byte string to the clean one that python 3 would use?
I really went around in circles on this one. In the end I used a kludge. But it works. My life can now move on.
I convert the double slash byte to a string. Then I use the replace method to replace '\\' with '\'. Then I use the eval function to recast it as a byte. I am open to suggestions for a cleaner way of doing this!
Here's some example code I used in a jupyter notebook session. test2 is the misformed byte string received over the serial interface and test3 is the cleaned byte that I can now unpack and extract the data from.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from struct import *
PACKER = ('2s5f2s')
test2=b'ST\\x00\\x00\\x00\\xe0\\xeaE\\x00\\x00HB\\x00\\x00`\\xc3\\x00\\x00\\x10C\\x00\\x00t\\xc4EN'
test3 = str(test2)
test3 = test3.replace('\\\\', '\\')
print('{}'.format(test3))
test3 = eval(test3)
print(test3)

print(unpack(PACKER,test3))
output:
b'ST\x00\x00\x00\xe0\xeaE\x00\x00HB\x00\x00`\xc3\x00\x00\x10C\x00\x00t\xc4EN'
b'ST\x00\x00\x00\xe0\xeaE\x00\x00HB\x00\x00`\xc3\x00\x00\x10C\x00\x00t\xc4EN'
(b'ST', 7516.0, 50.0, -224.0, 144.0, -976.0, b'EN')

The data was produced from reading the accelerometer on a BBC Microbit board then using struct.pack(PACKER,scan). I am programming the boards using micropython.
The data is packed using the packer format:
PACKER = ('2s5f2s')
The transmitted scan is constructed using:
values = (START, counter, DELTA, x, y, z, END)
scan = struct.pack(packer, *values)
Where values contains a START and END string ('ST' and 'EN' respectively), a constant called DELTA which represents the time in between samples and the x, y and z readings from the accelerometer. So PACKER means '2 characters followed by 5 floats followed by 2 characters'.
I was being obstinate in sending bytes over the serial interface instead of a string. Why use bytes and not just send a text string? Using the pack and unpack enforces a structure to the data packets and reduces the amount of data needed to be transmitted compared with a string. Consider a number '2048' sent using the packer function. This is coded as an 'f' meaning a float. This is 2 bytes long. Sending '2048' as a string would require 4 bytes, one for each of '2', '0', '4' and '8'. 
If I encode the string 'ST 7516.0 50.0 -224.0 144.0 -976.0 EN' using packer '2s5f2s', the message is 26 bytes. If I send it as a string, it will be 37 bytes. Please see the example code and its output below.

1
2
3
4
5
6
7
8
from struct import *
PACKER = ('2s5f2s')
test = 'ST 7516.0 50.0 -224.0 144.0 -976.0 EN'
test2 = (b'ST',7516.0,50.0,-224.0,144.0,-976.0,b'EN')
print('string length: {}'.format(len(test)))
packed_data = pack(PACKER,*test2)
print('packed length: {}'.format(len(packed_data)))
print('unpacked data: {}'.format(unpack(PACKER,packed_data)))
output:
string length: 37
packed length: 26
unpacked data: (b'ST', 7516.0, 50.0, -224.0, 144.0, -976.0, b'EN')
The second reason for using pack and unpack for data packed transmission over sending a stream is that this enforces error checking. If the data is corrupted while reading from the sensor, then an error will be raised during the pack process at the transmitter end. If the data packet is corrupted during transmission, an error will be raised during the unpack process at the receiving end. This can be caught using a try-except clause.

syntax highlighting with pygmentize

posted Apr 4, 2017, 7:28 AM by Matt Oppenheim

Syntax highlighting means making the code examples you put into your blogs look pretty. How to do this? There are some online highlighters where you post your code, then copy and paste the result back into your blog. For instance, hilite.me. I had been doing this. Now I am using pygmentize, which is part of the pygments library for python.

Here is the content of a simple example test file called pyg_test.py:

import math
for i in range(5):
    print('hello {} {:.2f}'.format(i,math.sin(i)))

The command:

pygmentize -l python -f html -O full,style=default,linenos=1 -o ./pyg_test.html ./pyg_test.py

Produces a file pyg_test.html which contains the prettified text. This can be opened in a browser, then copied and pasted into your blog:

1
2
3
import math
for i in range(5):
    print('hello {} {:.2f}'.format(i,math.sin(i)))

As you tweak your code and rerun the pygmentize command, all you have to do is refresh the browser window to get the latest prettified text. There's a bunch of styles that can be applied. Pygmentize works to highlight many languages. Have a look at the docs for more details.

CD player for the visually impaired

posted Jan 15, 2017, 7:49 AM by Matt Oppenheim   [ updated Jun 15, 2017, 9:44 AM ]

This blog details a method to allow somebody who is visually impaired to easily listen to their CD collection again. My Mother lost her eyesight through macular degeneration. She has a decent collection of classical music that she built up over a few years. But she can't see well enough to easily use a CD player anymore. On top of that, her mobility is restricted. I bought her a Roberts Concerto 2 CD player and radio designed for the visually impaired. Please find details and a review here. This is the best that I could find, but it is still fiddly and difficult for somebody without sight to load the CD. It is quite a bulky device, which makes putting it next to an elderly person awkward as it takes up most of chair side table, or the user has to get up and go to where it is placed. Which is a barrier to it ever being used when just getting out of a chair is no longer straight forwards.  I looked at a few potential solutions. I found a portable CD player on eBay and tried that. But again, it takes up a little too much table top space and it is fiddly to load. You don't realise how poorly controls are laid out on most devices until you try to explain to somebody without vision how to use it. I found some lovely projects where custom built players are built using tags. Audio books are loaded onto a memory card and played using something like a Raspberry Pi single board computer. An NFC coil is used to read a tag placed inside the case of an audiobook or CD and the audio is played from the memory card. Here is an example on Hackaday. I started going down this route. Then had another think. This will add another device to my mother's chair side table. I will have to:
1. Build it
2. Run off all her CDs to memory card
3. Show her how to use it
4. Maintain it
My mother uses a Sovereign USB stick player to listen to her talking books and newspapers. This is a well designed player aimed at the visually impaired. It has decent sound quality. The build cost of a custom device would exceed the cost of a Sovereign and for me to think I would match the sound quality is a tad arrogant. One of the design features of this player is that it will remember the place you were last at on the stick. You can even swap sticks and it remembers the last play position on the previous five sticks you played. Mum already has this next to her and knows how to use it. As a side note, there is now a smaller version of this player available called the sonic which I bought to listen to my podcasts with and loan to Mum when she visits.
So I ran off her CDs to MP3 and put each one on to a cheap USB stick. I bought some small key rings and used these to connect the memory sticks to postcards on which I printed the title of the CD. So far I have run off 10 of these. These 10 sticks and labels fit in an little box on her table next to the player. If the idea works, I will run off some more.
I've written this up so that other people in my position have a potential solution to enable others with disability to enjoy their music collections.
Having to pay a couple of dollars each for a cheap USB stick from eBay for each CD may seem a tad pricey, but compared with the time and cost of building a custom device, I think it is money well invested.
The proof of the pudding is in the eating. Please find a photo of Mum at my house, having dozed off while listening to Aled Jones with the bear she gave me about 40 years ago. The bear's arms are a little saggy now, but we all get a little infirm and need some help as we age.

 

EWMA filter exmple using pandas and python

posted Jan 9, 2017, 10:00 AM by Matt Oppenheim   [ updated Jan 10, 2017, 10:24 AM ]

This article gives an example of how to use an exponentially weighted moving average filter to remove noise from a data set using the pandas library in python 3. I am writing this as the syntax for the library function has changed. The syntax I had been using is shown in Connor Johnoson's well explained example here
I will give some example code, plot the data sets then explain the code. The pandas documentation for this function is here. Like a lot of pandas documentation it is thorough, but could do with some more worked examples. I hope this article will plug some of that gap.
Here's the example code.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
ewma = pd.Series.ewm

x = np.linspace(0, 2 * np.pi, 100)
y = 2 * np.sin(x) + 0.1 * np.random.normal(x)
df = pd.Series(y)
# take EWMA in both directions then average them
fwd = ewma(df,span=10).mean() # take EWMA in fwd direction
bwd = ewma(df[::-1],span=10).mean() # take EWMA in bwd direction
filtered = np.vstack(( fwd, bwd[::-1] )) # lump fwd and bwd together
filtered = np.mean(filtered, axis=0 ) # average
plt.title('filtered and raw data')
plt.plot(y, color = 'orange')
plt.plot(filtered, color='green')
plt.plot(fwd, color='red')
plt.plot(bwd, color='blue')
plt.xlabel('samples')
plt.ylabel('amplitude')
plt.show()
This produces the following plot. Orange line = noisy data set. Blue line = backwards filtered EWMA data set. Red line = forwards filtered EWMA data set. Green line = sum and average of the two EWMA data sets. This is the final filtered output.


Let's look at the example code. After importing the libraries I will need in lines 1-5, I create some example data. Line 6 creates 100 x values with values spaced evenly from 0 to 2 * pi. Line 7 creates 100 y-values from these 100 x-values. Each y value = 2*sin(x)+some noise. The noise is generated using the np.random.normal function. This noisy sine function is plotted in line 15 and can be seen as the jagged orange line on the plot.
Forwards and backwards EWMA filtered data sets are created in lines 10 and 11. 
Line 10 starts with the first x-sample and the corresponding y-sample and works forwards and creates an EWMA filtered data set called fwd. This is plotted in line 17 as the red line.
Line 11 starts at the opposite end of the data set and works backwards to the first - this is the backwards EWMA filtered set, called bwd. This is plotted in line 18 as the blue line.
These two EWMA filtered data sets are added and averaged in lines 12-13. This data set is called filtered. This data set is plotted in line 16 as the green line.
If you look at the ewma functions in line 10 and 11, there is a parameter called span. This controls the width of the filter. The lag of the backwards EWMA data behind the final averaged filtered output is equal to this value. Similarly the forward EWMA data set has an offset forwards of the noisy data set equal to this value. Increasing the span increases the smoothing and the lag. Increasing the value will also reduce the peaks of the filtered data in relation to the unfiltered data. You need to try out different values.
My present application for this filter is removing jitter from accelerometer data. I have also used this filter to smooth signals from hydrophones.



XBee series 1 accelerometer sampling

posted Dec 28, 2016, 10:23 AM by Matt Oppenheim   [ updated Jun 16, 2017, 3:39 AM ]


XBee modules have a built in ADC, so why not sample an analog accelerometer directly? This will allow me to make a smaller wireless accelerometer that I can strap to my participants for testing with. Long term I want a microcontroller in the system for onboard signal processing. But for initial data collection, the smaller and simpler the better. Make it work. Make it fast. Make it right.. I am using the ADXL335 analog output 3-axis accelerometer connected to D0, D1 and D2 of an XBee series 1. This idea is nothing new, I got the idea for this build from a website made by Dr. Eric Ayars
Associate Professor of Physics at the California State University, Chico here. Thanks Eric! Initially I tried lashing up his design with the series 2 XBees that I had to hand. The issues with this are the two main differences that I found between the Series 1 and the Series 2 XBee ADC (analog to digital converter).

1. With the Series 2 XBee, the range of analog input that can be read by the ADC is set to be ground to 1.2V. With the Series 1 module, you set the top voltage that the ADC can sample by connecting that voltage to the VRef pin on the module. There is a VRef pin labelled on the Series 2, but it is not connected to anything. Usually you connect the voltage that you are using to power the module with (e.g. 3.3V) to the VRef pin on the Series 1 to enable the ADC to sample from ground to the supply voltage. You cannot connect a higher voltage than the supply voltage to this pin. Or the World will End. The output from the ADXL335 is centered around half of the voltage that it is powered with. In my case this is 3.3/2 = 1.65V. The output for each of the 3 accelerometers in the chip varies by 330mV/g. So the outputs will rarely dip below 1.2V and be sampled by a Series 2 XBee. Of course I could use a simple resistor network to bring the voltage output from the accelerometer down to be centered around 0.6V and be in with a chance of reading it with the XBee series 2. But this brings us on to issue 2.

2. The sample rate of the Series 2 XBee is lower than that of the Series 1. Using the Digi International XCTU tool for configuring the modules, with the Series 2, the fastest sample rate that I am allowed to set is 50ms. When I tested it, I was only getting about 16Hz. Thinking for a little while, I realise that the 50Hz sampling was being split across the 3 analog inputs that I am sampling (x, y and z axis). 3x16=48, so it all kind of makes sense. The Series 1 can be set to sample silly fast, down to 1ms. However, this brings us on to reading some XBee series 1 data and information sheets. This article from Digi International states that the maximum sample rate for the Series 1 is 50Hz, but it can be set to sample at up to 1KHz. I am interested in seeing just how fast this module can go...

The picture below shows the XBee series 1 module connected with an ADXL335 board - which is on the right of the photo. On the left there is a AAA battery connected to a DC-DC converter board, which provides an output of 3.3V for the ADXL335 and the XBee module. The same 3.3V rail is used as an input to the VREF pin on the XBee module. So the ADC should work from ground to 3.3V. I would imagine that the ADC will stall at about a diode drop (0.6V) from either limit.




I set the sample rate on the XBee 1 to be 5ms using the XCTU tool, which equates to 200Hz.
I lashed up some code based on the XBee API samples. I use Python 3, which allows me to leverage the time.perf_counter() function in lines 12 and 16 to get microsecond timing. Please see the initial code and output below.

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from xbee import XBee
import serial
import time
PORT = '/dev/ttyUSB0'
BAUD_RATE = 115200 
# Open serial port
ser = serial.Serial(PORT, BAUD_RATE)
# Create XBee Series 1 object
xbee = XBee(ser, escaped=True)
print('created xbee at {} with baud {}'.format(PORT, BAUD_RATE))
print('listening for data...')
dt_old = time.perf_counter() 
# Continuously read and print packets
while True:
    dt_new = time.perf_counter()
    response = xbee.wait_read_frame()
    adc_dict=response['samples'][0]
    delta_millis = (dt_new-dt_old)*1000
    dt_old = dt_new
    try:
        print('{:.2f} {:.2f}'.format(delta_millis, 1000/delta_millis))
    except ZeroDivisionError as e:
        continue
    print(adc_dict['adc-0'], adc_dict['adc-1'], adc_dict['adc-2'])
ser.close()
Output:
created xbee at /dev/ttyUSB0 with baud 115200
listening for data...
0.00 1428571.69
526 409 502
10.67 93.73
526 409 503
0.25 4058.74
526 411 503
10.62 94.19
522 406 500
0.40 2474.43
523 409 502
11.26 88.85
516 412 505
0.62 1604.76
523 408 502
10.65 93.86
522 407 498
0.39 2591.94
522 403 500
10.64 94.02
Ignore the first line of data, I expected that to be garbage. The lines of data should be 
adc-0, adc-1, adc-2 - which looks about right
time in ms since the last sample, resulting frequency = 1000/time in ms since last sample - this doesn't look about right

We should be seeing a uniform sample and frequency. But it oscillates between about 11ms and 0.5ms. Which averages to be about 6ms. For all three channels. So the ADC is working at a sample rate of around 2ms. <evil laugh>.

I modified the code to include a 100 sample averaging calculation. This is implemented using a deque data container, initialised in line 13. The sample times are added in line 24. Prior to that, the oldest one is removed in line 13. The values are averaged and printed in line 26. The try, except clause around this line are necessary as the 'None' values that the deque is intialised with cause the np.mean function to crash with a TypeError. 

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from collections import deque
import numpy as np
from xbee import XBee
import serial
import time

PORT = '/dev/ttyUSB0'
BAUD_RATE = 115200 
# Open serial port
ser = serial.Serial(PORT, BAUD_RATE)
# Create XBee Series 1 object
xbee = XBee(ser, escaped=True)
sample_deque = deque([None]*100, maxlen=100)
print('created xbee at {} with baud {}'.format(PORT, BAUD_RATE))
print('listening for data...')
dt_old = time.perf_counter() 
# Continuously read and print packets
while True:
    dt_new = time.perf_counter()
    response = xbee.wait_read_frame()
    adc_dict=response['samples'][0]
    delta_millis = (dt_new-dt_old)*1000
    sample_deque.pop()
    sample_deque.appendleft(delta_millis)
    try:
        print('{:.2f}'.format(np.mean(sample_deque)))
    except TypeError:
        continue
    dt_old = dt_new
    try:
        print('{:.2f} {:.2f}'.format(delta_millis, 1000/delta_millis))
    except ZeroDivisionError as e:
        continue
    print(adc_dict['adc-0'], adc_dict['adc-1'], adc_dict['adc-2'])
ser.close()
output after a few hundred samples:
5.44
13.52 73.96
521 404 500
5.45
2.06 485.59
523 408 502
5.44
0.91 1103.39
526 409 504
5.44
11.06 90.38
516 412 507
5.45
The data should be:
averaged interval in ms - looks about right
last sample interval in ms, frequency calculated from last interval in Hz - still oscillating
adc-0, adc-1, adc-2
The average of around 5.5ms is close enough to the programmed value of 5ms for my purposes. Why does the sample time fluctuate? Probably something to do with my code. If you have an answer, please leave it below.
The rigorous way to verify the accuracy and speed of this module is to plug in a function generator to the analog channels, record data then analyse that. How hard could that be? Errrr..... I think that what I have now is 'good enough' to try out shake gesture recognition.
The next step is to get an output in 'g' - that is units of gravity. As the sensitivity of the ADXL335 is 330mV/g with an input of 3.3V, the output is centred on half of the rail voltage and the ADC has a range of 0-1024:
g = (ADC_count-512)/102.5
I made a python lambda function to do the conversion:
g = lambda x: (x-512)/102.4
So I can output formatted accelerometer values in g by altering line 34 of the last listing to:
print('{:.2f} {:.2f} {:.2f}'.format(g(adc_dict['adc-0']), g(adc_dict['adc-1']), g(adc_dict['adc-2'])))

using pyzmq to communicate between GUIs and processes

posted Oct 21, 2016, 9:41 AM by Matt Oppenheim   [ updated Oct 31, 2016, 10:48 AM ]

Graphical user interfaces (GUIs) all want to be the main thread. They don't play well together. Trying to run GUIs built with different libraries concurrently and get them to talk to one another took me a while to figure out. This article shows how I used the pyzmq library to communicate between two graphical user interfaces (GUIs). 

I am working on unique hand gesture recognition. One GUI represents a hand position. This is represented by a GUI built with pyqt with a few range sliders. The sliders will be used to represent pitch, roll and speed of motion in the final application. A second GUI represents the gesture recognition interface. For this example it is a simple label box set up in pyqtgraph. I used pyqtgraph as this is the tool kit I am using in my final application for real time data display from an accelerometer mounted on a hand. I based my pyzmq script on the examples here.

I played with the publisher subscriber (pubsub) examples. One of the nice things about the pubsub model is that if you send something from the publisher, even if there are no subscribers waiting for the message, nothing blocks or stalls your script. Pubsub is only one way communication, from the publisher to the subscriber. I opted instead to use the pair model. In this pattern, a socket is set up that allows an object at each end to send messages back and forwards.

Pyzmq comes with a partial implementation of the Tornado server. This is explained here. So you can set up an eventloop to trigger on poll events using ioloop. If you are already using a GUI, then odds on you have an events handler running in that GUI. Getting this event handling loops to play nicely with the Tornado server led me down the coding rabbit hole. So I opted to use the event handling loop set up by timer = QtCore.QTimer() in pyqtgraph to poll one end of the pyzmq pair socket that I set up. This is not aesthetic, but I can't see a more reliable method. I am using this QTimer to enable animation of the sensor data that I am using for displaying hand position, so it is already running. Which ever method I use to set up receiving data from the hand posture GUI, at some point I have to decide to look at the data and use it. I thought about using the pyzmq.Queue structure, which is process safe. I could use this to automatically update a list in my sensor display GUI with new posture positions. This won't be looked at until the QTimer triggers. So I may as well simplify things and look for the updated posture position in the QTimer handling method.

Here's the code I use to generate the rangeslider GUI. This can be downloaded from: github. Most of this is boilerplate to produce the GUI. Lines 102-107 create the pyzmq pair socket. Note the try/except wrapper in lines 97-99 around the socket.send_string. This raises a zmq.error.Again exception if there is nothing to receive the message. Using the try/except wrapper allows the code to continue. The 'flags=zmq.NOBLOCK' stops the code from blocking if there is nothing at the other end of the socket to receive the message. This isn't an issue with the pubsub model; a publisher doesn't care if there is no subscriber around to receive the message, but the pair pattern will fail without a receiver unless you explicitly tell it not to block.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
'''
Created on 10 Oct 2016

@author: matthew oppenheim
use pyzmq pair context for communication
'''

from multiprocessing import Process
from PyQt4 import QtGui, QtCore
from qrangeslider import QRangeSlider
import sys
import zmq
from zmq.eventloop import ioloop, zmqstream
from pubsub_zmq import PubZmq, SubZmq

class Example(QtGui.QWidget):
    
    def __init__(self):
        app = QtGui.QApplication(sys.argv)
        super().__init__()
        ioloop.install()
        self.port = 5556
        self.topic = "1"
        self.initUI()
        sys.exit(app.exec_())
        
    def initUI(self):
        self.range_duration = QRangeSlider()   
        self.range_duration.show()
        self.range_duration.setFixedWidth(300)
        self.range_duration.setFixedHeight(36)
        self.range_duration.setMin(0)
        self.range_duration.setMax(1000)
        self.range_duration.setRange(200,800)
        self.textbox = QtGui.QLineEdit()
        self.set_duration_btn = QtGui.QPushButton("send duration")
        self.set_duration_btn.clicked.connect(lambda: self.button_click('duration'))
        self.set_duration_btn.setFixedWidth(100)
        self.range_pitch = QRangeSlider()    
        self.range_pitch.show()    
        self.range_pitch.setFixedWidth(300)
        self.range_pitch.setFixedHeight(36)
        self.range_pitch.setMin(-80)
        self.range_pitch.setMax(80)
        self.range_pitch.setRange(-20, 20)
        self.set_pitch_btn = QtGui.QPushButton("send pitch")
        self.set_pitch_btn.setFixedWidth(100)
        self.set_pitch_btn.clicked.connect(lambda: self.button_click('pitch'))
        self.range_roll = QRangeSlider()    
        self.range_roll.show()    
        self.range_roll.setFixedWidth(300)
        self.range_roll.setFixedHeight(36)
        self.range_roll.setMin(-80)
        self.range_roll.setMax(80)
        self.range_roll.setRange(-20, 20)
        self.set_roll_btn = QtGui.QPushButton("send roll")
        self.set_roll_btn.setFixedWidth(100)
        self.set_roll_btn.clicked.connect(lambda: self.button_click('roll'))

        hbox_duration = QtGui.QHBoxLayout()
        hbox_duration.addStretch(1)
        hbox_duration.addWidget(self.range_duration)
        hbox_duration.addWidget(self.set_duration_btn)

        hbox_pitch = QtGui.QHBoxLayout()
        hbox_pitch.addStretch(1)
        hbox_pitch.addWidget(self.range_pitch)
        hbox_pitch.addWidget(self.set_pitch_btn)
        
        hbox_pitch = QtGui.QHBoxLayout()
        hbox_pitch.addStretch(1)
        hbox_pitch.addWidget(self.range_pitch)
        hbox_pitch.addWidget(self.set_pitch_btn)

        hbox_roll = QtGui.QHBoxLayout()
        hbox_roll.addStretch(1)
        hbox_roll.addWidget(self.range_roll)
        hbox_roll.addWidget(self.set_roll_btn)

        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox_pitch)
        vbox.addLayout(hbox_roll)
        vbox.addLayout(hbox_duration)
        vbox.addWidget(self.textbox)
        
        self.setLayout(vbox)    
        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('rangesliders')
        self.socket = self.create_socket(self.port)
        self.show()
     
    @QtCore.pyqtSlot()   
    def button_click(self, message):
        ''' handle button click event '''
        self.textbox.setText('sent {}'.format(message))
        try:
            self.socket.send_string(message, flags=zmq.NOBLOCK)
        except zmq.error.Again as e:
            print('no receiver for the message: {}'.format(e))
        
    def create_socket(self, port):
        ''' create a socket using pyzmq with PAIR context '''
        context = zmq.Context()
        socket = context.socket(zmq.PAIR)
        socket.bind("tcp://*:%s" % port)
        return socket
                
if __name__ == '__main__':
    ex = Example()
Here's the simple label box that I use to test out receiving messages:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
'''
pyqtgraph layout with a pyzmq pair context
for testing pubsub messaging with pyzmq
Created on 14 Oct 2016
using qt timer and polling instead of the tornado loop in zmq
@author: matthew oppenheim
'''

import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from pubsub_zmq import SubZmq
from multiprocessing import Process
import zmq
import sys
import time

FRAMES_PER_SECOND = 30

class PyqtgraphPair(QtGui.QWidget):
    def __init__(self):
        super().__init__()
        port = '5556'
        topic = '1'

        QtGui.QWidget.__init__(self)
        self.layout = QtGui.QVBoxLayout()
        self.setLayout(self.layout)
        self.label = QtGui.QLabel("test")
        self.set_label("new label")
        self.layout.addWidget(self.label)
        self.socket = self.create_socket(port)
        
    def create_socket(self, port):
        '''
        Constructor
        '''
        context = zmq.Context()
        socket = context.socket(zmq.PAIR)
        socket.connect('tcp://localhost:%s' % port) 
        return socket
    
    def process_message(self, message):
        ''' process the subscriber's message '''
        #topic, text = enumerate(a for a in message)
        message = message[0].decode()
        message = message.split()[1]
        print('sub received {}'.format(message))
        self.set_label(self.label, 'changed')
        #label.setText(message)
        if message == 'exit':
            ioloop.IOLoop.instance().stop()
            print('sub io loop stopped')

    def set_label(self, text):
        ''' set the label to text '''
        self.label.setText(text)

    def timer_timeout(self):
        ''' handle the QTimer timeout '''
        try:
            msg = self.socket.recv(flags=zmq.NOBLOCK).decode()
            print('message received {}'.format(msg))
            self.set_label(msg)
        except zmq.error.Again as e:
            return
        
        
if __name__ == '__main__':
    pg.mkQApp()
    win = PyqtgraphPair()
    win.show()
    win.resize(200,200)
    timer = QtCore.QTimer()
    timer.timeout.connect(win.timer_timeout)
    timer.start(1000/FRAMES_PER_SECOND)
    #win.set_label('hello')
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

Polling for a new message takes place in line 61. This has the same try/except wrapper as in the rangeslider example.

installing evil mode in emacs

posted Oct 14, 2016, 4:51 AM by Matt Oppenheim   [ updated Oct 14, 2016, 5:10 AM ]

This short article details how I got evil mode working in emacs in linux. This allows me to use vi key bindings in emacs. Making my life complete.

I tried following the instructions in the youtube video Emacs as my leader: evil-mode but emacs complained that it could not find evil. So I tried following the instructions here: https://www.emacswiki.org/emacs/Evil. This almost worked, but I needed to make a slight tweak. Please find the method that I got to work below.

Clone evil into .emacs.d/evil using:
hg clone https://bitbucket.org/lyro/evil ~/.emacs.d/evil

If you can't clone the repository, download the zip file and unzip it to the directory ~/.emacs.d/evil.

Edit your ~/.emacs file by adding these lines:

(push "~/.emacs.d/evil" load-path)
(require 'evil)
(evil-mode 1)

For whatever reason, most of the websites have an apostrophe sign in front of load-path: 'load-path. This stops the code from working. No apostrophe sign needed!
Run emacs.
To kill emacs, use <control> x <control> c.

1-10 of 12