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 shield.

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;
    }
  }
}

NFC (Near Field Communications)

If you would like to add some security to your Arduino projects, NFC or Near Field Communications is a low cost solution. We purchased a NFC module and card for under $5. This module is also supported by Raspberry Pi Python libraries.

To get things started you will need to load that Arduino MFRC522 library. You can do this by calling up the Arduino Library Manager, then search for 522.

mfrc522_lib

The default circuit wiring should be:

Uno_RFID_wiring

NFC tags can contain information besides just their tag ID number.

We modified one of the library examples, to include a check based on some “good” tag IDs. The code is below:

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9          // Configurable, see typical pin layout above
#define SS_PIN          10         // Configurable, see typical pin layout above

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance

#define NUMCARDS 2
int gooduid[NUMCARDS][10] = {
  {0xD5, 0xF6, 0xA6, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0},
  {0x13, 0x2F, 0x4E, 0xE5, 0x0, 0x0, 0x0, 0x0, 0x0}
  };

void setup() {
	Serial.begin(9600);		// Initialize serial communications with the PC

	SPI.begin();			// Init SPI bus
	mfrc522.PCD_Init();		// Init MFRC522
	mfrc522.PCD_DumpVersionToSerial();	// Show details of PCD - MFRC522 Card Reader details
	Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}

void loop() {
	// Look for new cards
	if ( ! mfrc522.PICC_IsNewCardPresent()) {
		return;
	}

	// Select one of the cards
	if ( ! mfrc522.PICC_ReadCardSerial()) {
		return;
	}

	// Dump debug info about the card; PICC_HaltA() is automatically called
	mfrc522.PICC_DumpToSerial(&(mfrc522.uid));

  // Reset the valid card checks
  bool anyok = false;
  bool cardok[NUMCARDS];
  for (int i ;  i < NUMCARDS; i++ ) {cardok[i] = false; }
  // Check the card ID   
  for (int j=0; j< NUMCARDS; j++) { 
    for (int i=0; i<mfrc522.uid.size; i++) {
      if (mfrc522.uid.uidByte[i] == gooduid[j][i]) {
        cardok[j] = true;
      } else {
        cardok[j] = false;
        break;
      }
    }
  }
  // Check status of card check
  for (int i=0 ;  i < NUMCARDS; i++ ) {
    if (cardok[i] == true) {
      anyok = true;
      break;
    }
  }
// Print the card check status  
  if (anyok == true) { 
    Serial.println ("Good Card -- do some action");
  } else {
    Serial.println ("Invalid Card");
  }
  
}

When the Arduino monitor is running the output should look something like:

nfc_output

Once we found our “good” tag Id we added a reference to it in our code:

int gooduid[NUMCARDS][10] = { {0xD5, 0xF6, 0xA6, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0},
{0x13, 0x2F, 0x4E, 0xE5, 0x0, 0x0, 0x0, 0x0, 0x0} };

Once the basic code and setup is working there are a lot of potential projects to work on.  Below is a example where we used a PowerSwitch Tail II with an NFC tag to control a power connection.

nfc_2_power

Tow Truck

The goal for our tow truck was to have a 4-axis crane and a movable vehicle that we could remotely control with an Android smart phone.

tow_truck2

The parts we used for this project were:

Hardware Setup

The tow truck project used a camera mount for up/down/left/right crane motion and an Arduino car chassis for mobility. The controls were done using Bluetooth.

Meccano was used to build a box for the main structure. Wire was used to secure everything together. We laid a folded piece of paper under the Arduino Mega to ensure that none of the Arduino solder connections shorted on the metal Meccano base.

tow_truck_inside

The motor and servo shield that we used did not expose any of the extra Arduino pins, so we needed to use the Mega board. We then wired the Bluetooth module to the exposed pins on the end of the Mega.

tow_truck_circuit

Arduino Code

The Arduino code will vary a little based on the motor/servo shield that is used. Our shield was an older version 1 (V1) board that used direct pin connections (no I2C or SDA/SCL connections).

