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.

 

 

MQTT and Javascript

MQTT (Message Queuing Telemetry Transport) is a  publish-subscribe-based messaging protocol that is used on many Internet of Things (IoT) projects. It works on top of the TCP/IP protocol and it is designed for connections with remote locations where a “small code footprint” is required or the network bandwidth is limited. The publish-subscribe messaging pattern requires a message broker.

MQTT_js_overview

There is support for MQTT on a large variety of programming languages and platforms. An Arduino or Raspberry Pi module can sent (or publish) I/O to a MQTT broker, and they can also receive (or subscribe) to data.

There are a number of MQTT brokers that can be used. One of the most popular ones is the Mosquitto MQTT broker, and it can be loaded on Windows, OSX and Linux systems. For this blog we will be using the Mosquitto test MQTT server. This Internet based server should not be used for real systems, but it is excellent for small short terms tests.

MQTT Web Sockets

The MQTT server has configurable MQTT and Web Socket ports. For a typical Raspberry Pi or Arduino connection, the default MQTT port 1883 would be used. In many Internet applications only certain ports are open, so for this reason a different MQTT Web Socket is used. This is configurable but ports like 80 or 8080 are typically used.MQTT_web_layout

Javascript Application

There are a number of MQTT javascript libraries that are available. One of the more popular ones is the Paho library that is available at:

https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js

As a first example we will look at creating 2 pages. The first page will  publish a value, and the second page will subscribe to the data.

MQTT_js_pubsub

The publish code is:

<html>
<head>
<title> MQTT Publish Message</title>
</head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>

<script>
// Define some MQTT variables
var mqtt_server = "";
var mqtt_port = "";
var mqtt_destname = "";

function send_mqtt_msg() {
// Send an MQTT message
  mqtt_server = document.getElementById("mqtt_server").value;
  mqtt_port = Number(document.getElementById("mqtt_port").value);
 

  client = new Paho.MQTT.Client(mqtt_server, mqtt_port,"");
  client.connect({onSuccess:onConnect});
  document.getElementById("pubmsg").innerHTML = "Trying to connect...
";
}
function onConnect() {
  document.getElementById("pubmsg").innerHTML = "New connection made...
";
  var mqtt_destname = document.getElementById("mqtt_destname").value;
  var mqtt_msg = document.getElementById("mqtt_msg").value;   
  message = new Paho.MQTT.Message(mqtt_msg);
  message.destinationName = mqtt_destname;
  client.send(message);
  document.getElementById("pubmsg").innerHTML = "topic:" + mqtt_destname + " = " + mqtt_msg + " ...sent";
}  
// called when a message arrives
</script>
<body>
<h1>MQTT Publish Test Page</h1>

Server Name: <input type="text" id="mqtt_server" value="test.mosquitto.org"><br><br>
Websocket: <input type="text" id="mqtt_port" value="8080"><br><br>
DestinationName: <input type="text" id="mqtt_destname" value="my_IoT_value1"><br><br>
Message: <input type="text" id="mqtt_msg" value="test message"><br><br>

<button onclick="send_mqtt_msg()">Publish MQTT Message</button>
</body>
<hr>
<div id=pubmsg></div>
</html>

The subscribe code is:

<html>
<head>
<title> MQTT Subscribe Message</title>
</head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
<script>

function sub_mqtt_msg() {
// Send an MQTT message
  var mqtt_server = document.getElementById("mqtt_server").value;
  var mqtt_port = Number(document.getElementById("mqtt_port").value);

  client = new Paho.MQTT.Client(mqtt_server, mqtt_port,"");
  client.onMessageArrived = onMessageArrived;
  client.onMessageArrived = onMessageArrived;
  client.connect({onSuccess:onConnect});
  document.getElementById("submsg").innerHTML = "Trying to connect...
";

}
function onConnect() {
  document.getElementById("submsg").innerHTML = "New connection made...
";
  var mqtt_destname = document.getElementById("mqtt_destname").value;  
  client.subscribe(mqtt_destname);
  document.getElementById("submsg").innerHTML = "Subscribing to topic: " + mqtt_destname + " ...
";
}
function onMessageArrived(message) {
  var result = message.destinationName + " : " + message.payloadString + "
";
  document.getElementById("submsg").innerHTML = result;
}

</script>
<body>
<h1>MQTT Subscribe Test Page</h1>

