Showing posts with label raspberry pi. Show all posts
Showing posts with label raspberry pi. Show all posts

Sunday, 17 June 2018

Pi Camera stop motion animation

In preparation for a Raspberry Pi event I decided to create a simple GUI for creating stop motion animations using the Pi camera module to use for a demo.


Its a really simple application, you start it up, you click "take image", you re-position the scene, you click "take image" and so on until you are happy with your animation and you click "save" to store it as an animated gif.



You can find the source code at goo.gl/4Xvu7b.

Install
1. Connect a camera module
2. Enable the camera (Menu > Preferences > Raspberry Pi Configuration, Interfaces, Camera)
3. Open a terminal (Menu > Accessories > Terminal), install the modules and download the code:
sudo pip3 install guizero
sudo pip3 install imageio
wget -O guizero_stopmotion.py https://goo.gl/zMTjas
4. Run the program:
python3 guizero_stopmotion.py

A couple of "interesting" things about this project

The gui was created using guizero which is a super simple to use library for creating GUI's, definitely have a look.

Most of the work was finding a way to create animated gifs in Python and working with images in memory rather than stored on disk

When the image is captured from the camera it isn't stored to a file, it is stored in a numpy array, this means each frame is only stored in memory making it faster:
# create the camera
camera = PiCamera(resolution="640x480")
camera_output = PiRGBArray(camera)
...
# capture the image
camera.capture(camera_output, "rgb")
# append the camera image to the list as a numpy array
animation.images.append(camera_output.array)
The python module imageio is used to create the gif by passing the frames as a list, but again rather than being written to disk each time it is created as an in memory BytesIO stream:
gif_output = BytesIO()
imageio.mimsave(gif_output, animation.images, format="gif")
When the animated gif is displayed in guizero the BytesIO stream has to be open into a PIL Image.
animation.image = Image.open(gif_output)


Friday, 29 December 2017

Setup Raspberry Pi Samba share

I almost always setup a samba share on every Raspberry Pi I install, it allows me to easily share files and work on my projects - so I thought I had better write down how I do it.
Install samba:
sudo apt-get install samba
Modify the Samba config file to add a share called pihome which points to the /home/pi directory:
sudo nano /etc/samba/smb.conf
Scroll to the bottom and add the following:

protocol = SMB2

[pihome]
   comment= Pi Home
   path=/home/pi
   browseable=Yes
   writeable=Yes
   only guest=no
   create mask=0644
   directory mask=0755
   public=no

Setup a samba password for the Pi user:
sudo smbpasswd -a pi
Restart the samba service:
sudo service smbd restart
You should now be able to connect to your Pi using the address:
//ip_address_of_pi/pihome

Friday, 7 July 2017

Python Bluetooth RFCOMM Client Server

As part of the Blue Dot project I needed to create a simple Bluetooth client / server library so that the communication could be managed. This library, btcomm, is part of bluedot but its not exclusive and can be used for Bluetooth communication in Python.

It uses a 2 way RFCOMM communication - you can send messages to and from 2 devices, 1 being the server which waits for connections, 1 being the client which makes a connection.

Install the library
sudo apt-get install python3-dbus
sudo pip3 install bluedot

Pairing

The 2 devices you which want to communicate between will need to be paired, the Blue Dot documentation describes how to pair 2 raspberry pi's which might be useful.

Simple Client / Server Example
Lets create a simple example, a server which waits for connections and when it receives data it echo's it back to the client.

Create a new Python program and save it as btserver.py:

from bluedot.btcomm import BluetoothServer
from signal import pause

def data_received(data):
    print(data)
    s.send(data)

s = BluetoothServer(data_received)
pause()

Create a 2nd program and save it as btclient.py:
from bluedot.btcomm import BluetoothClient
from signal import pause

def data_received(data):
    print(data)

c = BluetoothClient("nameofyourserver", data_received)
c.send("helloworld")

pause()

Run the server and then run the client, the client should connect and "Hello World" will be sent to the server and displayed on the screen, the server will then send the same "Hello World" message back to the client, which will print it to the screen.

Adapter

There is also a useful API for accessing the Bluetooth adapter allowing you to get its current status, power it on/off, make it discoverable or find the devices its paired with.
from bluedot.btcomm import BluetoothAdapter

a = BluetoothAdapter()

print("Powered = {}".format(a.powered))
print(a.paired_devices)
a.allow_pairing()

Documentation

There is comprehensive documentation for the btcomm library, which describes the API and how to use it.


Friday, 5 May 2017

Raspberry Pi Touchscreen Portrait

I recently wanted to turn my Raspberry Pi Official Touchscreen portrait (i.e. sideways!), which turns out is a bit of pain.

Turning the display is relatively easy but making the touch work is more difficult - there was a set of instructions on the Raspberry Pi forum, but a recent update to Jessie meant they no longer worked, so I pulled this set of instructions together:

Install xinput:
sudo apt-get install xinput
Rotate the display by editing config.txt:
sudo nano /boot/config.txt
 .. add this to the buttom of the file:
display_rotate=1
Use Ctrl X, Yes to Save
Create a script to rotate the touchscreen:
nano /home/pi/touch_rotate.sh
 .. add the following command
xinput --set-prop 'FT5406 memory based driver' 'Coordinate Transformation Matrix'  0 1 0 -1 0 1 0 0 1
Make the script executable:
chmod +x touch_rotate.sh
Make the script run when the GUI starts by editing autostart:
sudo nano ~/.config/lxsession/LXDE-pi/autostart
 .. add this to the bottom to run your script
