by jamie allen, Feb 6, 2007

This is a piece of code to help with the Sensors & Time assignments. It graphs three different pieces of statistical information about one piece of sample data, and could therefore be used as a starting point for an informative information visualization assignment for Sensors & Time.

Also this code is a great way to quickly characterize a sensors in future, as a diagnostic tool. If you're faced with a new sensor or new environment for your existing system, you could use this code to determine if more or less filtering was required (weighted averaging) or if something in the electronics has made your circuits noisy (look at the standard deviation).

This is a set of statistics test for your sensor data 

It's the same as the other data viewers on this end, but it expects three values:
1) raw sensor values
2) an averaged sensor value (weighted, non weighted, etc.)
3) standard deviation

It then graphs these values (with FAKE hysteresis) 

This program takes raw bytes from the serial port that are seperated by headers
"A", "B", etc. at 9600 baud and graphs them. 

Arduino code for sending this data in the first place from the board is 
commented in below the Processing code

based on Tom Igoe's & Melvin Ochsmann's work
done up for sensor workshop, Jamie Allen, 2007


import processing.serial.*;

Serial myPort;  // The serial port
PFont myFont;     // The display font: 

// initial variables:

String buff = "";
int val = 0;
int NEWLINE = 10;
int i = 1;                   // counter
int valueA, valueB, valueC;          // the converted data from serial port

int[] valuesA = new int[10];
int[] valuesB = new int[10];
int[] valuesC = new int[10];

float valNormA, valNormB,  valNormC;     // normalized values of A and B

String bufA="", bufB="", bufC="";     // buffers in which to store ascii data as it comes in
//String inString;
int buf;

int wrote = 0;
int legendary = 0;
int offset = 0;
int offsettext = 25;
int lf = 10;

