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.

Arduino talking MQTT to Node-Red

There are some great Arduino modules with integrated ESP-8266 wireless chips, some of the more popular modules are:

  • Adafruit HUZZAH
  • NodeMCU
  • WeMos

These modules allow you to do some interesting IoT (Internet of Things) projects. To connect the Arduino modules to PCs, Raspberry Pi’s or Linux nodes that are a number of communication choices. MQTT (Message Queue Telemetry Transport) is becoming one of the standards for this and it is pre-installed with Node-Red.

Plant Moisture Monitoring MQTT Example

Ard_mqtt_overview2

For our example we wanted to do a simple plant moisture example that used a solar charger and an Arduino Wemos module. We then setup an MQTT server on our Node-Red Raspberry Pi with a web dashboard.

Our goal was to get the MQTT technologies working, with some moisture inputs (and not a final plant monitoring system).

Moisture Sensors

Moisture sensors are very low cost and they start at about $2. The basic moisture sensor has 3 inputs; VCC, GND, and AO. Some sensors also include a digital output with a potentiometer to adjust the digital 0-1 moisture limit.

Our Arduino plant moisture setup is good for testing but not a good long term solution. When voltage is applied long term to moisture sensors ionization in the soil will cause a combination of false reading and deterioration of the sensor plates. We plan to do a future project where we will use relays to turn the sensors on/off and we will include solenoid values in a watering system.

MQTT on Arduino

There are a number of excellent MQTT libraries for Arduino, for this example we used the PubSubClient library. This library can be installed from the Arduino IDE by selecting the menu items:

Sketch -> Add Library -> Manage Libraries

To get MQTT setup you’ll need to:

  • define the SSID and password for your WAN
  • define the IP address for the MQTT server (the Node Red/Raspberry Pi node)
  • define some topic for the data

The nice thing about MQTT is that you can define topics for each of your data points. For this example we define the topic humidity to show the moisture sensor value, and msgtext to show the message (‘Needs Water’ or ‘).

Below is our sample Arduino code for passing the moisture data to our MQTT server.