Also because Tx/Rx (Tx0/Rx)) were not available once our motor/servo shield was installed we used Tx1/Rx1 and so our Bluetooth connection was on Serial1 and not Serial.

For the Bluetooth communications we used the following command letters:

  • R = drive right
  • L = drive left
  • f = drive forwards
  • b = drive backwards
  • s = stop driving
  • r = move crane right
  • l = move crane left
  • u= move crane up
  • d = move crane down

Our Arduino code is below:

#include <Servo.h> 

Servo servo1;
Servo servo2;

char thecmd;
int xpos = 90;
int ypos = 90;

AF_DCMotor motor1(1); 
AF_DCMotor motor2(2);

void setup() {
  pinMode( 19, INPUT_PULLUP );
  Serial1.begin(9600);
  Serial1.println("Crane Controls");
  Serial1.println("r = right, l = left, u= up, d = down");
  Serial1.println("Driving Controls");
  Serial1.println("R = right, L = left, f = forwards, b = backwards, s = stop");
  servo1.attach(9); // attaches the servo on pin 9 to the servo object 
  servo2.attach(10); // attaches the servo on pin 9 to the servo object 
  
  servo1.write(xpos);
  servo2.write(ypos);

  motor1.setSpeed(255);
  motor2.setSpeed(255);

}
void loop() {
  
  if (Serial1.available() > 0) {
        // read the incoming byte: 
       thecmd = Serial1.read();
       Serial1.println(thecmd);
       if (thecmd =='l') { move_crane(servo1, 5); }
       if (thecmd =='r') { move_crane(servo1, -5); }
       if (thecmd =='d') { move_crane(servo2, 5); }
       if (thecmd =='u') { move_crane(servo2, -5); }
       if (thecmd =='f') { 
            motor1.run(FORWARD); 
            motor2.run(FORWARD); 
       }
        if (thecmd =='b') { 
            motor1.run(BACKWARD); 
            motor2.run(BACKWARD); 
       }
       if (thecmd =='L') { 
           motor1.run(BACKWARD); 
           motor2.run(FORWARD); 
       }
       if (thecmd =='R') { 
            motor1.run(FORWARD); 
            motor2.run(BACKWARD); 
       }
       if (thecmd =='s') { 
           motor1.run(RELEASE); 
           motor2.run(RELEASE); 
       }
  }
}

void move_crane(Servo theservo, int direction) {
  int minpos = 50;
  int maxpos = 220;
  if (direction < 0) {
    if (ypos > minpos) {
      ypos = ypos + direction;
      theservo.write(ypos);
    }
  }
  else {
    if (ypos < maxpos) {
      ypos = ypos + direction;
      theservo.write(ypos); 
    }   
  }
}

Android Program

To communication to an Android smart phone we used MIT’s App inventor. This is a free Web based Android development tool.

There are many ways to layout a control screen, for us we used a 10×3 table and then populated it with buttons. Our layout is shown below:

ai_layout

The button logic will pass the required letter command to the Bluetooth component:

ai_logic

Our final running App looked like:

Screenshot_tow_truck

PSP Controlled Arduino Airboat

We thought that it would be fun to try and use an old PlayStation Portable (PSP) on some Arduino and Pi Projects. If you don’t a have PSP you can usually find a used one at a good price.

Some smart people were able to modify or “mod” the PSP firmware so that it is possible to run open source applications on the PSP. We tried using Python, Lua, sdlBasic and SSH to talk between our PSP  and our Arduinos and Pi’s, but none of these methods were simple or 100% reliable. In the end we found that basic built-in PSP Web browser worked the best and it didn’t require a ‘moded’ PSP.

airboat

PSP Setup and Limitations

We were using an older PSP-1000 so if you have a newer PSP GO or PSP Vita you may not have the same limitation that we found. However we think if you stick to our ‘worst case’ setup you should be good to go.

Our recommended setup was:

  • Simple Web Pages
  • No Browser Cache
  • Simple Wireless Network

