Raspberry Pi Internet Radio

Node-Red is graphical programming interface that allows logic to be created using nodes that are wired together. Node-Red has been pre-installed in the Raspian OS since 2015 and it has a very easy learning curve.

In this blog I wanted to show an example of Node-Red being used with a five button LCD faceplate to play Internet radio stations.

For this project I used a basic USB powered speaker, a Rasp Pi and a Pi 5-button LCD faceplate. The cost of the faceplates start at about $10.

pi_radio2

Getting Started with Internet Radio

There are a good number of Internet radio resources, https://www.internet-radio.com has a good selection of stations to choose from.

To find a URL of a radio station, look through the stations until you find what you like and then right click on the .pls link, and “Save Link as…”. Save this link as a file and then open the file in a text editor to get the URL.

radio_stations

 

MPD – Music Player Daemon

MPD is a Linux based music service that supports the playing of both music files and internet based radio stations. For command line operations that is also a MPD client application called mpc. To install both the service and client:

sudo apt-get install mpd mpc

Before I started building the node-red application I played with the mpc commands to ensure that I understood the basics.

Internet radio stations are added like a song list:

mpc add 'http://uk2.internet-radio.com:8062'
mpc add 'http://live.leanstream.co/CKNXFM'
mpc add 'http://66.85.88.2:7136'

Some key mpc control commands are:

mpc play  # play the current station
mpc play 3 # play radio station 3
mpc pause  # pause the music
mpc next  # play the next radio station
mpc prev  # play the previous radio station 

mpc volume 90 # set the volume to 90%
mpc volume +5 # increase the volume 5%
mpc volume -5 # decrease the volume 5%

The mpc status command will show the volume, what is playing along with the current station number and total number of stations:

$ mpc status
Comedy104 - A Star104.net Station: Doug Stanhope - To Tell You the Truth
[playing] #2/4 1:45/0:00 (0%)
volume: 75% repeat: off random: off single: off consume: off

Node-Red Logic

Node-Red can be started from the Raspberry Pi menus, or from the command line:

node-red-start &

To access the Node-Red web page, enter the Raspberry Pi ip address with port 1880, for example : http://192.168.0.121:1880

For this project two extra Node-Red components are needed, and they are for the LCD faceplate and the MPD music player. To add components use the “Palette Manager” option.

palette

For the LCD faceplate, search for Adafruit, and select the i2c-lcd-adafruit-sainsmart component.

adafruit_palette

Similarly search for mpd and add the node-red-contrib-mpd component.

mpd_palette To create logic select a node from the left node panel and drag it onto the center flow palette, and then “wire” the nodes together.

For the Internet music example I used four function nodes, and the two i2cLED and the two MPD nodes. (Comment nodes are only used to explain the logic).

node_red_radio

The first step is to double click on the MPD nodes and add an MPD server.

Select Button Logic

I used the select button to turn on and off the music player.

A context variable is created to save the current state of the player. Note: a context variable is only accessible for the node where it is defined..

The ic2_LCD_Input node message has a msg.button_name and msg.button_state item that is used to determine which button is pushed.

For the select button logic a group of messages was used to add the different radio stations.


// create an "is setup" variable
var issetup = context.get('issetup')||0;

if ((msg.button_name == "SELECT") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
if (issetup === 0) {
context.set('issetup',1);
var msg0 = { payload:"clear"};
var msg1 = { payload:"add http://185.33.21.112:11029" }; // 1.FM Trance
var msg2 = { payload:"add http://66.85.88.2:7136" }; // Comedy 104
var msg3 = { payload:"add http://live.leanstream.co/CKNXFM"}; // The One - Wingham
var msg4 = { payload:"add http://185.33.21.112:11269" }; // Baroque
var msg5 = { payload:"play" };
return [ [ msg0, msg1, msg2, msg3, msg4, msg5] ];
} else {
context.set('issetup',0);
msg0 = { payload:"pause"};
return msg0;
}
}

Up/Down Button Logic

The UP button will issue an MPD command equivalent to :

mpc volume +5

This will up the volume by 5%. The total volume will max at 100%.

The DOWN button will issue an MPD command equivalent to :

mpc volume -5

// Raise/Lower the volume
var msg1;
var thevolume = 5; //volume % increment to change

if ((msg.button_name == "UP") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
msg1 = { payload:"volume +" + thevolume };
return msg1;
}
if ((msg.button_name == "DOWN") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
msg1 = { payload:"volume -" + thevolume};
return msg1;
}

Current and Max Radio Station Logic

The ‘Current and Max Radio Stations’ node is updated from the MPD in node when there are any changes to the volume or when a new song or station is played.

This logic creates two flow variables (stnmax, stncnt) that are available in any node in this flow.  The station max (stnmax) and current radio station (stncnt) variables are used in the LEFT/RIGHT button logic to determine which station to change to.


// Get the max number of radio stations and the current radio statio
// Make context variables that can be used in other node, like the LEFT/RIGHT button

var msg1 = msg.payload.status ; //create a simplier message
var stnmax = msg1.playlistlength;
flow.set('stnmax',stnmax);
var stncur = msg1.nextsong;
if (isNaN(stncur)) {stncur = stnmax;} // ensure a valid station

flow.set('stncur',stncur);

return msg1; // only needed for possible debugging

While the code is running it is possible to view the context date.

context_flow

UP/DOWN Button Logic

The UP / DOWN logic changes between the radio stations using the mpc commands:

mpc next
mpc prev

It is important to not move past the range of the radio stations or MPD will hang. The stnmax and stncur variables are used to determine if the next or previous commands are to be allowed.


// Move left and right in radion stations
var stnmax = flow.get('stnmax');
var stncur = flow.get('stncur');
if ((msg.button_name == "LEFT") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
if (stncur > 1) {
var msg0 = {payload:"previous"};
return msg0;
}
}
if ((msg.button_name == "RIGHT") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
if (stncur < stnmax)
var msg1 = {payload:"next"};
return msg1;

}

Final Comments

The Pi LCD faceplate is an excellent hardware add-on for any Raspberry Pi project. However it important to know that clone hardware may work as expected. For my hardware I was not able to easily turn off the extra LED.

A future enhancement would be to add a Web interface so that you could change the volume or stations without using the 5 button Pi faceplate.

 

Simple Terminal Interfaces

Typically our interfaces for projects use colorful web pages or custom GUIs. However there are many cases where a simple text interface is all that is required. This is especially true for SSH or remote connections from a Window’s client into a Raspberry Pi or Linux server.

In this blog I’d like to review a 1980’s technology called curses, with three examples. The first example will be simulated Rasp Pi scanning app in “C” and Python. The second and third examples will be in Python and they will show large text presentation and dynamic bars.

Python Curses

Python curses are standard in Python, and they include features such as:

  • support ASCII draw characters
  • basic color support
  • window and pad objects which can be written to and cleared independently

As a first example I wanted to have a colored background, a header and footer and some dynamic text.

curses_text

The first step is to define a curses main screen object (stdscr). The next step is to enable color and to create some color pairs. Using color pairs and the screen size (height, width = stdscr.getmaxyx()) it is possible to add a header and footer strip using the srtdscr.addstr command.

The stdscr.nodelay command allow the program to cycle until the stdscr.getch() call returns a key.

# curses_text.py - create a curses app with 10 dynamic values
#
import curses , time, random

# create a curses object
stdscr = curses.initscr()
height, width = stdscr.getmaxyx() # get the window size

# define two color pairs, 1- header/footer , 2 - dynamic text, 3 - background
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_BLUE)

