Tuesday, June 4, 2013

Raspberry Pi Model A - Scratch performance with 2013-05-25 Raspbian image

With the new Raspbian 2013-05-25 image Scratch is suppose to be faster due to the work on the Pixman library.
So, I thought I would do a comparison between the new image and the 2012-07-15 image.
With the expectation that Scratch will mainly be used in schools and that these may be kitted out with Model As I did the testing on a Model A, so limited to 256MB of RAM

Video Below:


I download the images and flashed them fresh to 8GB SD cards - same make and model.
Then on boot up expanded the partition.
No updates were done and only a wireless keyboard/mouse USB dongle was plugged into the single USB port.

The Scratch program has 3 sprites + Stop Sprite. Each main sprite has 2 costumes and they move around the screen bouncing off the edge.
1 Sprite (Felix) also increments a global variable by 1 for each step.
This whole thing is repeated 500 times, giving a long enough time for the task to get a fair comparison. Shorter and the difference in speed would have been harder to measure. Longer and I would have fallen asleep.


Raspbian 2012-07-15 Image
Login was rejected a few times before I was allowed it. This is with the default username/password and using the same keyboard as for the 2013-05-25 image so it looks like some USB based issues were resolved since the 2012-07-15 image was released.
Scratch felt sluggish in loading and resizing the program to full screen
To keep it consistent I ran the program full screen. On the 2012-07-15 image full screen did not stretch but just put a 480 x 360 window in the middle of the screen
Test ran in 1m 22s


Raspbian 2013-05-25 Image
Login worked first time every time.
Scratch felt responsive in loading and resizing the program to full screen
Full Screen stretched to the resolution of the screen, so was true full screen experience.
You will see the updating of the Variable is not real time. It looks like variable updates on screen do not happen every time the variable is changed.
Test ran in 0m 51s 
37% faster than the 2012-07-15
An amazing increase in performance purely based on a software update

Raspbian 2013-05-25 Image Turbo Mode (1GHz)
I used raspi-conf to set the the Raspberry Pi to Turbo Mode to see what the maximum performance is.
Login worked first time every time.
Scratch felt really responsive in loading and resizing the program to full screen
Full Screen stretched to the resolution of the screen, so was true full screen experience.

You will see the updating of the Variable is not real time. It looks like variable updates on screen do not happen every time the variable is changed.

Test ran in 0m 33s 
60% faster than the 2012-07-15 (non-Turbo)
35% faster than 2013-05-25 (non-Turbo)

Compared to stock 2012-07-15 image the new 2013-05-25 image with Turbo mode set is nearly 3 times faster in Scratch.
All without a hardware upgrade and with user changeable settings.

The only limitation appears to be the speed at which variable are updated on the screen. The variables are updated in the background it is purely the screen update that is slow.

For most thing I'd expect the variable issue will not be a problem so a fantastic result.

Scratch is now definitely usable on the Raspberry Pi.

Thursday, April 18, 2013

Raspberry Pi, Unipolar Stepper motors, ULN2003 Darlington Pairs, USB Gamepad, Python

For the Raspberry Jam on Sunday I want to bring something that moved and was also interested in getting stepper motors to work with the Raspberry Pi so I decided to build a vehicle using stepper motors to drive the wheels (very slowly) and control it with a USB Gamepad/Joystick

The first thing I had to do was to sort out the sequence of pulses for the Stepper Motors. I did this originally with an Arduino Uno as I wanted to remove as many opportunities for human error as possible and since this would be the first time I would user stepper motors with the Raspberry I thought it best to begin with a platform where I was use to doing I/O.

To operate a stepper motor you send signals to the 4 lines in a set sequence.
For the motors I purchased are 28BYJ48 DC 5V and their sequence is. Yours may be different.

        Line1 Line2 Line3 Line4
Step 1    0     0     1     1  
Step 2    1     0     0     1  
Step 3    1     1     0     0  
Step 4    0     1     1     0  

To get the motor to go in reverse you just run this sequence in reverse order.
Also, strangely if you are doing this using an Arduino the stepper library worked first time for me even though  the sequence is different. But when I tried a second, third or more times it failed unless I changed the sequence.  See here for the details on the sequence in the Arduino Stepper Motor library: http://www.tigoe.net/pcomp/code/circuits/motors/stepper-motors/
This post also has a lot of useful information on stepper motors generally