For the PSP-1000 the web pages had to be very simple, no CSS (Cascading Style Sheets) and no advanced HTML tagging. We had hoped to show Node-Red Web pages from the PI but this was not possible.

In our testing we found that it was important to turn off the browser cache, otherwise we found that our commands would only work once. To turn off the PSP browser cache, go into the PSP browser and select “Tools”, then “Settings”, and “Cache Settings”.

cache

Our older PSP-1000 had some problems with the newer WPA2 wireless encryption, so to simplify things we created a small standalone open network. For Arduino projects this isn’t a problem because the Arduino can be made into a standalone access point. On Pi projects where you are using an existing wireless network you might need to do some tweeking to add a guest account.

To add a new connection on the PSP go to the “Network Settings” and select “Infrastructure Mode”. Then select “[New Connection]” and “Scan”. The scan will only show networks that the PSP is able to connect to.

networksettings

A Simple Web Form

An HTML form supports two types of action, a POST and a GET. The GET method is the simpler (but less secure) approach and it passes parameters on the URL command line.

Below is a simple Web form:

<html>
<body>
<h1>Click a button to control the car</h1>
<form action='GO' method='GET' >
<INPUT TYPE='submit' VALUE="GO" >
</form>
<form action='STOP' method='GET' >
<INPUT TYPE="submit" VALUE="STOP" >
</form>
<form action='LEFT' method='GET' >
<INPUT TYPE='submit' VALUE="LEFT" >
</form>
<form action='RIGHT' method='GET' >
<INPUT TYPE="submit" VALUE="RIGHT" >
</form>

</body>
</html>

menu

An Arduino Web Server

To create Arduino WiFi projects the ESP8266 based modules are low cost way to go. There are some good ESP8266 libraries and the examples are fairly easy to follow. The ESP8266 module can be wired into an Arduino Uno/Nano/Mega module or you can by buy boards with the ESP8266 chip integrated in. For our testing we used an older WeMo board, but other options like the NodeMCU, Adafruit HUZZAH or even the Arduino Yún could be used.

The ESP8266WebServer library has a simple standalone access point example. We modified this example (WifiAccessPoint) to include HTML form tags for all our required action.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

int pinleft = 12;
int pinright = 13;
int pinfront = 14;

/* Set these to your desired credentials. */
const char *ssid = "MY8266";
const char *password = "";

char *webpage = "<html><head><title>My8266 Control</title> \
</head><body> \
<h1>Click a button to control the car</h1>

<hr>

\
<form action='/go' method='GET' > \
<input type='submit' style='font-size:150px;color:lime' value='GO'></form>

\
<form action='/stop' method='GET'> \
<input type='submit' style='font-size:150px;color:red' value='STOP'></form>

 \
<form action='/left' method='GET'> \
<input type='submit' style='font-size:150px' value='LEFT'></form>

 \
<form action='/right' method='GET'> \
<input type='submit' style='font-size:150px' value='RIGHT'></form>

 \
</body></html>";

ESP8266WebServer server(80);

/* Just a little test message. Go to http://192.168.4.1 in a web browser
* connected to this access point to see it.
*/
void handleRoot() {
Serial.println("Base page");
server.send(200, "text/html", webpage);
}

void go() {
Serial.println("Go forward");
server.send(200, "text/html", webpage);
digitalWrite(pinleft,LOW);
digitalWrite(pinright,LOW);
digitalWrite(pinfront,LOW);

}
void stop() {
Serial.println("Stop");
server.send(200, "text/html", webpage);
digitalWrite(pinleft,HIGH);
digitalWrite(pinright,HIGH);
digitalWrite(pinfront,HIGH);
}
void left() {
Serial.println("Go left");
server.send(200, "text/html", webpage);
digitalWrite(pinleft,HIGH);
digitalWrite(pinright,LOW);
digitalWrite(pinfront,HIGH);

}
void right() {
Serial.println("Go right");
server.send(200, "text/html", webpage);
digitalWrite(pinleft,LOW);
digitalWrite(pinright,HIGH);
digitalWrite(pinfront,HIGH);
}