# Write a header and footer, first write colored strip, then write text
stdscr.bkgd(curses.color_pair(3))
stdscr.addstr(0, 0, " " * width,curses.color_pair(1) )
stdscr.addstr(height-1, 0, " " * (width - 1),curses.color_pair(1) )
stdscr.addstr(0, 0, " Curses Dynamic Text Example" ,curses.color_pair(1) )
stdscr.addstr(height-1, 0, " Key Commands : q - to quit " ,curses.color_pair(1) )
stdscr.addstr(3, 5, "RASPBERRY PI SIMULATED SENSOR VALUES" ,curses.A_BOLD )
stdscr.refresh()

# Cycle to update text. Enter a 'q' to quit
k = 0
stdscr.nodelay(1)
while (k != ord('q')):
# write 10 lines text with a label and then some random numbers
for i in range(1,11):
    stdscr.addstr(4+ i, 5, "Sensor " + str(i) + " : " ,curses.A_BOLD )
    stdscr.addstr(4+ i, 20, str(random.randint(10,99)) ,curses.color_pair(2) )
    time.sleep(2)
    k = stdscr.getch()

curses.endwin()

The simulated Pi values will refresh every  10 seconds until the “q” key is pushed and then the terminal setting are returned to normal (curses.endwin()) and the program exits.

“C” Curses Example

For this “C” example I used a Raspberry Pi. The curses library needs to be installed by:

 sudo apt-get install libncurses5-dev

The curses syntax is similar between “C” and Python but not 100%. For example in Python the addstr command includes a color pair reference, but in “C” this is not supported so an attribute on/off (attron/attroff) command is used to reference the color pair. Below is the “C” code:

/* c1.c - Basic Curses Example */

#include <curses.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    int row, col, k;    
// Create a curses object and define color pairs
    initscr();
    getmaxyx(stdscr,row,col);
    start_color();
    init_pair(1,COLOR_RED,COLOR_WHITE);
    init_pair(2,COLOR_GREEN,COLOR_BLACK);
    init_pair(3,COLOR_WHITE,COLOR_BLUE);
    curs_set(0);
    noecho();
    //keypad(stdscr,TRUE);
    nodelay(stdscr, TRUE);
// Write a header and footer, first write colored strip, then write text
    bkgd(COLOR_PAIR(3));
    attron(COLOR_PAIR(1));
// Create a top and bottom color strip
    for (int i = 0; i < col; i++) {
        mvaddstr(0, i,  " ");
        mvaddstr(row-1, i,  " ");
    }
    mvaddstr(0, 0,  " Curses C Dynamic Text Example");
    mvaddstr(row-1, 0,  " Key Commands: q - to quit");
    attroff(COLOR_PAIR(1));   
    mvaddstr(2, 5,"RASPBERRY PI SIMULATED SENSOR VALUES" );
    refresh();
// Cycle with new values every 2 seconds until a q key (133) is entered    
    while (k != 113)
    {
        attroff(COLOR_PAIR(2));
        for (int i = 0; i < 10; i++) {
            mvprintw((4+i), 5,  " Sensor %d : ",i);
        }
        attron(COLOR_PAIR(2));
        for (int i = 0; i < 10; i++) {
            mvprintw((4+i), 20,  "%d",rand() %100);
        }
        k = getch();
        sleep(2);
    }
    endwin();
    exit(0);
}

To compile and run the program (c1.c) enter:

gcc -o c1 c1.c -lncurses
./c1

The “C” example should look very similar to the earlier Python example.

Figlet for Large Custom Text

Large Custom Text can be generated using the Python Figlet library.  Figlet has an extensive selection of text presentations and it uses standard ASCII character to generate the large text presentations. The Figlet library is installed by:

pip install pyfiglet

An example from the Python shell:

pyshell_figlet

For a Figlet example, I wanted to create a large heading and a large dynamic value.

curses_di

The Figlet library can be used to generate a string with user defined texted presented a large text-like format. A little bit of testing is required because the Figlet generated text can be 3,4,5 or more characters tall and the string needs to be added to very left end of the window.

# curses_di.py - show a large heading and large dynamic value
#
import curses, time
import pyfiglet, random

def get_io():
    global value1
    testvalue = str(random.randint(100,1000)/10) + " C"
    value1 = pyfiglet.figlet_format(testvalue, font = "starwars" )

# Create a string of text based on the Figlet font object
title = pyfiglet.figlet_format("Raspi Data", font = "small" ) 

stdscr = curses.initscr() # create a curses object
# Create a couple of color definitions
curses.start_color()
curses.init_pair(1, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)

# Write the BIG TITLE text string
stdscr.addstr(1,0, title,curses.color_pair(1) )
stdscr.addstr(8,0, "Sensor 1: GPIO 7 Temperature Reading" ,curses.A_BOLD)

# Cycle getting new data, enter a 'q' to quit
stdscr.nodelay(1)
k = 0
while (k != ord('q')):
    get_io() # get the data values
    stdscr.addstr(10,0, value1,curses.color_pair(2) )
    stdscr.refresh()
    time.sleep(2)

    k = stdscr.getch()

curses.endwin()

I found that the the small and doom fonts worked well in my testing. To check out and test Figlet fonts online see:

http://patorjk.com/software/taag/#p=display&f=Slant&t=Dude%20what%20are%20you%20doing%20%3F

Curses Windows

By defining a curses window it is possible to clear and write to a window that it is independent from the background. The syntax to create a curses window object is:

mynewwindow = curses.newwin(height, width, begin_y, begin_x)

Windows are ideal for applications where multiple items such as Figlet objects are used. Below is an example with two large Figlet values.

Figlet2win


# Create a static 2 large values example
#
import curses, time
import pyfiglet, random
# Create a string of text based on the Figlet font object
title = pyfiglet.figlet_format("Weather Station 2", font = "small" )

stdscr = curses.initscr() # create a curses object
# Create a couple of color definitions
curses.start_color()
curses.init_pair(1, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)

# Write the BIG TITLE text string
stdscr.addstr(1,0, title,curses.color_pair(1) )
stdscr.refresh()

win1 = curses.newwin(9, 44, 6, 4)
win1.addstr(8,0, "Sensor 1: Temperature Reading" ,curses.A_BOLD)

win2 = curses.newwin(9, 44, 6, 50)
win2.addstr(8,0, "Sensor 2: Humidity Reading" ,curses.A_BOLD)
value1 = pyfiglet.figlet_format("23 C", font = "doom" )
win1.addstr(0,0,value1,curses.color_pair(2) )
win1.refresh()
value2 = pyfiglet.figlet_format("35 %", font = "doom" )
win2.addstr(0,0, value2 ,curses.color_pair(2) )
win2.refresh()

# Hit any key to exit
stdscr.getch()
curses.endwin()

Dynamic Bars Example

For the Dynamic bars example I created a get_io function to simulate two real time data  values.

As a first step I created some background information such as headings, a header and a footer. By using the call: height, width = stdscr.getmaxyx() , I am able to position banners at the top and bottom of the terminal window. All of the background info is written to the stdscr object.

Two windows objects (win1 and win2) are used for the real time dynamic bars. Old bar data is removed using the win1.clear() and win2.clear() calls. Like the static example the dynamic bars are created by writing a fill character multiplied by the actual real time value (win1.addstr(1, 1, bar * value1) ). A window.refresh() command is used to show the changes.

The stdscr.getch() method is used to catch keyboard input, and the terminal program is exited when a quit character, “q” is entered.

The complete two dynamic bar program is shown below:


# Simple bar value interface
#
import curses
import time

# get_io is using random values, but a real I/O handler would be here
def get_io():
    import random
    global value1, value2
    value1 = random.randint(1,30)
    value2 = random.randint(1,30)

