Showing posts with label sd card. Show all posts
Showing posts with label sd card. Show all posts

Thursday, April 30, 2015

The computer network in space - Part 2


- You're flying! How?
- Python!
( https://xkcd.com/353/ )

Continued


Part one of this series of articles on Team Near Space Circus flight NSC-01 can be found  >here<. It covered the physical layout of the 7 Raspberry Pis, 8 cameras and power system. It left off as we were about to go into the details of the network topology itself.

A satellite or a bowl of spaghetti?

Plan A: 2 Pi in a Pod...


The initial plan was for 2 Raspberry Pi using a serial communication protocol. The Model A+ does not have an ethernet port. Of course a Raspberry Pi 2 has one, and is more powerful, but it also consumes a lot more power than the A+. Our battery pack would have had to be much larger if we went down that path, and that meant significantly heavier. We had a goal of 2Kg for the payload itself, and we were getting close, so serial it was...

Serial communication


DB25 (serial or parallel)
DB9 (serial)

Many early personal computers typically had two interfaces, either built in or available through an expansion card: a serial (either a DB9 or DB25 connector) and a parallel interface (either a DB25 or 36 pins Centronics connector, aka IEE1824). The parallel interface was typically used for printers (and great for DIY DACs with resistor ladders, but that's a story for another time), while the serial port was mostly used for modems or scientific equipment.

RS-232 was introduced in 1962, so it's been around for quite a while now. Many serial connections were actually TTL (5V), such as on the Digital Decwriter III, but it was quite easy to convert them to a +/- voltage range (and even more so since the mid 80s with ICs such as the MAX232). The initial purpose was to connect a DTE (Data Terminal Equipment), a fancy way of saying a computer, and a DCE (Data Communication or Circuit-terminating Equipment).

On a DB9 connector, the pins would be (DB9 male connector pictured):

RxD is Receive Data and TxD is Transmit Data. What if you need to connect two DTE together? You need a Null-Modem adapter or cable. The simplest Null-Modem circuit requires 3 wires (2 if you can guarantee the ground on each end to be the same):

GND(7) to GND(7), TxD(5) to RxD(4), RxD(4) to TxD(5)

Raspberry Pi Header


The Raspberry Pi doesn't have a DB9 or DB25 connector. But if you look at the 40 pin header, notice on the top row, the 4th and 5th pins:
Raspberry Pi Model A+ with pinout as overlay

TXD and RXD. It is a serial port, but it is *not* RS-232 compatible as far as voltage levels are concerned (a 1 is marked by a negative voltage in the range -3V to -15V, and a 0 by a positive voltage in the range +3V to +15V), as it is a TTL interface (a 1 is marked by a positive voltage, either 3V3 or 5V and a 0 is marked by 0V). But that is fine, we are only connecting to another Raspberry Pi.

The primary issue with this serial port is that the Raspbian operating system has a console (TTY) associated it. Meaning that you could connect this to a computer, with the right cable, and see the boot process and get a console prompt, and be able to type commands, as if you had a keyboard and monitor connected directly to the Raspberry Pi.

In our case, however, we want to use the port for something else, our own communication network, so we have to disable this feature. It was once a manual process, and I had written a script to do it, but...

Configuration


sudo raspi-config

The raspi-config now has an option under the Advanced Options selection to disable the serial port console, so we can use it directly:

Advanced -> Serial
Once we have reconfigured the serial port, we can quit raspi-config and reboot. For each compute node, beside disabling the serial port console, we also enabled on the advanced page the SPI and I2C options, and on the main page we configured the time zone, enabled the camera and expanded the filesystem. We also overclocked nodes 1-3 to 800 MHz while nodes 4-6 ran at 700 MHz (so we can measure the impact of overclocking).

Plan B: A Pi cluster

By using a pair of Raspberry Pi, the communication process is pretty straightforward. With two nodes, we can use SLIP or Point to Point Protocol (PPP). Initially, this was the thought for NSC-01 and we would not have to write the code for the network layer.

6 Raspberry Pi cameras
1 Infrared Raspberry Pi camera (pointing down)

But without a functioning camera switcher, the only option was to use one node per camera (each Raspberry Pi has 1 CSI camera connector, even thought the Broadcom chip supports 2 - most cell phones have a front and rear camera). With 7 CSI cameras in total, that meant 7 Raspberry Pi.

Time to dig in our bag of tricks...

Token Ring

4 Node token ring network architecture


As I was going through the options, I started thinking about good old IBM Token Ring (and FDDI). Conceptually, at least. I wasn't going to make something compatible with 802.5, but instead reuse the concept with the serial UART, transmit to receive from one node to the next.

Conceptually, a node is always listening to the serial port. Is this data for me? (if it's a broadcast or if it's addressed to me) If not or if a broadcast, I send it right away to the next node, and process it if it is addressed to me. So let's get into the details.

The diagram


7 Raspberry Pi networked together
In the previous article, one of the pictures clearly showed 7 Raspberry Pi connected together inside the payload. I've included here a fritzing diagram to help in detailing what is going on here.

In the diagram above, I didn't have a Raspberry Pi model A+ for fritzing, so I used the closest thing for our purpose, a Raspberry Pi model B+. Both of them have a 40 pins GPIO header. First row at the top, from left to right has 5V, 5V, GND, TxD and RxD. We'll stop there for right now.

Here I've simply connected node 1 TxD to node 2 RxD (blue wire), node 2 TxD to node 3 RxD (pink), node 3 TxD to node 4 RxD (brown), node 4 TxD to node 5 RxD (cyan), node 5 TxD to node 6 RxD (orange), node 6 TxD to node 7 RxD (yellow) and finally node 7 TxD to node 1 RxD (green), in a nice circular (or ring) fashion.

The GND are not daisy chained from one Pi to the next, because they are all powered by the same source, through their microusb plugs.

Initially, I tried to power the Pi directly from the 5V pin on the GPIO header, but the signal was decoding only partly. By further connecting the 3.3V (that was key, the UART operates at 3V3 so the HIGH signal was not detected reliably when powered with only 5V) and GND together, it made it more reliable, but also more complicated. I reverted back to powering all the 7 nodes using microusb (as detailed in the previous article).

Python


What's great about Raspbian is that not only do we have Python installed, but Python 2.x and 3.x are there, along with many things that are normally optional, such as pygame, pyserial, RPi.GPIO etc. Batteries are quite definitely included with Python on the Raspberry Pi.

Still, for the networking software, we did install two modules from pypi using pip3 (we are using Python 3), docopt and sh.

For the bulk of the development, I used a Raspberry Pi Model B+ connected to my laptop and to a second Raspberry Pi using the serial port and the network switch. That way my laptop could see both Raspberry Pi and I could push new versions of the software using scp.

I also had a "field" version, in case I needed to do last minute changes:

Raspberry Pi sidekick in sidecar configuration (DHCP server on laptop)

Docopt


docopt: helps you to define interface for your command-line app, and automatically generate a parser for it.

For example, in the case of the network controller, token.py, it accepted one argument, the node number. In the future, we'll probably derive the node number from a personality module on the GPIO header, but it was a simple solution that could be done in software, not adding any weight to the payload. The downside was having to keep track of all the microsd cards:

micro SD card transport - For nodes 1 to 7 plus 1 backup


And, so back to docopt, here is how it was used for token.py. At the top of the file, after the shebang line (so the script can be an executable), I included the docstring which defines the interface (lines 2 to 5), which is a required field <address>. This is a simple case, but as cases get more complicated, it really pays off to use docopt.

Then on line 14 I import the docopt module (I group imports alphabetically first by what's included, then a blank line, then again alphabetically I add the imports that have to be installed - just a convention):

1 :  #!/usr/bin/env python3  
2 :  """  
3 :    
4 :  Usage: token.py <address>  
5 :  """  
6 :  from datetime import datetime  
7 :  import logging  
8 :  from logging.handlers import SysLogHandler  
9 :  import RPi.GPIO as gpio  
10:  import serial  
11:  import subprocess  
12:  from time import sleep, time  
13:    
14:  from docopt import docopt  


I then conclude the script by passing the arguments that docopt figured out are there (and validated) in the docstring (__doc__), to the main function:

168:  if __name__ == "__main__":  
169:    arguments = docopt(__doc__)  
170:    main(arguments)  
171:    


And how does one use it in the code itself? In the body of the main function I do the following:

97 :    
98 :  def main(args):  
99 :      my_addr = int(args['<address>'])  
100:




In case an air traveler wants to check our website...

SH

sh: is a full-fledged subprocess replacement for Python 2.6 - 3.4 that allows you to call any program as if it were a function:

`python from sh import ifconfig print ifconfig("eth0") `

Continuing with the code in token.py, you simply import what you need from the shell and sh transparently builds a function for you to call. In this case I needed access to date and rmmod:

1:  #!/usr/bin/env python3  
2:  """  
3:    
4:  Usage: token.py <address>  
5:  """  
6:  from datetime import datetime  
7:  import logging  
8:  from logging.handlers import SysLogHandler  
9:  import RPi.GPIO as gpio  
10:  import serial  
11:  import subprocess  
12:  from time import sleep, time  
13:    
14:  from docopt import docopt  
15:  from sh import date, rmmod  


Then further down in the code I can call date() to call the actual date command from the OS so it is not only in the right format, but in the right timezone (there are other ways to do this, but sh was going to be used heavily for gathering stats, so it made sense to introduce it here):

154:      # synchronize time  
155:      s = str(date())+' \n' # from the OS  
156:      bin = bytes(s, "ascii")  



Pyserial


So, how about accessing a serial port in Python? And are there things to keep in mind when using Python 3?

First thing first, we import serial (line 10). This module is already installed on the Raspberry Pi.

Then, within the body of the main function, we establish a serial connection (line 101). We specify the device that corresponds to the serial UART. This is /dev/ttyAMA0. We also specify the bitrate, expressed in bits per seconds (bps). 115200 is the fastest we can use with the default clock settings for the UART, and is what was used for the flight. As a reference, acoustic coupler modems worked at 300 bps...

We felt that at higher rates it might not give us as reliable a signal, but we will experience with faster rates on future flights (we only had time to do 1 flight within the Global Space Balloon Challenge window). Back in 2012, Gert Van Loo stated:

"The UART and the SPI can be driven by DMA so there is no SW limitation.The real maximum for all those interfaces is only set by the signal integrity.This is certainly the case for I2C as it has to use pull-up resistors to get the signals high again.I know the clocking is rather confused. I built the damn chip and I still have no idea how to control all the clocks.The clocking structure is very complex with a limited number of resources (PLL's) which are used for aplethora of things to drive. (A quick count shows 36! clocks being generated) There will be many cases where it is just not possible to give you ALL the frequencies you want."
And in fact, it is possible to get up to 921600 bps (8 times what we used) in a controlled environment, at the OS level In an environment full of RF, including a 10W 2M transmitter, with a python module, I'd be happy with 4 times (460800) or even twice (230400) our rate. If nothing else, it would drive our latency down some.

Wow, that was quite the detour, anyway, back to our establishing the serial connection. The last thing we specify  on line 101 is timeout=1. This is a 1 second timeout. We will be blocking on reads (line 113), but don't want to wait more than 1 second for the data. 

In the original token ring world, a frame is defined in the following way:



A start delimiter, access control, frame control, destination address and source address, then the data, and finally a frame check sequence, end delimiter and frame status. I found this later, after I designed the frame for our system, and it is interesting that it is quite similar, in functionality, but implemented slightly differently.

In our case, the frames are small in size, they should never go above 1 second to transmit. And if they do, then we kick out that partial request and the balance of the request and we'll get the next one that is complete. So, we assume that if we get a full frame, the first byte is always first, and the frame always conclude with a newline (that way the ser.readline() returns before the 1 second timeout). We thus avoid the start delimiter (the end delimiter is newline). 




The first byte for our protocol is the source address (1 byte = 254 addresses, 0 and 255 have special meaning). The second byte is the destination. On an 8 node setting (the default), this is a bitmask, and 255 (FF) is broadcast. On a "large" cluster,  this address is the individual node address, and 255 (FF) is still broadcast. You gain number of nodes, but loose directed broadcast (say, I want to send something to node 1, 2 and 6, I would use a destination of 9 in the 8 node mode, but in extended mode I have to send 3 individual frames). Then the status. This can be a static status (the packet gets back to the originator) or a counter status (each time a node processes the frame, it decreases the status) or a no status needed, where the nodes simple forward the original frame, but do not send a status. This is followed by a command. This allows for shorter data segments, since we just need the variable part of the command. Finally, the data, a variable number of bytes terminated by newline (\n).

As we get to higher speeds, I will definitely add the frame check sequence of the original token ring to our design.

10 :  import serial  
11 :  import subprocess  
12 :  from time import sleep, time  
13 :    
14 :  from docopt import docopt  
15 :  from sh import date, rmmod  
[...]
97:    
98 :  def main(args):  
99 :    my_addr = int(args['<address>'])  
100:    
101:    ser = serial.Serial('/dev/ttyAMA0', 115200, timeout=1)  
[...]
111:    while not master:  
112:      logger.debug("I am SLAVE") # Everywhere but node 1  
113:      tokens = ser.readline()
114:
115:      if len(tokens) == 0:  
116:        logger.debug("No token received in the past 1 second")


What about writes? As an example, in the case of the master controller, it starts its job by sending a KEEPALIVE frame. This is defined by a series of bytes on line 36. Source is 01 (master node), destination is 255 (FF) broadcast, status is FF (no response needed), then command 05 which is a keepalive. Data segment is of length 0 since \n is encountered immediately. This is the shortest well formed frame that can be sent. And that is what the master controller sends on line 152 using ser.write. As long as we keep everything as type bytes, the serial module works well in Python 3.

Pretty simple, overall (!). Well, considering this was all designed over the course of a handful of days. Python really helped.

[...]
36:  KEEPALIVE = b'\x01\xff\xff\x05\n' # keepalive packet  
[...]
149:    # I am master, or I am taking over   
150:    if my_addr == 1:  
151:      sleep(15)  
152:      ser.write(KEEPALIVE)  
153:      sleep(1)  

We will be creating a module for reuse out of the token.py application, so that other people can use a serial token ring setup themselves. This should be published to pypi. We'll also publish a demo implementation for a master/slave setup.

Deadman's Switch

I'm concluding this article here, as it is already quite long. But I invite you to read about a most interesting device, the Deadman's switch. This was used in our High Altitude Balloon, but it can be used in all kinds of scenarios. Read about it here: http://raspberry-python.blogspot.com/2015/04/deadmans-switch.html

And don't forget to check back regularly for more updates, such as part 3 of this series of articles (the data gathering aspects).

Francois Dion
@f_dion

Thursday, February 7, 2013

L'autoréplication


de RaspberryPi a SD


Il y a un certain challenge a faire une carte SD pour le Raspberry Pi.

Ceci est du en partie a la grande variété de systèmes d’opération et d'ordinateurs (et leur matériel). D'autre part, beaucoup de personnes n'ont que le Raspberry Pi comme ordinateur, les autres étant hors de portée du au prix.

Il y a certains groupes d'usagers ici et la qui se débrouillent pour aider les usagers a faire leur propre carte SD, alors que d'autres doivent acheter des cartes SD avec le système d’opération déjà installé. Mais comment faire pour faire une copie de cette carte SD, n'est on pas de retour a la case départ?

Le lecteur


En fait, une fois que l'on a un système d’opération  sur la carte SD, on peut alors faire de l'autoréplication. Quésaco? me direz vous... Et bien, c'est quand un système d’opération peut se répliquer lui même (faire une copie de carte SD seulement avec le Raspberry Pi).

Pour ce faire, il faut un lecteur de carte SD, car le Pi n'a qu'une seule fente SD. Un lecteur comme celui ci, par exemple (ici, avec une carte 16GB, mais on peut faire 4GB ou 8GB ou plus, de la même façon):




Et on le branche (avec un câble ou en direct) au port USB.



dd


La version la plus recente de Raspbian Wheezy est de Decembre 2012:
http://files.velocix.com/c1410/images/raspbian/2012-12-16-wheezy-raspbian/2012-12-16-wheezy-raspbian.zip

Ici, les instructions se basent sur une version plus vieille, mais c'est le meme principe. Il suffit de remplacer l'url http://www.gtlib.gatech.edu/pub/raspberrypi/images/raspbian/2012-09-18-wheezy-raspbian/2012-09-18-wheezy-raspbian.zip par une de cette liste ici (choisir un serveur plus pres de soi): http://downloads.raspberrypi.org/download.php?file=/images/raspbian/2012-12-16-wheezy-raspbian/2012-12-16-wheezy-raspbian.zip


Avant de commencer, il faut s'assurer d'avoir au moins 2.5GB de libre (df -h me donne 2.9G ici). On insere la carte SD vide dans le lecteur et on verra apparaitre /dev/sda ou sdb.



fdion@raspberrypi ~ $ df -h
Filesystem      Size  Used Avail Use% Mounted on
rootfs           15G   11G  2.9G  80% /
/dev/root        15G   11G  2.9G  80% /
devtmpfs        109M     0  109M   0% /dev
tmpfs            22M  248K   22M   2% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs            44M     0   44M   0% /run/shm
/dev/mmcblk0p1   56M   35M   22M  62% /boot
fdion@raspberrypi ~ $ wget http://www.gtlib.gatech.edu/pub/raspberrypi/images/raspbian/2012-09-18-wheezy-raspbian/2012-09-18-wheezy-raspbian.zip
fdion@raspberrypi ~ $ unzip 2012-09-18-wheezy-raspbian.zip
fdion@raspberrypi ~ $ ls /dev/sd*
ls: cannot access /dev/sd*: No such file or directory
fdion@raspberrypi ~ $ ls /dev/sd*
ls: cannot access /dev/sd*: No such file or directory
fdion@raspberrypi ~ $ ls /dev/sd*
/dev/sda  /dev/sda1
fdion@raspberrypi ~ $ ls *.img
2012-09-18-wheezy-raspbian.img


Ici, le lecteur de carte avec SD est /dev/sda (si c'est sdb, il faut ajuster la commande ci dessous avec of=/dev/sdb). On est pret a faire la copie de l'image de la carte SD (le fichier .img), a partir du Pi (on peut aussi utiliser bs=4M qui va beaucoup plus vite avec certaines cartes):


fdion@raspberrypi ~ $ sudo dd bs=1M if=2012-09-18-wheezy-raspbian.img of=/dev/sda
1850+0 records in
1850+0 records out
1939865600 bytes (1.9 GB) copied, 428.218 s, 4.5 MB/s
fdion@raspberrypi ~ $ sudo sync


Le sudo sync est très important. Si on ne le fait pas, ça ne fonctionnera pas.

Et c'est tout! On peut maintenant donner cette copie a une autre personne. Tres pratique pour les clubs.

Il existe aussi une méthode pour faire une copie, sans utiliser de fichier .img, mais cela sera pour une autre fois...


François
@f_dion

Monday, October 29, 2012

RISC OS

Acorn Proton


Que es? Al principio de los anos 80, el Acorn Proton paso de computadora obscura, un prototipo, a un de los mas famosos ordenador, a causa de la BBC. La máquina fue lanzada como el BBC Microcomputer (Beeb Micro), especialmente para ponerles en las escuelas. Fueron vendidos casi 1.5 million de machinas durante 12 anos, la mayoría en Europa. En los Estados Unidos, el Apple ][ fue sin competición en las escuelas.

Hay muchos detalles en wikipedia.

Acorn Archimedes


En 1987 empezó el Acorn Archimedes y el BBC Archimedes . Fue el primer ordenador personal con CPU RISC ARM de 32 bits.

RISC OS

RISC OS es el sistema operativo de los computadores Acorn con CPU ARM. El Raspberry Pi también es basado en el CPU ARM, y es posible ahora de utilizar RISC OS con el Pi.

Hay una pagina en ingles aqui

Preparación


Descargar el OS aqui: riscos-2012-10-16-RC5

Ponerlo en una tarjeta SD con dd. Si la instalación es directamente de un Raspberry Pi con el OS Raspbian, ver este articulo: autoreplicación.
Pero en vez de:
sudo dd bs=1M if=2012-09-18-wheezy-raspbian.img of=/dev/sda 
hay que hacer:
bunzip2 ro519-rc5-1876M.img.bz2
sudo dd bs=1M if=ro519-rc5-1876M.img of=/dev/sda

Cual sea el sistema operativo, el GPU busca el fichero config.txt. La definición se encuentra en elinux.

Raspberry Pi con tarjeta SD RISC OS
Al iniciar
Boot de RISC OS

El escritorio (1920x1080)

Hay mas detalles tambien en el sitio http://www.riscosopen.org, pero en este momento no es posible connectar al sitio.

Documentacion de RISC OS en el sitio: http://foundation.riscos.com/Private/

Foro RISC OS de raspberrypi.org: http://www.raspberrypi.org/phpBB3/viewforum.php?f=55

Wednesday, October 24, 2012

Pocket Mini Computer kit

On my previous post, Blue Screen Of Basic, I was showing the output of a computer mini kit I just built.

What

The Pocket Mini Computer is a simple kit that can be put together in an evening. Just like the Raspberry Pi, it is a bare board (quite like the Apple I). You then need a keyboard (PS/2), monitor (VGA) and power supply (5V, mini USB).

It is not a competitor to the Pi, particularly based on spec and price. It wont run Linux. At any rate, you are probably anxiously waiting on your backordered Raspberry Pi and Gertboard, so that'll give you something to do, hehehe.

Rather, it's something to learn soldering on (or in my case, have fun building stuff, like people do puzzles or crosswords), and play with Propeller Basic and SIDCOG, an emulation of the SID chip including filters using one of the 8 "cogs" (core) of the "processor".

I should really say microcontroller. It runs on a Parallax Propeller microcontroller QuickStart board which is included in the $39 price tag (and that is less than what Radio-Shack sells the P8X32A by itself!)

The box



The bags


  

The parts


Bonus

While supplies last, Jeff includes a laser cut acrylic reference block for the P8X32A GPIOs.

Now, let's build this.

Sunday, October 14, 2012

RPi.GPIO dot dash

In a previous blog, I mentionned how to get WiringPi on the Pi.

RPi.GPIO

Let's now talk about RPi.GPIO, another popular library (there is a third one that is popular which I'll cover soon) for interfacing with hardware on the Pi. We will start by installing it. One tool I always install on a machine that will be used for python, is easy_install. It is part of python-setuptools. After installing that, it is a simple task to install RPi.GPIO:



pi@raspberrypi ~ $ sudo apt-get install python-dev python-setuptools
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  libexpat1-dev libssl-dev libssl-doc python-pkg-resources python2.7-dev
Suggested packages:
  python-distribute python-distribute-doc
The following NEW packages will be installed:
  libexpat1-dev libssl-dev libssl-doc python-dev python-pkg-resources
  python-setuptools python2.7-dev
0 upgraded, 7 newly installed, 0 to remove and 43 not upgraded.
Need to get 31.9 MB of archives.
After this operation, 43.5 MB of additional disk space will be used.
Do you want to continue [Y/n]? y
[lots of stuff]


Installing RPi.GPIO

[EDIT: the below should now be ok, Pypi has 0.4.1a, see:

http://pypi.python.org/pypi/RPi.GPIO/0.4.1a ]


pi@raspberrypi ~ $ sudo easy_install RPi.GPIO
Searching for RPi.GPIO
Best match: RPi.GPIO 0.3.1a
Adding RPi.GPIO 0.3.1a to easy-install.pth file

Using /usr/lib/python2.7/dist-packages
Processing dependencies for RPi.GPIO
Finished processing dependencies for RPi.GPIO


Usually, this would be ok, but the pypi repository has the older 0.3.1a version. Still, we didn't waste our time, because we will use easy_install for some other stuff. So let us get the rpi.gpio from the raspbian repository:


pi@raspberrypi ~ $ sudo apt-get install python-rpi.gpio
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages will be upgraded:
  python-rpi.gpio
1 upgraded, 0 newly installed, 0 to remove and 42 not upgraded.
Need to get 14.3 kB of archives.
After this operation, 18.4 kB of additional disk space will be used.
Get:1 http://archive.raspberrypi.org/debian/ wheezy/main python-rpi.gpio armhf 0.4.1a-1 [14.3 kB]
Fetched 14.3 kB in 0s (28.0 kB/s)    
(Reading database ... 66708 files and directories currently installed.)
Preparing to replace python-rpi.gpio 0.3.1a-1 (using .../python-rpi.gpio_0.4.1a-1_armhf.deb) ...
Unpacking replacement python-rpi.gpio ...
Setting up python-rpi.gpio (0.4.1a-1) ...



All right, this is better, version 0.4.1a-1. We are now going to be writing some Python code. Do you have a way to use the GPIO connector, such as a PiCobbler? If you dont, do not worry. You can make your own or buy one later. Today we will stick to what you have.

OK Computer

Let's try the library, still, using a GPIO pin that is already wired to a LED, so we can play some with the code. This is found on the Raspberry Pi schematics (page 4 is showing the OK LED with a control of STATUS_LED_N, and page 2 is showing that this is coming from the BCM2835, GPIO16).



We will thus use the OK (Activity) LED. As the name implies however, it is already in use by a process. It is triggered by activity on the SD card. Let us first disable that trigger before we try to use the LED, else we won't get the results we expect.


pi@raspberrypi ~/hardware_project/led $ sudo su
root@raspberrypi:/home/pi/hardware_project/led# echo none >/sys/class/leds/led0/trigger
root@raspberrypi:/home/pi/hardware_project/led# exit
exit
pi@raspberrypi ~/hardware_project/led $ 



In order to re-enable the activity / ok LED trigger from the SD card, once you are done with testing the code in this article, you will have to run the following:


pi@raspberrypi ~/hardware_project/led $ sudo su
root@raspberrypi:/home/pi/hardware_project/led# echo none [mmc0] >/sys/class/leds/led0/trigger
root@raspberrypi:/home/pi/hardware_project/led# exit
exit
pi@raspberrypi ~/hardware_project/led $ 



Python

Now, let's do some Python programming. We have to save the following code in a file. We will name this okled.py:


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
OKLED - Turn on then off the OK / Activity LED.
"""
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

import RPi.GPIO as GPIO
from time import sleep

OKLED = 16

# setup GPIO, use Broadcom pin numbering.The only way to access pin 16
# on the BCM chip.
GPIO.setmode(GPIO.BCM)

# setup BCM pin 16 as an output
GPIO.setup(OKLED, GPIO.OUT)

# Turn on LED
GPIO.output(OKLED, GPIO.LOW)
sleep(5)

# Turn off LED
GPIO.output(OKLED, GPIO.HIGH)

# We are done, let's clean up after ourselves.
GPIO.cleanup()


Run with it

Let's run this. We have to execute with root privileges, because RPi.GPIO uses /dev/mem, and that is owned by root:root. We cant add ourselves to a mem group, because /dev/mem is owned by root group, not mem, unfortunately.


pi@raspberrypi ~/hardware_project/led $ sudo python okled.py 
pi@raspberrypi ~/hardware_project/led $ 



Yay, the OK LED came on for 5 seconds, then off. Surely we can do something better than this.

dahdah dahdahdah didahdit dididit dit

No, that's not from a song by George Kranz or Trio.

In teaching Python and mentoring programmers of all ages, I've found that using Morse code (from Samuel Morse, not Inspector Morse...) allows for a huge variety of tutorials, of problems, of material in general.

I can teach about dictionaries and list comprehensions (two obvious candidates). I can teach about timing, functions or, why not, GPIOs. There are also many potential projects that can be built around it, allowing to discuss all kinds of libraries:

  • audio decoder
  • audio encoder
  • digital signal processing
  • fourrier transforms
  • dits and dahs
  • Morse over ip
  • Prosigns
  • Q Codes
  • Morse trainer
  • QSO practive 
  • Morse in image
  • steganography
  • encryption
  • morsemail
  • sms
  • irc morsebot
  • JSON Morse
  • Web Morse
  • PDF Morse
and integrating software and hardware
  • visual Morse
  • auditive Morse
  • signal lamp
  • heliograph
  • Morse mood lamp
  • LCD Morse
  • Morse scroller
  • paper tape Morse
  • thermal printer Morse
  • Reprap 3d printer Morse
  • trainer with key
  • advanced dsp
  • keyer
  • twit-ah-morse
  • haptic receiver
  • persistence of vision
  • Morse in the sky
  • arm/disarm alarm
  • line of sight quadracopters, balloons
  • underwater transmission
  • Morse painter (trail)
And for those with the proper licenses:
  • Morse transmit / receive over the air
  • Morse from space (weather balloon)

These are just a few I can think of quickly. There are tons more possible. If you do some of these projects, please leave a comment with a link to it.  And I can use this to introduce more advanced stuff, depending on the audience. For example, trees. In this case, a dichotomic tree.

The obvious way to define a mapping of letters to Morse code would be to use a dictionary, such as:

l2m = { 'a':'.-',
    'b':'-...',

so on and so forth. There are over 70 characters that can be transmitted in Morse, so that will be a dictionary with over 70 lines. And I'm lazy, I don't like to do tedious stuff like that! (As a side note, I leave that as an exercise to the reader to do a working translator using a dictionary).

A dichotomic tree (an implicit binary tree - see also dichotomous keys) is similar to another type of tree. Everybody is familiar with a genealogical tree. I've downloaded one from about.com that was empty, and I put a few things in it to explain how this relates to Morse (just two levels to keep it simple, but we would need 6 for a complete solution):


Let's say that you have a code that is .- (that is a dot and a dash, or didah). We start at the bottom of the tree and at the first branching I have to choose. I go left on dot and right on dash (or stay there if it's the end of the stream for that character).

Since I got a dot first, I go left. Might be an E it looks like, unless the signal continues and it could be an I or an A. The signal does continue, and I receive a dash. From E, I take the right, looks like an A, and once I confirm that this is the last mark for this character, I stay in that box. An A.

So, that is cool, but what about the other way around? We have a nice fanned out binary tree, with equal branches and leafs, so we cheat a little. We start from the outer level (IANM is a level and ET is another in this example) and try to find the letter. We go on to the next lower level until we find our letter. Once found, if on a row I am on an odd space, it is a dot, and if it is even, it is a dash. But I know that A is not just dash, in fact it doesn't even start with dash, what's up with that?

What is happening is that we are building the sequence in reverse. We are starting from the leaf in the tree and trying to find our way to the trunk. Not a problem, once we have the whole sequence, we just reverse it and we get our Morse code sequence. To find the path on the next, we round to the next even number and divide by 2. This gives us our position on the next lower level. We continue until we reach the trunk.

So how does that look when put into code? We'll mix the OK LED from the previous code tidbit, but we also need to add some logic to handle the length of time the LED stays on or off, depending on:

  • a dot is 1 time unit on
  • a dash is 3 time units on
  • between marks should be 1 time unit off
  • between letters should be 3 time units off
  • between words should be 7 time units off
Adding comments and everything, we get the following:


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
MORSE - A morse code generator for the Raspberry Pi GPIO OK LED.
EN: Morse code generated from a dichotomic tree.
FR: On génère une séquence de code Morse, a partir d'un arbre dichotomique.
ES: Generador de codigo Morse, por medio de un árbol dicotómico.
RU: Создание последовательности кода Морзе (дихотомической валом).
PT: Codigo Morse criado a partir de uma árvore dicotômica.
"""
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

import RPi.GPIO as GPIO
from time import sleep

__author__ = "Francois Dion"
__email__ = "[email protected]"
__copyright__ = "Copyright 2012, Francois Dion"

WPM = 5  # average words per minute, based on standard word PARIS
TIMEUNIT = 1.2 / WPM  # we can force a different unit, in seconds

# Activity/OK LED GPIO pin
OK_LED = 16

# We are going to be signaling on OUTPORT.
# If you are using another GPIO pin, assign it here.
OUTPORT = OK_LED

# Our famous dichotomic tree, as 6 strings to represent the 6 levels
TREE = (
    """            ?_    "  .    @   '  -        ;! (     ,    :""",
    u"54ŝ3é ð2 è+ þàĵ16=/ ç ĥ 7 ĝñ8 90",
    u"hvfüläpjbxcyzqöš",
    "surwdkgo",
    "ianm",
    "et",
)

DT = DD = '-.'


def letter2morse(letter):
    """ We take a letter and convert it to a morse code sequence.
    Since we are going down our dichotomic tree from the leaf to the trunk,
    we will end up with a reversed sequence, so we simply return the
    reverse of that ([::-1]) to get it in the right order.
    """
    if letter == ' ':  # No conversion needed, just a pause marker
        return letter
    found = False
    morse = ''
    position = 0
    for i in range(6):
        if found:
            position += position % 2
            position /= 2
            morse += DD[position % 2]
        elif letter in TREE[i]:
            position = (TREE[i].find(letter)) + 1
            morse = DT[position % 2]
            found = True
    return morse[::-1]


def morse2gpio(morse):
    """ Signal a morse letter (a morse code sequence for 1 alphanumeric).
    Also handles the pause between marks, letters and words.
    """
    for mark in morse:
        delay = TIMEUNIT
        # support for the dash as - or dah
        if mark in ('-', 'dah'):
            delay *= 3  # dash is 3 x longer than dot

        # dot or dash depending on the delay
        if mark == ' ':  # word separator
            sleep(delay * 4)  # stay off 4 + 1 + 2 below, total 7 TIMEUNITs
        else:
            GPIO.output(OUTPORT, GPIO.LOW)
        sleep(delay)
        # 1 TIMEUNIT pause between each mark
        GPIO.output(OUTPORT, GPIO.HIGH)
        sleep(TIMEUNIT)
    # end of letter pause
    sleep(TIMEUNIT * 2)  # already paused TIMEUNIT, 3 in total between letters


def main():
    """Get a string from the user and send the converted morse code sequence
    to a GPIO pin
    """

    # First, we have to setup the pin to output.
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(OUTPORT, GPIO.OUT)

    words = raw_input("Type sentence to send in morse code:")

    for letter in words.lower():
        morsecode = letter2morse(letter)
        print morsecode
        morse2gpio(morsecode)
    GPIO.cleanup()

if __name__ == "__main__":
    main()




We run the above after saving it into a file named morse.py:


pi@raspberrypi ~/hardware_project/led $ chmod a+x morse.py 
pi@raspberrypi ~/hardware_project/led $ sudo ./morse.py
Type sentence to send in morse code:Paris
.--.
.-
.-.
..
...
pi@raspberrypi ~/hardware_project/led $ 



Lint, clean it

One last thing. It is always good to run your code through pep8 and pylint. It will report errors, and also warnings about style and code. Pylint will even rate your code. We'll install them and run the code through it:


pi@raspberrypi ~/hardware_project/led $ sudo easy_install pep8 pylint
Searching for pep8
Best match: pep8 1.3.3
Processing pep8-1.3.3-py2.7.egg
pep8 1.3.3 is already the active version in easy-install.pth
Installing pep8 script to /usr/local/bin

Using /usr/local/lib/python2.7/dist-packages/pep8-1.3.3-py2.7.egg
Processing dependencies for pep8
Finished processing dependencies for pep8
Searching for pylint
Best match: pylint 0.26.0
Processing pylint-0.26.0-py2.7.egg
pylint 0.26.0 is already the active version in easy-install.pth
Installing pyreverse script to /usr/local/bin
Installing pylint-gui script to /usr/local/bin
Installing epylint script to /usr/local/bin
Installing pylint script to /usr/local/bin
Installing symilar script to /usr/local/bin
Installing pylint script to /usr/local/bin
Installing epylint script to /usr/local/bin
Installing pyreverse script to /usr/local/bin
Installing pylint-gui script to /usr/local/bin
Installing symilar script to /usr/local/bin

Using /usr/local/lib/python2.7/dist-packages/pylint-0.26.0-py2.7.egg
Processing dependencies for pylint
Finished processing dependencies for pylint
pi@raspberrypi ~/hardware_project/led $ 
pi@raspberrypi ~/hardware_project/led $ pep8 morse.py 
pi@raspberrypi ~/hardware_project/led $ 
pi@raspberrypi ~/hardware_project/led $ sudo pylint morse.py 
No config file found, using default configuration


Report
======
49 statements analysed.

Raw metrics
-----------

+----------+-------+------+---------+-----------+
|type      |number |%     |previous |difference |
+==========+=======+======+=========+===========+
|code      |50     |53.19 |50       |=          |
+----------+-------+------+---------+-----------+
|docstring |23     |24.47 |23       |=          |
+----------+-------+------+---------+-----------+
|comment   |8      |8.51  |8        |=          |
+----------+-------+------+---------+-----------+
|empty     |13     |13.83 |13       |=          |
+----------+-------+------+---------+-----------+



Statistics by type
------------------

+---------+-------+-----------+-----------+------------+---------+
|type     |number |old number |difference |%documented |%badname |
+=========+=======+===========+===========+============+=========+
|module   |1      |1          |=          |100.00      |0.00     |
+---------+-------+-----------+-----------+------------+---------+
|class    |0      |0          |=          |0           |0        |
+---------+-------+-----------+-----------+------------+---------+
|method   |0      |0          |=          |0           |0        |
+---------+-------+-----------+-----------+------------+---------+
|function |3      |3          |=          |100.00      |0.00     |
+---------+-------+-----------+-----------+------------+---------+



Messages by category
--------------------

+-----------+-------+---------+-----------+
|type       |number |previous |difference |
+===========+=======+=========+===========+
|convention |0      |0        |=          |
+-----------+-------+---------+-----------+
|refactor   |0      |0        |=          |
+-----------+-------+---------+-----------+
|warning    |0      |0        |=          |
+-----------+-------+---------+-----------+
|error      |0      |0        |=          |
+-----------+-------+---------+-----------+



Global evaluation
-----------------
Your code has been rated at 10.00/10 (previous run: 10.00/10)

Duplication
-----------

+-------------------------+------+---------+-----------+
|                         |now   |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines      |0     |0        |=          |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |0.000    |=          |
+-------------------------+------+---------+-----------+



External dependencies
---------------------
::

    RPi 
      \-GPIO (morse)






That concludes our tutorial for the day. I hope you had fun, and don't forget to leave comments if you encounter problems, have questions, enjoyed the article or want to share what you've done with this.

Saturday, September 15, 2012

Day 1

SD Card

In the previous article, entitled Day 0, we talked about all the stuff you need before your Raspberry Pi can be up and running. We talked about the power supply as item #1.

Item #2 was the SD memory card (in blue on the picture):


This is the equivalent to a hard disk on a computer, for your raspberry pi. The official distribution (operating system), raspbian wheezy, requires a minimum of a 2GB SD card. These can be had for a few $. Less than $5 at any rate. But it is not worth the time. Start at least with 4GB. Considering that an 8GB card is to be found in brick and mortar stores for $8 and a 16GB for $14 (i'm writing this in september 2012), I'd even go with 8 or 16 if this is more than a toy.

If you go with a different distribution, such as the adafruit Occidentalis distro, then you have no choice but to go with 4GB as the minimum, and there it makes also a lot of sense to get an 8GB or 16GB card.

Having said that, there are also specialized distributions, one trick ponies so to speak, that can fit on 1GB and 2GB cards. It all depends what you want to do with your Pi.

Beside the capacity, you'll want to get the right speed, or "class". For example the blue sandisk 16GB on the picture above is a class 4 card. That is a minimum speed, imho. If you can get class 6 or 10, that is even better:






This one is a SanDisk 16GB also, but a class 10 that promises 30MB/s and that's a good thing.


Buyer Beware


Before rushing out and buying a card, I do want to mention a few things.

1. Not all cards are compatible. Check the LIST and make sure that it is supported. I've never had any problem with any SanDisk or AData cards with the Raspberry Pi, but apparently some people have had issues.

2. You will need a way to write the SD card. That means either an on board SD card slot (such as on the Mac mini and mac books) or a USB SD card reader like this:


This should work with Windows, Solaris / OpenIndiana, Linux and Mac OS/X. Typically, I've found that a lot of embedded SD slots, such as found on Sony Vaio (with the secondary slot for MagicGate) and Dell Precision laptops, tend to not create reliable images, under any OS. The USB on the other hand seems solid. In the end, the embedded SD slot on my Mac Mini seems rock solid as far as that, so I tend to stick to that.

3. The steps to copy the operating system software image to the SD card is a bit involved. If that scares you, there is another option:

Buy a card that already has the OS on it. This can be found on forums, through your local hackerspace, your local Linux user group, ebay, electronic distributors (in the case of Adafruit, they sell SD cards with their own distribution, Occidentalis, already installed) or online shops that specialize in the Raspberry Pi. You can also check the LIST for some other suggestions.

So, we are making some progress... Next time, we'll talk about the keyboard.



Friday, August 17, 2012