@/home/pi/touch_rotate.sh
Reboot:
sudo reboot

Monday, 24 April 2017

Blue Dot - a bluetooth remote for Raspberry Pi

Blue Dot is a really simple way to add Bluetooth remote control to your Raspberry Pi projects.


I created Blue Dot after being asked many times at Picademy “how can I get rid of all these wires?”. 

Blue dot is an android app (client) and really easy to use Python library which allows you to wirelessly control your Python projects, whether that is a light switch, remote camera, robot or anything else you can think of!


See the getting started guide for more info on 'getting started', or follow the tutorial below.

Installation & Use
These instructions assume your Raspberry Pi is running the latest version of Raspbian with Pixel.

You will need a Raspberry Pi with built-in Bluetooth (such as the Pi 3 or Pi Zero W) or a Raspberry Pi and a USB Bluetooth dongle.

Get the app
Download and install the Blue Dot app from the google play store.


If you are wondering why there is no iOS app? Its because iOS doesn't support Bluetooth serial comms; you can only really talk to 'standard devices' (cars, speakers, fitness trackers, etc).

Python library
Open a terminal (Menu > Accessories > Terminal) and type:
sudo apt-get install python3-dbus
sudo pip3 install bluedot
Or if you need to use Python 2 (please dont tho!):
sudo apt-get install python-dbus
sudo pip install bluedot

Pairing
In order to communicate over Bluetooth securely you need to pair your phone to your Raspberry Pi.

On your Android phone:
  1. Open Settings
  2. Select Bluetooth
  3. This will make your phone Discoverable
Using your Raspberry Pi
  1. Click the bluetooth icon on the taskbar
  2. Turn on Bluetooth (if its off)
  3. Click Make Discoverable
  4. Click Add Device
  5. Your phone will appear in the list, select it and click Pair
  6. Enter a PIN code
On your Android phone
  1. Enter the same PIN code when prompted
  2. Click Ok
Code
The simplest way to use the Blue Dot is as a button:
  1. Open Python 3 (Menu > Programming > Python 3)
  2. Create a new file (File > New File)
  3. The following code, will start up the Blue Dot, and wait for it to be pressed:
  4. from bluedot import BlueDot
    bd = BlueDot()
    bd.wait_for_press()
    print("You pressed the blue dot!")
  5. Save your program (File > Save) as mydot.py
  6. Run your program (Run > Run Module)
  7. Open the Blue Dot app
  8. Connect to your Raspberry Pi
  9. Press the Blue Dot
As well as waiting for something to happen you can also call functions when the button is pressed, released or the position its pressed moves.
from bluedot import BlueDot
from signal import pause

def say_hello():
    print("Hello World")

def say_goodbye():
    print("goodbye")

bd = BlueDot()
bd.when_pressed = say_hello
bd.when_released = say_goodbye

pause()
By using the position of where the button is pressed you can use the Blue Dot like a joystick:
from bluedot import BlueDot
from signal import pause

def dpad(pos):
    if pos.top:
        print("up")
    elif pos.bottom:
        print("down")
    elif pos.left:
        print("left")
    elif pos.right:
        print("right")
    elif pos.middle:
        print("fire")

bd = BlueDot()
bd.when_pressed = dpad

pause()
Add to this gpiozero's Robot functions, you can create a Bluetooth controlled robot with very little code.
from bluedot import BlueDot
from gpiozero import Robot
from signal import pause

bd = BlueDot()
robot = Robot(left=(lfpin, lbpin), right=(rfpin, rbpin))

def move(pos):
    if pos.top:
        robot.forward()
    elif pos.bottom:
        robot.backward()
    elif pos.left:
        robot.left()
    elif pos.right:
        robot.right()

def stop():
    robot.stop()

bd.when_pressed = move
bd.when_moved = move
bd.when_released = stop

pause()
Check out the Blue Dot documentation for more information and ideas - you really can do a lot with a simple circle :)

Thursday, 5 January 2017

Raspberry Pi - 4 digit 7 Segment display, gpiozero

I recently picked up some 'retro 4 digit LED displays' from pimoroni, noticing there was no support in gpiozero for 7 segment displays (either single or multi digit) I decided to add them and create a pull request.

This builds on the code I created for driving single 7 segment displays.


Hopefully the PR will get added into a gpiozero release soon, but until then add this code to your project and use the following to drive your display.
#setup the pins

#these are the pins the LED are connected too
# (in the order A, B, C, D, E, F, G, decimal point)
LED_PINS = (7, 22, 25, 17, 8, 27, 4, 24)
#these are the pins the digits are connected too
DIGIT_PINS = (23, 18, 15, 14)

#create the multi seven segment display
# use active_high=True when digit pins are cathode (ground)
multi_sev = MultiSevenSegmentDisplay(LED_PINS, DIGIT_PINS,
                                     active_high=True)

#display your message
multi_sev.display("LEDS")

#turn off the display using
multi_sev.off()
The display function works by plexing the display, turning the LEDs on one at a time, so quickly it tricks the eye into thinking the display is showing 1 message.



Thursday, 8 December 2016

Raspberry Pi - bash memory split check

I needed a bash script which would only launch a program on a Raspberry Pi if there was enough memory dedicated to the GPU.