bar = '█' # an extended ASCII 'fill' character
stdscr = curses.initscr()
height, width = stdscr.getmaxyx() # get the window size
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)

# layout the header and footer
stdscr.addstr(1,1, " " * (width -2),curses.color_pair(1) )
stdscr.addstr(1,15, "Raspberry Pi I/O",curses.color_pair(1) )
stdscr.hline(2,1,"_",width)
stdscr.addstr(height -1,1, " " * (width -2),curses.color_pair(1) )
stdscr.addstr(height -1,5, "Hit q to quit",curses.color_pair(1) )

# add some labels
stdscr.addstr(4,1, "Pi Sensor 1 :")
stdscr.addstr(8,1, "Pi Sensor 2 :")

# Define windows to be used for bar charts
win1 = curses.newwin(3, 32, 3, 15) # curses.newwin(height, width, begin_y, begin_x)
win2 = curses.newwin(3, 32, 7, 15) # curses.newwin(height, width, begin_y, begin_x)

# Use the 'q' key to quit
k = 0
while (k != ord('q')):
    get_io() # get the data values
    win1.clear()
    win1.border(0)
    win2.clear()
    win2.border(0)
# create bars bases on the returned values
    win1.addstr(1, 1, bar * value1, curses.color_pair(2))
    win1.refresh()
    win2.addstr(1, 1, bar * value2 , curses.color_pair(3))
    win2.refresh()
# add numeric values beside the bars
    stdscr.addstr(4,50, str(value1) + " Deg ",curses.A_BOLD )
    stdscr.addstr(8,50, str(value2) + " Deg ",curses.A_BOLD )
    stdscr.refresh()
    time.sleep(2)
    stdscr.nodelay(1)
    k = stdscr.getch() # look for a keyboard input, but don't wait

curses.endwin() # restore the terminal settings back to the original

cbars

For testing I used a random simulator for the data but the get_io function could be easily configured to connect to a Raspberry Pi or Arduino module.

The outline boxes in the window object could look strange if you are using a Window’s based SSH client like Putty. To create the problem in Putty’s settings, select: Window ->  Translations and use VSCII as the remote character set.

putty

Final Comments

Curses is definitely an ‘old school’ technology but it offers a simple solution for SSH and terminal based connections.

Pi with Neopixels/Simulated Neopixels

Neopixels are addressable full-colour RGB LEDs that come in a variety of different arrangements. Ranging from single LEDs, to matrix arrays and a variety of sewable components that can be used on wearable products.

dif_neopixels

Neopixels were originally made available for Arduino projects, but now there are also Python libraries for Raspberry Pi’s.

In this blog I will be looking at setting up neopixels components on Raspberry Pi’s, and then I will show some “soft” neopixel layouts using the Python Tkinter graphic library.

Getting Started

To load the Raspberry Pi neopixel libary comes from the nice people at Adafruit, and it is loaded by:

sudo pip3 install rpi_ws281x adafruit-circuitpython-neopixel

It is important to note that neopixels can draw a lot of power so consider looking at using external 5V power for projects with a lot of LEDs. The LED power consumption is based on:

  • How many neopixel LEDs are lit at one time, and
  • What the intensity of the LEDs is.

A few other import points are:

  • not all neopixel strips are the same. Different strips will vary greatly from the LED intensity, and more importantly on the RGB vs. GRB addressing.
  • NeoPixels must be connected to D10, D12, D18 or D21 to work
  • For NeoPixels to work on Raspberry Pi, you must run the code as root

The neopixels are wired with 3 pins : 5V (VCC), GND and signal in. The default signal in wires to Pi pin 18. Neopixel component can be connected in series with data in and out connectors.

data_in_out

Below is an example that will set all the LEDs to a light magenta and then it will cycle one LED to a brighter RED. The overall neopixel string has a 10% brightness.


# Python neopixel example to cycle an LEDs

import board
import neopixel
import time

ORDER = neopixel.RGB  # or neopixel.GRB
numpixels = 12

# Create a pixel object with 12 pixels and low intensity
pixels = neopixel.NeoPixel(board.D18,numpixels , brightness=0.10, auto_write=True, pixel_order=ORDER)
while True:
for i in range(numpixels):
   pixels.fill((10, 0, 40)) # fill all pixels in light magenta
   pixels[i] = (80,0,0) # fill one pixel in brighter red
   time.sleep(1)

Depending on the type and manufacturer of the neopixels the result will look a little different. Some trial and error testing will be required to determine if the strips are RGB or GRB.

 

Simulated Neopixels

If you don’t have neopixels or if what to simulate neopixels then the Python Tkinter graphic library can be used to create a variety of different arrangements. For my testing I create two arrangements: a strip and a matrix.

The important things that I learned was how to create a array object that could simulate the neopixel object. To do this in Python:


import tkinter as tk

root = tk.Tk()
root.title("Soft NeoPixel Strip")

numleds = 25

# Create an array that can be used later in Tkinter

ledstrip = ['' for i in range(numleds)]

for i in range(numleds):
   ledstrip[i] = tk.Label(root,relief='raised',width=3 ) # a label array
   ledstrip[i].grid(row = 0, column = i) # position the labels is a horizontal row
root.mainloop()

Simulated Strip Neopixel

Below is an example of a soft “strip” neopixel application with a demo function.

py_neo_Strip

# Python Neopixel Single Strip Presentation
#
import tkinter as tk

numleds = 25

theled = 0

def stringdemo():
    # move a coloured LED around the string
    global theled
    ledstrip[theled].configure(background= 'white')
    theled = theled + 1
    if theled >= numleds:
        theled = 0
    ledstrip[theled].configure(background= 'sky blue')
    root.after(500, stringdemo)
    
root = tk.Tk()
root.title("Soft NeoPixel Strip")

# create an LED object 
ledstrip = ['' for i in range(numleds)]

# put the LED object into a horizontal strip
for i in range(numleds):
    ledstrip[i] = tk.Label(root,width=2,height=1,relief='raised',background = 'white')
    ledstrip[i].grid(row = 0, column = (i+1))

root.after(500, stringdemo) #start a demo

root.mainloop()

Simulated Matrix Neopixels

Below is an example of a soft matrix neopixel application.

py_neo_Matrix

# Python Neopixel Matrix Presentation
#
import tkinter as tk

numleds = 100
rowcnt = 10
colcnt = int (numleds/rowcnt)

theled = 0

def stringdemo():
    # move a coloured LED around the string
    global theled
    ledstrip[theled].configure(background= 'dark gray')
    theled = theled + 1
    if theled >= numleds:
        theled = 0
    ledstrip[theled].configure(background= 'red')
    root.after(500, stringdemo)
    
    
root = tk.Tk()
root.title("Soft NeoPixel Matrix")

# create LED object
ledstrip = ['' for i in range(numleds)]

# put the LED object into a grid
for i in range(rowcnt):
    for j in range(colcnt):
        ledstrip[theled] = tk.Label(root,width=4,height=2,relief='raised',background = 'dark gray')
        ledstrip[theled].grid(row = i, column = j)
        theled = theled + 1

theled = 0 #reset the led index for the demo
root.after(500, stringdemo)

root.mainloop()

Summary

Neopixels can be used on custom lighting applicatons, for example I used them on a water fountain project.

Given a choice I would recommend using Arduino hardware over the Raspberry Pi hardware for neopixel projects. I found that the Arduino neopixel library to be much more stable and considerably faster than the Pi version.

Lua and Raspberry Pi

Over the years I’ve been seeing Lua programs pop up in places that I didn’t expect, for example:

