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.


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




Tuesday, 20 September 2016

Microbit making Minecraft Earthquakes

In this tutorial you are going to connect your Microbit up to your Raspberry Pi and program them so that when your Microbit is shaken it creates an earthquake in Minecraft.


This is part of a workshop I delivered at PyConUK 2016 - download the complete worksheet.

You are going to connect your micro:bit’s pins to the Raspberry Pi gpio pins using some cables and crocodile clips; programs on the micro@bit and Raspberry Pi will make Steve shake in Minecraft.

Install mu
To put you python program on your Micro:bit you will need the editor mu.

Open a Terminal (Menu > Accessories > Terminal) and type:
sudo apt-get update
sudo apt-get install mu
Angry micro:bit
The first task is to program your micro:bit so it gets angry when it's shaken.
  • Connect your micro:bit to the Raspberry Pi using the USB cable.
  • Open mu to create a new Python program for your micro:bit by clicking on Menu > Programming > mu.
  • Click New and type the following code into the editor.
from microbit import *
while True:
    if accelerometer.current_gesture() == "shake":
        display.show(Image.ANGRY)
    else:
        display.show(Image.HAPPY)
  • Click Flash to put your program on your micro:bit.
When the yellow light on the back of your micro:bit stops flashing your program will run.
You should see a happy face on your micro:bit - until it’s shaken!


Any errors will be scrolled on your micro:bit’s leds; if you get an error check your code carefully.

Pins
Now you need to finish your micro:bit program so that it turns pin 0 on and off when it's shaken
  • Go back to Mu and modify your program so that it turns pins 0 on (1) and off (0) when shaken.
from microbit import *
while True:
    if accelerometer.current_gesture() == "shake":
        display.show(Image.ANGRY)
        pin0.write_digital(1)
    else:
        display.show(Image.HAPPY)
        pin0.write_digital(0)
  • Click Flash to put your program on your micro:bit.
Connect it up
Next you will use a jumper cable and a crocodile clip to connect your micro:bit to the Raspberry Pi.
  • Connect the jumper cable to GPIO17 on the Raspberry Pi
  • Clip the crocodile clip to the end of the jumper cable
  • Clip the other end of the crocodile clip to pin0 on the micro:bit

Shake Steve
You now need to create your Minecraft program to shake Steve when the micro:bit is shaken and pin0 is set to 1.
  • Click Menu > Games > Minecraft: Pi Edition to run the game.
  • Click Start Game, then click Create New (or choose an existing one) to enter a world:
  • Press ESC to go back to the Minecraft menu but leave the game playing.
  • Open Python IDLE by clicking Menu > Programming > Python 3.
  • Use File > New Window to create a new program and save it as ‘mc_micro.py’.
  • Type the following code into the program to import the modules you will need
from mcpi.minecraft import Minecraft
from gpiozero import DigitalInputDevice
from time import sleep
  • Create a connection to Minecraft using the code.
mc = Minecraft.create()
  • Post a message to the chat window.
mc.postToChat("Micromine bitcraft earthquake")
  • Run your program by clicking Run > Run Module.
You should see your message appear in the Minecraft chat window.

Any errors will be displayed in the Python Shell in red.

Update your program to shake Steve, by adding the following code at the bottom of your program.
  • Create a pin which is connected to Pi GPIO 17 and micro:bit pin 0.
pin0 = DigitalInputDevice(17)
  • Create a loop which constants gets Steve’s position.
while True:
    sleep(0.1)
    pos = mc.player.getPos()
  • If pin0 is on (1) it adds 0.5 to Steve’s height (y).
    if pin0.value == 1:
        pos.y = pos.y + 0.5
        mc.player.setPos(pos)
  • Run your program by clicking Run > Run Module.
Shake your micro:bit and Steve will be shaken in Minecraft.

Challenges
Can you change the program so that it creates a more realistic earthquake by adding random values to the x, y, z values.

Complete the worksheet from PyConUK 2016 which includes using the micro:bit buttons to makes blocks disappear and appear.

Friday, 17 June 2016

Mayhem 2 - an open source cave shooter

I recently ported an abandoned version the classic amiga game, Mayhem, to the Raspberry Pi - I did this exclusively so I could play the game with my friend, Lee, using RetroPie.

Since then myself and Lee, an artist for sumo digital, have been modding the game, adding new levels, features and controls - it now really is Mayhem 2.


Mayhem 2 is available for Windows and Raspberry Pi.

I'm hoping there will be lots more features and changes to the game over the coming months, if anyone has any idea's or would like to contribute it would be great to hear from you.

Gameplay

Mayhem 2 is a multiplayer (2 - 4) flight shooter with a really simple objective - destroy your opponents before they destroy you.

Your ship has limited fuel which will run down when you boost, if you run out you will be unable to control your ship, to refuel, land on any flat surface.

You can protect yourself from attack using your shields which will stop all bullets, be careful though your shields run down quickly and you wont be able to boost while your shields are on.

Power -ups are dropped (sometimes) when a player is destroyed (by either crashing or being shot) and when collected will give you a temporary boost such as a triple-shot weapon, better shields or more thrust.


New levels have been added including new ones with no edges, where you can seamlessly fly across the edge of the map.


Options

Levels 1-3 are the original game levels, all other levels are new to Mayhem 2.

DCA are anti spaceship guns which will fire at the player if they get too close.

Wall collision can be turned off for new players to get used to the controls and playing the game.

Controls

Mayhem 2 supports  joystick and keyboard control, joystick controls can be configured via the main menu.

Default joystick controls, assume an "xbox / ps like" joystick:

ControlAction
Stick 1Left / Right
Button 1 (A)Thrust
Button 2 (B)Shield
Button 6 (RB)Fire

If joysticks are connected, they are used as the players controls, if there are less than 4 joysticks connected, keys are used for the rest of the players in order:

KeyLeftRightShieldThrustFire
1zxcvg
2leftrightpad delpad 0pad enter
3bn,ml
4yuoi0

Install

For windows:

Download the zip file from https://github.com/martinohanlon/mayhem/archive/master.zip, open and copy mayhem-master to a folder.

Double click Mayhem2.exe in the mayhem-master folder.

Note - You maybe presented with message saying that the application was stopped from starting as it is unrecognised, click 'more info' and and choose 'run anyway'.

For Raspberry Pi:

Open a terminal and type:
sudo apt-get install liballegro4.4 liballegro4-dev
git clone https://github.com/martinohanlon/mayhem-pi
Run using:
cd mayhem-pi
./start
Code

The full source code is available on GitHub for Windows and Raspberry Pi.

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!