ESP8266 NodeMCU ssd1306 WiFi scanner

Long time since I posted my last one. Meanwhile I went into programming arduino and trying out some sensors and displays. Here I am presensting you the source code to make yourself a portable wifi scanner with the tiny OLED ssd1306.

Check out a short video to understand what we are talking about.

This article is not for the beginners but indeed easy to make.

Get your hands on one of the convinient developer boards with ESP8266 such as NodeMCU and a ssd1306 OLED with I2C interface and 128×64 pixels.

display VCC –> 3.3V
display GND –> GND
display SCL –> D1
display SDA –> D2

You will need to install the board libraries for the ESP8266 and the adafruit_gfx and adafruit_ssd1306 library.

#include <ESP8266WiFi.h>
//#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET LED_BUILTIN // 4 // GPIO4
Adafruit_SSD1306 display(OLED_RESET);

#define BLINK (millis()%1000<500)?WHITE:BLACK

#define BLINKING_RATE 3 // in Hertz
#define BLINKING (millis()%(1000/BLINKING_RATE)<(500/BLINKING_RATE))?WHITE:BLACK

/* inspired by the example code and this post
* http://arduinom.org/kablosuz-aglarin-oled-ekranda-listelenmesi/
* SCL --> PIN D1 GPIO 5
* SDA --> PIN D2 GPIO 4
*/

int dB2y(int32_t in) {
// transform Signal Strength into vertical Pixel Position
int a = 40; // strong signal limit
int b = 90; // low signal limit
int outmin = 11; // upper pixel {0..
int outmax = display.height()-1; // lower pixel ..63}
return map(constrain(abs(in), a, b), a, b, outmin, outmax);
}

int ch2x(int32_t ch) {
// transform channel into horizontal pixel position
int a = -1; // low channel limit {1..
int b = 15; // high channel limit ..13}
int outmin = 0; // upper pixel {0..
int outmax = display.width(); // lower pixel ..127}
return map(constrain(ch, a, b), a, b, outmin, outmax);
}

int sortBySignal() {

}

void drawInit() {
display.clearDisplay();
display.setCursor(0, 0);
display.println("Scanning Networks");
display.display();
}

void drawNoNetwork() {
// update OLED
display.clearDisplay();
display.setCursor(0, 0);
display.println("No Network found");
display.display();
}

void drawBand(int32_t rssi, int32_t ch, bool color) {
//int x0 = ch2x(ch);
int x1 = ch2x(ch-2);
int x2 = ch2x(ch+2);
int y0 = dB2y(rssi);
int y1 = dB2y(-200);
display.drawLine(x1-3, y1, x1+4, y0, color); // left boundery
display.drawLine(x2+3, y1, x2-4, y0, color); // right boundery
display.drawLine(x1+4, y0, x2-4, y0, color); // upper line
}

void drawSelection(int32_t rssi, int32_t ch) {
int x = ch2x(ch);
int y = dB2y(rssi);
display.drawRect(x-3, y-3, 7, 7, BLACK);
display.drawRect(x-2, y-2, 5, 5, WHITE);
display.drawRect(x-1, y-1, 3, 3, BLACK);
display.drawPixel(x, y, BLACK);
}

void drawAxis() {
for (int x = 1; x <= 13; x++) {
display.drawPixel(ch2x(x), 63, WHITE);
}
}

void drawNetworks(int i, int n) {
long int timestamp = millis();
long int duration = 2000;
while ((millis()-timestamp < duration) && (millis() >= timestamp)) {
display.clearDisplay();
display.setCursor(0, 0);
//display.print(i + 1);
//display.print(" / ");
//display.println(n);

//display.print((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "e ");
display.print(WiFi.SSID(i));
//display.print(WiFi.BSSIDstr(i));
//display.print(WiFi.RSSI(i));
//display.print(WiFi.channel(i));

display.setCursor(display.width()-24,0);
display.setTextColor(BLACK, WHITE);
display.print("ch");
if (WiFi.channel(i) < 10) display.print(" "); // align right
display.print(WiFi.channel(i));
display.setTextColor(WHITE, BLACK);

//int enc = WiFi.encryptionType(i);
//drawAxis();

for (int net = 0; net < n; net++) {
if (net!=i) drawBand(WiFi.RSSI(net), WiFi.channel(net), WHITE);
}
drawBand(WiFi.RSSI(i), WiFi.channel(i), BLINKING);
drawSelection(WiFi.RSSI(i), WiFi.channel(i));

display.display();
delay(100); // minimum frame rate 50ms
}
}