In this blog I wanted to document a couple of examples of using Lua on Raspberry Pi projects. (For a blog on using Lua with simple curses GUI’s).

An Introduction of Lua

Lua is a lightweight interpreted scripting language. The Lua interpreter is supported on most operating systems, however like Python not all of its libraries are support on all OS’s.

Lua’s greatest following is in the gaming world. The love2d  graphic framework is open source and it works on Windows, Mac OS X, Linux, Android and iOS.

There are some good Lua tutorials to get you jump started. If you’re familiar with Python and Basic programming the Lua language is fairly easy to learn.

Install of Lua on a Raspberry Pi

To install Lua  on a Pi enter the following lines:

sudo apt-get update
sudo apt-get install lua5.1
sudo apt-get install liblua5.1-0-dev -- development files, need by LuaRocks
sudo apt-get install lua-socket
sudo apt-get install luarocks -- package manager for Lua modules

sudo luarocks install luasocket

There a number of versions of Lua, going from 5, 5.1, 5.2 to 5.3. I used version 5.1 because most of the examples used this version, but there isn’t a problem loading multiple versions.

Lua has a package manager called luarocks, this is similar to pip on Python, where you can install custom libraries or packages on the Pi.

There are a number of choices on how Lua can access Pi GPIO pin. I found that the lua-periphery library to be a reliable option. To install this library enter:

sudo luarocks install lua-periphery

GPIO Examples

To read a GPIO input:

local GPIO = require('periphery').GPIO

-- Open GPIO 10 with input direction
local gpio_in = GPIO(10, "in")

local value = gpio_in:read()
print ("GPIO pin 10 :", value)

gpio_in:close()

To toggle an LED with a keyboard value :

-- toggle.lua : get user a user value to send to a GPIO

local GPIO = require('periphery').GPIO

-- Open GPIO 4 with output direction
local gpio_out = GPIO(4, "out")

while (true)
do
        print ("Enter an output value:")
        s = io.read("*n")
        gpio_out:write(s)
        print ("Output value:", gpio_out:read(),"\n")
end
gpio_out:close()

To run the LED toggle program:

$ sudo lua5.1 gpio1.lua
Enter an output value:
1
Output value: true

Enter an output value:
0
Output value: false

To exit the program enter “Control-C”

Lua Socket Applications

For many application you want to remotely view or control data. One way to do this is to create an application that is a socket server. A simple Lua socket server application that show what a remote socket client sends is:

-- sock.lua : a socket server that prints client input
local socket = require("socket")

-- create a TCP socket and bind it to the local host, at any port
local server = socket.bind("*", 444)

-- loop forever waiting for clients
print ("Lua Socket Server on Port 444")
while 1 do
  -- wait for a connection from any client
  local client = server:accept()

  -- receive the line
  local line, err = client:receive()
  print("Input:", line)

  client:close()

The socket server can be tested by opening a second terminal window and then use a bash script to send a text string to the open port (444 for this example):

$ echo "1" > /dev/tcp/localhost/444
$ echo "0" > /dev/tcp/localhost/444

Our Lua socket server application will show the client text that is sent:

 $ sudo lua5.1 sock.lua
Lua Socket Server on Port 444
Input: 1
Input: 0

 

A Lua Socket Server with GPIO Control

The next step is combine that socket server with GPIO call. For the next example if a 0 or 1 is sent, then the GPIO output will be set to 0 or 1.

-- load libraries
local socket = require("socket")
local GPIO = require('periphery').GPIO

local gpio_out = GPIO(4, "out")

-- create a TCP socket and bind it to the local host, at any port
local server = socket.bind("*", 444)


-- loop forever waiting for clients
print ("Lua Socket Server on Port 444")
while 1 do
  -- wait for a connection from any client
  local client = server:accept()

  -- receive the line
  local line, err = client:receive()
  print("Input:", line)
  if (line == "0")
  then
    gpio_out:write(0)
  elseif (line == "1")
  then
    gpio_out:write(1)
  end

  client:close()

You can use the same bash script from the above example to toggle a GPIO pin.

Summary

I like how in 1 day I was able to go from, no real knowledge on Lua, to remotely using sockets to control Raspberry Pi GPIO pins.

I found my biggest issue was a standard and simple graphic interface. Love2D has a lot of potential but it’s a little like Python PyGame great for games but a little over kill if you want to create a dialog with a couple of buttons. The Lua curses library offers an “old style” interface but the documentation and examples are a little weak.

I can’t see myself giving up Python for Lua but I could perhaps see using Lua on projects where:

  • Lua is used on ESP-8266/NodeMCU and you want to include some Raspberry Pi integration
  • Lua is been used on a Redis database server and either a Pi or a ESP-8266 is passing up/down data

 

ODROID – A Raspberry Pi Alternative

The ODROID is a series of single-board computers manufactured by Hardkernel in South Korea. The ODROID-C1+ ($35) and the ODROID-C2 ($46) have a form factor similar to the Raspberry Pi 3. The higher end ODROID-XU($59) which is around 5 times faster than the Pi 3 has a signifigently different board layout.

For my testing I looked at the ODROID-C2 ($46), it is a little more expensive than a Pi 3 but the literature states that it’s 2-3 times faster.

My goal was to see if I could use the ODROID-C2 for some typical Raspberry Pi applications. In this blog I will be looking at doing C, Python and NodeRed programming from a Pi user perspective.

OD_PI

I’ve been happy with the functionality and openness of the Raspberry Pi platform, however I find its desktop performance to be sluggish. For only a few dollars more than a Pi 3 the ODROID-C2 CPU, RAM and GPU specs are impressive.

ODROID-C2 / Raspberry Pi 3 Hardware Comparison
Odroid C2 Raspberry Pi 3
CPU Amlogic S905 SoC
4 x ARM Cortex-A53 1.5GHz
64bit ARMv8 Architecture @28nm
Broadcom BCM2837
4 x ARM Cortex-A53 1.2Ghz
64bit ARMv7 Architecture @40nm
RAM 2GB 32bit DDR3 912MHz 1GB 32bit LPDDR2 450MHz
GPU 3 x ARM Mali-450 MP 700MHz 1 x VideoCore IV 250MHz
USB 4 Ports 4 Ports
Ethernet / LAN 10 / 100 / 1000 Mbit/s 10 / 100 Mbit/s
Built in WiFi No Yes
Built in Bluetooth No Yes
IR Receiver Built in Needs add-on
I/O Expansion 40 + 7 pin port
GPIO / UART / I2C / I2S / ADC
40 pin port
GPIO / UART / SPI / I2S
Camera Input USB 720p MIPI CSI 1080p
List Price (US) $46 $35

First Impressions

The ODROID-C2 is almost the same footprint as the Raspberry Pi 3 but not exactly. I found that because the microSD mounting is different some, but not all, of my Pi cases could be used .

lego_case

When you are designing your projects it is important to note that the ODROID-C2 does not have a built in Wifi or Bluetooth adapters, so you’ll need wired connections or USB adapters. Like some of the Orange Pi modules the ODROID-C2 has a built-in IR connection.

ODROID-C2 can be loaded with Ubuntu, Arch Linux and Android images. For my testing I used the Armbian 5.40 Ubuntu desktop and the performance was signifigently faster than my Raspberry Pi 3 Raspian desktop. I could definitely see ODROID-C2 being used as a low cost Web browser station.

The ODROID-C2 images are quite lean, so you will need to go the ODROID Wiki, https://wiki.odroid.com, for instructions on loading additional software components.

The ODROID-C2 has a 40 pin General Purpose Input/Output (GPIO) arrangement like the Pi 3, so it is possible to use Pi prototyping hats on the ODROID-C2 .

