Source Code for Chart Recorder Application

The following code accompanies the O'Reilly Biocoder article, A Simple Data Acquisition and Plotting System for Low Cost Experimentation. The source code is a Processing.org sketch.



jcline/sketch_20130615_jcline1_20140906.pde.txt

[Source Code for Chart Recorder Application]

// Source code for Simple Chart Recorder Application example,
// UBW Microcontroller Analog Data Acquisition example ; 
// Runs in Processing.Org environment.
//
// Copyright 2013 Jonathan Cline jcline@ieee.org All Rights Reserved
//
// jcline@ieee.org 2009-03 Initial version
// jcline@ieee.org 2013-06-10 Branch for Enhancements. Used with 
//      UV Illuminator/Detector from Amersham Biosciences.
// jcline@ieee.org 2013-06-13 Add averaging line with data points.
//      Compatible with UBW Firmware version "D"

// Note, a useful exercise would be to rewrite this application
// in Python and Qt.  Another useful exercise: write timestamped data
// as a .csv file.

// About Processing:
// Processing is an application programming system developed 
// originally for artists or non-computer scientists to create 
// digital interactive artwork and to control mechatronic 
// animation, without spending excessive effort to learn 
// programming skills (refer to Processing.org).  Processing 
// is a fast way to develop applications which display graphical 
// results.  The programming language is reduced and simplified 
// and is well documented with a large archive of examples.  The 
// application system includes a scratch pad to rapidly prototype 
// programs as they are written.  Processing uses Java to allow 
// software applications to either run on a computer as a native 
// application, or as a web application within a web browser.  
// Software applications are immediately compatible with different 
// operating systems (Apple, Microsoft, GNU/Linux, Unix).  
// Processing is also used as the basis for the Arduino 
// programming system; however, the UBW is a better fit to most 
// requirements so using Arduino hardware is not recommended (see 
// the UBW web site).

// The USB microcontroller communication is a simple serial device
import processing.serial.*;
Serial ubw; 

int loops = 0;
int[] xvals;

void setup() {
  String ubwVersion;
  boolean found;
  float tdelay;
  found = false;
  size(1000,500);
  background(0); // black

  // Frame rate sets draw rate. Should correspond to UBW's sample timer period.
  frameRate(10); 

  stroke(255); // white
  xvals = new int[width];

  try {
    println(Serial.list());

    // Important: Change the index [2] below to correspond to the serial 
    // device in the device list of the computer.  On OS/X and Linux systems, 
    // this is easily done; on Microsoft systems, it is more difficult. 
    // Serial rate doesnt matter (it's USB), so it is listed as 9600 below.
    ubw = new Serial(this, Serial.list()[2], 9600); 

    tdelay=millis() + 5000;
    // Read firmware version from connected UBW Microcontroller; jcline 2009-03
    for (int i=0; millis() < tdelay ; i++) {
        // Get+Verify UBW version string, print to console
        ubw.write("v\n");
        delay(100);
        ubwVersion = ubw.readStringUntil('\n');
        if (ubwVersion != null) {
          if (ubwVersion.startsWith("UBW", 0)) {
            println("Found UBW attached to USB:\n");
            println(ubwVersion);
            found = true;
            break;
          }
        }
    }
  }
  catch (Exception e) {
    println("device access error\n");
  }

  if (found != true) {
    println("No UBW found, exit\n");
    exit();
  }
  else {
    ubw.write("C,255,255,255,1\n");
    ubw.write("CU,1,0\n");
    ubw.write("T,200,1\n"); // set timered update in millisec, A=1
    ubw.clear(); // flush
  }
}

// UBW's firmware uses %4u output for values of analog data
// Convert "0023" string to Int 23
Integer string4uToInt(String s) {
   char a[] = s.toCharArray();
   Integer i = 0;
   Integer v;
   // char array into positional math based on ASCII value
   v = byte(a[0]) - 48;
   i += v * 1000;
   v = byte(a[1]) - 48;
   i += v * 100;
   v = byte(a[2]) - 48;
   i += v * 10;
   v = byte(a[3]) - 48;
   i += v;
   return (i);
}

// Read analog data depending on framerate & draw graphical level indicator
void draw() {
  String cmd;
  String data;
  Integer val;

  loops++;
  // Important: Stop the plot after beaker is dry (20000 iterations)
  if (loops > 20000) {
    noLoop();
    ubw.write("T,0,1\n"); // Turn off timered updates
    println("done\n");
    ubw.stop();
  }

  data = ubw.readStringUntil('\n'); // read timered update
  if (data == null) {
    return; // no periodic data yet
  }
  if (data.startsWith("A,", 0) == false) {
    println("bad input: "+data+"\n");
    return;
  }
  
  val = string4uToInt(data.substring(2, data.length()));
  println(val);
  val = val/5;
  
  for(int i=1; i<width; i++) {
    xvals[i-1] = xvals[i];
  }
  
  stroke(255, 0, 0);
  xvals[width-1] = height - val;
  
  background(248, 248, 248);
  for (int i=4, sum = 0, sumLast = 0; i<width; i++) {
    //point(i, xvals[i]); // See processing.org's example "mouse signals"
    //point(i, xvals[i]-1);

    // Calculate Moving average from raw data history
    sum = xvals[i];
    sum += xvals[i-1];
    sum += xvals[i-2];
    sum += xvals[i-3];
    sum += xvals[i-4];
    sum /= 5;
    
    // If a bold connecting line of raw data is desired, include the below.
    //line(i, xvals[i], i, xvals[(i-1)]); 

    point(i, xvals[i]);
    line(i, sum, i, sumLast);
    line(i, sum+1, i, sumLast+1);
    sumLast = sum;
  }
}

// End of source