I pulled this script together which checks to see if there is at least 128 MB dedicated to the GPU before starting the program.

#!/bin/bash

#check there is enough gpu memory to start
#get the gpu memory and output to a file called gpu_mem
vcgencmd get_mem gpu > gpu_mem
source gpu_mem
#strip last char from the output (i.e. 64M)
gpu=${gpu%?}
if (( $gpu >= 128 )); then
    ./launch_program
else
    echo "The program needs at least 128MB of memory allocated to the GPU"
    echo "Use sudo raspi-config (Advanced Options > Memory Split) to change"
fi
#remove the gpu_mem file
rm gpu_mem

Tuesday, 29 November 2016

Compile Allegro 5.x for Raspberry Pi

I am in the process of porting Mayhem 2 to Allegro 5 (with the help of Jonas Karlsson), and wanted to compile the latest version of Allegro on the Pi, as only an older version is available through apt.

Install the dependencies
sudo apt-get install build-essential git cmake cmake-curses-gui xorg-dev libgl1-mesa-dev libglu-dev libpng-dev libcurl4-nss-dev libfreetype6-dev libjpeg-dev libvorbis-dev libopenal-dev libphysfs-dev libgtk2.0-dev libpulse-dev libflac-dev libdumb1-dev
Get the Code
git clone https://github.com/liballeg/allegro5.git
cd allegro5
Check out the version you want - see here for a list of versions
git checkout 5.2.1
Build it
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-raspberrypi.cmake
make
Install it
sudo make install
export PKG_CONFIG_PATH=/home/pi/allegro5/build/lib/pkgconfig
sudo ldconfig

Monday, 24 October 2016

Raspberry Pi, 7 Segment Display, gpiozero

I've been working on an update to gpiozero to add a 7 segment display - hopefully this will make it into gpiozero soon, but until then if you want to use a 7 segment display with your Raspberry Pi you can follow the details below.

Update - I have updated this to support single and multiple digit 7 segment displays.


Wire it up

The seven segment display will have 7 pins to connect the digit's LEDs, 1 pin to connect the decimal point LED and 1 pin which is the common pin.

The LED pins should connect to 8 GPIO pins depending on whether your 7 segment display is a common-anode or a common-cathode the common pin should connect to either a 3.3v pin or the ground. Your also going to need a suitable resistor (probably a 330) between your GPIO and LED pins.

Wired up as a common-anode
Code

Create a new program and add the following to the top of the your program (this is the class which will allow you to control the display).

from gpiozero import LEDBoard, OutputDeviceError, LEDCollection