// read the encryption type and print out the name:
void printEncryptionType(int thisType) {
switch (thisType) {
case ENC_TYPE_WEP:
Serial.print("WEP");
break;
case ENC_TYPE_TKIP:
Serial.print("WPA");
break;
case ENC_TYPE_CCMP:
Serial.print("WPA2");
break;
case ENC_TYPE_NONE:
Serial.print("None");
break;
case ENC_TYPE_AUTO:
Serial.print("Auto");
break;
}
}

void SerialPrintNetworks(int i) {
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print("; (");
Serial.print(WiFi.RSSI(i));
Serial.print(" dBm) ");
//Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? "open " : "enc ");
printEncryptionType(WiFi.encryptionType(i));
Serial.println("");
}

void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay(); // delete Adafruit Splash Screen
display.display();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setTextWrap(0);
display.setCursor(0, 0);
display.println("ESP8266 WiFi Scanner");
display.display();

Serial.begin(115200);
Serial.println("Setup ESP8266");
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);

delay(2000);
}

void loop() {
Serial.println("Scanning Networks");
drawInit();
int n = WiFi.scanNetworks();

if (n == 0) {
Serial.println("No Network found");
drawNoNetwork();
}
else {
Serial.print(n);
Serial.println(" Networks found");
for (int i = 0; i < n; ++i) {
SerialPrintNetworks(i);
drawNetworks(i, n);
}
}
Serial.println("");
}

 

colorful sparkling lights for your offspring

This project was made to enjoy my baby boy with random colorful sparkling lights. The result was pretty beautiful and the programming code can easily be changed to a more disco like appearence.

I recently entered the world of arduino and was estonished how fast you are making ideas come to life. One of my finished projects is to use a digispark with ATtiny85 micro controller and USB power supply to drive daisy chained WS2812 LEDs. Using Arduino or mini pro will also do the job done. One of the key problems you might run into is the current limitation of you power source. The presented project however uses little current because the source code will only switch on one light at a time. Maximum brightness can be defined and you will never have a pure white light.

You will need

  1. a PC Win/Linux/Mac with the arduino IDE 1.6.5 or higher for programming.
  2. Digispark, Digistump or Arduino Uno/mini/pro mini – I bought some cheap Shenzhen versions of digispark at aliexpress for less than 2,- Euros including shipping
  3. single WS2812B LEDs such as NeoPixel or single parts auf WS2811 strip
  4. some wires, e.g. flat ribbon cable you could salvage from your old computer’s parallel ATA hard drive
  5. diffusor, e.g. white table tennis balls; grumbled paper or origami figures, white plastic cups
  6. tools such as a soldering iron and lead; super glue/hot glue

To use the digispark with your arduino IDE you will probably need to install some USB drivers and include the digispark via IDE’s board manager, see instructions.

Before soldering everything you should start testing the micro controller and LEDs with a breadboard. Note: digispark will wait for 5 seconds to get programmed after powering up, then the (already) uploaded code will run. Therfore unplug before programming and connect to you computer when you are asked to.

Since I don’t want white or grey colors included, the approach is to utilize the HSV color modell, especially the so called hue. Hue is essentially a one-variable-value to specify a color wheel. To use hue for a RGB model, it need to be converted, see the wikipedia article for HSL/HSV.

The source  code uses random values to select color, blinking duration and pauses.


#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