The first thing that I discovered was that the stepper motors and driver board that was a ULN2003 and some indicator LEDS with the right connector for the motors I bought (Amazon UK / Amazon US - these are available cheaper on eBay) had a slightly different pulse sequence than the examples I found so once I figured that out the motors turned clockwise and anti-clockwise as I expected.

All good, motors and driver board working as expected.

Next to get it wired up to the Raspberry Pi.





Here is an layout using a breadboard and a couple of UNL2003 Darlington Pair ICs. These are the same ICs as on the board so the wiring is the same. The board just makes it a lot easier.

NOTE: I used Fritzing to make the layout and it shows the motor as having 6 wires. There are 6 terminals on  unipolar stepper motor, but the model I bought had the two power lines tied together so only came out to a single wire.  Depending on the unipolar motor you have it may have 5 or 6 wires.



With all the wiring done next it was onto the code.

As I said above the goal was to get the motors controlled by a USB gamepad. The gamepad I used is a Saitek P380 (Amazon UK / Amazon US). I bought mine in PC World for about £10.00 For this I decided to use Python as learning to code properly in Python is one of my 2013 resolutions. Also, Python has a nice library in Raspbian for the GPIO pins and I knew that Pygame which works with Python 2.6x had the ability to read USB gamepads.

After figuring out all the mad stuff to do with getting the motors to work on the Arduino I was delighted that using Python and the GPIO library I got the motor to spin with no real problems.

After a bit of hunting on the Internet I got the code to read the USB gamepad and depending on how you push/pull the analog sticks the motors turned.  Effectively allowing you to drive the vehicle like a tank with independent control for both wheels.

Here is the code I used. It is very simplistic as my hope is that it will be easily understandable by others so it can form the basis of something more interesting. I would expect with a bit of effort even I could reduce the code to about a 3rd of its current size and someone who can code properly could get it even shorter. But for this exercise I wanted to literally show every step in the sequence so it is easy to read, easy to understand and east to adapt.


#!/usr/bin/env python

import os, sys, pygame 
from pygame import locals
import time
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
GPIO.cleanup()

# set the delay between steps
stepDelay = 0.002

# set up motor 1
GPIO.setup(8, GPIO.OUT)
GPIO.setup(16, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)

GPIO.output(8, GPIO.LOW)
GPIO.output(16, GPIO.LOW)
GPIO.output(18, GPIO.LOW)
GPIO.output(22, GPIO.LOW)

# set up motor 2
GPIO.setup(11, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)
GPIO.setup(15, GPIO.OUT)
GPIO.setup(21, GPIO.OUT)

GPIO.output(11, GPIO.HIGH)
GPIO.output(13, GPIO.HIGH)
GPIO.output(15, GPIO.HIGH)
GPIO.output(21, GPIO.HIGH)


os.environ["SDL_VIDEODRIVER"] = "dummy"
pygame.init()

pygame.joystick.init() # main joystick device system

deadZone = 0.6 # make a wide deadzone
m1 = 0 # motor 1 (1 = forward / 2 = backwards)
m2 = 0 # motor 2 (1 = forward / 2 = backwards)
try:
   j = pygame.joystick.Joystick(0) # create a joystick instance
   j.init() # init instance
   print 'Enabled joystick: ' + j.get_name()
except pygame.error:
   print 'no joystick found.'


while 1:
   for e in pygame.event.get(): # iterate over event stack
      if e.type == pygame.locals.JOYAXISMOTION: # Read Analog Joystick Axis
         x1 , y1 = j.get_axis(0), j.get_axis(1) # Left Stick
         y2 , x2 = j.get_axis(2), j.get_axis(3) # Right Stick

         print x1
         print y1
         print x2
         print y2

         if x1 < -1 * deadZone:
             print 'Left Joystick 1'

         if x1 > deadZone:
             print 'Right Joystick 1'

         if y1 <= deadZone and y1 >= -1 * deadZone:
    m1 = 0 # Dont go forward or backwards

         if y1 < -1 * deadZone:
             print 'Up Joystick 1'
             m1 = 1 # go forward
             print m1
             
         if y1 > deadZone:
             print 'Down Joystick 1'
             m1 = 2 # go forward
             print m1

         if y2 <= deadZone and y2 >= -1 * deadZone:
    m2 = 0 # Dont go forward or backwards
              
         if y2 < -1 * deadZone:
             print 'Up Joystick 2'
             m2 = 1

         if y2 > deadZone:
             print 'Down Joystick 2'
             m2 = 2

         if x2 < -1 * deadZone:
            print 'Left Joystick 2'

         if x2 > deadZone:
            print 'Right Joystick 2'

         
   if m1 == 1: # motor 1 go forward