/*
 Basic ESP8266 MQTT publish client example for a moisture sensor
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.
const char* ssid = "YOUR_SSID_NAME";
const char* password = "YOUR_PASSWORD";
const char* mqtt_server = "YOUR_NODE_RED_IP";

WiFiClient espClient;
PubSubClient client(espClient);

void setup_wifi() {
  // Connecting to a WiFi network
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // Loop until we're reconnected
  Serial.println("In reconnect...");
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("Arduino_Moisture")) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(9600);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
}

void loop() {
  char msg[10];
  char msgtext[25];
  String themsg;
  if (!client.connected()) {
    reconnect();
  }
  
  int soil_moisture=analogRead(A0);  // read from analog pin A0
  Serial.print("analog value: ");
  Serial.println(soil_moisture);
  
  if((soil_moisture>300)&&(soil_moisture<700)) {
    Serial.println("Humid soil");
    sprintf(msgtext,"Humid soil",soil_moisture);
  } 
  else if ((soil_moisture>700)&&(soil_moisture<950)){
    Serial.println("Moist Soil");
    sprintf(msgtext,"Moist Soil",soil_moisture);
  }
  else if (soil_moisture <300) ){
    Serial.println("Needs water");    
    sprintf(msgtext,"Needs water",soil_moisture);
  }
  else
  {
      sprintf(msgtext,"Sensor Problem",soil_moisture);
  }

  sprintf(msg,"%i",soil_moisture);
  client.publish("humidity", msg);
  client.publish("soil", msgtext);
  delay(5000);
}

Node-Red

Node-Red is an excellent visual programming environment that is part of the Raspberry Pi base install. Node-Red is a simple tool to create your own Internet of Things applications. The base Node-Red installation includes MQTT interfacing components but it does not include an MQTT server.

If you don’t have a Raspberry Pi you can install Node-Red on Window, Mac OS or Linux systems. I’ve had good results running Node-Red on a very old low end laptop running Xubuntu, (see steps 1-3 in this linked guide).

MQTT Server on Node-Red

There are a number of free internet MQTT servers (iot.eclipse.org) that can be used or an MQTT server can be loaded directly on a local server (i.e. Mosquito).

Mosca is a standalone MQTT server that can be installed directly into Node-Red. The Mosca Node-Red component can be either installed at the command line by:

cd $HOME/.node-red

npm install node-red-contrib-mqtt-broker

Or the component can be install within the Node-Red web interface by selecting the “manage palette” option, and then search for mosca.

mosca_install

After Mosca is installed, all that is required is that a “mosca” node needs to be dragged and dropped into the Node-Red project.

mosca_mqtt_nodes

To connect the Arduino module to Node-Red mqtt inputs are added to the project.  The Arduino topics are defined in Node-Red by double-clicking on the mqtt node and then define the topic to match the Arduino topic.

mqtt_topic

After the MQTT connections are configured Web dashboards can present the final data. The Web dashboards offer a number of different components that could be used for this example I used a gauge and a text node.

To compile and view the Node-Red application, click on the Deploy button on the right side of the menu bar. To access the web dashboard enter: http://your-ip:1880/ui . Below is a example of what you should see.

NodeRed_MQTT_GUI

Final Thoughts

The Mosca MQTT server component allows for a simple standalone architecture to connect wireless Arduino modules into a Node-Red IoT solution.

We can also load Node-Red on Raspberry Pi data collection nodes, and have them publish data to a central Node-Red server.

 

 

 

Arduino talking TCP to Node-Red and Python

There are some great Arduino modules with integrated ESP-8266 wireless chips, some of the most popular of these modules are:

  • Adafruit HUZZAH
  • NodeMCU
  • WeMos

Along with these modules comes some excellent libraries.

For Arduino to PC or Raspberry Pi communications that are a few options to choose from. A TCP client/server is simple and straightforward and it is excellent for sending single point information. For sending multiple data points take a look at MQTT (Message Queuing Telemetry Transport), it’s a common standard for IoT applications and it’s built into Node-Red.

Arduino TCP Client

The Arduino module can be a simple TCP client that can talk to either a Python or a Node-Red TCP server. Below is an example that sends a random integer to a TCP server every 5 seconds.

/*
Test TCP client to send a random number
 */
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

ESP8266WiFiMulti WiFiMulti;