pi_hats

There are some noticeable differences in the pin definitions between the two modules, so for this reason I didn’t risk using any of my intelligent Pi hats on the ODROID-C2. The gpio command line tool can be used to view the pin definitions:

Od_readall

The Raspberry Pi GPIO names are in the range of 2 to 27, whereas the ODROID-C2 GPIO ranges are in the 200’s, because of this don’t expect to be able to run all your Raspberry Pi code “as is” on the ODROID-C2.

Unlike the Arduino the Raspberry Pi platform has no built in support for analog inputs. I got pretty excited when I noticed that the ODROID-C2 had two built in Analog-to-Digital Converter (ADC) pins (AIN.1 on pin 37 and AIN.0 on pin 40). However after some investigation I found that these pins had virtually no example code and they only support 1.8 volts. Most of my analog input sensors require 3.3V or 5V so I’m not sure how often these ADC pins will be used.

Python Applications

The ODROID-C2 Wiki references the RPi.GPIO and wiringpi Python libraries. I tested both of these libraries and I found that standard reads and writes worked, but neither of these libraries supported the callback functions like the Raspberry Pi versions.

For existing Pi projects where you are using callback functions for rising and/or falling digital signals (like intrusion alarms) you will need to do some re-coding with a polling method. It’s also important to note that the ODROID RPi.GPIO library is a little confusing because it uses the Pi pin names and not the ODROID pin names, so for example ODROID-C2 physical pin 7 is referenced as GPIO.04 (as on a PI) and not GPIO.249 (the ODROID-C2 name). Below is simple Python example that polls for a button press and then toggles an LED output when a button press is caught.

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