# step 1 motor 1
      GPIO.output(8,GPIO.LOW)
      GPIO.output(16,GPIO.LOW)
      GPIO.output(18,GPIO.HIGH)
      GPIO.output(22,GPIO.HIGH)

   if m2 == 1: # motor 2 go forward
# step 1 motor 2
      GPIO.output(11,GPIO.LOW)
      GPIO.output(13,GPIO.LOW)
      GPIO.output(15,GPIO.HIGH)
      GPIO.output(21,GPIO.HIGH)

   time.sleep(stepDelay)



   if m1 == 1: # motor 1 go forward
# step 2 motor 1
      GPIO.output(8,GPIO.HIGH)
      GPIO.output(16,GPIO.LOW)
      GPIO.output(18,GPIO.LOW)
      GPIO.output(22,GPIO.HIGH)

   if m2 == 1: # motor 2 go forward
# step 2 motor 2
      GPIO.output(11,GPIO.HIGH)
      GPIO.output(13,GPIO.LOW)
      GPIO.output(15,GPIO.LOW)
      GPIO.output(21,GPIO.HIGH)

   time.sleep(stepDelay)

   if m1 == 1: # motor 1 go forward
# step 3 motor 1
      GPIO.output(8,GPIO.HIGH)
      GPIO.output(16,GPIO.HIGH)
      GPIO.output(18,GPIO.LOW)
      GPIO.output(22,GPIO.LOW)

   if m2 == 1: # motor 2 go forward
# step 3 motor 2
      GPIO.output(11,GPIO.HIGH)
      GPIO.output(13,GPIO.HIGH)
      GPIO.output(15,GPIO.LOW)
      GPIO.output(21,GPIO.LOW)

   time.sleep(stepDelay)

   if m1 == 1: # motor 1 go forward
# step 4 motor 1
      GPIO.output(8,GPIO.LOW)
      GPIO.output(16,GPIO.HIGH)
      GPIO.output(18,GPIO.HIGH)
      GPIO.output(22,GPIO.LOW)

   if m2 == 1: # motor 2 go forward
# step 4 motor 2
      GPIO.output(11,GPIO.LOW)
      GPIO.output(13,GPIO.HIGH)
      GPIO.output(15,GPIO.HIGH)
      GPIO.output(21,GPIO.LOW)

   time.sleep(stepDelay)

   if m1 == 2: # motor 1 go reverse
# step 4 motor 1
      GPIO.output(8,GPIO.LOW)
      GPIO.output(16,GPIO.HIGH)
      GPIO.output(18,GPIO.HIGH)
      GPIO.output(22,GPIO.LOW)

   if m2 == 2: # motor 2 go reverse
# step 4 motor 2
      GPIO.output(11,GPIO.LOW)
      GPIO.output(13,GPIO.HIGH)
      GPIO.output(15,GPIO.HIGH)
      GPIO.output(21,GPIO.LOW)

   time.sleep(stepDelay)

   if m1 == 2: # motor 1 go reverse
# step 3 motor 1
      GPIO.output(8,GPIO.HIGH)
      GPIO.output(16,GPIO.HIGH)
      GPIO.output(18,GPIO.LOW)
      GPIO.output(22,GPIO.LOW)

   if m2 == 2: # motor 2 go reverse
# step 3 motor 2
      GPIO.output(11,GPIO.HIGH)
      GPIO.output(13,GPIO.HIGH)
      GPIO.output(15,GPIO.LOW)
      GPIO.output(21,GPIO.LOW)

   time.sleep(stepDelay)

   if m1 == 2: # motor 1 go reverse
# step 2 motor 1
      GPIO.output(8,GPIO.HIGH)
      GPIO.output(16,GPIO.LOW)
      GPIO.output(18,GPIO.LOW)
      GPIO.output(22,GPIO.HIGH)

   if m2 == 2: # motor 2 go reverse