class SevenSegmentDisplay(LEDBoard):
    """
    Extends :class:`LEDBoard` for a 7 segment LED display 

    7 segment displays have either 7 or 8 pins, 7 pins for the digit display 
    and an optional 8th pin for a decimal point. 7 segment displays 
    typically have either a common anode or common cathode pin, when
    using a common anode display 'active_high' should be set to False.
    Instances of this class can be used to display characters or control 
    individual leds on the display. For example::

        from gpiozero import SevenSegmentDisplay

        seven = SevenSegmentDisplay(1,2,3,4,5,6,7,8,active_high=False)
        seven.display("7")
    
    :param int \*pins:
        Specify the GPIO pins that the 7 segment display is attached to.
        Pins should be in the LED segment order A,B,C,D,E,F,G,decimal_point 
        (the decimal_point is optional).

    :param bool pwm:
        If ``True``, construct :class:`PWMLED` instances for each pin. If
        ``False`` (the default), construct regular :class:`LED` instances. This
        parameter can only be specified as a keyword parameter.

    :param bool active_high:
        If ``True`` (the default), the :meth:`on` method will set all the
        associated pins to HIGH. If ``False``, the :meth:`on` method will set
        all pins to LOW (the :meth:`off` method always does the opposite). This
        parameter can only be specified as a keyword parameter.

    :param bool initial_value:
        If ``False`` (the default), all LEDs will be off initially. If
        ``None``, each device will be left in whatever state the pin is found
        in when configured for output (warning: this can be on). If ``True``,
        the device will be switched on initially. This parameter can only be
        specified as a keyword parameter.    
    
    """
    def __init__(self, *pins, **kwargs):
        # 7 segment displays must have 7 or 8 pins
        if len(pins) < 7 or len(pins) > 8:
            raise ValueError('SevenSegmentDisplay must have 7 or 8 pins')
        # Don't allow 7 segments to contain collections
        for pin in pins:
            assert not isinstance(pin, LEDCollection)
        pwm = kwargs.pop('pwm', False)
        active_high = kwargs.pop('active_high', True)
        initial_value = kwargs.pop('initial_value', False)
        if kwargs:
            raise TypeError('unexpected keyword argument: %s' % kwargs.popitem()[0])
            
        self._layouts = {
            '1': (False, True, True, False, False, False, False),
            '2': (True, True, False, True, True, False, True),
            '3': (True, True, True, True, False, False, True),
            '4': (False, True, True, False, False, True, True),
            '5': (True, False, True, True, False, True, True),
            '6': (True, False, True, True, True, True, True),
            '7': (True, True, True, False, False, False, False),
            '8': (True, True, True, True, True, True, True),
            '9': (True, True, True, True, False, True, True),
            '0': (True, True, True, True, True, True, False),
            'A': (True, True, True, False, True, True, True),
            'B': (False, False, True, True, True, True, True),
            'C': (True, False, False, True, True, True, False),
            'D': (False, True, True, True, True, False, True),
            'E': (True, False, False, True, True, True, True),
            'F': (True, False, False, False, True, True, True),
            'G': (True, False, True, True, True, True, False),
            'H': (False, True, True, False, True, True, True),
            'I': (False, False, False, False, True, True, False),
            'J': (False, True, True, True, True, False, False),
            'K': (True, False, True, False, True, True, True),
            'L': (False, False, False, True, True, True, False),
            'M': (True, False, True, False, True, False, False),
            'N': (True, True, True, False, True, True, False),
            'O': (True, True, True, True, True, True, False),
            'P': (True, True, False, False, True, True, True),
            'Q': (True, True, False, True, False, True, True),
            'R': (True, True, False, False, True, True, False),
            'S': (True, False, True, True, False, True, True),
            'T': (False, False, False, True, True, True, True),
            'U': (False, False, True, True, True, False, False),
            'V': (False, True, True, True, True, True, False),
            'W': (False, True, False, True, False, True, False),
            'X': (False, True, True, False, True, True, True),
            'Y': (False, True, True, True, False, True, True),
            'Z': (True, True, False, True, True, False, True),
            '-': (False, False, False, False, False, False, True),
            ' ': (False, False, False, False, False, False, False),
            '=': (False, False, False, True, False, False, True)
        }
        
        super(SevenSegmentDisplay, self).__init__(*pins, pwm=pwm, active_high=active_high, initial_value=initial_value)
    
    def display(self, char):
        """
        Display a character on the 7 segment display

        :param string char:
            A single character to be displayed 
        """
        char = str(char).upper()
        if len(char) > 1:
            raise ValueError('only a single character can be displayed')
        if char not in self._layouts:
            raise ValueError('there is no layout for character - %s' % char)
        layout = self._layouts[char]
        for led in range(7):
            self[led].value = layout[led]
            
    def display_hex(self, hexnumber):
        """
        Display a hex number (0-F) on the 7 segment display

        :param int hexnumber:
            The number to be displayed in hex 
        """
        self.display(hex(hexnumber)[2:])

    @property
    def decimal_point(self):
        """
        Represents the status of the decimal point led
        """
        # does the 7seg display have a decimal point (i.e pin 8)
        if len(self) > 7:
            return self[7].value 
        else:
            raise OutputDeviceError('there is no 8th pin for the decimal point')
    
    @decimal_point.setter
    def decimal_point(self, value):
        """
        Sets the status of the decimal point led
        """
        if len(self) > 7:
            self[7].value = value
        else:
            raise OutputDeviceError('there is no 8th pin for the decimal point')    
    
    def set_char_layout(self, char, layout):
        """
        Create or update a custom character layout, which can be used with the 
        `display` method.

        :param string char:
            A single character to be displayed 
            
        :param tuple layout:
            A 7 bool tuple of LED values in the segment order A, B, C, D, E, F, G
        """
        char = str(char).upper()
        if len(char) != 1:
            raise ValueError('only a single character can be used in a layout')
        if len(layout) != 7:
            raise ValueError('a character layout must have 7 segments')
        self._layouts[char] = layout

Create the display
Create an instance on the 7 segment display object, you need to pass the pins in the order, A,B,C,D,E,F,G,Decimal Point.
seven_seg = SevenSegmentDisplay(20, 21, 6, 22, 27, 18, 15, 13)
If you have a common-anode display you will also need to set the active_high parameter to False:
seven_seg = SevenSegmentDisplay(20, 21, 6, 22, 27, 18, 15, 13, 
                                active_high=False)
Display a character

You can show a character on the display using the .display() method.
seven_seg.display("8")
Add a new character

You can add your own characters to the display, using the .set_char_layout() method passing the character and a tuple of 7 booleans for each of the LEDs in order A,B,C,D,E,F,G.
seven_seg.set_char_layout("_", (False, False, False, True, False, False, False))
seven_seg.display("_")

Decimal point

You can turn the decimal point on by setting the .decimal_point property.
seven_seg.decimal_point = True




Thursday, 19 May 2016

Raspberry Pi - Playing a Sound File with Python

A question I get asked a lot in Picademy is how to I play a sound file using Python.

Using just whats on the standard Raspbian image the easiest way, IMO, is to use Pygame.

This small code snippet below shows you how. Just put the wav file in the same place as your program.

import pygame
from time import sleep

#Initialise pygame and the mixer
pygame.init()
pygame.mixer.init()

#load the sound file
mysound = pygame.mixer.Sound("mysound.wav")

#play the sound file for 10 seconds and then stop it
mysound.play()
time.sleep(10)
mysound.stop()

You will have to use wav files, as opposed to other sounds files such as mp3, ogg, etc - use media.io to convert them.

Tuesday, 12 April 2016

Mayhem, Amiga game, ported to Raspberry Pi

Update - I've taken Mayhem forward to create Mayhem 2.

I had a Commodore Amiga and a game I played, a lot, was Mayhem, its a multiplayer (2-4) shooter - imagine multiplayer asteroids, with gravity, fuel and shields!

It was ported to the PC in 2002 by devpack who released the code in 2011 on github and google code which is where I picked it up and ported it to the Raspberry Pi.

This is the port, but check out the original Amiga game.

