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.

 

 

Pi with PS2/PS3/XBox/Generic Keypads

For a lot of Pi projects it’s nice to have some kind of handheld controller.

Our goal was to create a simple Meccano crane that we could control with one of the game controllers that we had around the house.

PS3_crane1

There are some options like PyGame that offer a keyboard and joystick interface, but we found the evdev library to be a little simpler and more straight-forward.

The Python evdev Libraray

The Python evdev library offers is a simple way to connect up almost any USB keypad type device, the libary is installed by:

sudo apt-get install python-evdev

A simple monitoring program

The first step is to figure out what the PS2, PS3 or Xbox button codes are. The code below will show you the keycodes and key descriptions for your key presses:

from evdev import InputDevice, categorize, ecodes
gamepad = InputDevice('/dev/input/event0')

for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY and event.value == 1:
        try:
            keyevent = categorize(event)
            print (keyevent) # comment to remove key messages

        except:
            print ("Problem key pressed...")

The output will appears something like:


key event at 1488557515.714249, 295 (BTN_BASE2), down
key event at 1488557517.491448, 294 (BTN_BASE), down
key event at 1488557519.594712, 293 (BTN_PINKIE), down

Once you’ve got the required keycode then you’re ready to start your main project.

Generic Keypad

There are many low cost generic pads that can be used. Below is a picture of a generic gamepad with some sample code to pick up its keys. (Note: the joystick keys are not addressed in this project).

gamepad_pi

from evdev import InputDevice, categorize, ecodes
gamepad = InputDevice('/dev/input/event0')

for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY and event.value == 1:
        try:
            keyevent = categorize(event)
            if keyevent.keycode == "BTN_THUMB":
                print ("Right - 'A' button")
            if keyevent.keycode == "BTN_THUMB2":
                print ("Bottom - 'B' button")
            if 'BTN_TRIGGER' in keyevent.keycode: # ['BTN_JOYSTICK', 'BTN_TRIGGER']
                print ("Top - 'X' button")
            if keyevent.keycode == "BTN_TOP":
                print ("Left - 'Y' button")
            if keyevent.keycode == "BTN_TOP2":
                print ("Left shoulder button")
            if keyevent.keycode == "BTN_PINKIE":
                print ("Right shoulder button")
            if keyevent.keycode == "BTN_BASE3":
                print ("SELECT button")
            if keyevent.keycode == "BTN_BASE4":
                print ("START button")

            print (keyevent) # comment to remove key messages

        except:
            print ("Problem key pressed...")

PS2 Controller

The PS2 key mapping is not the same as a generic keypad. Some issues that we found using PS2 controllers:

  • the PS2 controller may require the “PS” key to be pushed before the controller is active
  • the right “triangle”, “circle” and “X” are not directly supported in the keyevent.keycode.

controller_ps2_diagram

Below is some Python code to pick up the PS2 keys.

from evdev import InputDevice, categorize, ecodes
gamepad = InputDevice('/dev/input/event0')

for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY and event.value == 1:
        try:
            keyevent = categorize(event)
            if 'BTN_TRIGGER_HAPPY1' in keyevent.keycode: #'BTN_TRIGGER_HAPPY', 'BTN_TRIGGER_HAPPY1'
                print ("Playstation button")

            if keyevent.keycode == "BTN_TOP2":
                print ("Top D-Pad button")
            if keyevent.keycode == "BTN_BASE":
                print ("Bottom D-Pad button")
            if keyevent.keycode == "(BTN_BASE2":
                print ("Left D-Pad button")
            if keyevent.keycode == "(BTN_PINKIE":
                print ("Right D-Pad button")

            if keyevent.keycode == "BTN_BASE5":
                print ("Left top shoulder button")
            if keyevent.keycode == "BTN_BASE6":
                print ("Right top shoulder button")
            if keyevent.keycode == "BTN_BASE3":
                print ("Left bottom shoulder button")
            if keyevent.keycode == "BTN_BASE4":
                print ("Right bottom shoulder button")

            if "BTN_TRIGGER" in keyevent.keycode: #'BTN_JOYSTICK', 'BTN_TRIGGER'
                print ("SELECT button")
            if keyevent.keycode == "BTN_TOP":
                print ("START button")

            if keyevent.keycode == "BTN_DEAD":
                print ("SQUARE button")

            print (keyevent) # comment to remove key messages

        except:
            print ("Problem key pressed...")