# step 2 motor 2
      GPIO.output(11,GPIO.HIGH)
      GPIO.output(13,GPIO.LOW)
      GPIO.output(15,GPIO.LOW)
      GPIO.output(21,GPIO.HIGH)

   time.sleep(stepDelay)

   if m1 == 2: # motor 1 go reverse
# step 1 motor 1
      GPIO.output(8,GPIO.LOW)
      GPIO.output(16,GPIO.LOW)
      GPIO.output(18,GPIO.HIGH)
      GPIO.output(22,GPIO.HIGH)

   if m2 == 2: # motor 2 go reverse
# step 1 motor 2
      GPIO.output(11,GPIO.LOW)
      GPIO.output(13,GPIO.LOW)
      GPIO.output(15,GPIO.HIGH)
      GPIO.output(21,GPIO.HIGH)

   time.sleep(stepDelay)


Once I put together the physical vehicle using cardboard, Nutella jar lids and some glue I tried it out.
It worked. The main thing that would need to be improved is the wheels.
As the Nutella jar lids are light plastic they wobbled a lot causing them to grind on the cardboard chassis. I used some toothpicks to stop the wheels turning in too much and this for the most part stopped the problem.
Here is a short video of it working.


As you can see I definitely won't be racing this bad boy, but it was great to work with stepper motors, python and pygame as well as upgrade some of my cardboard cutting and shaping skills.


Tuesday, February 26, 2013

Raspberry Pi Minecraft and Python - create lots of blank worlds to mess about in.

I've been playing with Python and Minecraft on the Raspberry Pi.  Using the API included you can write Python scripts to build in Minecraft.
I like to work with a blank world rather than an existing landscape because the landscape may not work or if I make a mistake it can be hard to undo.

So, I wanted to quickly create a bunch of disposable worlds that I can run my very rough scripts in to see where I need to tweak them.

NOTE: for some reason some flowers and things are left behind. It might be an attribute thing...

Code Below:

#!/usr/bin/python

import sys
import mcpi.minecraft as minecraft
import mcpi.block as block

mc = minecraft.Minecraft.create()

mc.postToChat("Clearing ALL")

blockId = block.AIR.id
mc.setBlocks(-127,-127,-127,127,127,127,blockId)

mc.postToChat("create floor")

blockId = block.DIRT.id
mc.setBlocks(-127,0,-127,127,0,127,blockId)


There are no indents so should just work if cut and pasted to a text file and saved.  I used clearall.py

Start Minecraft
Create a World

RUN:
python clearall.py

The whole world is made of AIR with a 'ground' of DIRT is added at Y=0.
This will take a while to run so be patient.

Now you have one blank world.

But that would be slow to do that every time you need a blank world.

Well with a bit of jiggery pokery you don't have to.
The method I used is probably a bit manual but it is faster than coding for new worlds every time.
Maybe somebody brighter than me can automate it.

Step 1. Make sure you can see hidden folders when browsing on your Raspberry Pi.
The got to ~/pi (if your username is pi)
Open .minecraft/games/com.mojang/minecraftWorlds
This is the directory where your saved Worlds are stored.
One per folder. For me the blank world I created was called world

Create a bunch of new directories. I used world-blank1, world-blank2 etc....
Make as many as you want.

Go into the original world directory and copy all the files.
Then paste those files into each of your world-blank directories.
Now you have replicated the blank world you created with the above script too all of these world maps.

If you go back into Minecraft they will be there. Only problem is they will all have the same name. Not ideal when you want to know which ones you have broken and which are still clean.

This is where it gets a bit more low level but still quite simple.

Installed ghex a gui based hex editor with sudo apt-get install ghex

Then for each folder open level.dat using ghex and find where it says world
Modify the text to a different name making sure to keep it the same size.  Maybe B0001.
Save the updated level.dat.
Do this for each of your new directories making sure to use a different name (B0002,B0003,...) .

If you run minecraft you will get a list of Worlds all with different names.  For me the order was still a bit mixed up but I can live with that.

Finally, rather than having to do this every time I created a new directory under com.mojang called backup-blank and copied all my newly made blank world into that directory.

Now I can mess about with the blank worlds. Building all kinds of strange shapes not caring about  it being perfect as I can just delete that world and replace it with a new clean one by just copying the saved worlds in backup-blank  back into the minecraftWorlds directory and start again.

Note: For me I have 20(ish) blank worlds created as I mess up a fair bit and this usually allows me to do one good session without having to copy across or create a new blank world.

Happy Coding.