void setup() {
delay(1000);
Serial.begin(115200);
Serial.println();
pinMode(pinleft,OUTPUT);
pinMode(pinright,OUTPUT);
pinMode(pinfront,OUTPUT);

digitalWrite(pinleft,HIGH);
digitalWrite(pinright,HIGH);
digitalWrite(pinfront,HIGH);

Serial.print("Configuring access point...");
/* You can remove the password parameter if you want the AP to be open. */
WiFi.softAP(ssid, password);

IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.on("/", handleRoot);
server.on("/go",go);
server.on("/stop",stop);
server.on("/left",left);
server.on("/right",right);
server.begin();
Serial.println("HTTP server started");
}

void loop() {
server.handleClient();
}

Using QR Codes

The typical UPC-A barcode is visually represented by strips of bars and spaces, that encode a 12-digit number. A QR code (Quick Response Code) is a matrix barcode that can contain up to 4296 characters.

For Pi or Arduino projects QR codes could be used to document what the module is doing, or “if lost please call…”, or web links.

Create Your Own QR Codes

There are a number of different tools available to create your own QR codes. One of the simplest methods is to use Google Charts, and it is called by:

http://chart.apis.google.com?

The important parameters are:
cht=qr – chart type = qr
chs=x – chart size, and
chl= – the data or URL to encode

An example of encoding “Hello World” in a 100×100 QR would be:

http://chart.apis.google.com/chart?chl=Hello+World&chs=100×100&cht=qr

A simple web form that can be used to create QR codes

 <html>  
 <head>  
  <title>Create QR Codes</title>  
 </head>  
 <body>  
 <h1>Create QR Codes</h1>  
 <form action="http://chart.apis.google.com/chart" method="get">  
      Text to embed in QR Code</br>  
      <textarea name="chl" style="height:100px;width:300px;"></textarea>  
      </br>Image Size :</br>   
      <select name="chs">  
           <option value="100x100">100x100</option>  
           <option value="150x150">150x150</option>  
           <option value="200x200">200x200</option>  
           <option value="250x250">250x250</option>  
           <option value="300x300" selected>300x300</option>  
           <option value="350x350">350x350</option>  
           <option value="400x400">400x400</option>  
           <option value="500x500">500x500</option>  
      </select>  
      <input type="hidden" name="cht" value="qr"></br>  
      <p><input type="submit" value="Create QR Code"></p>  
 </form>  
 </body>  
 </html>  

 

html_code

After the image is generated it can be printed, cut to size and then taped to your equipment. If you have a number of Arduino or Pi modules QR codes could be an easy way to determine what is loaded on each module.

Create an Android QR Reader App

MIT’s AppInventer is a great option for Android smart phones and tablets.

For our application we used the following components:

  • 1 Button – to start QR scanning
  • 1 Textbox – to show QR scan results
  • 1 Button – to call a browser if the QR code is a Web link
  • 1 BarcodeScanner – to turn on the camera and process the QR code
  • 1 ActivityStarter – to launch the web browser

screen

The logic starts by defining the camera to be used for the QR scanning, and setting the ActivityStarter.Action to be a browser (android.intent.action.VIEW).

The BarcodeScanner1.AfterScan block puts the decoded QR data into the textbox. If the QR code starts with “http” then the “Open Link” button is enabled.

logic

Once our custom QR reader app is on our device we can start to customize it to our needs. The picture below shows the basic app reading a 100×100 QR code used on an Arduino project. Some future considerations that could be added to this simple app could things like: recording the geographic location of the device or is the data valid.

scan2

QR Codes and the Internet of Things (IoT)

For projects with a lot of sensors or devices QR codes can be helpful to identify and document what each device is used for. If the device is a source of data then a QR code could link to that specific devices data.

The picture below is an example of a solar powered weather device. The ESP8266 based Arduino module uses MQTT to send data back to a Pi node running Node-Red. The QR code on the side of the enclosure has a link to the Node-Red web page.