/* * *** Baby Light with DigiSpark and WS2812 NeoPixels *** */
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN 1
#define __AVR_ATtiny85__

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 5
#define MAX_BRIGHT 96

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
 // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
 if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
 // End of trinket special code
 randomSeed(analogRead(0));
 pixels.begin(); // This initializes the NeoPixel library.
}

bool between(int val, int min, int max) {
 if ((val > min) && (val <= max)) { return 1; }
 else { return 0; }
}

byte hue2red(int hue) {
 if (hue <= 43) { return 255; };
 if (between(hue, 43, 85)) { return map(hue, 43, 85, 255, 0); };
 if (between(hue, 85, 128)) { return 0; };
 if (between(hue, 128, 171)) { return 0; };
 if (between(hue, 171, 213)) { return map(hue, 171, 213, 0, 255); };
 if (hue > 213) { return 255; };
}
byte hue2green(int hue) {
 if (hue <= 43) { return map(hue, 0, 43, 0, 255); };
 if (between(hue, 43, 85)) {return 255; };
 if (between(hue, 85, 128)) { return 255; };
 if (between(hue, 128, 171)) { return map(hue, 128, 171, 255, 0); };
 if (between(hue, 171, 213)) { return 0; };
 if (hue > 213) { return 0; };
}
byte hue2blue(int hue) {
 if (hue <= 43) {return 0;};
 if (between(hue, 43, 85)) { return 0; };
 if (between(hue, 85, 128)) { return map(hue, 85, 138, 0, 255); };
 if (between(hue, 128, 171)) { return 255; };
 if (between(hue, 171, 213)) { return 255; };
 if (hue > 213) { return map(hue, 213, 255, 255, 0); };
}

void loop() {
 byte randNumber = random(NUMPIXELS);
 int randColor = random(256);
 int randDelay = random(1000);
 int starSpeed = random(15, 30);

 for (int b = 0; b < MAX_BRIGHT; b++) {
 pixels.setPixelColor(randNumber, pixels.Color(hue2red(randColor) / MAX_BRIGHT * b, hue2green(randColor) / MAX_BRIGHT * b, hue2blue(randColor) / MAX_BRIGHT * b));
 pixels.show(); // This sends the updated pixel color to the hardware.
 delay(starSpeed); // Delay for a period of time (in milliseconds).
 }
 for (int b = MAX_BRIGHT - b; b >= 0; b--) {
 pixels.setPixelColor(randNumber, pixels.Color(hue2red(randColor) / MAX_BRIGHT * b, hue2green(randColor) / MAX_BRIGHT * b, hue2blue(randColor) / MAX_BRIGHT * b));
 pixels.show(); // This sends the updated pixel color to the hardware.
 delay(starSpeed); // Delay for a period of time (in milliseconds).
 }
 delay(randDelay);
}

… This is an unfinished blog entry. Sorry, other projects are waiting to be published… 

sine and cosine function in sqlite – look up table workaround

Besides calculating the sine of an angle outside sqlite (e.g. python, bash or php), you may want to calculate your data inside sqlite. My workaround uses a simple look up table, which can be used for virtually all kinds of limited x-y-allocation.

CREATE TABLE lut(angle FLOAT, sin FLOAT, cos FLOAT);
.mode csv
.separator ","
.import lut trigonometry.csv
.separator "|"
.headers on
.mode columns
.show
select * from lut limit 3;
angle       sin         cos
----------  ----------  ----------
0.0         0.0         1.0
0.1         0.002       1.0
0.2         0.003       1.0
...

The table is limited to all angles between 0.0 to 180.0 in 0.1 steps – you may want adapt this depending on your needs. You can simply look up the sine and/or cosine like this:

sqlite> select *, (sin / cos) AS tan, (cos / sin) AS cotan from lut where angle = 60;
angle       sin         cos         tan         cotan
----------  ----------  ----------  ----------  -----------------
60.0        0.866       0.5         1.732       0.577367205542725