button = 4 # physical pin 7, PI GPIO.04, ODROID-C2 GPIO.249
led = 17 # physical pin 11, PI GPIO.17, ODROID-C2 GPIO.247
GPIO.setup(led, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(button, GPIO.IN, pull_up_down = GPIO.PUD_UP)

print ('Wait for button...')
while True:
    if GPIO.input(button) == 1:
        GPIO.output(led,0)
    else:
    GPIO.output(led,1)
    print "button pressed"

There are some excellent Python libraries that are designed to work with the Raspberry Pi. However it will require some trial and error to determine which libraries will and won’t work with the ODROID-C2.

I tried testing the DHT11 temperature and humidity sensor with the ODROID-C2, and unfortunately the library had a “Segmentation Error” when I tried running an example.

Node-Red

NodeRed can be installed on ODROID-C2 by using the manual install instructions for Raspbian at nodered.org. This install procedure will add a start item to the desktop Application menu, but due to hardware differences the Raspberry Pi GPIO input/output components will not load.

To read and write to Raspberry Pi GPIO a simple workaround is to use exec nodes to call the gpio utility.

Odroid_NodeRed

The command line syntax for writing a gpio output is: gpio write pin state, and for reading it is: gpio read pin. One of the limitations of this workaround is that you will need to add a polling mechanism, luckily there are some good scheduling nodes such as Big Timer that can be used.

C Applications

Programming in C is fairly well documented and an ODROID “C Tinkering Kit” is sold separately. The wiringPi library is used in C applications.

Below is a C version of the Python example above. It is important to note that these 2 examples talk to the same physical pins but the C wiringPi library uses the ODROID-C2 wPi numbers and the Python RPi.GPIO library uses the Pi BCM numbers.

// led.c - Read/Write "C" example for an Odroid-C2
//
#include  <wiringPi.h>

int main(void)
{
    wiringPiSetup();
    int led = 0;
    int button = 7;
    pinMode(led, OUTPUT);
    pinMode(button, INPUT);
 
    for (;;)
    {
        if (digitalRead(button) == 1) {
           digitalWrite(led, LOW); 
        }
	else
	{
           digitalWrite(led, HIGH); 
        }
    }
    return 0;
}

To compile and run this program:

$ gcc -o led led.c -lwiringPi -lpthread
$ sudo ./led

Summary

I liked that I could reuse some of my Pi cases and prototyping hats with the ODROID-C2.

As a PI user I found that coding in C, Python and NodeRed on the ODROID-C2 was fairly easy, but there were many limitation compared to the Pi platform. The ODROID Wiki had the key product documentation, but it was no where near the incredibly rich documentation that exists with Raspberry Pi modules.

There are a some excellent Python libraries and PI hardware add-ons that support a variety of sensors and I/O applications, these may or not work with the ODROID hardware.

During the development cycle of a project it is nice to have a faster interface, but typically my final projects do not need any high end processing and they can often run on low end Raspberry Pi 1 modules. So for now I would stick to a Raspberry Pi for GPIO/hardware type projects.

I enjoying playing with the ODROID-C2 and for projects requiring higher performance such as video servers, graphic or Web applications then the ODROID-C2 module is definitely worth considering.

Pi Thermometer using Python Turtles

Python Turtles are a great way to start kids in programming. Turtles offer a simple step-by-step graphical presentation that has tons of tutorials and examples.

Turtles can also be used on Raspberry Pi projects. In this blog I wanted to look at a Turtle example that reads a temperature sensor and graphically shows the result as an “old style” thermometer.

Getting Started

Python Turtles is probably already  loaded on your system, if not enter:

pip install turtles

The turtle library opens a graphic screen with the very center of the screen being (0,0). This is a little different than many other graphic systems (like PyGame) where the top left  is (0,0).

Different turtle objects can be defined and moved around the screen. A useful feature of turtles is that you can clear all the drawing from one turtle without effecting what the other turtles have done. (Note: this is useful in this thermometer example where we can have a static background turtle and a dynamic turtle that updates with new information).

Below is an example with 3 turtles. The first turtle (t1) is set to red and then moved forward, left, forward and then sent home. The second turtle (t2) is set to purple and given a turtle symbol. The third turtle (t3) is set to green and then moved to a position and a thick circle is drawn.


from turtle import *

setup(500, 400)
Screen()
title(" 3 Turtles")

# First red turtle goes forward, left, forward and back home
t1=Turtle()
t1.color("red")
t1.forward(100)
t1.left(90)
t1.forward(100)
t1.home()

# Second purple turtle has a turtle shape
t2=Turtle()
t2.shape("turtle")
t2.color("purple")
t2.right(45)
t2.forward(100)
t2.left(90)
t2.forward(100)

# Third green turtle goes to a location and makes a thick circle
t3=Turtle()
t3.color("green")
t3.pensize(10)
t3.up()
t3.goto(-100,-10)
t3.down()
t3.circle(80)

turtles1

Drawing an “Old Style” Thermometer

For this project we wanted to draw an “old style” mercury thermometer, with a bulb of red mercury at the bottom and a tube above it.

therm_bg

Using the simple turtle commands like : move, left, right, forward etc. is great to learn but it can be awkward for more difficult drawings.

A more efficient approach is to define an array of x,y coordinates and then move to each position in the array. For example the upper tube can be drawn by:


# Define an array of x,y coordinates for the top tube
outline = ((0,-50),(25,-50),(25,210),(-25,210),(-25,-50),(0,-50))
for pos in outline: # move to each tube x,y point
       thermo.goto(pos)
       thermo.pendown()

Circles are created using a turtle.circle(width) command. To fill an object or a group of objects a turtle.begin_fill() and a turtle.end_fill() set of command is used.

For our example the filled circle for the bulb is created by:


# put the pen up and move to the circle starting
thermo.penup()
thermo.goto(0,-137)
thermo.pendown()
thermo.pensize(5)
thermo.color("black","red")
# draw the circle with fill
thermo.begin_fill()
thermo.circle(50)
thermo.end_fill()

The complete code to draw the complete thermometer background would be:


# Create a background for an "old style" thermometer
from turtle import Turtle,Screen, mainloop
import random, time

# Define a Turtle object
thermo = Turtle()
thermo.penup()
thermo.hideturtle()
thermo.pensize(5)

# Define an array of x,y coordinates for the top tube
outline = ((0,-50),(25,-50),(25,210),(-25,210),(-25,-50),(0,-50))
for pos in outline: # move to each tube x,y point
       thermo.goto(pos)
       thermo.pendown()
# put the pen up and move to the circle starting
thermo.penup()
thermo.goto(0,-137)
thermo.pendown()
thermo.pensize(5)
thermo.color("black","red")
# draw the circle with fill
thermo.begin_fill()
thermo.circle(50)
thermo.end_fill()

mainloop()

Raspberry Pi Hardware Setup

There are a number of different temperature sensors that can be used. For our example we used a low cost ($5) DHT11 temperature/humidity sensor. The DHT11 sensor that we used had 3 pins, (Signal, 5V and GND), and we wired the Signal pin to the Pi physical pin 7.

DHT11-Wiring-Diagram

There is a DHT temperature/sensor Python library that is installed by:

sudo pip install Adafruit_DHT

A Python DHT test program would be:


#!/usr/bin/python
import sys
import Adafruit_DHT

sensor_type = 11 # sensor type could also be 22, for DHT22

dht_pin = 4 # Note: BCM pin 4 = physical pin 7

humidity, temperature = Adafruit_DHT.read_retry(sensor_type, dht_pin)

print( "Temp: ", temperature, " deg C")

print( "humidity: ", humidity, " %")

Turtle Thermometer

Now for the final project we can start pulling things together.

For the thermometer project we used 2 turtles, a static background turtle (thermo) and a dynamic turtle (bar). The bar turtle is cleared and redrawn in the drawbar() function.

A screen object wn is used to resize the window and add a title.

For testing a random integer can be used. This is also useful for checking the 0-40C range of the bar.

The full code and an screen shot are below:


from turtle import Turtle,Screen, mainloop
import random, time
import Adafruit_DHT

# Update the temperature bar height and value
def drawbar(temp):
       top = (-50 + 260 * temp/40)
       boutline = ((0,-50),(20,-50),(20,top),(-20,top),(-20,-50),(0,-50))
       bar.penup()
       bar.clear()  # clear the old bar and text
       bar.begin_fill()
       for pos in boutline:
              bar.goto(pos)
       bar.end_fill()
       bar.goto(30,top)
       bar.write(str(temp) + " C",font=("Arial",24, "bold"))

# Setup a default screen size and Title
wn = Screen()
wn.setup(width = 500, height = 500)
wn.title("RaspPi Temperature Sensor")

# define a static thermo backgroup object and a dynamic bar object
thermo = Turtle()
thermo.penup()
bar = Turtle()
bar.color("red")
bar.hideturtle()

# define an array for the top tube
outline = ((0,-50),(25,-50),(25,210),(-25,210),(-25,-50),(0,-50))
thermo.hideturtle()
thermo.pensize(5)
for pos in outline:
       thermo.goto(pos)
       thermo.pendown()

# add some temperature labels
thermo.penup()
thermo.goto(50,-50)
thermo.write("0 C")
thermo.goto(50,210)
thermo.write("40 C")

# draw the filled bulb at the bottom
thermo.goto(0,-137)
thermo.pendown()
thermo.pensize(5)
thermo.color("black","red")
thermo.begin_fill()
thermo.circle(50)
thermo.end_fill()

# Update the temperature
while True:
       humidity, temperature = Adafruit_DHT.read_retry(11, 4)
       # use a random number for testing
       #temperature = random.randint(0,40)
       drawbar(temperature)
       time.sleep(5)

PI_thermo

Final Comments

Compared to other Python graphic libraries (like PyGame, Tkinter or Qt) Turtle graphics can be slow and perhaps limiting, but for kids Turtles projects are super easy to configure. If you are doing simple stuff Turtles requires a lot less code than the other graphic libraries (keyboard input is a good example of this).

There are a lot of possible fun Raspberry Pi projects that can be done with Turtle. Some of the other projects that we have done include:

  • use a Wii remote to draw pictures
  • create a Turtle drawing as you drive a rover (show the path)

Raspberry Pi and Go Programming

Go or GoLang is a compiled programming language developed by Google.

Go is widely used for Web Development and it is one of the fastest growing programming languages today. Unlike Java which is runs in a JVM (Java Virtual Machine) , Go compiles directly to Windows, OS X or Linux  executable files. 

In this blog we will look at creating two Go programs that talk to Raspberry Pi GPIO. The first will be a simple keyboard input program and the second will be a standalone Go web app to control GPIO pins.

Installation

To install go enter:

sudo apt-get install golang

To test that the install is working you can check the Go version number:

$ go version
go version go1.7.4 linux/arm

A “Hello World” example (hello.go) is:


package main

import "fmt"

func main() {

  fmt.Println("Hello World");

}

The hello.go code is compiled and ran by:

$ go build hello.go  # compile the go code

$ ./hello   # run the go code

Hello World

Raspberry Pi GPIO

There are a number of different ways to have Go connect to the Pi General Purpose Inputs and Outputs (GPIO). For this example I am going to look at shelling out to the gpio command line utility, but there are also go rpi libaries that can be used.

For testing I like to use the gpio utility because it offers a good selection of commands and I can manually test and verify the command before I use them in my Go code. For help on gpio  use the -h option.

The Raspberry Pi hardware setup used an LED with a resistor on physical pin 7 (BCM pin 4).

Led_setu

Our first go program (keyin.go) will read keyboard input and then to shell out twice to gpio, first time to write a value and the second time to read the value back.

package main

import (
    "bufio"
    "fmt"
    "os/exec"
    "os"
)

func main() {
    // Get keyboard input
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Enter value for GPIO pin 7 : ")
    pinval, _ := reader.ReadString('\n')

    // Write to GPIO pin 7 using keyboard input 
    testCmd := exec.Command("gpio","write", "7", pinval)
    testOut, err := testCmd.Output()
    if err != nil {
        println(err)
    }
    // Read back the GPIO pin 7 status
    testCmd = exec.Command("gpio","read", "7")
    testOut, err = testCmd.Output()
    if err != nil {
        println(err)
    } else {
      fmt.Print("GPIO Pin 4 value : ")
      fmt.Println(string(testOut))
    }
}

To compile and run the keyin.go program:

 $ go build keyin.go
 $ ./keyin
Enter value for GPIO pin 7 : 1
GPIO Pin 4 value : 1

Simple Go Web Application

For a starting example we’ll make a go web application (web_static.go) show a web page called web_static.html.

The web_static.html file will be :

<html>
  <head>
    <title>GO PI Static Page</title>
  </head>
  <body>
    <h1>GO PI Static Page</h1>
    <hr>
    This is a static test page
  </body>
</html>

The web_static.go program will need to import the “net/http” library. A http.HandleFunc call is used to look for the default address “/” and serve up our web_static.html file. The http.ListenAndServe function listens for web requests on port 8081.

package main

import (
    "log"
    "net/http"
)

func main() {
    // Create default web handler, and call a starting web page
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "web_static.html")
        println("Default Web Page")
    })
    // start a listening on port 8081
    log.Fatal(http.ListenAndServe("8081", nil))

}

The Go code can be compiled and run by:

$ go build web_static.go
$ ./web_static
Default Web Page

From a web browse pointed at the Raspberry Pi on port 8081, our web page will show as:

web_static

Go Web App with Pi GPIO