Server Name: <input type="text" id="mqtt_server" value="test.mosquitto.org"><br><br>
Websocket: <input type="text" id="mqtt_port" value="8080"><br><br>
DestinationName: <input type="text" id="mqtt_destname" value="my_IoT_value1"><br><br>

<button onclick="sub_mqtt_msg()">Subscript to MQTT</button>
<hr>
<h2>Subscribed Messages:</h2>
<div id=submsg></div>
</body>

</html>

Once you’ve got the basics down it’s possible to make some more advanced web interface pages.

Chart and Gauges

For your IoT projects there are a lot of Dash board options that are available. One of my favorites is Node-Red because it is totally free and standalone.

If you would like to create your own web interface there are a number of good javascript charting and gauge libraries available. For my examples I used Google charts with the Gauge chart . To simulate the inputs I used three of our MQTT Publish pages.

MQTT_js_sim

The code for the gauges page is :

<html>
<head>
<title>IoT - MQTT to JS</title>
</head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
<script src="https://www.gstatic.com/charts/loader.js" type="text/javascript"></script>

<script>
// MQTT variables
var MQTTnames = ["my_IoT_value1","my_IoT_value2","my_IoT_value3"];
var MQTTvalues = [0,0,0];


// Define the Google gauge chart
      google.charts.load('current', {'packages':['gauge']});
      google.charts.setOnLoadCallback(drawChart);

      function drawChart() {

        var data = google.visualization.arrayToDataTable([
          ['Label', 'Value'], 
      [MQTTnames[0], MQTTvalues[0]],
      [MQTTnames[1], MQTTvalues[1]],
      [MQTTnames[2], MQTTvalues[2]],      
        ]);

        var options = {
          width: 800, height: 1000,
          redFrom: 90, redTo: 100,
          yellowFrom:75, yellowTo: 90,
          minorTicks: 5
        };

        var chart = new google.visualization.Gauge(document.getElementById('chart_div'));

        chart.draw(data, options);

        setInterval(function() {
      for (var i=0; i < MQTTnames.length; i++) {
      data.setValue(i, 1, MQTTvalues[i]);
      }
          chart.draw(data, options);

      }, 1000);
}

// Create a client instance
client = new Paho.MQTT.Client("test.mosquitto.org", 8080,"");
client.onMessageArrived = onMessageArrived;

// connect the client
client.connect({onSuccess:onConnect});

// called when the client connects
function onMessageArrived(message) {
  
  for (var i=0; i < MQTTnames.length; i++) {
  if (message.destinationName == MQTTnames[i]) {
    MQTTvalues[i] = Number(message.payloadString);
  }
  }
}
function onConnect() {
  // Once a connection has been made, make a subscription and send a message.
  for (var i=0; i < MQTTnames.length; i++) {
  client.subscribe(MQTTnames[i]);
  }
}
// called when a message arrives
</script>
<body>
<h1  style = 'font-size: xx-large'>IoT - MQTT to JavaScript</h1>
<div id="chart_div" style="width: 100%; height: 100%;"></div>
</body>
</html>

There are many other charting options that could be used. Below is an example using the Google Chart library with bars instead of gauges.

MQTT_js_bars.png

Final Comment

Using Javascript to interact with your IoT projects open up a lot of potential for adding functionality. I found that the Python version of the Paho MQTT library to have better documentation and perhaps some more functions, but at the end of the day I was able to do all the I wanted.

IoT with Google Firebase

For many Internet of Things (IoT) projects a message queuing system like MQTT (Message Queue Telemetry Transport) is all that is needed to connect sensors, devices and graphic interfaces together. If however you require a database, with sorting, queuing and multi-media support then there are some great cloud storage platforms that are available. One that is definitely worth taking a look at is Google Firebase.

Like any IoT solution, Google Firebase can have inputs and sensors sending data directly into it, and a variety of client applications to view the data (Figure 1), but Google Firebase also offers other features such as: file storage, machine learning, messaging, and server side functions. In this article I will look at:

In this article I will look at:

  1. Setting up a sample Firebase IoT project
  2. Use Python to simulate I/O data
  3. Create a Web dashboard with Node-Red to view and write data
  4. Create an Android app using AppInventor to view and write data
  5. Look at a more complex data monitoring example in Python

Getting Started

Google Firebase [1] may not have the huge variety of options that Amazon Web Services (AWS) has, but I found as an IoT engineer Google Firebase had all the features that I needed to get up and running quickly, and I didn’t need a credit card for my free activation.