Wireless Keyboards

We found that evdev also worked well with wireless keyboards. One of the advantages of evdev is that it will get the key stroke directly and you don’t need to do an “Enter”.

We used a wireless keyboard to drive and control a mobile missile launcher.

wl_kb_missile2

 

Gesture Controlled Radio

Our goal was to make an kitchen Internet radio player where we could change the volume and stations without touching the player. For this project we used:

  • Raspberry Pi 3 (or 2 with a Wifi dongle)
  • Pimoroni SkyWriter (~$20US)
  • USB powered portable speaker

The Pimoroni SkyWriter supports both gesture and touch control.

Setup

To install the Pimoroni SkyWriter python libraries:

curl -sS get.pimoroni.com/skywriter | bash

For the internet music interface we used the mpd , Music Player Daemon, it can be installed by:

sudo apt-get install mpd mpc

The SkyWriter can be wrapped in plastic wrap and the gestures can still be picked up. For a more robust project it would probably be best to design a more waterproof enclosure.

Python Code

A few years ago we found that the mpc calls were bullet-proof, but when we did this project we found that the mpd service would often lock up  if a new internet radio station was selected (mpc next). For this reason we used external calls to adjust the volume and we restarted the mpd service when we changed radio stations

We used a gesture of up/down to adjust the volume, and a left/right to change the station.

OLYMPUS DIGITAL CAMERA

Below is our final code:

#!/usr/bin/env python
import skywriter
import signal
import os

thevolume = 70 #starting volume
thestation = 1 #starting station

# Some internet radio stations
stations = (('http://185.33.21.112:11029','1.FM Amsterdam Trance Radio'),
            ('http://www.partyviberadio.com:8000','Raggae Roots'),
            ('http://66.85.88.2:7136','Comedy 104'),
            ('http://eu.radioboss.fm:8121','Yoga'),
            ('http://185.33.21.112:11269','Baroque'))

def setvolume(voldif):
  global thevolume
  if (thevolume + voldif >= 0) and (thevolume + voldif <100):
    thevolume = thevolume + voldif
    os.system('amixer sset "PCM" ' + str(thevolume) + '%')
    print 'volume = ' , thevolume

def newstation(direction):
  global thestation
  print direction , thestation , len(stations)
  if ((direction + thestation >0) and (direction + thestation <= len(stations))):
    thestation = thestation + direction
    os.system('mpc clear')
    os.system('sudo service mpd restart')
    os.system('mpc add ' + stations[thestation][0] )
    os.system('mpc play')
    print thestation, stations[thestation][0], stations[thestation][1]

@skywriter.flick()
def flick(start,finish):
  print('Got a flick!', start, finish)
  if (finish == 'north'):
    setvolume(5)
  if (finish == 'south'):
    setvolume(-5)
  if (finish == 'west'):
    newstation(-1)    
  if (finish == 'east'):
    newstation(1)

# start playing the radio at defaults 
setvolume(0)
newstation(0)

signal.pause() # wait for a new gesture

Pi FM Radio

There are few options for FM radio projects on the Pi, such as the RDA5807M and TEA5767 chips. We’ve built an Arduino FM radio using the RDA5807M chips but we found the documentation to be quite weak. There are some Python libraries for TEA5767 chips, unfortunately however you have no volume control with this chip.

RTL-SDR is a software defined radio (SDR) that uses a DVB-T TV tuner dongle based on the RTL2832U chipset. RTL-SDR dongles are well priced at $10-$15 and they are easy to use for Pi FM radio projects. Software Defined Radios have a large list applications that can done, some of the cooler projects include: tracking airplanes and ships, free-to-air TV, and monitoring satellite data.

Getting Started

For setup an external speaker is required (powered speakers work the best). The RTL-SDR dongle includes an externally connected antenna, if possible try to place the antenna close to a window.

fm_setup

To install the basic software enter:


sudo apt-get install rtl-sdr

The rtl_fm utility is an FM demodulator that is used to read and sample a selected frequency. The output of rtl_fm needs to be directed to an audio player program such as aplay. There are a few sampling and buffering options that must be defined. The syntax with the required options to play an FM radio station at 107.9 MHz is:

rtl_fm -f 107.9e6 -M wbfm -s 200000 -r 48000 | aplay -r 48k -f S16_LE

The rtl_fm application needs to be stopped when a new radio station is selected. If the rtl_fm is running in the background the ps command can be used to find process IDs. The kill command can then be used to terminate the task. An example to find and terminate the rtl_fm task:

pi@raspberrypi:~ $ ps -e | grep rtl_fm
1709 pts/0 00:00:33 rtl_fm
pi@raspberrypi:~ $ kill 1709
pi@raspberrypi:~ $ ps -e | grep rtl_fm
pi@raspberrypi:~ $

Adjusting the Volume

Typically the audio would be from an external speaker rather than the HDMI connection. To force the audio connection, use raspi-config and select the “Advanced” menu option, then selection “Audio”.

advanced

There are a few ways to adjust the volume on the audio. One method is to use the amixer utility. An example to change the radio volume to 70%:

amixer sset "PCM" 70%

A Python Example

For our basic testing we created a simple Python command line application. This application accepts new FM radio frequencies and volume settings. When a new radio frequency is selected the rtl_fm task needs to be stopped and restarted.

This application used a few Python libraries. The os library has the os.system call to shell out to external programs like amixer. The os.kill call terminates processes with a give process ID. The subprocess library has the call:


# simple PI FM radio utility using a RTL SDR dongle 

import subprocess, signal, os

def newstation(station):
    global process, stnum
      
    part1 = "rtl_fm -f " 
    part2 = "e6 -M wbfm -s 200000 -r 48000 | aplay -r 48k -f S16_LE"
    cmd = part1 + station + part2
    
    print 'Playing station :', station 

    # kill the old fm connection
    if process != 0:
        process = int(subprocess.check_output(["pidof","rtl_fm"] ))
        print "Process pid = ", process
        os.kill(process,signal.SIGINT) 

    # start the new fm connection
    print cmd
    process = subprocess.Popen(cmd, shell=True)


def setvolume(thevolume):

    os.system('amixer sset "PCM" ' + thevolume)
    print 'volume = ' , thevolume


process = 0

while True:
    answer = raw_input("Enter a radio station (i.e. 107.9) or volume (i.e. 50%): ")

    if answer.find('%') > 0:  
        setvolume(answer)
    else: 
        newstation(answer)                      

Summary

After the command line FM radio is working, it is possible to create standalone PI radio projects that use pushbuttons, TV remotes or Wii controllers to adjust the volume and stations. We built some projects using PiFace and LCD button shields.

The LCD Python code is below:


#!/usr/bin/python
# Pi FM Radio using an LCD Shield for controlling the stations and volume 

import os, subprocess, signal
import time
import Adafruit_CharLCD as LCD


def newstation(direction):
        global stnum, stations, process

        print 'stnum=',stnum,'direction=',direction              
          
        part1 = "rtl_fm -f " 
        part2 = " -M wbfm -s 200000 -r 48000 | aplay -r 48k -f S16_LE"

        if (stnum + direction  -1):
                stnum = stnum + direction
                print('Playing station :', stations[stnum]) 
                cmd = part1 + stations[stnum] + part2
                if process != 0:
                        process = int(subprocess.check_output(["pidof","rtl_fm"] ))
                        print "Process pid = ", process
                        os.kill(process,signal.SIGINT) 
                # start the new fm connection
                print cmd
                process = subprocess.Popen(cmd, shell=True)
            


def setvolume(voldif):
        global thevolume
        if (thevolume + voldif > 0) and (thevolume + voldif <100):
                thevolume = thevolume + voldif
                os.system('amixer sset "PCM" ' + str(thevolume) + '%')
                print 'volume = ' , thevolume
                

lcd = LCD.Adafruit_CharLCDPlate()
lcd.set_color(0,0,0)

## Add your own stations and station info
stations = ['95.3e6','94.7e6','102.9e6','107.9e6']
sinfo = ['95.3 country', '94.7 light','102.9 easy','Y108 Rock\nHamilton ']
thevolume = 40

stnum = 1               #pick a starting station
process = 0
newstation(0)
lcd.message(sinfo[stnum])
setvolume(thevolume)