I got some help from the Raspberry Pi forums in getting it to compile then it was case of sorting out a few case sensitive filename bugs (it was original written for Windows!) and tracking down a bug in the original code which was causing a memory access error and segmentation fault.

The code is on github.com/martinohanlon/mayhem-pi.

Install
sudo apt-get install liballegro4.4 liballegro4-dev
git clone https://github.com/martinohanlon/mayhem-pi

Run
cd mayhem-pi
./start

Keys
Player 1 - z, x, c, v, g
Player 2 - left, right, pad del, pad 0, pad enter
Player 3 - b, n, 'comma', m, l
Player 4 - y, u, o, i, 0
Change level - 1, 2, 3

Compile
If you want to modify the game, I've got a couple of things on my list, you can recompile it with.
cd mayhem-pi
make


Sunday, 27 March 2016

Raspberry Pi - Take screenshots of Minecraft

If you going to take a screenshot of Minecraft: Pi edition (or anything else for that matter), I really like a command line utility called raspi2png, its simple and screenshots images which have been created using the GPU (like games) as well.


Download
Open a terminal and clone the repository from github:
cd ~
git clone https://github.com/AndrewFromMelbourne/raspi2png

Use
Change directory to raspi2png and run the program's help to show all the options:
cd ~/raspi2png
./raspi2png --help
Usage: raspi2png [--pngname name] [--width ] [--height ] [--compression ] 
[--delay ] [--display ] [--stdout] [--help]

    --pngname,-p - name of png file to create (default is snapshot.png)
    --height,-h - image height (default is screen height)
    --width,-w - image width (default is screen width)
    --compression,-c - PNG compression level (0 - 9)
    --delay,-d - delay in seconds (default 0)
    --display,-D - Raspberry Pi display number (default 0)
    --stdout,-s - write file to stdout
    --help,-H - print this usage information

To take screenshot you have to use the -p option and pass an image filename:
./raspi2png -p myscreenshot.png
Another really useful option is -d to delay when to take the picture, this enables you to get the screen ready for a shot - to take a picture delayed by 10 seconds:
./raspi2png -p mydelayedshot.png -d 10
The image files will be created in the ~/raspi2png directory - if you want them in a different directory use a full path:
./raspi2png -p /home/pi/mydir/myscreenshot.png
If you use a filename which already exists raspi2png will overwrite the file without warning and the old image will be lost.

Fyi - I wrote this blog post using a Raspberry Pi 3...  First time I've used a Pi to write about a Pi - thats progress!

Friday, 25 March 2016

Raspberry Pi gpiozero holdable button

The current release of gpiozero doesn't have the support to hold a button down e.g. when a button is pressed and then held down for 1 second something happens.

This is really useful when building hardware projects and you want 1 button to do 2 things, say turn something on when pressed, and turn off when held down.

I needed this for a project so I thought I would create a new HoldableButton class using gpiozero. Hopefully this will be incorporated into gpiozero, but until then you can use the class below.

The code is also available here as a gist.

You will need to add the HoldableButton class to your Python program:
from gpiozero import Button
from threading import Timer

class HoldableButton(Button):
    def __init__(self, pin=None, pull_up=True, bounce_time=None, 
                 hold_time=1, repeat=False): 

        super(HoldableButton, self).__init__(pin, pull_up, bounce_time)

        # Set Button when_pressed and when_released to call local functions
        # cant use super() as it doesn't support setters
        Button.when_pressed.fset(self, self._when_button_pressed)
        Button.when_released.fset(self, self._when_button_released)

        self._when_held = None
        self._when_pressed = None
        self._when_released = None
        self._is_held = False

        self.hold_time = hold_time
        self.repeat = repeat
        self._held_timer = None

    #override button when_pressed and when_released
    @property
    def when_pressed(self):
        return self._when_pressed

    @when_pressed.setter
    def when_pressed(self, value):
        self._when_pressed = value

    @property
    def when_released(self):
        return self._when_released

    @when_released.setter
    def when_released(self, value):
        self._when_released = value

    @property
    def when_held(self):
        return self._when_held

    @when_held.setter
    def when_held(self, value):
        self._when_held = value

    @property
    def is_held(self):
        return self._is_held

    def _when_button_pressed(self):
        self._start_hold()
        if self._when_pressed != None:
            self._when_pressed()

    def _when_button_released(self):
        self._is_held = False
        self._stop_hold()
        if self._when_released != None:
            self.when_released()

    def _start_hold(self):
        self._held_timer = Timer(self.hold_time, self._button_held)
        self._held_timer.start()

    def _stop_hold(self):
        if self._held_timer != None:
            self._held_timer.cancel()

    def _button_held(self):
        self._is_held = True
        if self._when_held != None:
            if self.repeat and self.is_pressed:
                self._start_hold()
            self._when_held()
Using the HoldableButton class is pretty simple, similar to the gpiozero Button class and can be swapped for the Button class with no other changes.
holdbutton = HoldableButton(pin, hold_time = 1, repeat = False)
You have to pass a pin number and optional hold_time and repeat values:
  • hold_time - is the number of seconds after the button is pressed before the button is consider 'held'
  • repeat - is a boolean and if set to True will occur 'button held' events to be repeated after each hold_time
You can use all the same methods and properties of the Button class, when_pressed, when_released, is_pressed, etc.