But if you want to do calculation with sine, you need to encapsulate the sine look up. I am calculating the incidence angle on a solar panel according to its tilt and orientation, as well as the elevation and azimuth of the sun (at given location) at a certain timestamp.
The have a sun position table with precalculated entries downloaded from NREL: sun-position-algorithm
the view to calculate the incidence angle is defined in sandia: angle-of-incidence
I applied this formula in sqlite (zenith is exchanged by elevation) in a view with fixed tilt of 30 degrees and south orientation (180 degrees):

CREATE VIEW [incidence] AS
SELECT
datetime(timestamp, 'localtime') as LOCAL,
elevation,
azimuth,
180 AS orientation,
30 AS tilt,
(select sin from lut where angle = round(elevation, 1)) *
(select cos from lut where angle = 30) +
(select cos from lut where angle = round(elevation, 1)) *
(select sin from lut where angle = 30) *
(select cos from lut where angle = round(azimuth - 180, 1))
AS COS_INCIDENT
from sunpos;

Since the sun position table only has entries for each full minute, we need to narrow the timestamp using the substr() and ‘||‘ (concatination) functions like this:

sqlite> select * from incidence where LOCAL = substr(datetime('now', 'localtime'),1,17)||'00' limit 1;

Additionally the table entries and search values should be narrowed to a number of decimals. You can should include this during the creation of your triginometry.csv source file (in excel or libre) by a round() function. the search term in sqlite works like this.

sqlite> select round(3.141592653589793, 2) AS pi;
pi
----------
3.14

The lookup with calculations and big source tables may take its time – especially when working on raspberry pi. You may want to use the limit 1 switch and check your performance with .timer on option.

Raspberry PI 2 B hands-on

The new Raspberry PI 2 Model B 1GB specs:

  • QUAD Core Broadcom BCM2836 CPU with 1GB RAM
  • 40pin extended GPIO
  • Micro SD slot
  • Multiple Ports: 4 USB ports, Full Size HDMI, 4 pole Stereo output and Composite video port, CSI camera port & DSI display port
  • Micro USB power source

Today (5.2.2015) the new model Raspberry PI 2 B 1GB arrived and I would like to share my first hands-on experience. In the package purchased at pollin.de for € 38,90 incl. VAT. Included is a quick-guide with references to the element14 website for OS download.

I attached the portapow USB current/voltage meter to measure the used current. From raspberrypi.org tried out the snappy-ubuntu core alpha-02. It booted into a shell without system information during the boot-up. Login with ubuntu/ubuntu. startx didn’t work here. Having a powerful fanless ubuntu system was the intention to get the new raspi model in the first place.

The current was between 0.2 and 0.4 amps – of course without a graphical interface. A USB source with at least 0.6 amps is highly recommended in the specs – you better take one with 2 amps to provide enough current for peripherals.

I wanted to try out a graphical OS and so prepared my 16GB micro SDHC-card with the NOOBS package (Version: 1.3.12 Release date: 2015-02-02). Snappy-Ubuntu isn’t included here, and the offered openELEC is the old 4.x version.

The raspbian (kernel 3.18.5-v7+) still includes a free Mathematica. I was curious what it is capable to perform on the new engine, but according to the cpu-load graph it just runs on one v7 core. Check it out yourself with this nice 3D plot:

Graphics3D[{Opacity[.8],
Glow[RGBColor[1, 0, 0]],
EdgeForm[White], Lighting ->
None, PolyhedronData["Echidnahedron", "Faces"]}]

The raspi-config tool offers overclocking up-to 1000MHz. Raspbian Openbox is initially running with a resolution of 1776×952 pixels on my FullHD screen leaving a big black bezel. I hope to be able to change it soon.

Impression:

The new model runs with low power consumption. Distributions aren’t yet ready to utilize the QUAD core ARMv7.
I am a little disappointed that there is still the useless DSI display port (no available devices so far). There’s still the fragile Micro USB power-in and no reset button. And you can barely find Linux distributions for 2GB SD cards.