outside2

Arduino FM Radio

There are a few FM receiver modules that are available. We used an FM module that is based on the RDA5807M chip and unlike some of the other chips this one supports volume control. To keep our project compact we used the Arduino Nano, but we tested the UNO and Mega and they both worked well.

fmradio

There are a lot of options on how to control your FM radio. We liked the idea of using an old TV remote because this allowed us to change the volume and radio stations anywhere in the room. For this project we used:

• an I2C FM receiver module ($13)
• any Arduino module that has SDA and SCL pins
• an IR receiver ($5)
• a small bread board and some jumpers

The FM receiver module works on the I2C bus that connects to the SDA and SCL pins. Depending on which IR chip you use the wiring will vary slight. The key thing is to connect the data pin correctly. For our example we used pin 11.

circuit2

FM Module

When we first starting working this project there was no Arduino FM library available, so we started to by talking directly with the I2C bus. However now an Arduino FM library is available.

The RDA5807M chip [3] on the FM receiver module has a number of register addresses that we needed to write to. The key ones were:

  • REGISTER 2 – Initialize the chip (0xC003)
  • REGISTER 2 – Enable radio communications (0xC00D)
  • REGISTER 3 – Set the frequency
  • REGISTER 5 – Set the volume

We needed to pause between initializing and enabling radio communications. Setting the radio frequency is done by offsetting the frequency from the minimum range (870) and then splitting the value into high and low bytes.

The volume on register 5 is adjusted between a value of 0, the lowest, and 15 or 0xF the highest. There are other options on register 5 so our code changed the value from 0x84D0 to 0x84DF.

The Arduino modules uses the wire.h library to read and write to I2C devices. To write to a register the important commands are:

  • Wire.beginTransmission
  • Wire.write
  • Wire.endTransmission

An example using these commands would be:


//This is an example of setting the volume to 1
//This sets up communications to a device
Wire.beginTransmission(0x11); // 0x11 is the RDA5807M chip
Wire.write(0x05); // address 0x05 is where you want to write to
Wire.write(0x84); // write 0x84 to the first byte of address 0x05
Wire.write(0xD1); // write 0xD1 to the second byte of address 0x05
Wire.endTransmission(); // finish the write

We made a simple radio test program (Listing 1) that set up the radio to a local frequency at volume 1.


#include <Wire.h>

int freq;
int freqB;
byte freqH, freqL;

void setup()
{
Wire.begin();

// Initialize the RDA5807M chip

Wire.beginTransmission(0x11); // Device address is 0x11
Wire.write(0x02); // Register address 0x02
Wire.write(0xC0); Wire.write(0x03); // Initialize the settings
Wire.endTransmission(); // stop condition
delay(500); // wait 500ms

Wire.beginTransmission(0x11); // Device address is 0x11
Wire.write(0x02); // Register address 0x02
Wire.write(0xC0); Wire.write(0x0D); // Setup the radio for communications
Wire.endTransmission();
delay(500);

// Define an FM station to listen to

freq = 1079; // 107.9 MHz our local FM station
freqB = freq - 870; // chip needs to have freq offset from lowest freq (870)
freqH = freqB>>2; // you need to break the offset freq into 2 parts (hi/low)
freqL = (freqB&3)<<6; // Shift channel selection for matching register 0x03

Wire.beginTransmission(0x11);
Wire.write(0x03);
Wire.write(freqH); // write High freq byte
Wire.write(freqL + 0x10); // write Low freq byte
Wire.endTransmission();

// The volume is from 0-F and its the first bytes, leave all the bytes (0x84D0 - 0x84DF)

Wire.beginTransmission(0x11);
Wire.write(0x05);
Wire.write(0x84); Wire.write(0xD1); // set volume to 1
Wire.endTransmission();
}

void loop()
{

}

Finding TV Remote Codes

We used a small program to find the IR (Infrared) codes from our TV remote. For our program we used the volume up, volume down, channel up and channel down keys. Different TV remotes will have different codes so you will have to find the codes that work with your remote.