There are 2 additional properties is_held, which will return a boolean stating whether the button has been held down for the 'hold_time' and when_held which when assigned a function will cause the function to be called when the button is held down.
def myheldfunction():
    print("button held")

holdbutton.when_held = myheldfunction
Now when a button is held down, the function will be called and "button held" printed.

Monday, 18 January 2016

Pocket PiGRRL - Battery Monitor

I recently made myself an Adafruit Pocket PiGRRL and I wanted to modify it so it would warn me when the battery was running low - there is a small red LED but its hidden inside the case!


The plan was to create a program which would sense the battery getting low and put a warning icon on the top left of screen giving me time to shutdown the Pi properly or plug it in.



TLDR - just scroll down to install Grrl Battery Monitor.

I started with the software as I, foolishly, thought this would be the hardest part, the problem with creating an icon is that is has to go over the top of everything regardless of what is on the screen (command prompt, emulators, emulation station, everything) or what hardware was rendering it.

My first plan was that I could use Picamera's overlay function which I knew used the GPU to output directly to the screen, and with a bit of help from Dave Jones who put together a prototype, it was looking good, but while the icon appeared on top of emulators and the command prompt, it didnt write over emulation station.

I came across Low Level Graphics on Raspberry Pi which walks you through writing graphics directly to the Linux framebuffer using C, this was a lot lower level than I hoped to get into but it would definitely write my icon over anything that was on the screen - using this I wrote a program to create an icon on the screen when a GPIO pin was triggered.

Next I needed to be able to read from the power booster when the battery was running low, my original plan was to use the LBO (low battery output) pin, but this proved to be way more difficult than I expected, read this post on Adafruit's forum if your really interested.

I ended up connecting a wire to the low battery warning led (red) on the power booster and using this to switch a transistor which connected a GPIO to ground.


Its been frustrating but I am really pleased with how it worked out - if you want to add the batter monitor to your own Pocket PiGRRL follow the instructions below.

Install Grrl Battery Monitor

You will need a few parts:
  • Some wire
  • 2N3904 NPN transistor
  • 47k resistor
  • Strip board
Note - if you are doing this on a PiGRRL 2 with a Pi 3, be sure to check out Christian's comments about the pin to use and wiring-pi install before starting.

1. Open up your Pi GRRL and connect a small length of wire to the red (low power) led on the power booster.



2. Solder the components to the strip board. including 2 lengths of wire which will connect to GPIO 19 and GND.




3. Flip over your Pi and solder the GPIO and GND wires to the underside of the Pi's GPIO header.


The yellow wire is for my mute / un-mute amp function.

4, Solder the wire from the low power (red) led to the strip board.


5. Stick the strip board to the case in-between the power booster and the amp with a bit of glue and put your PiGRRL back together.

6. Download the program from github.com/martinohanlon/grrl-bat-monitor
cd ~
git clone https://github.com/martinohanlon/grrl-bat-monitor
7. Make the program run at boot by editing /etc/rc.local
sudo nano /etc/rc.local
Scroll down and add the command under '/usr/local/bin/retrogame &' but before 'exit 0':
/home/pi/grrl-bat-monitor/grrl_bat_mon &
8. Reboot and test!

Sunday, 10 January 2016

Pocket PiGRRL - adding a mute

I recently made myself a Pocket PiGRRL based on Adafruit's tutorial - it really is a great machine and a brilliant learning experience.


There was one aspect though that I didn't like, the speaker hisses, not particularly loud and when your playing a game its not that noticeable but if you have got the volume down it is really annoying. Its all down the Raspberry Pi's noisy analogue audio out so there isn't a great deal you can do to clean it up.

My solution was to add the ability to shutdown the amp, effectively muting it when I didn't want any sound.

The PAM8302A amp breakout board has a shutdown (SD) pin which when a ground (logic zero) is connected it puts the amp into idle mode, muting the amp and reducing power consumption.


I soldered a piece of wire between GPIO 26 on the bottom of the A+ and the shutdown pin on the PAM8302A ampl.

Note - a few people have noted in the comments that on other models of PiGrrl (such as the Pi Grrl 2) GPIO 26 is already used and have suggested changing this to GPIO 7.



This gave me a way of triggering the shutdown pin next I needed some software and a way of running it.

Using this tutorial as the basis I added an 'Apps' tab to emulation station where I could run 2 scripts to mute and unmute the amp.

If you want to configure your Pocket PiGRRL to do the same, you can follow the instructions below.

Download and install wiring pi
The software uses wiringpi's gpio command line utility, so download and install it.

Configure emulation station
Copy the emulation station default settings file to the pi user's emulation station configuration:
cp /etc/emulationstation/es_systems.cfg ~/.emulationstation/
Add the 'apps' tab to the settings file:
nano ~/.emulationstation/es_systems.cfg
Scroll down to the bottom and add the following before the </systemList> text:
  <system>
    <fullname>Applications</fullname>
    <name>Apps</name>
    <path>~/RetroPie/roms/apps</path>
    <extension>.sh .SH .py .PY</extension>
    <command>%ROM%</command>
    <platform>apps</platform>
    <theme>esconfig</theme>
  </system>
Save and exit using Ctrl X.

Make a directory in roms to hold the scripts:
mkdir ~/RetroPie/roms/apps
Create a script to mute the amp by setting GPIO 26 to low (0):
nano ~/RetroPie/roms/apps/mute_amp.sh