print 'Press Ctrl-C to quit.'
while True:
        if lcd.is_pressed(LCD.UP):
                setvolume(5)
                time.sleep(0.25)
        if lcd.is_pressed(LCD.DOWN):
                setvolume(-5)
                time.sleep(0.25)
        if lcd.is_pressed(LCD.LEFT):
                newstation(-1)
                lcd.clear()
                lcd.message(sinfo[stnum])
                time.sleep(0.25)
        if lcd.is_pressed(LCD.RIGHT):
                newstation(1)
                lcd.clear()
                lcd.message(sinfo[stnum])
                time.sleep(0.25)   

Wake Up !!! Wake Up !!!

The goal of the “Wake me up” programs is to set a wake time that will turn on the lights and say something like: “Wake Up Wake Up”.  The logic was done in Node-Red and it was quite straightforward. The hardware used was:

The PowerSwitch Tail II ($26) is a power cord that is enabled/disabled with I/O pins. The PowerSwitch pins connect to GPIO18 and GND on the Pi. A desk light is plugged into the PowerSwitch Tail and speakers are connected to audio jack on the Pi.

pi2switch

With the PowerSwitch Tail II there are a lot of devices that could be controlled. For us, we simply connected it to a desk lamp. The setup shown below was later moved into a bedroom and place on a bedside table.

wakeup_hwd

For this project two Node-Red libraries were used; a scheduler library and a text to speech library. They are installed by:

 sudo apt-get install festival
cd $HOME/.node-red
npm install node-red-contrib-say
npm install node-red-contrib-simple-weekly-scheduler 

After the packages are loaded restart the Pi, and then start up Node-Red. On the Node-Red configuration Web page, drop a scheduler, and wire a Pi GPIO, and a Say node to the scheduler.

3nodes

Double-click the scheduler node and set the wake up times. The start/end payloads are numeric and 1/0. Below is the scheduler configuration.

schedule

For the Pi GPIO node, set the GPIO to Pin 12/GPIO18.

powerinfo

Next for the Say node, enter the text you want spoken .

wakeup

For the final circuit, inject nodes can be included for testing. An “on” inject sends a 1, and an “off” inject sends 0. At the wake up time the schedule node will send a payload that will trigger the speaking of the wake up text and the desk light will be turned on for 15 minutes.

final_wakeup

Pi/Node-Red Car

The goal of the Pi/Node-Red car project was to create a small vehicle that can be controlled from a smart phone . For the project we used:

  • 1 Car chassis for Arduino ($15)
  • 1 Pimoroni Explorer HAT Pro  ($23)
  • 1 Portable microUSB charger
  • 1 USB WiFi Adapter
  • 4 short alligator clips and 4 connectors
  • Duct tape

The Arduino car chassis may require a small amount of assembly. Rather than soldering connections we like to use short alligator clips. It is not recommended to wire DC motors directly to a Raspberry Pi so the Pimoroni Explorer HAT Pro is used to connect the 2 DC motors.

The Raspberry Pi and the portable microUSB charger are secured to the top of the car chassis with duct tape. The left motor is wired to the motor 1 connectors on the Explorer Hat, and the right motor is wired to motor 2 connectors. Note you may have to do a little trial and error on the Explorer HAT “+” and “-” motor connections to get both wheels spinning in a forward direction.

The Explorer HAT Node-Red library is installed by:

 cd $HOME/.node-red
npm install node-red-dashboard 

The Web dashboard presentation is configured in the “dashboard” tab. For this example we create 2 groups: a control group to drive the vehicle, and a light group to turn on the Explorer Pro lights. Use the “+group” button to add a group, and the “edit” to change an existing group.
dash_conf

To control a motor, an “Explorer HAT” node and a dashboard button node are dropped and connected together. All the configuration is done in the button node . The button configure options are:

  • the group the button will appear in (Controls)
  • the size of the button (3×1 = 50% of width and narrow)
  • Topic, motor.one or motor.twois used for motor control
  • Payload, -100 = reverse, 0=stop, 100 = forward

Control_conf

The Explorer HAT has 4 colored LEDs. To toggle the LEDS, the topic is light.color with 1=ON, and 0=OFF . We thought that it would be fun to also add some Web dashboard button to control the colored lights.

light_conf

The Node-Red dashboard user interface is accessed by: ipaddress:1880/UI, so for example 192.168.1.102:1880/ui. Below is a picture that shows the final vehicle logic and the Web dashboard.

 

final_logic2