void setup() {
    Serial.begin(9600);

    // We start by connecting to a WiFi network
    const char * ssid = "your_ssid";       // your WLAN ssid
    const char * password = "your_password"; // your WLAN password
    WiFiMulti.addAP(ssid, password);

    Serial.println();
    Serial.print("Wait for WiFi... ");

    while(WiFiMulti.run() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    delay(500);
}

void loop() {
    const uint16_t port = 8888;          // port to use
    const char * host = "192.168.0.123"; // address of server
    String msg;

    // Use WiFiClient class to create TCP connections
    WiFiClient client;

    if (!client.connect(host, port)) {
        Serial.println("connection failed");
        Serial.println("wait 5 sec...");
        delay(5000);
        return;
    }
   
    // Send a random number to the TCP server
    msg = String(random(0,100));
    client.print(msg);
    Serial.print("Sent : ");
    Serial.println(msg);
    client.stop();    
    delay(5000);
}

Node-Red TCP Server

Node-Red is an excellent visual programming environment that is part of the Raspberry Pi base install. Node-Red is a simple tool to create your own Internet of Things applications. The base Node-Red installation includes a TCP server and client.

To install the web dashboards enter:

sudo apt-get install npm
cd ~/.node-red
npm i node-red-dashboard

To start Node-Red either use the on-screen menus or from the command line enter:

node-red-start &

Once Node-Red is running the programming is done via the web interface at: //localhost:1880 or //your_Pi_ip_address:1880 .

To configure the TCP server, go to the Input nodes section and drag and drop the TCP in node. After the node is inserted double-click on it and edit the port and message settings.

nodered_tcp

To create a gauge Web dashboard, go to the dashboard nodes section and drag and drop the gauge node. After the node is inserted double-click on it and edit the dashboard group, labels and ranges.

nodered_tcp_gauge

For debugging and testing an output debug node is useful.

To access the Web dashboard enter: //localhost:1880/ui or //your_Pi_ip_address:1880/ui

TCP Python Server

The python TCP server will see the incoming Arduino message as a Unicode (UTF-8) text, so convert message to an integer use: thevalue = int(data.decode(“utf-8”)). Below is the full code.

import socket
import sys

HOST = '' # Symbolic name, meaning all available interfaces
PORT = 8888 # Arbitrary non-privileged port

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print ('Socket created')

#Bind socket to local host and port
try:
s.bind((HOST, PORT))
except socket.error as msg:
print ('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
sys.exit()

print ('Socket bind complete')

#Start listening on socket
s.listen(10)
print ('Socket now listening')

#now keep talking with the client
while True:
#wait to accept a connection - blocking call
conn, addr = s.accept()
data = conn.recv(1024)
print ('Connected with ' + addr[0] + ':' + str(addr[1]) + " " )
thevalue = int(data.decode("utf-8"))
print ("Value: ", thevalue)

s.close(

Summary

In our final application we mounted the Arduino module outside and we powered it with a small solar charger. We also include a humidity value with the temperature, and we used a QR code that linked to our web page.

Control Lights with TV Remotes

Our TV remotes control most of our entertainment like the TV, DVD player and Kodi box. We thought that it would be useful if the same TV remotes could also turn on powered devices like the lights so that we could get to the kitchen or bathroom while we’re watching a movie.

We setup our project to work with 2 different TV remotes, and we selected 2 push buttons on the remotes that we not being used in our home entertainment arrangement.

For this project we used 2 small Arduino programs. The first program we used to find TV remote IR codes and the second program used our codes to control the light switch.

The equipment we used:

Power Switch

The PowerTail II switch is an isolated DC-actuated power cord (NO or NC) for controlling power to 120VAC appliances with a 2 wire input.

ir_switch_powertail

An Arduino Power Relay module or Shield is much cheaper than a Power Switch and the code for this project will be identical. We used the Power Tail Switch because we have one and we didn’t want to cut up our power cords.

The Setup

The Setup is very straightforward (see code for pin outs), one data pin for the IR module and one digital output for the relay or Power Tail Switch.

ir_switch_nano

Finding TV Remote IR Codes

The simple code below is used to catch IR codes:

 /*
 
 IR TEST PROGRAM

PINOUTS:

lEFT = DATA PIN
MIDDLE = gnd
RIGHT = 3.3 volts

*/

#include 

int RECV_PIN = 11;  //11;

IRrecv irrecv(RECV_PIN);

decode_results results;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
  Serial.println("Setup Complete");
}

void loop()
{
  if (irrecv.decode(&results))
    {
     Serial.println(results.value, HEX);
     irrecv.resume(); // Receive the next value
     delay(500);
     
    }
}

Final Code

We used 2 remotes, a newer Samsung, and an old Philips. On the Samsung we used the A and B buttons to turn the lights on and off. On the Philips we used the sharp and natural buttons.

Below is our final code:

 /*
 
TV Remote Control of Switches and Powered Devices
PINOUTS:

lEFT = DATA PIN
MIDDLE = gnd
RIGHT = 3.3 volts

*/

#include <IRremote.h>

int RECV_PIN = 11;  //11;

IRrecv irrecv(RECV_PIN);

decode_results results;

int swtpin = 10;

void setup()
{
  Serial.begin(9600);
  pinMode(swtpin, OUTPUT);
  irrecv.enableIRIn(); // Start the receiver
  Serial.println("Setup Complete");
}

void loop()
{
  if (irrecv.decode(&results))
    {
      switch (results.value) {
     case 0x94CC5A5E: 
       Serial.println("A pushed - light on");
       digitalWrite(swtpin, HIGH);
       break;     
     case 0x23B1EF88:
       Serial.println("B pushed - light off");
       digitalWrite(swtpin, LOW);
       break;    
         case 0x16892E01: 
       Serial.println("natural pushed - light on");
       digitalWrite(swtpin, HIGH);
       break;     
     case 0xDAD07464:
       Serial.println("sharp pushed - light off");
       digitalWrite(swtpin, LOW);
       break;   
     default:
       Serial.println(results.value, HEX);
      }
      irrecv.resume();
     delay(500);
     
    }
}

Handheld Gas Leak Monitor

There are some very low cost MQ-2 sensors  ($3-$5) that can be used to measure common combustible gases such as hydrogen, butane, methane, propane and liquid petroleum gas (LPG).

My goal for this project was to make a handheld monitor that we could use to measure potential leaks in our propane BBQ or natural gas leaks in and around our furnace.

mq2_setup

Setup

For our setup we used:

MQ2_circuitpng

Some duct tape can be used to secure the bread board to the USB battery unit. We used a low cost 4 digit display and we mounted it direct to the bread board.

Code

We were only interested in a “GAS” or “NO GAS” reading, so because of this our code was super simple.

#include <Arduino.h>
#include <TM1637Display.h>

// Module connection pins (Digital Pins)
#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);

int smokeA0 = A0;
// Your threshold value
int sensorThres = 400;

void setup() {
  pinMode(smokeA0, INPUT);
  Serial.begin(9600);
  display.setBrightness(0x0f);
}

void loop() {
  int analogSensor = analogRead(smokeA0);

  Serial.print("Pin A0: ");
  Serial.println(analogSensor);
  display.showNumberDec(analogSensor, false);
  delay(1000);
}

If you want to get accurate gas measurements then you’ll need to do some calibration. We were only interested in a seeing something above a “clear air” reading.  For our setup a “clear air” reading was about 100-115. To test our project we used a butane lighter and we open it very quickly (<1 second).

Arduino Dust Monitor

There are some low cost analyzers  for about $15US. For home or test projects these sensors are great but I wouldn’t recommend them for any serious environmental studies. (For true air quality monitoring you need to compensate for temperature and humidity).

For this projects we used a 90mA PM2.5 Dust Sensor PPD42NS / PPD42NJ Sensor. These sensor need to be mounted upright, so we used some Meccano to build a simple stand.

The sensors have 5 pins, with the pinouts of:

  • Pin 1  => GND
  • Pin 3  => 5 VDC
  • Pin 4  => Data Pin

To see the results we used a 16×2 LCD sheild.

OLYMPUS DIGITAL CAMERA

The product example samples every 30 seconds and it returns a value of particles per 1/100 of a cubic foot (pcs/0.01cf).

The returned values (in pcs/0.01cf) will need some validation but from our testing we found that :

  • 0-500 pcs/0.01cf = a clear room
  • 500-1500 pcs/0.01cf = a “fairly” clean room
  • 1500-4000 pcs/0.01cf = a room in need of dusting (but not super dusty)
  • 4000+ pcs/0.01cf = if you have allergies you may notice the room

Our code for the project is:

90mA PM2.5 Dust Sensor PPD42NS / PPD42NJ Sensor with an 16x2 LCD Shield

JST Pin 1 (Black Wire) => Arduino GND
JST Pin 3 (Red wire) => Arduino 5VDC
JST Pin 4 (Yellow wire) => Arduino Digital Pin 8
*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

int pin = 28;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;
float maxcon = 0;

void setup() {
 Serial.begin(9600);
 pinMode(8,INPUT);
 starttime = millis();
 lcd.begin(16, 2);
}
void loop() {
 duration = pulseIn(pin, LOW);
 lowpulseoccupancy = lowpulseoccupancy+duration;
 if ((millis()-starttime) > sampletime_ms)
 {
 ratio = lowpulseoccupancy/(sampletime_ms*10.0); // Integer percentage 0=>100
 concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62;
 if (concentration > maxcon){ maxcon = concentration;}
 lcd.setCursor(0, 0);
 lcd.print("now: ");
 lcd.setCursor(5, 0);
 lcd.print(concentration);
 
 lcd.setCursor(0, 1);
 lcd.print("max: ");
 lcd.setCursor(5, 1);
 lcd.print(maxcon);

 Serial.print(" concentration: ");
 Serial.print(concentration);
 Serial.println(" pcs/0.01cf");
 Serial.print(" max concentration: ");
 Serial.print(maxcon);
 Serial.println(" pcs/0.01cf");
 lowpulseoccupancy = 0;
 starttime = millis();
 }
}

Animated Bike Light

For this project we wanted to create a portable animated light that we could mount below our bike seat or attach it to a knapsack. The parts that we used were:

parts

We happened to have a Squarewear module from some past work. This is a nice module for wearable projects because it has a built-in rechargeable battery and easy to solder connections. However other small Arduino modules could also be used.

The case that we used we got from a local dollar store (for $1) and it had a hard back and neoprene front pocket.  The four wires that we soldered to the LED Matrix were fairly stiff so we bent them over the lip of the neoprene pocket. The Squarewear module was slipped in behind for easy access to its power switch.

mounting

The Adafruit 8×8 matrix has four connections: SDA, SCL, VCC and GND. In the arduino code you will need to include the libraries:

  • Adafruit_GFX.h
  • Adafruit_LEDBackpack.h

There are a lot of options on what you could display. Our first example used a set of squares that changed sizes.

Our second example used some “old school” animated alien images.

In our final project we used the button on the Squarewear to trigger four different modes:

  • Alien mode
  • Square mode (best for biking)
  • Scroll the temperature
  • Show some text

Have fun.

//****************************************************/
#include <Wire.h>
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include <PciManager.h>
#include <PciListenerImp.h>

#define ulong unsigned long
#define uchar unsigned char
#define pinButton 4
#define temp_sensor A1

PciListenerImp listener(pinButton, onPinButtonChange);

uchar buttonPressed = 10; //4 = onboard button
//-----------------------------------------------------------
void onPinButtonChange(byte changeKind) {
  static ulong t = 0;
  if (changeKind == 1 && millis() > t + 250) {
    t = millis();
    buttonPressed = 1;    
  }
}

Adafruit_8x8matrix matrix = Adafruit_8x8matrix();
//-----------------------------------------------------------
void setup() {
  pinMode(pinButton, INPUT);
  digitalWrite(pinButton, HIGH);
  PciManager.registerListener(pinButton, &listener); 
  
  
  matrix.begin(0x70);  // pass in the address
}
//-----------------------------------------------------------
void loop() {
  // Make 3 modes, 1) Temperature, 2) Bike, 3) Pictures
  
   buttonPressed = 0;
  loop_pic();
  
  buttonPressed = 0;
  loop_temp();

  buttonPressed = 0;
  loop_bike();
  
  buttonPressed = 0;
  loop_text();

}
//----------------------------------------
void loop_temp() {
  // Loop thru showing the temperature until the button is pressed
  
  float temp;
  
  matrix.setTextWrap(false); 
  matrix.setTextSize(0.5);
  matrix.setRotation(3);
  while(!buttonPressed) { 
    // convert to celcius
    temp = (float)analogRead(temp_sensor);
    temp = (temp * 3.3 / 1024 - 0.5) / 0.01;

    for (int8_t x=0; x>=-20; x--) {
      matrix.clear();
      matrix.setCursor(x,0);
      matrix.print(int(temp) );
      matrix.print("c");
      matrix.writeDisplay();
      delay(300);
      if(buttonPressed)  break;
    }
  }
}
//-------------------------------------------------------------
void loop_bike() {
  // Loop with a visible biking safe pattern until button pushed
  while(!buttonPressed) {
  
    matrix.clear();
    matrix.fillRect(3,3, 2,2, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
        if(buttonPressed)  break;
    delay(250);
    
    matrix.clear();
    matrix.fillRect(2,2, 4,4, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
    if(buttonPressed)  break;
    delay(250);
    
    matrix.clear();
    matrix.fillRect(1,1, 6,6, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
    if(buttonPressed)  break;
    delay(250);
    
    matrix.clear();
    matrix.fillRect(0,0, 8,8, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
    if(buttonPressed)  break;
    delay(250);
    
  }
} 
// ------------------------------------------------------------
const uint8_t PROGMEM alien[21][8] = {
{
  B00011000, // This is the first frame for alien #1
  B00111100, // If you squint you can kind of see the
  B01111110, // image in the 0's and 1's.
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B10100101	},

  {
  B00011000, // This is the second frame for alien #1
  B00111100,
  B01111110,
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B01000010 },

  {
  B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B10100101 },
  {
  B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B01000010 },

  {
  B00000000, // First frame for alien #2
  B00111100,
  B01111110,
  B11011011,
  B11011011,
  B01111110,
  B00100100,
  B11000011 },

  {
  B00111100, // Second frame for alien #2
  B01111110,
  B11011011,
  B11011011,
  B01111110,
  B00100100,
  B00100100,
  B00100100 },

  {
  B00000000, B00111100, B01111110, B11011011, B11011011, B01111110, B00100100, B11000011 },
  {
  B00111100, B01111110, B11011011, B11011011, B01111110, B00100100, B00100100, B00100100 },

  {
  B00100100, // First frame for alien #3
  B00100100,
  B01111110,
  B11011011,
  B11111111,
  B11111111,
  B10100101,
  B00100100},

  {
  B00100100, // Second frame for alien #3
  B10100101,
  B11111111,
  B11011011,
  B11111111,
  B01111110,
  B00100100,
  B01000010 },

  {
  B00100100, B00100100, B01111110, B11011011, B11111111, B11111111, B10100101, B00100100 },
  {
  B00100100, B10100101, B11111111, B11011011, B11111111, B01111110, B00100100, B01000010 },
  
  {
  B00111100, // First frame for alien #4
  B01111110,
  B00110011,
  B01111110,
  B00111100,
  B00000000,
  B00001000,
  B00000000 },

  {
  B00111100, // Second frame for alien #4
  B01111110,
  B10011001,
  B01111110,
  B00111100,
  B00000000,
  B00001000,
  B00001000 },

  {
  B00111100, // Third frame for alien #4 (NOT a repeat of frame 1)
  B01111110,
  B11001100,
  B01111110,
  B00111100,
  B00000000,
  B00000000,
  B00001000 },
 
  {
  B00111100, // Fourth frame for alien #4 (NOT a repeat of frame 2)
  B01111110,
  B01100110,
  B01111110,
  B00111100,
  B00000000,
  B00000000,
  B00000000},

  {
  B00111100, B01111110, B00110011, B01111110, B00111100, B00000000, B00001000, B00000000 },
  {
  B00111100, B01111110, B10011001, B01111110, B00111100, B00000000, B00001000, B00001000 },
  {
  B00111100, B01111110, B11001100, B01111110, B00111100, B00000000, B00000000, B00001000 },
  {
  B00111100, B01111110, B01100110, B01111110, B00111100, B00000000, B00000000, B00000000 },
  
};

 
 
//-------------------------------------------------------------
static const uint8_t PROGMEM
  smile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10100101,
    B10011001,
    B01000010,
    B00111100 },
    
  wink_bmp[] =
  { B00111100,
    B01000010,
    B10100001,
    B10000001,
    B10100101,
    B10011001,
    B01000010,
    B00111100 },
    
  lsmile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10100001,
    B10011001,
    B01000010,
    B00111100 },
    
   rsmile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10000101,
    B10011001,
    B01000010,
    B00111100 };

//----------------------------------------
void loop_pic() {
  
  while(!buttonPressed) {
    matrix.clear();
    matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1500);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, wink_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(500);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1500);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, lsmile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1000);
    
    matrix.clear();
    matrix.drawBitmap(0, 0, rsmile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1000);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1500);
    
    matrix.setRotation(3);
    for (int i=0; i< 21; i++) {
      matrix.clear();
      matrix.drawBitmap(0, 0, alien[i], 8, 8, LED_ON);
      matrix.writeDisplay();
      delay(250);
      if(buttonPressed)  break;
    }
  }
}
//----------------------------------------
void loop_text() {
  // Loop thru showing the temperature until the button is pressed
   
  matrix.setTextWrap(false); 
  matrix.setTextSize(1);
  matrix.setRotation(3);
  char thetext[] = "Hello";
  while(!buttonPressed) { 
 
    for (int8_t x=0; x>=-40; x--) {
      matrix.clear();
      matrix.setCursor(x,0);
      matrix.print(thetext);
       matrix.writeDisplay();
      delay(300);
      if(buttonPressed)  break;
    }
  }
}