If you want to tinker with sensors and LCD-Displays, motors and RGB-LEDs you should give the new model a chance. It is still at least as good as the previous Model B+

For those who like a low power consuming graphical linux system with reasonable power, I suggest the odroid U3 from hardkernel.com with

  • SAMSUNG Exynos4412 Prime Cortex-A9 Quad Core 1,7 Ghz
  • 2GB DDR2 880
  • Mali-400 Quad Core 440 MHz for up-to 1080p
  • 3 USB ports and ethernet
  • optional eMMC flash drive (8 times faster than micro sdhc)
  • Linux Xubuntu or Android 4.x

It also runs a very smooth XBMC in 1080p

Conways Game of Life in a 8×8 LED Matrix

So, I wrote some python functions and brought them together in the “Game of Life”-Animation running on the small SPI-interfaced 8 by 8 LED matrix with max7219 controler. My set of python functions can easily be used to create further funny code.
The basic libraries were already written Richard Hull and reused.

Have fun.

#!/usr/bin/env python

import max7219.led as led
import max7219.canvas as canvas
# import max7219.transitions as transitions
import time
from random import randrange

initgol = [0, 0b100, 0b100, 0b110, 0, 0b01110011, 0b00010101, 0b110]

def buf2mat(buffer):
    matrix = [[0 for i in range(8)] for j in range(len(buffer))]
    for col in range(len(buffer)): 
        for row in range(8):
            matrix[col][row] = buffer[col] & pow(2, row) # bitwise eval
    return matrix

def mat2buf(matrix):
    buffer = [0 for i in range(len(matrix))] 
    for col in range(len(matrix)):
        for row in range(len(matrix[col])):
            buffer[col] = buffer[col] | (pow(2, row) * matrix[col][row])
    return buffer

def countneighbors(matrix, wrap):
    neighbors = [[0 for i in range(len(matrix[0]))] for j in range(len(matrix))]
    for col in range(len(matrix)):
    for row in range(len(matrix[col])):
        for c, r in [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]:
            if wrap:
                neighbors[col][row] += matrix[(col + c) % len(matrix)][(row + r) % len(matrix[col])]
            if not wrap:
                if not ( (col + c) < 0 or (row + r) < 0 or (col + c) >= len(matrix) or (row + r) >= len(matrix[col]) ):
                neighbors[col][row] += matrix[col + c][row + r]
    return neighbors

def randbit(probability):
    if randrange(100) < probability:
        return 1
    else:
        return 0

def randmatrix(probability, rows, cols):
    matrix = [[0 for i in range(rows)] for j in range(cols)]
    for col in range(cols):
        for row in range(rows):
            matrix[col][row] = randbit(probability)
    return matrix

def golrules(matrix):
    neighbors = countneighbors(matrix, 0)
    nextgen = [[0 for i in range(len(matrix[0]))] for j in range(len(matrix))]

    for col in range(len(matrix)):
        for row in range(len(matrix[0])):
            if matrix[col][row] == 0:
                if neighbors[col][row] == 3:
                    nextgen[col][row] = 1
                else:
                    nextgen[col][row] = 0
            elif matrix[col][row] == 1:
                                if neighbors[col][row] < 2:
                                        nextgen[col][row] = 0
                                elif neighbors[col][row] > 3:
                                        nextgen[col][row] = 0
                else:
                    nextgen[col][row] = 1
        return nextgen

def text2buf(text):
    buffer = []
    for i in range(len(text)):
        if text[i].upper() in font5x3:
            buffer = buffer + font5x3[text[i].upper()]
        buffer = buffer + [0]
    return buffer

def drawbuf(buf, up, left):
    buffer = [0,0,0,0,0,0,0,0]
    for i in range(8):
        buffer[i] = buf[(i + left) % len(buf)] >> abs(up)
    canvas.gfxbuf = buffer
    return buffer

led.init()
horz = 0
vert = 0
speed = 4
led.brightness(0)
i = 0