The next step is to create a Web Page that can pass some parameters. For this application we’ll turn a GPIO output on and off.

A new web page (go_buttons.html) is created with two buttons. HTML anchor tags are used to pass  /on and /off parameters to our Web app.

A CACHE-CONTROL meta tag set to NO-CACHE is needed to ensure that the web page always refreshes.  I also included an EXPIRES meta tag (= 0) so that the browser always see the page as expired . If you don’t include meta tags the web page may only update once.

<html>
  <head>
    <title>GO PI GPIO</title>
    <META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
    <META HTTP-EQUIV="EXPIRES" CONTENT="0">
  </head>
  <body>
    <h1>Go Raspberry Pi GPIO Test</h1>
    <a href="/on"><button>Set LED ON</button></a><br>
    <a href="/off"><button>Set LED OFF</button></a>
  </body>
</html>

Our new Go Web app (go_buttons.go) now includes two more http.HandleFunc handler functions, one for /on and for /off. These handler functions call a new function called gpio that is used to write a outputs and read back the output status. 

Our newly created gpio function shells out twice to the gpio command line utility, first time to write a value and the second time to read the value back.

package main

import (
	"log"
	"net/http"
	"os/exec"
)
func gpio( pinval string) {
    testCmd := exec.Command("gpio","write", "7", pinval)  
    testOut, err := testCmd.Output()      
    if err != nil {
        println(err)
    }
    testCmd = exec.Command("gpio","read", "7") 
    testOut, err = testCmd.Output()
    if err != nil {
        println(err)
    } else { 
      print("GPIO Pin 4 value : ")  
      println(string(testOut))
    }
}

func main() {

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "go_buttons.html")
		println("Default Web Page")
	})

	http.HandleFunc("/on", func(w http.ResponseWriter, r *http.Request) {
                 http.ServeFile(w, r, "go_buttons.html")
		 println("Web Page sent : ON")
		 gpio("1")

	})

	http.HandleFunc("/off", func(w http.ResponseWriter, r *http.Request) {
                 http.ServeFile(w, r, "go_buttons.html")
		 println("Web Page sent : OFF")
		 gpio("0")
	})

	log.Fatal(http.ListenAndServe(":8081", nil))

}

To compile and run of web app go_buttons:

$ go build go_buttons.go
$ ./go_buttons

Default Web Page
Web Page sent : ON
GPIO Pin 4 value : 1

Default Web Page
Web Page sent : OFF
GPIO Pin 4 value : 0

The web page should look something like:

go_buttons

Summary

For a polished application I would rather use a native Go library for GPIO calls, but for prototyping I found that the gpio command line utility to be easier for trouble-shooting.

After getting a basic web page working for anchor tags for /on and /off, the next step will be to use some Javascript with AJAX to show dynamic values.

 

X Windows from Raspberry Pi to Android

So you’ve done a cool graphic app on your Raspberry Pi wouldn’t it be nice to see it on your Android phone ? Well luckily there are a couple of X Server apps that you can run on your Android phone that allows you to see your Pi graphic apps.

X Server Apps for Android

When you do a search for “x servers” on Android Play Store, you will get a few results.

apps_xservers

X Server XSDL from pelya, has good reviews and I found it to be fairly robust. My only comment was I wasn’t sure how to clear the background help statements, and it made the screen look a little messy.

X server from Darkside is a beta release. I preferred the background and SSH integration over XSDL, however this app did not always close the window when the code requested it.

Both options are free and open source.

Getting Started with X Windows

There are two main ways for X Windows to be run on a remote system. The first method is that the Window is sent from the Pi (or any Linux/X Windows system) to the Android phone. The second method is what you typically do on a Windows PC using an SSH terminal program like  Putty; you SSH (Secure Shell) in to the Rasp Pi and then when you run an app it open on your remote node.

xwindow_overview

Calling windows from a PC is pretty easy but calling windows from an Android phone is challenging and slow because of the keyboard. The two X Servers apps do not have SSH components built-in but there are some Android SSH client apps that can be run independently or integrated with the Darkside X app.

Sending an X Window from a Raspberry Pi or Linux node is rather straightforward. The sending location of an X window is defined by the DISPLAY session variable. To check its value enter:

~$ echo $DISPLAY
localhost:10.0

A typical results will be something like: localhost:10.0 .

To change the DISPLAY variable enter the ipaddress:window_number.  In my case the phone’s IP is 192.168.0.102, and the window number is 0, so in a terminal on the PI the command is:

export DISPLAY=192.168.0.102:0

This will now direct all X Windows in my terminal session to my phone. To test things we can send the Python IDE to the phone by:

$ idle

x_idle

Unfortunately the Android X Server app does not support moving or resizing of X Windows, so the app is pinned to the top left. Luckily if we are writing Python apps we can set the sizing and positioning when the app starts up.

Python Tkinter

The tkinter graphic library allows you to create a simple graphic front end to your Python projects.

The following code will update a label with the time every second. The geometry setting is used to move the window (300 left, and 100 down).

# test1.py - Show the time every second
#
import tkinter as tk
from datetime import datetime

root = tk.Tk()
root.geometry("+300+100")

def Updatetime():
label['text'] = datetime.now().strftime("%H:%M:%S")
root.after(1000,Updatetime)

def CallBack():
root.destroy()

label = tk.Label( root, text='', font = "Aria 72 bold" )
label.pack()

B = tk.Button(root, text ="Quit", font = "Aria 72 bold", command = CallBack)

B.pack()

root.after(1000,Updatetime)
root.mainloop()

To set the X window to my phone and run this Python application enter:

~$ export DISPLAY=192.168.0.102:0
~$ python test1.py

test1

Final Comment

I found that Python 3 was much more solid than Python 2. For example a Pi real time bars example worked perfectly on Python3, but on Python2 the bar wouldn’t update at all.

x_bars

 

 

 

 

 

 

 

Android Python App Talking to a Raspberry Pi

If you’d like to use some of your Python experience on Android, then the QPython Android IDE may surprise you with what it has to offer.

Out of the box QPython includes some excellent libraries, especially if you’re interested in doing some data mining or modeling.

libraries

In this blog I’d like to look at an example of building an Android QPython GUI app that controls some lights on a Raspberry Pi.

A Button Interface

Luckily you can do all your development on a Windows/Linux/MacOS/Raspberry Pi before you need to move the application to Android.

QPython comes with a number of pre-installed libraries such as: a standalone Web Server, a low level Android interface, sockets, and the QPython supports the Pygame library.

I used a simple 4 button application, but there are lots of other graphic features that are possible in Pygame. The only real difference with a pygame QPython app is that a #qpy:pygame reference is required at the top of the file. This reference is overlooking in Window/Linux/MacOS.

#qpy:pygame

import pygame
import socket

pygame.init()

def draw_button(button, screen):
    #Draw the button rect and the text surface
    pygame.draw.rect(screen, button['color'], button['rect'])
    screen.blit(button['text'], button['text rect'])

def create_button(x, y, w, h, bg, text, callback):
    # Create a buttondictionary of the rect, text,
    # text rect, color and the callback function.
    FONT = pygame.font.Font(None, 50)
    text_surf = FONT.render(text, True, pygame.Color('black'))
    button_rect = pygame.Rect(x, y, w, h)
    text_rect = text_surf.get_rect(center=button_rect.center)
    button = {
        'rect': button_rect,
        'text': text_surf,
        'text rect': text_rect,
        'color': pygame.Color(bg),
        'callback': callback,
        }
    return button

