The Sense HAT is an add-on board for the Raspberry Pi, made especially for the Astro Pi competition. The board adds the ability to sense all kinds of things, and to output information using a built-in LED matrix. You can find out more about what the Sense HAT can do by following the Astro Pi Guide, which will show you how to connect and test your Sense HAT. It also has some helpful explanations and examples of what the different inputs and outputs can do.
Once you are set up and have run your first program using the guide, you can begin to experiment further using this worksheet. If you don't have access to a Sense HAT, then you can use the emulator available on trinket.io, or the embeded trinkets in this worksheet.
Start by opening Python 3 from the main menu.
When following the guide, you will have written a sample program which scrolls text across the LED matrix. The program contains two crucial lines, which import the Sense HAT software and create a sense
object which represents the Sense HAT.
from sense_hat import SenseHat
sense = SenseHat()
The third line is the one that makes the Sense HAT do something:
sense.show_message("Hello my name is Tim Peake")
You have probably already discovered that you can easily change the message to your own text, but there are more things that we can do.
-
We can expand the
sense.show_message
command to include some extra parameters which will change the behaviour of the message.Parameter Effect scroll_speed The scroll_speed
parameter affects how quickly the text moves on the screen. The default value is 0.1. The bigger the number, the slower the speedtext_colour The text_colour
parameter alters the colour of the text and is specified using three values for red, green, blue. Each value can be between 0 and 255, so (255,0,255) would be red + blue = purpleback_colour The back_colour
parameter alters the colour of the background and works in the same way as thetext_colour
So this program would display the text
Astro Pi is awesome!!
more slowly, with the text in yellow (255, 255, 0) and the background in blue (0, 0, 255):<iframe src="https://trinket.io/embed/python/d62aa7f30a" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>from sense_hat import SenseHat sense = SenseHat() yellow = (255, 255, 0) blue = (0, 0, 255) message = "Astro Pi is awesome!!" speed = 0.05 sense.show_message(message, speed, text_colour=yellow, back_colour=blue)
You could also make the message repeat using a while loop like this:
<iframe src="https://trinket.io/embed/python/f833a60218" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>from sense_hat import SenseHat sense = SenseHat() yellow = (255, 255, 0) blue = (0, 0, 255) message = "Astro Pi is awesome!!" speed = 0.05 while True: sense.show_message(message, speed, text_colour=yellow, back_colour=blue)
-
Click File -- Save As, give your program a name e.g.
loop_text.py
, then press F5 to run. -
The LED matrix can also display a single character, rather than an entire message, using the
sense.show_letter
function which also has some optional parameters.Parameter Effect scroll_speed The scroll_speed
parameter affects how quickly the text moves on the screen. The default value is 0.1. The bigger the number, the slower the speedtext_colour The text_colour
parameter alters the colour of the text and is specified as three values for red, green, blue. Each value can be between 0 and 255, so (255, 0, 255) would be red + blue = purpleback_colour The back_colour
parameter alters the colour of the background and is specified as three values for red, green, blue. Each value can be between 0 and 255, so (255, 0, 255) would be red + blue = purpleSo this program would display a single red "J":
from sense_hat import SenseHat sense = SenseHat() red = (255, 0, 0) sense.show_letter("J", red)
And this program would add the sleep function to display letters separated by a brief pause:
from sense_hat import SenseHat from time import sleep sense = SenseHat() red = (255, 0, 0) blue = (0, 0, 255) green = (0, 255, 0) black = (0, 0, 0) white = (255, 255, 255) sense.show_letter("O", red) sleep(1) sense.show_letter("M", blue) sleep(1) sense.show_letter("G", green) sleep(1) sense.show_letter("!", black, white) sleep(1) sense.clear()
Click File and Save As, give your program a name eg
<iframe src="https://trinket.io/embed/python/ccb58a3d9d" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>omg.py
, then pressF5
to run.For added interest you could use a random number generator to choose a number between 0 and 255 for the colours:
from sense_hat import SenseHat from time import sleep from random import randint sense = SenseHat() r = randint(0,255) sense.show_letter("O", (r, 0, 0)) sleep(1) r = randint(0,255) sense.show_letter("M", (0, 0, r)) sleep(1) r = randint(0,255) sense.show_letter("G", (0, r, 0)) sleep(1) sense.show_letter("!", (0, 0, 0), (255, 255, 255)) sleep(1) sense.clear()
-
Click File and Save As, give your program a name eg
random_omg.py
, then pressF5
to run.In both these programs the
<iframe src="https://trinket.io/embed/python/45b0f19b65" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>sense.clear()
method has been used at the end to clear the matrix.
- Could you use the ideas used so far to tell a joke via the LED screen?
- All the examples so far could be made shorter, while still achieving the same thing. Can you find ways to make these shorter and more efficient?
- How would you choose a totally random colour, rather than just a random shade of a colour?
- If your Sense HAT is connected to the internet, you could use a Twitter library to make it display incoming tweets!
The LED matrix can display more than just text! We can control each LED individually to create an image. We can accomplish this in a couple of ways.
-
The first approach is to set pixels (LEDs) individually; we can do this using the
sense.set_pixel()
method. First, we need to be clear about how we describe each pixel.The Sense HAT uses a coordinate system like the one shown below; crucially the numbering begins at 0, not 1. Also, the origin is in the top left rather than the bottom left as you may be used to.
- the blue pixel is at coordinates (0, 2)
- the red pixel is at coordinates (7, 4)
To replicate the above diagram you would enter a program like this:
<iframe src="https://trinket.io/embed/python/9a4266d360" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>from sense_hat import SenseHat sense = SenseHat() sense.set_pixel(0, 2, (0, 0, 255)) sense.set_pixel(7, 4, (255, 0, 0))
Can you guess what the following code creates? Have a go at editing the code.
from sense_hat import SenseHat sense = SenseHat() sense.set_pixel(2, 2, (0, 0, 255)) sense.set_pixel(4, 2, (0, 0, 255)) sense.set_pixel(3, 4, (100, 0, 0)) sense.set_pixel(1, 5, (255, 0, 0)) sense.set_pixel(2, 6, (255, 0, 0)) sense.set_pixel(3, 6, (255, 0, 0)) sense.set_pixel(4, 6, (255, 0, 0)) sense.set_pixel(5, 5, (255, 0, 0))
-
Click File and Save As, give your program a name e.g.
simple_image.py
, then pressF5
to run. -
Setting pixels individually can work brilliantly, but it gets rather complex when you want to set more pixels. There is another method which can set all the pixels in one go called
sense.set_pixels
. Its use is quite straightforward; we just give a list of colour values for each pixel in the matrix.We could enter something like...
sense.set_pixels([(255, 0, 0), (255, 0, 0), (255, 0, 0), (255, 0, 0),......])
...but this would take ages and be really complex.
Instead, you can use some variables to define your colour palette (in this example, we're using the seven colours of the rainbow):
r = (255, 0, 0) o = (255, 127, 0) y = (255, 255, 0) g = (0, 255, 0) b = (0, 0, 255) i = (75, 0, 130) v = (159, 0, 255) e = (0, 0, 0) # e stands for empty/black
We can then describe our matrix by creating a 2D list of colour names:
image = [ e,e,e,e,e,e,e,e, e,e,e,r,r,e,e,e, e,r,r,o,o,r,r,e, r,o,o,y,y,o,o,r, o,y,y,g,g,y,y,o, y,g,g,b,b,g,g,y, b,b,b,i,i,b,b,b, b,i,i,v,v,i,i,b ]
We then give the
image
list to thesense.set_pixels
method and draw the image. The finished program would look like this:from sense_hat import SenseHat sense = SenseHat() r = (255, 0, 0) o = (255, 127, 0) y = (255, 255, 0) g = (0, 255, 0) b = (0, 0, 255) i = (75, 0, 130) v = (159, 0, 255) e = (0, 0, 0) image = [ e,e,e,e,e,e,e,e, e,e,e,r,r,e,e,e, e,r,r,o,o,r,r,e, r,o,o,y,y,o,o,r, o,y,y,g,g,y,y,o, y,g,g,b,b,g,g,y, b,b,b,i,i,b,b,b, b,i,i,v,v,i,i,b ] sense.set_pixels(image)
-
Click File and Save As, give your program a name e.g.
rainbow.py
, then pressF5
to run.You should have a beautiful rainbow displayed on your LED matrix.
<iframe src="https://trinket.io/embed/python/8f36929035" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>
- Now that you can create images on your LED matrix in two different ways, try creating your own images or sprites.
- Can you alternate between images to create an animation? Check out this Geek Gurl Diaries video for some inspiration.
So far, all our text and images have appeared the same way up, assuming that the HDMI port is at the bottom. However, this may not always be the case (especially in space) so you may want to change the orientation of the matrix. To do this, you can use the sense.set_rotation()
method and inside the brackets enter one of four angles (0, 90, 180, 270).
To rotate your screen by 180 degrees you'd use this line:
sense.set_rotation(180)
-
When used in the rainbow program it would look like this:
from sense_hat import SenseHat sense = SenseHat() r = (255, 0, 0) o = (255, 127, 0) y = (255, 255, 0) g = (0, 255, 0) b = (0, 0, 255) i = (75, 0, 130) v = (159, 0, 255) e = (0, 0, 0) image = [ e,e,e,e,e,e,e,e, e,e,e,r,r,e,e,e, e,r,r,o,o,r,r,e, r,o,o,y,y,o,o,r, o,y,y,g,g,y,y,o, y,g,g,b,b,g,g,y, b,b,b,i,i,b,b,b, b,i,i,v,v,i,i,b ] sense.set_pixels(image) sense.set_rotation(180)
-
Click File and Save As, give your program a name e.g.
rainbow_flip.py
, then pressF5
to run. -
You could also create spinning text using a for loop:
from sense_hat import SenseHat from time import sleep sense = SenseHat() sense.show_letter("J") angles = [0, 90, 180, 270, 0, 90, 180, 270] for r in angles: sense.set_rotation(r) sleep(0.5)
This program displays the letter "J" and then sets the rotation to each value in the angles list with a 0.5 second pause.
-
Click File and Save As, give your program a name e.g.
<iframe src="https://trinket.io/embed/python/2f48e31b56" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>spinning_j.py
, then pressF5
to run. -
You can also flip the image on the screen, either horizontally or vertically, using these lines:
sense.flip_h()
or
sense.flip_v()
With this example you could create a simple animation by flipping the image repeatedly:
from sense_hat import SenseHat from time import sleep sense = SenseHat() w = (150, 150, 150) b = (0, 0, 255) e = (0, 0, 0) image = [ e,e,e,e,e,e,e,e, e,e,e,e,e,e,e,e, w,w,w,e,e,w,w,w, w,w,b,e,e,w,w,b, w,w,w,e,e,w,w,w, e,e,e,e,e,e,e,e, e,e,e,e,e,e,e,e, e,e,e,e,e,e,e,e ] sense.set_pixels(image) while True: sleep(1) sense.flip_h()
-
Click File and Save As, give your program a name e.g.
eyes.py
, then pressF5
to run.
- Create a spinning image, using one of the drawing techniques shown already, and then use the
sense.set_rotation
method to make it spin. - Using what you've done so far, you should be able to make an electronic die like the one shown here:
This die makes use of:
- Displaying text
- Timing
- Setting rotation
- Random numbers
- Variables
The Sense HAT has a set of environmental sensors for detecting the conditions around it. It can detect:
- Pressure
- Temperature
- Humidity
We can collect these readings using three simple methods:
sense.get_temperature()
- this will return the temperature in Celsius.sense.get_pressure()
- this will return the pressure in millibars.sense.get_humidity()
- this will return the humidity as a percentage.
-
Using these, we could create a simple scrolling text display which could keep people informed about current conditions.
from sense_hat import SenseHat sense = SenseHat() while True: t = sense.get_temperature() p = sense.get_pressure() h = sense.get_humidity() t = round(t, 1) p = round(p, 1) h = round(h, 1) msg = "Temperature = {0}, Pressure = {1}, Humidity = {2}".format(t,p,h) sense.show_message(msg, scroll_speed=0.05)
-
Click File and Save As, give your program a name e.g.
<iframe src="https://trinket.io/embed/python/a246815131" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>env.py
, then pressF5
to run. -
You could now use some colour to let the astronauts know whether conditions are within sensible ranges.
According to some online documentation, the International Space Station maintains these conditions at the following levels:
- Temperature (18.3 - 26.7 Celsius)
- Pressure (979 - 1027 millibars)
- Humidity (around 60%)
You could use an
if
statement in your code to check these conditions, and set a background colour for the scroll:if t > 18.3 and t < 26.7: bg = (0, 100, 0) # green else: bg = (100, 0, 0) # red
Your complete program would look like this:
from sense_hat import SenseHat sense = SenseHat() while True: t = sense.get_temperature() p = sense.get_pressure() h = sense.get_humidity() t = round(t, 1) p = round(p, 1) h = round(h, 1) if t > 18.3 and t < 26.7: bg = (0, 100, 0) # green else: bg = (100, 0, 0) # red msg = "Temperature = {0}, Pressure = {1}, Humidity - {2}".format(t, p, h) sense.show_message(msg, scroll_speed=0.05, back_colour=bg)
-
Click File and Save As, give your program a name e.g.
<iframe src="https://trinket.io/embed/python/2f03745830" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>scrolling_env.py
, then pressF5
to run.
- Currently, the scrolling program only warns about abnormal temperature. Can you add the same behaviour for pressure and humidity?
- You could create a simple graphical thermometer which outputs different colours or patterns depending on the temperature.
- If you haven't done so already, experiment with a bottle and the pressure sensor.
The Sense HAT has a set of sensors that can detect movement. It has an IMU (inertial measurement unit) chip which includes:
- A gyroscope (for detecting which way up the board is)
- An accelerometer (for detecting movement)
- A magnetometer (for detecting magnetic fields)
Before you start experimenting with motion sensing, it's important to understand three key terms covered in the guide and in this video.
The three axes uses to describe motion are:
- Pitch (like a plane taking off)
- Roll (the plane doing a victory roll)
- Yaw (imagine steering the plane like a car)
You can find out the orientation of the Sense HAT using the sense.get_orientation()
method:
orientation = sense.get_orientation()
pitch = orientation['pitch']
roll = orientation['roll']
yaw = orientation['yaw']
This would get the three orientation values (measured in degrees) and store them as the three variables pitch
, roll
and yaw
.
-
You can explore these values with a simple program:
<iframe src="https://trinket.io/embed/python/883c34059d" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>from sense_hat import SenseHat sense = SenseHat() while True: orientation = sense.get_orientation() pitch = orientation['pitch'] roll = orientation['roll'] yaw = orientation['yaw'] print("pitch={0}, roll={1}, yaw={2}".format(pitch, roll, yaw))
Note: When using the movement sensors it is important to poll them often in a tight loop. If you poll them too slowly, for example with
time.sleep(0.5)
in your loop, you will see strange results. This is because the code behind needs lots of measurements in order to successfully combine the data coming from the gyroscope, accelerometer and magnetometer. -
You can click and drag around the Sense HAT in the emulator to see the values change.
-
Another way to detect orientation is to use the
sense.get_accelerometer_raw()
method which tells you the amount of g-force acting on each axis. If any axis has ±1g then you know that axis is pointing downwards.In this example, the amount of gravitational acceleration for each axis (x, y, and z) is extracted and is then rounded to the nearest whole number:
<iframe src="https://trinket.io/embed/python/f714d301d3" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>from sense_hat import SenseHat sense = SenseHat() while True: acceleration = sense.get_accelerometer_raw() x = acceleration['x'] y = acceleration['y'] z = acceleration['z'] x=round(x, 0) y=round(y, 0) z=round(z, 0) print("x={0}, y={1}, z={2}".format(x, y, z))
As you rotate the Sense HAT, you should see the values for x and y change between -1 and 1. If you place the Pi flat or turn it upside down, the z axis will be 1 and then -1.
-
If we know which way round the Raspberry Pi is, then we can use that information to set the orientation of the LED matrix. First you would display something on the matrix, then continually check which way round the board is, and use that to update the orientation of the display.
<iframe src="https://trinket.io/embed/python/ecd677033b" width="100%" height="600" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>from sense_hat import SenseHat sense = SenseHat() sense.show_letter("J") while True: x = sense.get_accelerometer_raw()['x'] y = sense.get_accelerometer_raw()['y'] z = sense.get_accelerometer_raw()['z'] x = round(x, 0) y = round(y, 0) if x == -1: sense.set_rotation(180) elif y == 1: sense.set_rotation(90) elif y == -1: sense.set_rotation(270) else: sense.set_rotation(0)
In this program you are using an
if, elif, else
structure to check which way round the Raspberry Pi is. Theif
andelif
test three of the orientations, and if the orientation doesn't match any of these then the program assumes it is the "right" way round. By using theelse
statement we also catch all those other situations, like when the board is at 45 degrees or sitting level. -
If the board is only rotated, it will only experience 1 g of acceleration in any direction; if we were to shake it, the sensor would experience more than 1 g. We could then detect that rapid motion and respond. For this program we will introduce the
abs()
function which is not specific to the Sense HAT library and is part of standard Python.abs()
gives us the size of a value and ignores whether the value is positive or negative. This is helpful as we don't care which direction the sensor is being shaken, just that it is shaken.from sense_hat import SenseHat sense = SenseHat() red = (255, 0, 0) while True: acceleration = sense.get_accelerometer_raw() x = acceleration['x'] y = acceleration['y'] z = acceleration['z'] x = abs(x) y = abs(y) z = abs(z) if x > 1 or y > 1 or z > 1: sense.show_letter("!", red) else: sense.clear()
-
This is a little tricky to emulate, so you should try this one using IDLE and a real Sense HAT. Click File and Save As, give your program a name e.g.
shake.py
, then pressF5
to run.You might find this is quite sensitive, but you could change the value from 1 to a higher number.
- You could write a program that displays an arrow (or other symbol) on screen; this symbol could be used to point to which way is down. This way, the astronauts (in low gravity) always know where the Earth is.
- You could improve the die program from earlier in this activity, so that shaking the Pi triggers the roll of the die.
- You could use the accelerometer to sense small movements; this could form part of a game, alarm system, or even an earthquake sensor.
Now that you've explored most of the features of the Sense HAT, you could combine them to create a project. Here's an example reaction game, which could be used by the astronauts to test their reflexes.
The game will display an arrow on the LED matrix and select a random orientation for it. The player must rotate the board to match the arrow. If they match it in time the arrow turns green and their score increases; if not their arrow turns red and the game ends, telling them their score. The game keeps showing arrows in new orientations until the player loses, and each turn gets faster.
This idea combines:
- Showing messages and images on the LED matrix
- Setting and detecting the orientation
- Use of variables, randomisation, iteration, and selection
As this is more complicated than previous programs it's worth planning out the steps involved in pseudocode.
import the required libraries (sense_hat, time, random)
create a sense objectSet up the colours needed
Create three different arrows (white, green, red)
Set a variable pause to 3 (the initial time between turns)
Set variables score and angle to 0
Create a variable called play set toTrue
(this will be used to stop the game later)Begin a loop which continues while
play == True
Set a new random angle (use random.choice() method)
Show the white arrow and sleep for current pause length
Check whether orientation matches the arrow
---if it does then add a point and turn the arrow green
---otherwise set play toFalse
and turn the arrow red
Shorten the pause duration slightly
Pause before the next arrowWhen loop is exited, display a message with the score
If you turned this into Python it could look like this:
from sense_hat import SenseHat
from time import sleep
from random import choice
sense = SenseHat()
# set up the colours (white, green, red, empty)
w = (150, 150, 150)
g = (0, 255, 0)
r = (255, 0, 0)
e = (0, 0, 0)
# create images for three different coloured arrows
arrow = [
e,e,e,w,w,e,e,e,
e,e,w,w,w,w,e,e,
e,w,e,w,w,e,w,e,
w,e,e,w,w,e,e,w,
e,e,e,w,w,e,e,e,
e,e,e,w,w,e,e,e,
e,e,e,w,w,e,e,e,
e,e,e,w,w,e,e,e
]
arrow_red = [
e,e,e,r,r,e,e,e,
e,e,r,r,r,r,e,e,
e,r,e,r,r,e,r,e,
r,e,e,r,r,e,e,r,
e,e,e,r,r,e,e,e,
e,e,e,r,r,e,e,e,
e,e,e,r,r,e,e,e,
e,e,e,r,r,e,e,e
]
arrow_green = [
e,e,e,g,g,e,e,e,
e,e,g,g,g,g,e,e,
e,g,e,g,g,e,g,e,
g,e,e,g,g,e,e,g,
e,e,e,g,g,e,e,e,
e,e,e,g,g,e,e,e,
e,e,e,g,g,e,e,e,
e,e,e,g,g,e,e,e
]
pause = 3
score = 0
angle = 0
play = True
sense.show_message("Keep the arrow pointing up", scroll_speed=0.05, text_colour=[100,100,100])
while play:
last_angle = angle
while angle == last_angle:
angle = choice([0, 90, 180, 270])
sense.set_rotation(angle)
sense.set_pixels(arrow)
sleep(pause)
acceleration = sense.get_accelerometer_raw()
x = acceleration['x']
y = acceleration['y']
z = acceleration['z']
x = round(x, 0)
y = round(y, 0)
print(angle)
print(x)
print(y)
if x == -1 and angle == 180:
sense.set_pixels(arrow_green)
score += 1
elif x == 1 and angle == 0:
sense.set_pixels(arrow_green)
score += 1
elif y == -1 and angle == 90:
sense.set_pixels(arrow_green)
score += 1
elif y == 1 and angle == 270:
sense.set_pixels(arrow_green)
score += 1
else:
sense.set_pixels(arrow_red)
play = False
pause = pause * 0.95
sleep(0.5)
msg = "Your score was %s" % score
sense.show_message(msg, scroll_speed=0.05, text_colour=[100, 100, 100])
Click File -- Save As, give your program a name e.g. reaction_game.py
, then press F5 to run.
There are lots of potential developments for this game:
- Include shake actions as well as orientation.
- Make use of the humidity sensor to detect breath; the player could be prompted to breathe on the board as an action.
- Include more than four directions; players have to hold the Sense HAT at 45 degree angles.
- Now that you have explored the basics of the Sense HAT, you might investigate the Python library itself to see some other functions it offers.
- Try out the 3D Soyuz Demo which allows you to use the Sense HAT to move a virtual 3D model. Why not try creating your own models with Sketchup or Blender?
- Link the the Sense HAT sensor data with minecraft-pi.