Sensorbase datalogger

Sensorbase.org is an open data repository run by the Center for Embedded Networked Sensing at UCLA. It’s a database of sensor databases. You can upload datasets to it, and you can browse other datasets as well. If you’ve never set up a database before but are interested in logging sensor data, it’s a good tool to get started. If you know what you’re doing already and you need to log a lot of sensor data, or compare it to similar work from others, it’s a convenient tool.

Since I like working with sensors and microcontrollers connected directly to the Internet, I thought it would be useful to be able to put data directly into Sensorbase from sensors connected to a microcontroller, without a personal computer in between.

The microcontroller (an Arduino,in this case) speaks to a Lantronix Xport. Through the Xport, it makes a HTTP request for a PHP script on my server. The PHP script takes the microcontroller’s data and formats it appropriately for Sensorbase, and uploads it. Easy!

sensorbase_system.png

The microcontroller HTTP code is based on this example from Making Things Talk. The PHP code requires a separate file, sensorbase_config.php, which contains the sensorbase username and password. For security purposes, you might want to limit read, write, and execute permissions on this file to read and write for the user only (rw——-). Here’s the Arduino program. The Xport is connected as shown in the comments. In addition, the Xport’s TX is connected to the Arduino’s RX and vice versa.

/*
 SensorBase.org poster
 Language: Wiring/Arduino (pin numbers defined for Arduino)

 Microcontroller is connected to a Lantronix serial-to-ethernet device.
 This program connects to a HTTP server through the Lantronix module,
 makes a HTTP GET request for a PHP script, and parses the returned string.
 Lantronix device communicates at 9600-8-n-1 non-inverted (true) serial.

 This program is designed to contact a PHP script that returns a null byte
 (\0) as its last byte.

 Analog sensor on analog pin 0

 Digital I/O connections as defined in the defines, below

 Lantronix serial settings:
 Baudrate 9600, I/F Mode 4C, Flow 00
 Port 10001
 Remote IP Addr: --- none ---, Port 00000
 Connect Mode : D4
 Disconn Mode : 00
 Flush   Mode : 00

   created  11 Feb 2008
   modified 22 Sep 2008
   by Tom Igoe
 */

#include <SoftwareSerial.h>

// Defines for the  program's status (used for status variable):
#define disconnected 0
#define connecting 1
#define connected 2
#define requesting 3
#define reading 4
#define requestComplete 5

// Defines for I/O pins:

#define connectedLED 2        // indicates when there's a TCP connection
#define requestingLED 3       // indicates a HTTP request has been made
#define readingLED 4          // indicates device is reading HTTP results
#define requestCompleteLED 5  // indicates a successful read
#define programResetLED 6     // indicates reset of Arduino
#define deviceResetPin 7      // resets Lantronix Device
#define softwareTX 8          // softwareSerial TX to Xport
#define softwareRX 9          // softwareSerial RX, not used
// variables:
int inByte= -1;              // incoming byte from serial RX

int status = 0;              // Lantronix device's connection status
long lastCompletionTime = 0; // counter for delay after last completion
int moduleID = 1;            // ID of the sensor module (assumes several sensors
// are sending code to the PHP script)
int voltage = 0;             // the analog voltage from the sensor that you're sending

// set up softwareSerial to talk to the Xport:
SoftwareSerial xport =  SoftwareSerial(softwareRX,softwareTX);

void setup() {
  // set all status LED pins and Lantronix device reset pin:
  pinMode(connectedLED, OUTPUT);
  pinMode(requestingLED, OUTPUT);
  pinMode(requestCompleteLED, OUTPUT);
  pinMode(programResetLED, OUTPUT);
  pinMode(deviceResetPin, OUTPUT);

// define pin modes for SoftwareSerial tx, rx pins:
  pinMode(softwareRX, INPUT);
  pinMode(softwareTX, OUTPUT);
  // set the data rate for the SoftwareSerial port
  xport.begin(9600);

  // start serial port, 9600 8-N-1:
  Serial.begin(9600);

  //reset Lantronix device:
  resetDevice();
  // blink reset LED:
  blink(3);
}

void loop() {
  // read the data from the sensor:
  voltage = analogRead(0);
  // check the state of the HTTP request:
  stateCheck();
  // set the LEDs to inform the user about the state of the request:
  setLEDs();
}

/*
  Check the status of the connection and take appropriate action:
 */

void stateCheck() {
  switch (status) {
  case disconnected:
    // attempt to connect to the server:
    deviceConnect();
    break;
  case connecting:
    // until you get a C, keep trying to connect:
    // read the serial port:
    if (Serial.available()) {
      inByte = Serial.read();
      Serial.print(inByte, BYTE);
      if (inByte == 'C') {  // 'C' in ascii
        status = connected;
      }
      else {
        // if you got anything other than a C, disconnect:
        status = disconnected;
      }
    }
    break;
  case connected:
    // send HTTP GET request for CGI script:
    httpRequest();
    break;
  case requesting:
    waitForResponse();
    break;
  case requestComplete:
    waitForNextRequest();
  }
}

/*
  Set the indicator LEDs according to the state of the program
 */
void setLEDs() {
  /*  Except for the disconnected and connecting states,
   all the states of the program have corresponding LEDS.
   so you can use a for-next loop to set them  by
   turning them all off except for the one that has
   the same number as the current program state:
   */

  for (int thisLED = 2; thisLED <= 5; thisLED++) {
    if (thisLED == status) {
      digitalWrite(thisLED, HIGH);
    }
    else {
      digitalWrite(thisLED, LOW);
    }
  }
}