def main():
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Rasp Pi Interface: ")
    clock = pygame.time.Clock()    

    def bt_func(input):  # A callback function for the button.
        pygame.display.set_caption("Rasp Pi Interface: Send - " + input)
        print(input)

    button1 = create_button(100, 50, 250, 80,'blue','BLUE LED',lambda: bt_func('blue'))
    button2 = create_button(100, 150, 250, 80,'yellow', 'YELOW LED',lambda:bt_func('yellow'))
    button3 = create_button(100, 250, 250, 80,'red', 'RED LED', lambda: bt_func('red') )
    button4 = create_button(100, 350, 250, 80,'green', 'GREEN LED',lambda: bt_func('green'))
    
    # A list that contains all buttons.
    button_list = [button1, button2,button3, button4]

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            # This block is executed once for each MOUSEBUTTONDOWN event.
            elif event.type == pygame.MOUSEBUTTONDOWN:
                # 1 is the left mouse button, 2 is middle, 3 is right.
                if event.button == 1:
                    for button in button_list:
                        # `event.pos` is the mouse position.
                        if button['rect'].collidepoint(event.pos):
                            # Increment the number by calling the callback
                            # function in the button list.
                            button['callback']()


        screen.fill(pygame.Color('white'))
        for button in button_list:
            draw_button(button, screen)
        pygame.display.update()
        clock.tick(30)

main()
pygame.quit()

When the Pygame application is running the buttons will send toggle the display caption and write a print() message.

rasp_app_linux

Socket Communications

Sockets are a simple way to have multiple devices communicate together. For this example the Raspberry Pi will be a Socket Server and a PC, Linux node or an Android phone could be socket clients.

# Simple Python Socket Server
#
import socketserver

class Handler_TCPServer(socketserver.BaseRequestHandler):
    # Handler to manage incoming requests
    def handle(self):
        # self.request - TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        # self.data - is the incoming message
        print("{} sent:".format(self.client_address[0]))
        print(self.data)
        # if required a request could be send back
        #self.request.sendall("ACK from TCP Server".encode())

if __name__ == "__main__":
    # Define the host and port to use
    HOST, PORT = "192.168.0.133", 9999

    # Init the TCP server object, bind it to the localhost on 9999 port
    tcp_server = socketserver.TCPServer((HOST, PORT), Handler_TCPServer)
    print("Socket Server Started on : " + HOST + " port: " + str(PORT))
    # Activate the TCP server.

    tcp_server.serve_forever()

A simple Python TCP socket client would be:

import socket

host_ip, server_port = "192.168.0.133", 9999
data = " Hello how are you?\n"

# Initialize a TCP client socket using SOCK_STREAM
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # Establish connection to TCP server and exchange data
    tcp_client.connect((host_ip, server_port))
    tcp_client.sendall(data.encode())

    # Read data from the TCP server and close the connection
    received = tcp_client.recv(1024)
finally:
    tcp_client.close()

print ("Bytes Sent:     {}".format(data))
print ("Bytes Received: {}".format(received.decode()))

Put It Together

Once the socket communications and the Pygame interface is working, the full program can be completed. The first step is to define some output LED pins on the Raspberry Pi. For my project I used a Pimoroni Explorer Hat Pro. This Pi top had some built in colored LED pins.

# Pi Socket Server to toggle 4 color LEDs
import socketserver

class Handler_TCPServer(socketserver.BaseRequestHandler):
    # Handler to manage incoming message requests

    def handle(self):
        # self.request - TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} sent:".format(self.client_address[0]))
        print(self.data)
        if self.data == "red":
            GPIO.output(redpin, not GPIO.input(redpin))
        if self.data == "yellow":
            GPIO.output(yellowpin,not GPIO.input(yellowpin))  
        if self.data == "blue":
            GPIO.output(bluepin,not GPIO.input(bluepin))
        if self.data == "green":
            GPIO.output(greenpin,not GPIO.input(greenpin))            
        # just send back ACK for data arrival confirmation
        #self.request.sendall("ACK from TCP Server".encode())

if __name__ == "__main__":
    # Define 4 LED pins as outputs
    import RPi.GPIO as GPIO
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    yellowpin = 17
    bluepin = 4
    redpin = 27
    greenpin = 5
    GPIO.setup(redpin,GPIO.OUT)
    GPIO.setup(yellowpin,GPIO.OUT)
    GPIO.setup(bluepin,GPIO.OUT)
    GPIO.setup(greenpin,GPIO.OUT)
     
    HOST, PORT = "192.168.0.133", 9999

    # Init the TCP server object, bind it to the localhost on 9999 port
    tcp_server = socketserver.TCPServer((HOST, PORT), Handler_TCPServer)
    print("Socket Server Started on : " + HOST + " port: " + str(PORT))
    # Activate the TCP server.
    # To abort the TCP server, press Ctrl-C.
    tcp_server.serve_forever()

Next our Pygame interface needs to include  some TCP socket client code that sends the color message.

#qpy:pygame

import pygame
import socket

pygame.init()

def draw_button(button, screen):
    #Draw the button rect and the text surface
    pygame.draw.rect(screen, button['color'], button['rect'])
    screen.blit(button['text'], button['text rect'])

def create_button(x, y, w, h, bg, text, callback):
    # Create a buttondictionary of the rect, text,
    # text rect, color and the callback function.
    FONT = pygame.font.Font(None, 50)
    text_surf = FONT.render(text, True, pygame.Color('black'))
    button_rect = pygame.Rect(x, y, w, h)
    text_rect = text_surf.get_rect(center=button_rect.center)
    button = {
        'rect': button_rect,
        'text': text_surf,
        'text rect': text_rect,
        'color': pygame.Color(bg),
        'callback': callback,
        }
    return button

def main():
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Rasp Pi Interface: ")
    clock = pygame.time.Clock()    

    def bt_func(input):  # A callback function for the button.
        pygame.display.set_caption("Rasp Pi Interface: Send - " + input)
        print(input)
        host_ip, server_port = "192.168.0.133", 9999
        tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
        # Establish connection to TCP server and exchange data
            tcp_client.connect((host_ip, server_port))
            tcp_client.sendall(input.encode())
        finally:
            tcp_client.close()

    button1 = create_button(100, 50, 250, 80,'blue','BLUE LED',lambda: bt_func('blue'))
    button2 = create_button(100, 150, 250, 80,'yellow', 'YELOW LED',lambda: bt_func('yellow'))
    button3 = create_button(100, 250, 250, 80,'red', 'RED LED', lambda: bt_func('red'))
    button4 = create_button(100, 350, 250, 80,'green', 'GREEN LED',lambda: bt_func('green'))
    
    # A list that contains all buttons.
    button_list = [button1, button2,button3, button4]

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            # This block is executed once for each MOUSEBUTTONDOWN event.
            elif event.type == pygame.MOUSEBUTTONDOWN:
                # 1 is the left mouse button, 2 is middle, 3 is right.
                if event.button == 1:
                    for button in button_list:
                        # `event.pos` is the mouse position.
                        if button['rect'].collidepoint(event.pos):
                            # Increment the number by calling the callback
                            # function in the button list.
                            button['callback']()


        screen.fill(pygame.Color('white'))
        for button in button_list:
            draw_button(button, screen)
        pygame.display.update()
        clock.tick(30)

main()
pygame.quit()

Loading the Application on Android

To load the application on my phone, I connected my phone to a PC and I move the file to the phone’s /Download directory. QPython files can be created/edited and run using the Editor button. New Python applications can be created using the PygameApp menu option.

It’s important to note that if you are using print() functions in your application then a log directory file location prompt will come up the first time that your app is run.

QPython applications can be made into a desktop short cut.

Once everything is configured, you should be able to click on the desktop QPython icon and start controlling the Raspberry Pi.

android2pi