Hands-on with the Internet of Things – Part 3

This entry is the next part of a series in which I share my experience building a solar-powered camera node for a ZigBee mesh network. These posts are not intended to be a tutorial on ZigBee; there are lots of places to find that information, one of the most digestible being Building Wireless Sensor Networks by Robert Faludi.

Now that I had the radios talking to each other, I wanted to try out the camera. I chose the Weatherproof TTL Serial JPEG Camera from Adafruit.com, based on the VC0706 security camera digital processor from Vimicro. The core part of the camera is a 1/4-inch CMOS sensor. A microcontroller can talk to the camera through a serial port; sending a command over the serial port to take a photo and retrieve it will return a JPEG image up to 640 x 480 pixels. The serial port baud rate can go up to 115200 baud. You can send it commands to change focus, and also to take a snapshot when it detects movement.

Another nice thing about this camera is the weatherproof enclosure, and the array of infrared LEDs and a built-in light sensor; if the light sensor detects there is not enough visible light, it switches on the infrared LEDs to illuminate the scene, making it possible to take night shots. At USD$55, I reckon it’s good value.

weathervc0706_LRG

The camera module uses 3.3V logic on its pins; the Arduino uses 5V, so I needed to have a voltage divider on the input to the camera so that voltage levels don’t blow it away.

This is the circuit:

IMG_1010

The TX and RX pins refer to the transmit and receive pins, relative to their module. Pins 2 and 3 on the Arduino will be used for serial receiving and transmitting respectively. RX and TX on the camera are for receiving to and transmitting from the camera.

For now, I wired this up on a small breadboard.

IMG_1008

Adafruit have done a lot of work to make this camera as easy to use as possible. They have published an Arduino library and examples for talking to VC0706-based serial JPEG cameras with a higher-level API. It’s quite simple to get the camera to take a snapshot and read it out from the serial port. Then I only needed to send it to the serial port on the XBee module to get the image to the Coordinator and the receiving node.

Since I was now sending a couple of different types of messages, binary images and text debug and information messages, I needed to design a simple message protocol. I decided to use JSON format.

A debug and information message looks like this:

{ “type”: “inf”, “msg”: “an info message” }

And an image message uses Base64 encoding for each 32-byte segment, also providing the number of bytes remaining in the image:

{ “type”: “img”, “msg”: { “seg”: { “rem”: 345, “enc”: “YW4gZW5jb2RlZCBtc2c=” } } }

To decode these message types, I changed the Ruby server to handle each type. If it receives an ‘inf’ message type, it just prints out the message contents. If it receives an ‘img’ message type, it decodes the Base64 string into an array of bytes. When it has received all the image in the byte array, it writes the image to a file and waits to receive the next one.

require ‘serialport’
require ‘json’
require ‘base64’
sp = SerialPort.new “/dev/cu.usbmodem1d11”, 115200, 8, 1, SerialPort::NONE
imageBytes = “”
loop {
jsonMessage = sp.gets.tr(“\n\r”, “”)
begin
parsedMessage = JSON(jsonMessage)
if parsedMessage[“type”] == “inf”
puts parsedMessage[“msg”]
elsif parsedMessage[“type”] == “img”
imageBytes += Base64.decode64(parsedMessage[“msg”][“seg”][“enc”])
if parsedMessage[“msg”][“seg”][“rem”] <= 0
# Write the image bytes to a file
File.open(“photo.jpg”, “wb”) do |f|
f.write(imageBytes)
end
# Clear the image bytes for the next one
imageBytes = “”
puts “Wrote an image to the file. Waiting for the next one.”
end
end
rescue Exception => e
puts e.message
imageBytes = “”
end
}

On the Arduino side, I needed to set up the serial port as before, and to initiate the camera. As indicated in the diagram above, the camera module is connected to digital ports 2 and 3 on the Arduino. Basing the code on the Adafruit snapshot example, it looked like this:

// This program takes a snapshot and sends it to the XBee module
#include <Adafruit_VC0706.h>
#include <SoftwareSerial.h>
#include <Base64.h>
// On Uno: camera TX connected to pin 2, camera RX to pin 3:
SoftwareSerial cameraconnection = SoftwareSerial(2, 3); // Arduino RX, TX
Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection);
void sendMsg(String msgType, String msg) {
Serial.println(“{\”type\”: \”” + msgType + “\”, \”msg\”: ” + msg + “}”);
}
String formatImagePayload(uint16_t bytesRemaining, String encodedImageSegment) {
String result = “{\”seg\”: {\”rem\”:” + String(bytesRemaining) + “, \”enc\”:\”” + encodedImageSegment + “\”}}”;
return result;
}
String formatInfoPayload(String msg) {
String result = “\”” + msg + “\””;
return result;
}
void setup() {
Serial.begin(115200);
sendMsg(“inf”, formatInfoPayload(“VC0706 Camera snapshot test”));
// Try to locate the camera
if (cam.begin()) {
sendMsg(“inf”, formatInfoPayload(“Camera found.”));
}
else {
sendMsg(“inf”, formatInfoPayload(“No camera found?”));
return;
}
// Set the picture size
cam.setImageSize(VC0706_640x480); // biggest
}
void loop() {
// Take the picture
sendMsg(“inf”, formatInfoPayload(“Snap in 3 secs…”));
delay(3000);
if (! cam.takePicture())
sendMsg(“inf”, formatInfoPayload(“Failed to snap!”));
else
sendMsg(“inf”, formatInfoPayload(“Picture taken!”));
// Get the size of the image (frame) taken
uint16_t jpglen = cam.frameLength();
sendMsg(“inf”, formatInfoPayload(“Sending ” + String(jpglen, DEC) + ” byte image.”));
int32_t time = millis();
// Read all the data up to # bytes!
while (jpglen > 0) {
// read 32 bytes at a time;
uint8_t *buffer;
uint8_t bytesToRead = min(32, jpglen); // change 32 to 64 for a speedup but may not work with all setups!
buffer = cam.readPicture(bytesToRead);
int encodedStringLength = base64_enc_len(bytesToRead);
char encodedStringBuffer[encodedStringLength];
encodedStringLength = base64_encode(encodedStringBuffer, (char *)buffer, bytesToRead);
jpglen -= bytesToRead;
sendMsg(“img”, formatImagePayload(jpglen, encodedStringBuffer));
}
time = millis() – time;
sendMsg(“inf”, formatInfoPayload(String(time)+” ms elapsed”));
}

The resulting image is fair quality; serviceable, but certainly not the best I’ve seen from a serial JPEG module. To be fair, the image below was taken in-doors, in artificial light at night.

photo-1

The best part is how well the infrared LEDs automatically illuminated the same scene when I turned off the lights.

photo-2

I’m hoping this will work really well out in the field. More on that later.

With the XBees running at 115200 baud, it seemed to take an average of 20 seconds to send a 45K-byte image. Keen observers, or even casual ones, will note that by choosing a JSON format for the messages, I have effectively doubled the number of bytes required to send an image to the server. I will fix that problem in the next installment.

Want to know more about how DiUS can help you?

Offices

Melbourne

Level 3, 31 Queen St
Melbourne, Victoria, 3000
Phone: 03 9008 5400

DiUS wishes to acknowledge the Traditional Custodians of the lands on which we work and gather at both our Melbourne and Sydney offices. We pay respect to Elders past, present and emerging and celebrate the diversity of Aboriginal peoples and their ongoing cultures and connections to the lands and waters of Australia.

Subscribe to updates from DiUS

Sign up to receive the latest news, insights and event invites from DiUS straight into your inbox.

© 2024 DiUS®. All rights reserved.

Privacy  |  Terms