while True: 
    m = randmatrix(20, 8, 8)
    for cycle in range(15):
        drawbuf(mat2buf(m), 0, 0)
        canvas.render()
        time.sleep( 1.0 / speed )
        m = golrules(m)

spidev is broken

Since the today’s Raspbian upudate, the spi interface not working anymore.

uname -a
Linux raspberrypi1 3.18.5+ #746 PREEMPT Mon Feb 2 13:57:16 GMT 2015 armv6l GNU/Linux

All commands below and numberous rebooting show, that the support for spi is simply voided.

dmesg | grep spi
ls -l /dev/spi*
ls /sys/bus/spi/devices
lsmod
insmod spi
modprobe spidev
modinfo spidev

I also took care of the blacklist

sudo nano /etc/modprobe.d/raspi-blacklist.conf

nothing worked out so far 😦

edit[2015-02-03]
I may have found an official explaination for the SPI problems…
http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=98318
http://www.raspberrypi.org/forums/viewtopic.php?p=675658#p675658

All that’s to be done is adding the following lines into /boot/config.txt as super user

dtparam=spi=on
dtparam=i2c=on
dtparam=i2s=on

edit[2015-04-15]
An easy approach to solve the issue with 1-wire sensors DS18B20 etc. is described here .

edit[2015-06-04]
You just add the following line at the end of file /boot/config.txt to use PIN 7 (GPIO=4) as w1-DATA

# 1wire bus for DS18B20
dtoverlay=w1-gpio,gpiopin=4,pullup=on

reboot and check your result with

ls /sys/bus/w1/devices/
cat /sys/devices/w1_bus_master1/28*/w1_slave | grep t=

if you have an installed 1-wire sensor, such as the DS18B20

Raspberry PI max7219 8×8 LED Matrix – scrolling digital clock

Based on these fine instructions by raspi.tv I wrote some tiny python code to display a scrolling digit clock on an 8 by 8 LED matrix using the Raspberry PI.

The datatype of font5x3 = {“…” : …} is a so-called dictionary and works intuitively. The binary notation such as 0b01110 makes it easy to conceive and edit the pixel based graphics of the font.

My functions creatematrix() and drawmatrix() can also be used for different purpose such as scrolling a text. They can be nested: drawmatrix(creatematrix(“Hello World”), 0, i)
You can easily extend the font5x3 with additional characters. different size such as 8×8 will work seamlessly.

#!/usr/bin/env python

import max7219.led as led
import max7219.canvas as canvas
import time

font5x3 = { # python data type dictionary for the pixelfont
    "0" : [0b01110, 0b10001, 0b01110], # "0"
    "1" : [0b10010, 0b11111, 0b10000], # "1"
    "2" : [0b11001, 0b10101, 0b10010], # "2"
    "3" : [0b10001, 0b10101, 0b01110], # "3"
    "4" : [0b01110, 0b01000, 0b11111], # "4"
    "5" : [0b10111, 0b10101, 0b01001], # "5"
    "6" : [0b01110, 0b10101, 0b01000], # "6"
    "7" : [0b10001, 0b01001, 0b00111], # "7"
    "8" : [0b01010, 0b10101, 0b01010], # "8"
    "9" : [0b00010, 0b10101, 0b01110], # "9"
    ":" : [0b01010], # ":"
    "-" : [0b00100, 0b00100, 0b00100], # "-"
    "|" : [      0], # one blank line
    " " : [      0,       0,       0] # space
    } # create additional letters

def creatematrix(text):
    text = str(text)
    matrix = []
    for i in range(len(text)): # write complete pixelmatrix in a buffer
        if text[i].upper() in font5x3: # check if dictionary entry exists
            matrix = matrix + font5x3[text[i].upper()] # add letter; upper()
            matrix = matrix + [0] # add separator/space
    return matrix

def drawmatrix(matrix, up=0, left=0):
    for i in range(8): # fill the 8x8 matrix buffer canvas.gfxbuf
        if up < 0:
            canvas.gfxbuf[i] = matrix[(i + left) % len(matrix)] << abs(up) # move down
        else:
    canvas.gfxbuf[i] = matrix[(i + left) % len(matrix)] >> abs(up)