gpio -g mode 26 out
gpio -g write 26 0
And an unmute script:
nano ~/RetroPie/roms/apps/unmute_amp.sh

gpio -g mode 26 out
gpio -g write 26 1
Make the scripts executable:
chmod +x ~/RetroPie/roms/apps/mute_amp.sh
chmod +x ~/RetroPie/roms/apps/unmute_amp.sh
You can now test the scripts by running them:
~/RetroPie/roms/apps/mute_amp.sh
~/RetroPie/roms/apps/unmute_amp.sh
Reboot and the Apps tab will appear in emulation station with 2 options to mute and unmute the amp.

I wanted the amp to be muted by default, so I added the script to be run at boot by editing /etc/rc.local:
sudo nano /etc/rc.local
Scroll down and add the unmute command under '/usr/local/bin/retrogame &' but before 'exit 0':
/home/pi/RetroPie/roms/apps/mute_amp.sh &

Tuesday, 10 November 2015

Raspberry Pi PiAware Aircraft Radar

After creating the PiAware Flight Indicator LED I was keen to see what else I could do with the aircraft data my PiAware setup was retrieving for me.

I thought I would see if I could make an 'old fashioned' radar to show what aircraft were being picked up so I could have my own desk based radar.



I found an example of a radar written in pygame, which became the basis of my code (although I am pretty sure the original author wouldn't recognise it now) and created a radar class.

I plugged in the GPS coordinates of the aircraft using the PiAware flight data class I created to produce a pretty swanky, even if I say so myself, radar of all the aircraft I am picking up signals from.

Setup PiAware
If you want to have a go, first you need to setup a PiAware server to receive data - you don't need a lot of equipment and its really easy to do.

Download my project
The code is on github at github.com/martinohanlon/PiAwareRadar.
git clone https://github.com/martinohanlon/PiAwareRadar
Run the program
The program expects a number of command line parameters, the mandatory being the latitude and longitude of your PiAware server, which will be the centre of the radar.
cd PiAwareRadar/piawareradar
python3 piawareradar.py mylat mylon
You can set other parameters for the IP address of the PiAware server, if your radar is running on a different machine, whether you want it to run full screen and the layout of your screen (normal or touch).

Usage
    usage: piawareradar.py [-h] [--piawareip PIAWAREIP] [--screen SCREEN] [--fullscreen] lat lon
    
    PiAware Flight Radar
    
    positional arguments:
      lat                   The latitude of the receiver
      lon                   The longitude of the receiver

    optional arguments:
      -h, --help            show this help message and exit
      --piawareip PIAWAREIP The ip address of the piaware server
      --screen SCREEN       The screen config to use [normal / touch]
      --fullscreen          Fullscreen radar
The plus and minus buttons in the top right allow you to zoom in and out, if you click on a dot, the data about that flight will be display in the bottom right hand corner.


Sunday, 4 October 2015

PiAware - Aircraft Overhead Indicator LED

I've been using the PiAware software to track aircraft flying near me and I liked the idea of turning an LED on when a plane was overhead (or at least near by!).


The first step was creating a way of reading data from PiAware using Python 3, so I created a module called flightdata.py.

Once I had the data it was simply a case of looping through each of the aircraft signals found, calculating the distance between my gps co-ordinates and the gps position of the aircraft. If the distance was less than 10km I turned the led on!

I 'reused' the code to calculate the distance between 2 GPS co-ords from codecodex.

Setting up

You'll need PiAware installed on your Raspberry Pi, up and running and tracking aircraft.

You'll need an LED, appropriate resistor, breadboard and a couple of Male/Female jumper cables to connect it togther.

The LED is connected to ground and pin 17 with the resistor in between.

Install
cd ~
git clone https://github.com/martinohanlon/flightlight
Usage
Launch the flightlight program passing the latitude (lat) and longitude (lon) of your PiAware station (use www.whatsmygps.com to find your gps location) and the range that should be used to detect if an aircraft is overhead.
usage: flightlight.py [-h] lat lon range
e.g. using GPS coords of 52.4539, -1.7481 (Birmingham, UK Airport) with a range of 10km
cd ~/flightlight/flightlight
sudo python3 flightlight.py 52.4539 -1.7481 10
Code
import RPi.GPIO as GPIO
import argparse

from flightdata import FlightData
from haversine import points2distance
from time import sleep

#pin of the LED to light
LEDPIN = 17

class LED():
    def __init__(self, ledPin):
        self.ledPin = ledPin
        GPIO.setup(ledPin, GPIO.OUT)

    def on(self):
        GPIO.output(self.ledPin, True)

    def off(self):
        GPIO.output(self.ledPin, False)

#read command line options
parser = argparse.ArgumentParser(description="PiAware Flight Light")
parser.add_argument("lat", type=float, help="The latitude of the receiver")
parser.add_argument("lon", type=float, help="The longitude of the receiver")
parser.add_argument("range", type=int, help="The range in km for how close an aircraft should be to turn on the led")
args = parser.parse_args()

#get the flight data
myflights = FlightData()

#set GPIO mode
GPIO.setmode(GPIO.BCM)

try:

    #create LED
    led = LED(LEDPIN)

    #loop forever
    while True:
        
        plane_in_range = False

        #loop through the aircraft and see if one is in range
        for aircraft in myflights.aircraft:
            if aircraft.validposition == 1:
                startpos = ((args.lat, 0, 0), (args.lon, 0, 0))
                endpos = ((aircraft.lat, 0, 0), (aircraft.lon, 0, 0))
                distance = points2distance(startpos, endpos)
                #debug
                #print(distance)
                if distance <= args.range:
                    plane_in_range = True

        #turn the led on / off
        if plane_in_range:
            led.on()
            #print("on")
        else:
            led.off()
            #print("off")
            
        sleep(1)

        #refresh the data
        myflights.refresh()

finally:
    #tidy up GPIO
    GPIO.cleanup()

Sunday, 27 September 2015

Read PiAware Flight Data with Python

I have been using Piaware for a couple of weeks and I like the idea of being able to read the signals from aircraft just using simple equipment.


I wanted to use this information to do interesting 'stuff', I'm imagining home built radars and led's which flash, but before I could do anything cool I needed to find a way of getting to this data using Python.

Its possible to read data directly from the dump1090 program using tcp sockets, but the data is a raw stream and it seemed like too much work (I'm all for simplicity)!

The Piaware install also comes with a web front end so you can see the data you are receiving [http://localhost:8080], you can also get to the json data [http://localhost:8080/data.json] which is feeding this web page and this looked like a much easier way of getting the data out.

I create a small Python 3 class called FlightData [link to github flightdata.py] which reads the data from the web page and parses it into an object.

You can install it by cloning the flightdata github repository and copying the flightdata.py file to your project (if there is sufficient interest I'll make it into an 'installable' module):
git clone https://github.com/martinohanlon/flightdata
cp ./flightdata/fightdata.py ./myprojectpath
You can test it by running the flightdata.py program file:
python3 flightdata.py
Below is a sample program which shows how to use it:
from flightdata import FlightData
from time import sleep

myflights = FlightData()
while True:
    #loop through each aircraft found
    for aircraft in myflights.aircraft:
   
        #read the aircraft data
        print(aircraft.hex)
        print(aircraft.squawk)
        print(aircraft.flight)
        print(aircraft.lat)
        print(aircraft.lon)
        print(aircraft.validposition)
        print(aircraft.altitude)
        print(aircraft.vert_rate)
        print(aircraft.track)
        print(aircraft.validtrack)
        print(aircraft.speed)
        print(aircraft.messages)
        print(aircraft.seen)
        print(aircraft.mlat)
   
   sleep(1)

   #refresh the flight data
   myflights.refresh()

Wednesday, 24 June 2015

Raspberry Pi CPU Temperature

For a project I am working on I needed a really quick way of reading the Raspberry Pi's CPU temperature, most of the solutions I found ran the command line
vcgencmd measure_temp
... via Python's subprocess function and parsed the result.

This just wasn't quick enough for my project as I needed something which would gather lots of data, not just the cpu temperature every second, so I pulled together my own module and class which reads the data directly from /sys/class/thermal/thermal_zone0/temp.

You can find the module at github.com/martinohanlon/CPUTemp, but to use it, just add the following CPUTemp class to your code:
class CPUTemp:
    def __init__(self, tempfilename = "/sys/class/thermal/thermal_zone0/temp"):
        self.tempfilename = tempfilename

    def __enter__(self):
        self.open()
        return self

    def open(self):
        self.tempfile = open(self.tempfilename, "r")
    
    def read(self):
        self.tempfile.seek(0)
        return self.tempfile.read().rstrip()

    def get_temperature(self):
        return self.get_temperature_in_c()

    def get_temperature_in_c(self):
        tempraw = self.read()
        return float(tempraw[:-3] + "." + tempraw[-3:])

    def get_temperature_in_f(self):
        return self.convert_c_to_f(self.get_temperature_in_c())
    
    def convert_c_to_f(self, c):
        return c * 9.0 / 5.0 + 32.0

    def __exit__(self, type, value, traceback):
        self.close()
            
    def close(self):
        self.tempfile.close()
Then to read the temperature use:
with CPUTemp() as cpu_temp:
    print("{} C".format(cpu_temp.get_temperature()))
    print("{} F".format(cpu_temp.get_temperature_in_f()))

Sunday, 21 June 2015

Astro Pi Snake Game

I recently made Snake for the Astro Pi, just for fun really, but I am putting it online as I would like to see what others can do with it - it is literally screaming out to be hacked and improved.

I've given some ideas and tips but there is much more than could be done.

Anyone budding coders up for the challenge?


I started with the code I originally wrote for Minecraft - Snake and made the changes so that rather than creating blocks in Minecraft it turned on leds and used the joystick to control the motion. I was also surprised by how much I improved the overall structure of the code, it wasn't poorly coded it was just a bit untidy.

To try it yourself, get the code from github.com/martinohanlon/AstroPiSnake and run it, by opening a terminal and using the commands:
git clone https://github.com/martinohanlon/AstroPiSnake
cd AstroPiSnake
sudo python astropisnake.py
What else could you do? How would you improve it? Can you improve it?

These are some of the ideas I thought of:
  1. Introduce levels, the better the player does, the faster the snake should go. The speed of the snake is set by the sleep in the startGame() method.
  2. The original game had more than one apple. The apple property would have to be changed to a list and the move() method would have to check if one of many apples had been eaten.
  3. Power ups - special apples which appear for a short amount of time and if you eat them you can go across the screen i.e. exiting the screen on the left would make you appear on the right.
Let me know how you get on - perhaps they'll be a prize!