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 the what the other turtles have done. (Note: this is useful in this thermometer example where we can have 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 check 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
       #temp = random.randint(0,40)
       drawbar(temp)
       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 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

 

 

 

Simulate Raspberry Pi Hardware

The Raspberry Pi has some great add-on hardware, such as Pi Tops that fit directly on top of the Pi module and wired components.

A good number of the wired Arduino designed parts now can also be used with Rasp PI’s. Some examples of this includes the HT16K33 and TM1637 seven segment displays.

Nothing beats using real hardware to show Pi values and status, but if you’re missing the hardware or you’d like to duplicate a displayed value remotely, then a soft version of the hardware can be very useful.

In this blog we’ll look at a three Python soft display examples, a seven-segment display, a LCD Keypad Top and a gauge.

Seven Segment Display

The tk_tools module is based on the Python tkinter module and it is has some cool components such as LEDs, Charts, Gauges and Seven Segment displays. The module is installed by:

pip install tk_tools

The tk_tools Seven Segment component can function like an Arduino TM1637 or HT16K33 display component. The tk_tools seven-segment display supports a height, digit_color and a background color.

Below is a some example code that shows the Pi’s CPU temperature in the soft seven segment display. 

import tkinter as tk
import tk_tools

root = tk.Tk()
root.title("CPU Temp")

ss = tk_tools.SevenSegmentDigits(root, digits=5, background='black',   
  digit_color='yellow', height=100)
ss.grid(row=0, column=1, sticky='news')

# Update the Pi CPU Temperature every 1 second
def update_gauge():
    # Get the Raspberry CPU Temp
    tFile = open('/sys/class/thermal/thermal_zone0/temp')
    # Scale the temp from milliC to C
    thetemp = int(float(tFile.read())/1000)

    ss.set_value(str(thetemp))
    root.after(1000, update_gauge)

root.after(500, update_gauge)

root.mainloop()

 

LCD Keypad 

The LCD Keypad I’ve used on a lot of my Pi Projects, (below is a PI FM radio example). Its supports 2 lines of text and it has 5 (or 6) buttons that can be used in your Python app. 

LCD_radio

The standard Python Tkinter library can be used to create a custom LCD keypad display. For my example I tried to replicate the look-and-feel of the Pi Top that I had, but you could enhance or change it to meet your requirements.

Below is an example that writes the button pushed to the 2 line label.

lcd_keypad_up

import tkinter as tk

def myfunc(action):
   print ("Requested action: ",action)
   Line1.config(text = "Requested action: \n" + action)

root = tk.Tk()
root.title("LCD Keypad Shield")
root.configure(background='black')

Line1 = tk.Label(root, 
		 text="ADC key testing     \nRight Key OK        ",
		 fg = "white",
		 bg = "blue",
		 font = "Courier 45",
                 borderwidth=4, relief="raised")
Line1.grid(row = 0,column = 0, columnspan =15, rowspan = 2)

selectB = tk.Button(root, width=10,text= "SELECT",bg='silver' ,
  command = lambda: myfunc("SELECT"),relief="raised")
selectB.grid(row = 3,column = 0)

leftB = tk.Button(root, width=10,text= "LEFT", bg='silver' ,
  command = lambda: myfunc("LEFT"),relief="raised")
leftB.grid(row = 3,column = 1)

rootB = tk.Button(root, width=10,text= "UP", bg='silver' ,
  command = lambda: myfunc("UP"),relief="raised")
rootB.grid(row = 2,column = 2)

rightB = tk.Button(root, width=10,text= "DOWN", bg='silver' , 
  command = lambda: myfunc("DOWN"),relief="raised")
rightB.grid(row = 3,column = 3)

bottomB = tk.Button(root, width=10,text= "RIGHT", bg='silver',
 command = lambda: myfunc("RIGHT"),relief="raised")
bottomB.grid(row = 4,column = 2)

rstB = tk.Button(root, width=10,text= "RST", bg='silver' ,
  command = lambda: myfunc("RESET"),relief="raised")
rstB.grid(row = 3,column = 4)

root.mainloop()

Gauge and Rotary Scale

There aren’t any mainstream low cost gauges that are available for the Rasp Pi, but I wanted to show how to setup a soft gauge.

The tk_tools gauge component is very similar to a speedometer. The rotary scale is more like a 180° circular meter. Both components support digital values, units and  color scales.gaugedoc

Below is a gauge example that reads the Pi CPU temperature every second.

import tkinter as tk
import tk_tools

root = tk.Tk()
root.title("CPU Temp")

my_gauge = tk_tools.Gauge(root, height = 200, width = 400,
                             max_value=70,
                             label='CPU Temp',
                             unit='°C',
                             bg='grey')
my_gauge.grid(row=0, column=0, sticky='news')

def update_gauge():
    # Get the Raspberry CPU Temp
    tFile = open('/sys/class/thermal/thermal_zone0/temp')
    # Scale the temp from milliC to C
    thetemp = int(float(tFile.read())/1000)
    my_gauge.set_value(thetemp)

    # update the gauges according to their value

    root.after(1000, update_gauge)


root.after(500, update_gauge)

root.mainloop()

gauge_temp

Final Thoughts

There are a lot of soft hardware components that could be created.

I found myself getting tripped up thinking : “What would be a good tkinter component and what should be  a Web component”. This is especially true when looking at charting examples, or when I was looking a remote connections.

NodeJS Raspberry Pi Rover

My typical Raspberry Pi projects are done in Python. I thought that I’d try some Node.js testing because I find the standalone Python Webserver (http.server library) to be a little slow on the Pi hardware.

Getting Started with Node.js on Raspberry Pi

Node.js can be installed on your Pi by:

$ sudo apt-get update
$ sudo apt-get install -y nodejs

There are a few options on how to talk to the GPIO (General Purpose Input/Output) pins on the Pi. I tested a few and I found that pigpio worked well for my setup. It is installed by:

sudo apt-get install pigpio

To set a GPIO pin to be an output and next to turn it on, a simple Node.js program (gpio.js) would be:

// gpio.js - set GPIO pin 4 to ON

const Gpio = require('pigpio').Gpio; 
const led = new Gpio(4, {mode: Gpio.OUTPUT});
led.digitalWrite(1);

To run the program:

sudo node gpio.js

It is important to note that access to the GPIO pins require admin rights so you will need to run your scripts with sudo (super user do).

Node.js Webserver

To make a simple webserver (mywebserver.js) :

// mywebserver.js - a simple websever on port 8080
//
var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server
  fs.readFile(__dirname + '/index.html', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    } 
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

This script references the http and the fs (file system) modules, and this allow us to reference an external index.html page. The handler function is used to manage the HTTP requests.

Next you’ll need to make an index.html page, below is a simple example:

<!DOCTYPE html>
<html>
<body>
<h1>Dummy HTML Test Page</h1><hr>
</html>
</body>
</html>

To test this page run: node mywebserver.js , and from a browser use your Pi’s IP address with port 8080, for example : 

dummy

Make the Web Page Dynamic

To make the Web Page dynamic we can use the socket.io package. It is installed by:

$ npm install socket.io --save

By using socket.io, javascript functions on the web page can communicate to functions running on the Node.js web server.socketio

Once a function is created in the Node.js webserver application the Web page can pass data to that function.

The Raspberry Pi Rover

There are some low cost Arduino car frames that cost under $10. These car frames can be used with a Raspberry Pi but you will need to be careful on how the motors are powered. Depending on the motors you might be able to drive them directly from Pi GPIO pins, however it is recommended that you use some external hardware to protect your Pi. There are some good Pi motor top options available, for my project I used the Pimoroni Explorer Hat Pro

js_rover2

My hardware setup used a Pi 3 with a portable phone charger. I used some duct tape to secure the wiring, charger and Pi together.

The motor pins will vary based on hardware that you use, so my code my need to be tweeked for your setup. The ‘control’ function is what I used to define the motor state. Some key words : forward, left, right, stop or back were passed between the web page and the server app to define the rover’s motor action. 

// ws_2_rover.js - NodeJS WebServer App to control a Rover

var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module
var io = require('socket.io')(http) //require socket.io module and pass the http object (server)

const Gpio = require('pigpio').Gpio;
// modify for your motor pinouts
const MOTOR1 = new Gpio(21, {mode: Gpio.OUTPUT}); 
const MOTOR2 = new Gpio(19, {mode: Gpio.OUTPUT}); 
const MOTOR3 = new Gpio(20, {mode: Gpio.OUTPUT}); 
const MOTOR4 = new Gpio(26, {mode: Gpio.OUTPUT}); 

// Ensure that the rover app starts without the motors running
function rover_stop() {
    MOTOR1.digitalWrite(0);
    MOTOR2.digitalWrite(0);
    MOTOR3.digitalWrite(0);
    MOTOR4.digitalWrite(0);
}

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server

  fs.readFile(__dirname + '/web_2_rover.htm', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    } 
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

rover_stop();

io.sockets.on('connection', function (socket) {// WebSocket Connection
  console.log('A user connected');
  var controlvalue = ""; // variable for current status of the rover

  socket.on('control', function(data) { //get light switch status from client
    controlvalue = data;
    console.log('control input: ' + data);

    rover_stop(); // stop the motors, and then do the required action

    if (controlvalue == "forward") { 
      MOTOR1.digitalWrite(1); 
      MOTOR2.digitalWrite(1); 
    }
    if (controlvalue == "left") { 
      MOTOR2.digitalWrite(1); 
    }
    if (controlvalue == "right") { 
      MOTOR1.digitalWrite(1); 
    }

    if (controlvalue == "backward") { 
      MOTOR3.digitalWrite(1); 
      MOTOR4.digitalWrite(1); 
    }

  });
});

I used the Bootstrap template to offer a mobile friendly web interface. A button onclick function was used to pass the requested motor action (forward, left, right, stop, backward) to the control socket function. Below is my web page (web_2_rover.htm):

<!DOCTYPE html>
<html>
<head>
<title>NodeJS Web Rover Control</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>

<script>
var socket = io(); //load socket.io-client and connect to the host that serves the page
</script>
</head>

<body>
<div class="container">
  <h1>NodeJS Web Rover Control</h1>
  <button onclick="socket.emit('control','forward')" class="btn btn-success" style="width: 100%">Forwards</button>
  <button onclick="socket.emit('control','left')" class="btn btn-primary" style="width: 49%">Left</button>
  <button onclick="socket.emit('control','right')" class="btn btn-primary" style="width: 49%">Right</button>
  <button onclick="socket.emit('control','stop')" class="btn btn-danger" style="width: 100%">Stop</button>
  <button onclick="socket.emit('control','backward')" class="btn btn-warning" style="width: 100%">Backwards</button>     
</div>
</body>
</html>

To run the rover app enter:

sudo node ws_2_rover.js

Final Comments

I have done this project also in Python. The Python code is a little cleaner because the Pimoroni Explorer Hat has a Python library so I could easily adjust the motor speeds. However I found that the Node.js web interface to be a little faster than Python on the Pi.