/*
IR TEST PROGRAM

PINOUTS:

LEFT = DATA PIN
MIDDLE = GND
RIGHT = 3.3 volts

*/
#include <IRremote.h>

int RECV_PIN = 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);

}
}

ircode

When our IR test program was running we used the Arduino monitor window to show us the different key codes. We manually recorded these codes and used them in our final project.

Using a TV Remote

Our goal was to use a TV remote to change the FM station and to adjust the volume. There are a lot of websites you can use to find nearby radio stations. A good website for this is: http://radio-locator.com.

Our final program  used a number of predefined FM stations that would change with channel up and channel down on the TV remote. The remote’s volume up and down would change the radio’s volume. Remember to change the IR codes to match your TV remote, and the FM stations for your area.


// RDA5807M Radio controlled with a TV Remote
//
#include <Wire.h>
#include <IRremote.h>

int RECV_PIN = 11; //Connect the IR data pin in 11
IRrecv irrecv(RECV_PIN);
decode_results results;

int volume = 0; // start with the volume low
int channellist[]= {999,1029,1021,1079}; // Define some radio stations
int maxc = 4; // Define the number of radio stations used
int channel = 2; // Start with a favorite station

void setup()
{
Wire.begin();

Serial.begin(9600);

irrecv.enableIRIn(); // Start the receiver

radio_init();
setfreq(channellist[channel]);
setvolume(volume);

}
void loop() {
if (irrecv.decode(&results)) {

switch (results.value) {
case 0xE0E0E01F:
Serial.println("vol up");
if (volume < 15) {
volume++;
setvolume(volume);
}
break;
case 0xE0E0D02F:
Serial.println("vol down");
if (volume > 0) {
volume--;
setvolume(volume);
}
break;
case 0xE0E048B7:
Serial.println("chan up");
if (channel < maxc) {
channel++;
setfreq(channellist[channel]);
}
break;
case 0xE0E008F7:
Serial.println("chan down");
if (channel > 0) {
channel--;
setfreq(channellist[channel]);
}
break;
default:
Serial.println(results.value, HEX);
}
irrecv.resume(); // Receive the next value
}
delay(100);
}
//===============================
void setvolume(int thevolume)
{
byte volbyte;

volbyte = thevolume + 0xD0;
Wire.beginTransmission(0x11);
Wire.write(0x05);
Wire.write(0x84); Wire.write(volbyte);
Wire.endTransmission();
delay(500);
}
//===============================
void setfreq(int thefreq)
{
int freqB;
byte freqH, freqL;

freqB = thefreq - 870;
freqH = freqB >> 2;
freqL = (freqB & 3) <<6;

Wire.beginTransmission(0x11);
Wire.write(0x03);
Wire.write(freqH); // write frequency into bits 15:6, set tune bit
Wire.write(freqL + 0x10);
Wire.endTransmission();
delay(500);
}
//================================
void radio_init()
{
Wire.beginTransmission(0x11); // Device address 0x11 (random access)
Wire.write(0x02); // Register address 0x02
Wire.write(0xC0); Wire.write(0x03); // Initialize the settings
Wire.endTransmission();
delay(500); // wait 500ms to finalize setup

Wire.beginTransmission(0x11); // Device address 0x11 (random access)
Wire.write(0x02);
Wire.write(0xC0); Wire.write(0x0D); // Setup radio settings
Wire.endTransmission();
delay(500); // wait 500ms to finalize settings
}

Packaging our Project

We wanted to make our radio project portable, so we came up with two ideas. The first idea was to put all the electronics into a plastic container. For this we used a soap dish ($1) that we drilled three holes in. The first two holes were for the power and speaker cords. The last hole was for the IR receiver. To power our project we used a solar charger ($10), so we could listen to music outside.

soapdish1

Our second idea was to place the electronics inside the pocket of a cooler bag. It is important to have the IR receiver poking out of the pocket.

coolerbag