/*
  Command the Lantronix device to connect to the server
 */
void deviceConnect() {
  //   fill in your server's numerical address below:
  xport.print("C000.000.000.000/80\n");
  status = connecting;
  Serial.println("connecting");
}

/*
  Send a HTTP GET request
 */
void httpRequest() {
  // make sure you've cleared the last byte
  // from the last request:
  inByte = -1;
  //  Make HTTP GET request. Fill in the path to your version
  //  of the CGI script:
  xport.print("GET /~youraccount/sensorbase_xml_slog.php?");

  xport.print("sensor_ID");
  xport.print(moduleID, DEC);

  xport.print("&voltage=");
  xport.print(voltage, DEC);

  xport.print(" HTTP/1.1\n");

  //  Fill in your server's name:
  xport.print("HOST:yourserver.com\n\n");
  // update the state of the program:
  status = requesting;
  Serial.print("requesting");
}

/*
  Read the results sent by the server until you get a < character.
 */
void waitForResponse() {
  // wait for bytes from server:
  if (Serial.available()) {
    inByte = Serial.read();
    Serial.print(inByte, BYTE);
    // when the PHP script sends a 0, the script is done,
    // and the request is complete:
    if (inByte == 0) {
      status = requestComplete;
      lastCompletionTime = millis();
    }
  }
}
/*
  Wait two minutes before initiating a new request.
 */
void waitForNextRequest() {
  if (millis() - lastCompletionTime >= 120000) {
    // reset Lantronix device before next request:
    resetDevice();
    status = disconnected;
  }
}

/*
  Take the Lantronix device's reset pin low to reset it
 */
void resetDevice() {
  digitalWrite(deviceResetPin, LOW);
  delay(50);
  digitalWrite(deviceResetPin, HIGH);
  // pause to let Lantronix device boot up:
  delay(2000);
}
/*
   Blink the reset LED.
 */
void blink(int howManyTimes) {
  int i;
  for (i=0; i< howManyTimes; i++) {
    digitalWrite(programResetLED, HIGH);
    delay(200);
    digitalWrite(programResetLED, LOW);
    delay(200);
  }
}

Here’s the PHP script. This lives on your server. Put in the same directory as the sensorbase_config.php script that follows:

<?php

/*
    Sensorbase.org data slogger
    language: PHP

This script expects a series of incoming paramaters that match the fields
of a database in http://sensorbase.org.   The email, password, and database info
is held in a file called sensorbase_config.php.

The script generates an XML string formatted for upload to sensorbase.org 
from the incoming GET/POST parameter string.  If there is no date_time
parameter, it adds one with the current server date and time. Parameters listed in 
the $fieldNames array are  passed through (including date_time, if it's there). 

When the XML string is built, the script uses the PHP curlib to pass the data to 
sensorbase.org using HTTP POST. The server response  or any curlib error is returned.

created 15 Jan 2008
by Tom Igoe

*/

	// contains $user and $password and the database info that you need to change:
	include "sensorbase_config.php";           
	
	// Where's the database:
	$url="http://sensorbase.org";
	$page = "/upload.php";
	
	// combine URL with page:
	$url .= $page;
	
	
	// build the XML header:
	$data_string = "<table>\n\r\t<row>\n\r\n\r";
		
		
	// add date and time at the beginning if there's no date_time parameter
	// in the incoming GET/POST string:
	
	if (empty($_REQUEST["date_time"])) {
		// generate a date/timestamp:
		$date_time = date("Y-m-d H:i:s");
		$data_string .= "\t<field name=\"date_time\">".$date_time."</field>\n\r";
	}
	
	// add the fields from the incoming GET/POST string:
	foreach ($fieldNames as $thisField) {
		if (!empty($_REQUEST[$thisField])) {
			$data_string .= "\t<field name=\"$thisField\">".$_REQUEST["$thisField"]."</field>\n\r";
		}
	}

	// Add the XML footer:
	$data_string .= "\n\r\t</row>\n\r</table>\n\r";
	
	// build the paramter request string to call the URL:
	$parameters = "email=".urlencode($user)."&password=".urlencode($password);
	$parameters .= "&project_id=".urlencode($projectId);
	$parameters .= "&table_name=".urlencode($tableName);
	$parameters .= "&data_string=".urlencode($data_string);
	$parameters .= "&type=xml";

	// prepare to send to sensorBase:
	$curlHandle = curl_init();
	curl_setopt($curlHandle, CURLOPT_URL,$url);
	curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curlHandle, CURLOPT_TIMEOUT, 10);    
	
	  // Add the data to the curl call:
	curl_setopt($curlHandle, CURLOPT_POST, 1);
	curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $parameters);

	// make the curl call:
    $response = curl_exec($curlHandle);
 
    if (curl_errno($curlHandle)) {
		print "Error: " . curl_error($curlHandle);
	} else {
		// Show the result
		var_dump($response);
		curl_close($curlHandle);
	}

   echo "\0";
?>

Finally, here’s the sensorbase_config.php script. This goes in the same directory as the PHP script above. You may want to set the permissions so that others cannot read or write to this file.

<?php
$user="you@example.com";		// the email address you registered with ensorbase.org
$password="p4ssw0rd!";		     // your password

$projectId = 000;				// your project ID
$tableName = "your_table";			// the table you want to fill data into

$fieldNames = array(			// names of the fields in the table
	"date_time",
	"sensor_ID",
	"voltage"
);

?>

2 Replies to “Sensorbase datalogger”

  1. Pingback: The Daily Glyph

Comments are closed.