led.init()
horz = 0
vert = -2
speed = 15
led.brightness(0) # 0, 3, 7, 15 seems to work

while True:
    m = creatematrix(time.strftime("%k:%M ")) # write temporary matrix m
    horz = (horz + 1) % len(m) # scroll left
    drawmatrix(m, vert, horz) # draw 8x8 frame into gfxbuf 
    canvas.render() # draw gfxbuf into matrix
    time.sleep( 1.0 / speed ) # pause

If you like to include my small 5×3 pixel alphabeth, you can use the following font5x3 definition:

font5x3 = {
    "0" : [0b11111, 0b10001, 0b11111], # "0"
    "1" : [0b10010, 0b11111, 0b10000], # "1"
    "2" : [0b11001, 0b10101, 0b10111], # "2"
    "3" : [0b10101, 0b10101, 0b01110], # "3"
    "4" : [0b01110, 0b01000, 0b11111], # "4"
    "5" : [0b10111, 0b10101, 0b11001], # "5"
    "6" : [0b01110, 0b10101, 0b01000], # "6"
    "7" : [0b10001, 0b01001, 0b00111], # "7"
    "8" : [0b01010, 0b10101, 0b01010], # "8"
    "9" : [0b00010, 0b10101, 0b01110], # "9"

    "A" : [0b11111, 0b01001, 0b11110], # "A"
    "B" : [0b11111, 0b10101, 0b01010], # "B"
    "C" : [0b01110, 0b10001, 0b10001], # "C"
    "D" : [0b11111, 0b10001, 0b01110], # "D"
    "E" : [0b01110, 0b10101, 0b10101], # "E"
    "F" : [0b11110, 0b00101, 0b00101], # "F"
    "G" : [0b01110, 0b10001, 0b11101], # "G"
    "H" : [0b11111, 0b00100, 0b11111], # "H"
    "I" : [0b10001, 0b11111, 0b10001], # "I"
    "J" : [0b10000, 0b10000, 0b01111], # "J"
    "K" : [0b11111, 0b00100, 0b11011], # "K"
    "L" : [0b01111, 0b10000, 0b10000], # "L"
    "M" : [0b11111, 0b00010, 0b11111], # "M"
    "N" : [0b11111, 0b00001, 0b11110], # "N"
    "O" : [0b01110, 0b10001, 0b01110], # "O"
    "P" : [0b11111, 0b01001, 0b00110], # "P"
    "Q" : [0b01110, 0b10001, 0b01111], # "Q"
    "R" : [0b11111, 0b01001, 0b10110], # "R"
    "S" : [0b10010, 0b10101, 0b01001], # "S"
    "T" : [0b00001, 0b11111, 0b00001], # "T"
    "U" : [0b01111, 0b10000, 0b01111], # "U"
    "V" : [0b00111, 0b11000, 0b00111], # "V"
    "W" : [0b11111, 0b01000, 0b11111], # "W"
    "X" : [0b11011, 0b00100, 0b11011], # "X"
    "Y" : [0b00011, 0b11100, 0b00011], # "Y"
    "Z" : [0b11001, 0b10101, 0b10011], # "Z"

    "." : [0b10000], # "."
    "?" : [0b10101, 0b00101, 0b00010], # "?"
    "!" : [0b10111], # "!"
    ";" : [0b11010], # ";"
    "," : [0b11000], # ","
    ":" : [0b01010], # ":"
    "-" : [0b00100, 0b00100, 0b00100], # "-"
    "|" : [ 0],
    " " : [ 0, 0, 0]
    }

The code is not yet ready for multiple 8×8 LED matrices. You can try to work out the following instructions on multiple LED matrices: http://www.tutorials-raspberrypi.de/allgemein/bibliothek-fuer-mehrzeilige-m-x-n-max7219-led-matrizen/