void setup () {
  size(500, 500);        // window size

  myFont = loadFont("ArialMT-12.vlw"); 
  textFont(myFont, 12); 
  fill(#E9FF5B, 200);

  // set inital background:
  background(#000000, 200);

  // List all the available serial ports
  // I know that the third port in the serial list on my mac
  // is always my  Keyspan adaptor, so I open Serial.list()[2].
  // Open whatever port is the one you're using.
  myPort = new Serial(this, Serial.list()[2], 9600);


void draw()

  rect(0,0, width, height); 

  while (myPort.available() > 0) {

  valNormA = valueA/1023.0;
  valNormB = valueB/1023.0;
  valNormC = valueC/1023.0;

  ellipse(i, height - valNormA*height, 7, 7);

  ellipse(i, height - valNormB*height, 5, 5);

  ellipse(i, height/2 - valNormC*height, 2, 2);  //center around the midpoint of the screen

  // at the edge of the screen, go back to the beginning:
  if (i > width) {
    i = 0;
    rect(0,0, width, height); 

  else {

  //only display the value once every 75 readings
  //just to keep things clean 
  if (wrote > 75)
  text(valNormA, i, (height - valNormA*height)-15);
  text(valNormB, i, (height - valNormB*height)-30);
  text(valNormC, i, (height/2 - valNormC*height)-30);
  wrote = 0;

  if (legendary == 9){legend(); legendary = 0;}

//Serial parsing stuff to get the raw values from 
//the serial event
void serialEvent(int serial){
  if(serial!=10) {      
      if (serial=='A') buf = 1;
      if (serial=='B') buf = 2;
      if (serial=='C') buf = 3;

      if (buf==1) {if (serial!='A') bufA += char(serial);}
      else if (buf==2) { if (serial!='B') bufB+= char(serial);}
      else if (buf==3) { if (serial!='C') bufC+= char(serial);}
   } else {
      if (buf==1)        {valueA = int(bufA); bufA="";} 
      else if (buf==2)   {valueB = int(bufB); bufB="";}
      else if (buf==3)   {valueC = int(bufC); bufC="";}
     // println(valueA);      
     // println(valueB);
     // println(valueC);

void legend()
  ellipse(width/20, height/30, 7, 7);

  ellipse((width/20), (height/30)+20, 5, 5);

  ellipse((width/20), (height/30)+40, 2, 2);

  text("Raw Value:", (width/20)+10, 5+(height/30)); 
  text("Weighted Average Value:", (width/20)+10, 5+(height/30)+20); 
  text("Normalized Standard Deviation", (width/20)+10, 5+(height/30)+40); 


Arduino code to send data to this Processing program:
  Sending three pieces of data - the original data, a weighted average and the standard deviation,
  deliminated by a header "A", "B", "C" at the beginning and a line break at the end

  based on stuff from Melvin Ochsmann and Tom Igoe. 
  reworked a bit for Sensor Workshop class by Jamie Allen, 2007

#include <math.h>  //required for our sqrt function!

long val[6] = { 0, 0, 0, 0, 0, 0 };
long current;
long lastVal1;
long sensorPin[6] = { 0, 1, 2, 3, 4, 5 };
long ledPin = 13;   // select the pin for the LED
long i; 

long stdVal[10];
long accVal;
long sampleDeviation = 0;  //an individual samples' 'deviation'
long sampleDeviation2[5];  //each individual samples' squared (deviation)
long sampleDeviationSum = 0;  //each individual samples' squared (deviation)
long avg;

long standardDeviation = 0; //the standard deviation result

void setup() {
  pinMode(ledPin, OUTPUT);  // declare the ledPin as an OUTPUT
  for (i=0; i<=6; i++)
    pinMode(sensorPin[i], INPUT);  // declare the ledPin as an OUTPUT
  Serial.begin(9600);	// opens serial port, sets data rate to 9600 bps

void loop() {

  digitalWrite(ledPin, HIGH);    // sets the LED on
  val[1] = analogRead(sensorPin[1]);    // read the value from the sensor

  current = (1*val[1] + 9*lastVal1)/10; //almost the same as the above but using integer math  
  lastVal1 = current;

  standardDeviation = standardDeviator(sensorPin[1], 10);  //pin to do the standard deviation on, and number of sample to use to calculate it

  //Out protocol for data transmission is [AXXX BXXX CXXXX AXXXX BXX CXX ...]
  //which we then check for at the processing end

  Serial.print("A");  //header variable, so we know which sensor value is which
  Serial.print(val[1], DEC);  //send as a ascii encoded number - we'll turn it back into a number at the other end
  Serial.print(10, BYTE);  //terminating character

  Serial.print("B");  //header variable, so we know which sensor value is which
  Serial.print(current, DEC);  //send as a ascii encoded number - we'll turn it back into a number at the other end
  Serial.print(10, BYTE);  //terminating character

  Serial.print("C");  //header variable, so we know which sensor value is which
  Serial.print(standardDeviation, DEC);  //send as a ascii encoded number - we'll turn it back into a number at the other end
  Serial.print(10, BYTE);  //terminating character


int standardDeviator(int pin, int howManyToAverage)
   accVal = 0;  // clear the accumulator

  for (i=0; i < howManyToAverage; i++)
    stdVal[i] = analogRead(pin);    // read the value from the sensor

    //Serial.print("Raw Value: ");
    //Serial.println(val, DEC);  // print as an ASCII-encoded decimal

    accVal = accVal + stdVal[i];
    //Serial.print("Accumulator: ");
    //Serial.println(accVal, DEC);  // print as an ASCII-encoded decimal


  avg = accVal/howManyToAverage;
  //Serial.print("Simple Average Value: ");
  //Serial.println(avg, DEC);  // print as an ASCII-encoded decimal

  for (i=0; i<howManyToAverage; i++)
    sampleDeviation = abs(stdVal[i]-avg);
    sampleDeviation2[i] = sampleDeviation*sampleDeviation;        

  sampleDeviationSum = 0;  
  for (i=0; i<howManyToAverage; i++)
    sampleDeviationSum = sampleDeviationSum + sampleDeviation2[i]; 

  return sqrt(sampleDeviationSum/(howManyToAverage-1));

 // Serial.print("Standard Deviation Value: ");
 // Serial.println(standardDeviation, DEC);  // print as an ASCII-encoded decimal