To begin log in with your Google account at https://firebase.google.com/, and select “Get Started”. I created a sample project called My-IoT. After the project is created Firebase will give it a unique identifier name, such as: my-iot-4ded7.

Firebase data can be stored in either a Realtime Database, or a Cloud Firestore. The Realtime Database is an unstructured NoSQL format that has been around for about 4 years, so there are lots of 3rd party components. The Cloud Firestore stores the data in structure collections and it’s fairly new so the 3rd party options are a little more limited.

For our test IoT project we will use the  real-time database, and it can be created from the Database -> Data menu option.

rtdb

The database structure can be built directly from the Firebase web console or imported from a JSON file. For this database 3 grouping are defined, one for Android, Node-Red and Raspberry Pi I/O values. The Firebase console allows for setting, changing and viewing the database values.

db_struct

The security is configured in the Database -> Rules menu option. To keep things simple for testing read and write security is set to true for public access,  (remember to change this for a production system).

security

The project’s remote access settings are shown in the Authentication section with the “Web setup” button.

webconfig

Python and Firebase

There are a number of Python libraries to access Firebase. The pyrebase library has some added functionality such as: queries, sorting, file downloads and streaming support. The pyrebase library only support Python 3, and it is installed by:

sudo pip3 install pyrebase
sudo pip3 install --upgrade google-auth-oauthlib

A Python 3 test program to set two values (pi/pi_value1 and pi/pi_value2) and read a single point and a group of point is shown below. Remember to change the configuration settings to match your project settings.

import pyrebase, random

# define the Firebase as per your settings
config = {
  "apiKey": "AIzaSyCq_WytLdmOy1AIzaSyCq_WytLdmOy1",
  "authDomain": "my-iot-4ded7.firebaseapp.com",
  "databaseURL": "https://my-iot-4ded7.firebaseio.com",
  "storageBucket": "my-iot-4ded7.appspot.com"
}

firebase = pyrebase.initialize_app(config)
db = firebase.database()

# set 2 values with random numbers
db.child("pi").child("pi_value1").set(random.randint(0,100))
db.child("pi").child("pi_value2").set(random.randint(0,100))

# readback a single value
thevalue = db.child("pi").child("pi_value1").get().val()
print ("Pi Value 1: ", thevalue)

# get all android values
all_users = db.child("android").get()
for user in all_users.each():
    print(user.key(), user.val())

The Firebase web console can be used to check the results from the test program.

Node-Red and Firebase

Node-Red is a visual programming environment that allows users to create applications by dragging and dropping nodes on the screen. Logic flows are then created by connecting the different nodes together.

Node-Red has been preinstalled on Raspbian Jesse since the November 2015. Node-Red can also be installed on Windows, Linux and OSX.  To install and run Node-Red on your specific system see https://nodered.org/docs/getting-started/installation.

To install the Firebase components, select the Manage palette option from the right side of the menu bar. Then search for “firebase” and install node-red-contrib-firebase.

nr_fb_install

For our Node-Red example we will use web gauges to show the values of our two Python simulated test points. We will also send a text comment back to our Firebase database. The complete Node-Red logic for this is done in only 6 nodes!

nr_logic

To read the Pi values two firebase.on() nodes are used. The output from these nodes is connected to two dashboard gauge nodes. Double-clicking on firebase_on() node to configure Firebase database and the item to read from. Double-clicking on the gauge node allows you to edit gauge properties.

nr_value1 To send a text string to Firebase a text input node is wired to a firebase modify node. Edit the firebase modify node with the correct database address and value to set.

nr_modify

After the logic is complete, hit the Deploy button on the right side of the menu bar to run the logic.  The Node-Red dashboard user interface is accessed by: http://ipaddress:1880/ui, so for example 192.168.1.108:1880/ui.

nr_screen

AppInventor