There are also some fun projects where you could use LCD Keyboard Shields or Touchscreens.

lcd

 

 

Bluetooth Controlled Arduino Airboat

By using plastic bottles, K’Nex and some duct tape we built a boat frame. The fans on the Arduino module are controlled with Bluetooth from a phone.

OLYMPUS DIGITAL CAMERA
Airboat making a Left Turn

For this project the electrical parts that you need are:

– Arduino Uno
– 3x Fan Modules for Arduino ($6 each)
– 6x AA Battery Case w/ Power Plug ($4)
– Prototype Shield w/ Breadboard ($5)
JY-MCU Bluetooth Module ($7)

The fans are powered directly from the Arduino 5V pins, so no extra batteries are needed. With 3 fan modules the airboat moves quite well, 2 fans will also work. The fans will spin in either direction depending on the inputs used (INA or INB).

For flotation we used two medium sized plastic bottles, and for the frame we use K’Nex pieces. To connect the K’Nex frame to the bottles duct tape works well. The fans can be attached to the frame with wire, string or bolts and screws. To protect the Arduino some Tupperware can be taped to the middle of the frame.

boat_frame2

For the JY-MCU Bluetooth Module, you need to cross the TX and RX pins. TX on the module goes to RX on the Arduino, and RX on the module goes to TX on Arduino.

arduino-bluetooth

To connect your phone to the Arduino project your phone will need a Bluetooth Terminal program. For our Android phone we use the “Bluetooth Terminal” from Qwerty it’s free and easy to use.

You will also need to pair your phone with the JY-MCU Bluetooth Module. When you scan for Bluetooth devices the Arduino Bluetooth module will probably be called HC-06 .The pairing code will be: 1234 .

To control the airboat we use the following letters:
g – go forward
s – stop
l – left turn
r – right turn

phone_test

For the wiring of the fans we use pins 6 to 11. The Arduino and the battery pack will need to be balanced in the tupperware, otherwise the fans might touch the water.

The final code that we used is below.

// Airboat with 3 fans controlled by a bluetooth phone

int INA1 = 6; // back fan
int INB1 = 7; // back fan
int INA2 = 8; // right side fan
int INB2 = 9; // right side fan
int INA3 = 10; // left side fan
int INB3 = 11; // left side fan

char thekey; // input from Bluetooth phone

void setup()
{
// initialize the serial communication:
Serial.begin(9600); //baud rate of Bluetooth Module
// define the pins of the 3 fans as outputs
pinMode(INA1,OUTPUT);
pinMode(INB1,OUTPUT);
pinMode(INA2,OUTPUT);
pinMode(INB2,OUTPUT);
pinMode(INA3,OUTPUT);
pinMode(INB3,OUTPUT);
// start with all fans turned off
digitalWrite(INA1,LOW);
digitalWrite(INB1,LOW);
digitalWrite(INA2,LOW);
digitalWrite(INB2,LOW);
digitalWrite(INA3,LOW);
digitalWrite(INB3,LOW);
}

void loop() {

if (Serial.available() > 0) {
thekey = Serial.read(); // get the key from the phone

// "s" stops all fans
if (thekey == 's') {
Serial.println("Fans are stopped");
digitalWrite(INB1,LOW);
digitalWrite(INB2,LOW);
digitalWrite(INB3,LOW);
delay(1500);
}
// "g" runs all fans
if (thekey == 'g') {
Serial.println("Fans are going");
digitalWrite(INB1,HIGH);
digitalWrite(INB2,HIGH);
digitalWrite(INB3,HIGH);
}
// "l" only run right fan, turn left
if (thekey == 'l') {
Serial.println("Turn left");
digitalWrite(INB1,LOW);
digitalWrite(INB2,HIGH);
digitalWrite(INB3,LOW);
}
// "r" only run left fan, turn right
if (thekey == 'r') {
Serial.println("Turn right");
digitalWrite(INB1,LOW);
digitalWrite(INB2,LOW);
digitalWrite(INB3,HIGH);
}
}
}