AppInventor is a Web based Android app creation tool (http://appinventor.mit.edu), that uses a graphical programming environment.

AppInventor has two main screens. The Designer screen is used for the layout or presentation of the Android app and the Blocks screen is used to build the logic. On the right side of the top menu bar, the Designer and Blocks buttons allow you to toggle between these two screens.

On the Designer screen, an app layout is created by dragging a component from the Palette window onto the Viewer window.

We will use AppInventor to create an Android app that will read Pi values from our Firebase IoT database, and write a value back.

For the visuals on this application a Button, Label (2),  ListView, Textbox and FirebaseDB component will be used. After a component is added to the application, the Components window is used to rename or delete that component. The Properties window is used to edit the features on a component. For the FirebaseDB component configure the Token and URL to match with your project settings.

fb_app_setup

Once the layout design is complete, logic can be added by clicking on the Blocks button on the top menu bar.

Logic is built by selecting an object in the Blocks window, and then click on the specific block that you would like to use.

AppInventor is pretty amazing when it comes to doing some very quick prototyping. Our entire app only uses two main blocks.

The when FirebaseDB1.DataChanged block is executed whenever new data arrives into the Firebase database. The DataChanged block returns a tag and a value variable. The tag variable is the top level item name, (“pi” or “nodered” for our example). The value will be a string of the items and their values, for example: “{pi_value2 = 34, pi_value1=77}”. We use a replace all text block to remove the “{” and “}” characters, then we pass the string to the ListView component. Note this same code will pass 2 or 200 tags in the pi tag section.

The when BT_PUT.click block will pass the text that we enter on the screen into the android/and_value1 item in our database.

fb_app_code

After the screen layout and logic is complete, the menu item Build will compile the app. The app can be made available as an APK downloadable file or as a QR code link.

Below is picture of the final Android app synced with the Firebase data.

Firebase2

Python Data Monitoring Example

Our starting IoT Firebase realtime database was quite simple. Data monitoring or SCADA (Supervisory Control and Data Aquisition) projects usually require more information than a just a value. A more realistic sensor database would includes fields such as tag name, description, status, time and units.

fb_db2

By adding some indexing in the Firebase Security Rules it is possible to create some queries and sorts of the data. Some typical queries would be: “Points in alarm” or “Point values between 2:00 and 2:15”.

fb_rules2

In the code listing below the syntax statement of .order_by_child(“status”).equal_to(“ALARM”).get() is used to show only records that have a status = ALARM. Some other filter options include: .start_at(time1).end_at(time2).get, .limit_to_first(n), and .order_by_value().

A good next step would be to pass important filtered information to Google Firebase’s messaging feature.

import pyrebase, random

# define the Firebase as per your settings
config = {
  "apiKey": "AIzaSyCq_AIzaSyCq_Wyt",
  "authDomain": "my-iot-7ded7.firebaseapp.com",
  "databaseURL": "https://my-iot-7ded7.firebaseio.com",
  "storageBucket": "my-iot-7ded7.appspot.com"
}

firebase = pyrebase.initialize_app(config)
db = firebase.database()

tag_sum = db.child("pi").order_by_child("status").equal_to("ALARM").get()

# print alarm points
for tag in tag_sum.each():
    info = tag.val()
    print (tag.key()," - ", info["description"],":", info["value"],info["units"], info["status"])

For more Python examples see the Pyrebase documentation.

Summary

Without a lot of configuration it is possible to configure a Google Firebase project and have Python, Node-Red and Android app read and write values. The level of programming complexity is on par with an MQTT implementation, however Google Firebase can offer a lot more future functionality that you wouldn’t have with MQTT, such as file storage, machine learning, messaging, and server side functions.

At the time of writing this article I found the Arduino Firebase library to be unreliable but Google offers some options like an MQTT bridge.

 

Pi Network Monitoring

There are some great full featured networking packages like Nagios and MRTG that can be loaded on Raspberry Pi’s. If, however, you are looking for something smaller scale that you can play with then Node-Red might be your answer. Node-Red is a visual programming environment that allows users to create applications by dragging and dropping blocks (nodes) on the screen. Logic flows are then created by connecting wires between the different blocks (nodes). Node-Red also comes with Web Dashboards, so you can view data or do control from your smart phone.

In this blog we’ll look at:

  • running some SNMP commands
  • setup NodeRed for SNMP
  • making read/write SNMP values on the Pi

 

Getting Started with SNMP

Simple Network Management Protocol (SNMP) is the standard for communicating and monitoring of network devices. Common device information is grouped into MIBs or Management Information Bases. Data items are called OIDs or Object Identifiers. OIDs are referenced by either their MIB name or by their OID numeric name. So for example the SNMP device name object could be queried by either its MIB name of: SNMPv2-MIB::sysName.0 or the object identifier number of: .1.3.6.1.2.1.1.5.0.

To install both the SNMP monitor and server on your Pi enter:

sudo apt-get update
sudo apt-get install snmp snmpd snmp-mibs-downloader

To show meaningful MIB names, you will need to modify the SNMP config file by:

sudo nano /etc/snmp/snmp.conf

The first line should be commented out, it should just read: #mibs .

There are many configuration options in the SNMP server agent that need to be considered. For a real/product system you will need to consider your user security but for a test system we can open up the Pi by:

sudo nano /etc/snmp/snmpd.conf

Then uncomment the agentAddress line so that all interfaces are open, and in the Access Control section comment out all the existing user access and add a new line with public access set to read/write (definitely not recommended in a real system):

# Listen for connections on all interfaces (both IPv4 *and* IPv6)
agentAddress udp:161,udp6:[::1]:161

# ACCESS CONTROL
#
# Set read/write access to public anywhere
#
rwcommunity public

After saving the changes to snmpd.conf, the service needs to be restarted:

sudo service snmpd restart

There are a number of useful SNMP command line programs, such as:

  • snmpget – gets a SNMP message for a specific OID
  • snmpset – sets a SNMP OID (OID needs to be writeable)
  • snmpwalk – gets multiple OID values in a MIB tree

The basic syntax for these commands is:

command -c community -v version node OID

To test that SNMP is working, enter the following:

pi@raspberrypi:~ $ snmpwalk -c public -v 1 localhost .1.3 

SNMPv2-MIB::sysDescr.0 = STRING: Linux raspberrypi 4.4.21-v7+ ...
SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (319234) 0:53:12.34
SNMPv2-MIB::sysContact.0 = STRING: Me <me@example.org>
SNMPv2-MIB::sysName.0 = STRING: raspberrypi
...

If SNMP is working correctly a very long list of SNMP objects will be shown.

Get a Specific SNMP Result

There are thousands of SNMP results that can be read. Some of the more common and useful SNMP are:

1 minute CPU Load: .1.3.6.1.4.1.2021.10.1.3.1
5 minute CPU Load: .1.3.6.1.4.1.2021.10.1.3.2
15 minute CPU Load: .1.3.6.1.4.1.2021.10.1.3.3

Idle CPU time (%): .1.3.6.1.4.1.2021.11.11.0

Total RAM in machine: .1.3.6.1.4.1.2021.4.5.0
Total RAM used: .1.3.6.1.4.1.2021.4.6.0
Total RAM Free: .1.3.6.1.4.1.2021.4.11.0

Total disk/partion size(kBytes): .1.3.6.1.4.1.2021.9.1.6.1
Available space on the disk: .1.3.6.1.4.1.2021.9.1.7.1
Used space on the disk: .1.3.6.1.4.1.2021.9.1.8.1

An example to get the Idle CPU time with the SNMP command line tool would be:

$ snmpget -c public -v 1 localhost .1.3.6.1.4.1.2021.11.11.0

UCD-SNMP-MIB::ssCpuIdle.0 = INTEGER: 97

If the syntax is correct, a result is returned with the OID identifier, result type and the  result value.

Getting Started with NodeRed

NodeRed is pre-installed with the Raspbian images but it will need to have some SNMP and some support options loaded. At a terminal window enter the commands:

sudo apt-get update
sudo apt-get install npm
cd $HOME/.node-red
npm install node-red-node-snmp
npm install node-red-dashboard
npm install node-red-contrib-bigtimer

node-red-start &

Once Node-RED starts, you use a web browser to build applications. If you are working directly on your Pi, enter 127.0.0.1:1880 in the URL address box of your browser. You drop palettes from the left pane into the large flow window in the middle and wire them together in the correct order.

NodeRed Ping Monitor

A good starting program is to make a Web Dashboard that shows ping (node-to-node) delay times. The dashboards are defined in the right panel of Node-Red. A dashboard items are put into groups, and groups are put into tabs. Each tab will be shown as a separate page on your smart phone.

pinglogic

By double-clicking on the Ping node you can enter the different IP address.

pingnode

Similarly by double-clicking on the Chart node, you can define the label and look and feel of the chart.

chartnode

After the configuration is finished, click the Deploy button,  (top right on menu  bar). The Node-RED dashboard user interface is accessed by entering <IPaddress>:1880/ui (e.g., 192.168.1.102:1880/ui). Chart data values are shown by clicking on the chart line.

pingSS

NodeRed with SNMP Nodes

The ping node is quite simple and it returns just the ping value. The snmp node is more complex and it returns multiple pieces of information. To use snmp nodes in a Node-Red program you need some support nodes to parse/pass the payload messages. To send SNMP data to a chart dashboard, the following nodes are used:

  • Big Timer – to trigger the polling of data
  • snmp – gets SNMP/OID information
  • split – split the message into addressable variables
  • change – put the OID value into the message playload
  • chart – show the payload

snmplogic

The SNMP node configuration can get multiple values, a simple example to get the CPU free time is below (Note: the leading “.” is NOT included) :

snmpnode

The split node is used move the SNMP array data in a payload string. The change node is then used to move just the value into the payload.

changenode

The final step is to configure the charts to show the correct label information.

snmpSS

An SNMP Readable Pi Value

The Net-SNMP agent (snmpd) supports the creation of custom read/write objects (OIDs). The “Pass-through” MIB extension command in snmpd.conf allows for script files to be called.

Pass-through script files need to follow a few rules:

  • snmpget request passes a “-g” parameter (get)
  • the snmpget response needs to be 3 lines: OID, data type, and value
  • an snmpset request passes a “-s” parameter (set)

A simple example would be to define the Rasperry Pi board temperature as an SNMP object. The board temp is available by:

cat /sys/class/thermal/thermal_zone0/temp
46160

This returns the value in 1/1000s of a degree C. So to get just the Celsius temperature we can use:

echo $(($(cat /sys/class/thermal/thermal_zone0/temp) / 1000))
46

A SNMP bash script (/home/pi/pitemp), with our Pi CPU temp as OID  .1.3.6.1.4.1.8072.2.1 would be:

#!/bin/bash
if [ "$1" = "-g" ]
then
echo .1.3.6.1.4.1.8072.2.1
echo integer
echo $(($(cat /sys/class/thermal/thermal_zone0/temp) / 1000))
fi
exit 0

After the file is saved remember to make it executable (chmod +x pitemp).

In the “Pass-through” section of  /etc/snmp/snmpd.conf  a line is added to reference the new OID, shell and command:

#
# "Pass-through" MIB extension command
#
pass .1.3.6.1.2.1.8072.2.1 /bin/bash /home/pi/pitemp

After snmpd.conf is updated the snmpd service needs to restarted (sudo service snmpd restart), then our Pi temp OID can be accessed by:

snmpget -c public -v 1 localhost .1.3.6.1.2.1.8072.2.1
NET-SNMP-EXAMPLES-MIB::netSnmpExampleScalars = INTEGER: 47

Pi Writable SNMP GPIO Value

My goal was to use SNMP to turn on and off powered devices, so for this I used a PowerSwitch Tail II, however simple low cost relays could also be used.

The PowerSwitch Tail II ($26) is a power cord that is enabled/disabled with I/O pins. The PowerSwitch pins connect to the Pi pins 6 and 12.

pi_setup

The gpio tool is used to read and write to GPIO pins.  GPIO 1 is made writable by:

gpio mode 1 out

The SNMP script (/home/pi/powerswitch) to read and write to GPIO pin 1 (physical pin 12) is:

#!/bin/bash
if [ "$1" = "-g" ]
then
echo .1.3.6.1.2.1.8072.2.2
echo integer
gpio read 1
fi

if [ "$1" = "-s" ]
then
gpio write 1 $4
fi

exit 0

This new script file needs to made executable by: chmod +x powerswitch. The powerswitch script file is referenced in the SNMP server configuration file (/etc/snmp/snmpd.conf ):

#
# "Pass-through" MIB extension command
#
pass .1.3.6.1.2.1.8072.2.1 /bin/bash /home/pi/pitemp
pass .1.3.6.1.2.1.8072.2.2 /bin/bash /home/pi/powerswitch

Once again the smnpd needs to be restarted. Our read/write actions can be tested by:

$ snmpget -c public -v 1 localhost .1.3.6.1.2.1.8072.2.2
SNMPv2-SMI::mib-2.8072.2.2 = INTEGER: 0
$ snmpset -c public -v 1 localhost .1.3.6.1.2.1.8072.2.2 i 1
SNMPv2-SMI::mib-2.8072.2.2 = INTEGER: 1
$ snmpget -c public -v 1 localhost .1.3.6.1.2.1.8072.2.2
SNMPv2-SMI::mib-2.8072.2.2 = INTEGER: 1

NodeRed Setting SNMP Values

The exec node can be used to call the snmpset command.  A example with an ON and OFF button is:

gpio_logic

The configuration for the exec node is:

exec_node

And the web dashboard is:

gpioSS

Summary

Network monitoring and SNMP is huge topic, hopefully this will give you a good start.