Arduino to Pure Data through serial: Cannot unpack

61 views Asked by At

I'm using an Arduino to measure voltages through the analog pins and I want to send those values to Pure Data. Before sending them, I need to do some simple statistical analysis (average, median, st deviation, variance), and send them to PD to control other parameters of my patch. We have a code that does the statistics in Arduino and seed them through the serial port:

#include "StatManager.hpp" 
//CONST VALUES 
const int MAX_TENSION = 5; // 5.0 V from Arduino, 
const int RESOLUTION = 1023; //10 bit resolution = 1024 steps 
const int TRANSMISSION_SPEED = 9600; // 9600 bits per second 
const int LOOP_DELAY = 200; //milliseconds 
const int NB_ANALOG_PINS = 3; 
 
const int analogPins[] = {A0, A1, A2}; 
 
StatManager statManager(NB_ANALOG_PINS); 
 
//VARIABLES 
int analogValues[NB_ANALOG_PINS]; 
 
void setup() { 
  // Initialize Serial communication 
  Serial.begin(TRANSMISSION_SPEED); 
} 
 
void loop() { 
  printAnalogValues(); 
 
  delay(LOOP_DELAY); 
} 
 
//FUNCTIONS 
 
/** 
* Convert analogValue to voltage value 
* 
* @param analogValue integer representing the analog value 
* @return float number representing voltage value 
*/ 
float analogToVolt(int analogValue) { 
  return (float) analogValue * MAX_TENSION / RESOLUTION; 
} 
 
/** 
  * Prints analog value in serial monitor 
*/ 
void printAnalogValues() { 
    for (int i = 0; i < NB_ANALOG_PINS; i++) { 
        Serial.print(analogRead(analogPins[i])); 
        Serial.print(" "); 
    } 
 
    Serial.println(); 
 
    int pinValues[NB_ANALOG_PINS]; 
    for (int i = 0; i < NB_ANALOG_PINS; i++) { 
        pinValues[i] = analogRead(analogPins[i]); 
    } 
 
    float* averages = statManager.getAverages(); 
    float* medians = statManager.getMedians();
    float* minimums = statManager.getMinimums(); 
    float* maximums = statManager.getMaximums(); 
    float* deviations = statManager.getStandardDeviations(); 
    float* variances = statManager.getVariances(); 
 
    for (int i = 0; i < NB_ANALOG_PINS; i++) { 
        Serial.print("Stats for Pin A"); 
        Serial.print(i); 
        Serial.print(": Avg="); 
        Serial.print(averages[i]); 
        Serial.print(" Median=");
        Serial.print(medians[i]);
        Serial.print(" Min="); 
        Serial.print(minimums[i]); 
        Serial.print(" Max="); 
        Serial.print(maximums[i]); 
        Serial.print(" StdDev="); 
        Serial.print(deviations[i]); 
        Serial.print(" Var="); 
        Serial.println(variances[i]); 
    } 
 
    delete[] averages; 
    delete[] minimums; 
    delete[] maximums; 
    delete[] deviations; 
    delete[] variances; 
 
    statManager.addValues(pinValues); 
    //============================================= 
 
  
 
  Serial.println(); 
} 

And as you can see we're using a stat manager code in C++

#include "StatManager.hpp"
#include <cmath>

StatManager::StatManager(int numPins) {
  this->numPins = numPins;
  values = new int*[numPins];
  counts = new int[numPins];

  for (int i = 0; i < numPins; ++i) {
    values[i] = nullptr;
    counts[i] = 0;
  }
}

StatManager::~StatManager() {
  for (int i = 0; i < numPins; ++i) {
    delete[] values[i];
  }
  delete[] values;
  delete[] counts;
}

void StatManager::addValues(int* pinValues) {
  for (int i = 0; i < numPins; ++i) {
    int* newValues = new int[counts[i] + 1];

    for (int j = 0; j < counts[i]; ++j) {
      newValues[j] = values[i][j];
    }

    newValues[counts[i]] = pinValues[i];

    delete[] values[i];

    values[i] = newValues;
    counts[i]++;
  }
}

float* StatManager::getAverages() {
  float* averages = new float[numPins];

  for (int i = 0; i < numPins; ++i) {
    float sum = 0;
    for (int j = 0; j < counts[i]; ++j) {
      sum += values[i][j];
    }
    averages[i] = (counts[i] > 0) ? sum / counts[i] : 0;
  }

  return averages;
}

float* getMedians();

float* StatManager::getMedians() {
  float* medians = new float[numPins];

  for (int i = 0; i < numPins; ++i) {
    int* sortedValues = new int[counts[i]];
    for (int j = 0; j < counts[i]; ++j) {
      sortedValues[j] = values[i][j];
    }

    for (int j = 0; j < counts[i] - 1; ++j) {
      for (int k = 0; k < counts[i] - j - 1; ++k) {
        if (sortedValues[k] > sortedValues[k + 1]) {
          int temp = sortedValues[k];
          sortedValues[k] = sortedValues[k + 1];
          sortedValues[k + 1] = temp;
        }
      }
    }

    if (counts[i] % 2 == 0) {
      medians[i] = (sortedValues[counts[i] / 2 - 1] + sortedValues[counts[i] / 2]) / 2.0f;
    } else {
      medians[i] = sortedValues[counts[i] / 2];
    }

    delete[] sortedValues;
  }

  return medians;
}

float* StatManager::getMinimums() {
  float* minimums = new float[numPins];

  for (int i = 0; i < numPins; ++i) {
    int minVal = (counts[i] > 0) ? values[i][0] : 0;
    for (int j = 1; j < counts[i]; ++j) {
      if (values[i][j] < minVal) {
        minVal = values[i][j];
      }
    }
    minimums[i] = minVal;
  }

  return minimums;
}

float* StatManager::getMaximums() {
  float* maximums = new float[numPins];

  for (int i = 0; i < numPins; ++i) {
    int maxVal = (counts[i] > 0) ? values[i][0] : 0;
    for (int j = 1; j < counts[i]; ++j) {
      if (values[i][j] > maxVal) {
        maxVal = values[i][j];
      }
    }
    maximums[i] = maxVal;
  }

  return maximums;
}

float* StatManager::getStandardDeviations() {
  float* deviations = new float[numPins];

  for (int i = 0; i < numPins; ++i) {
    deviations[i] = 0;
    if (counts[i] > 1) {
      float mean = getAverages()[i];
      float squaredDiffSum = 0;
      for (int j = 0; j < counts[i]; ++j) {
        float diff = values[i][j] - mean;
        squaredDiffSum += diff * diff;
      }
      deviations[i] = sqrt(squaredDiffSum / (counts[i] - 1));
    }
  }

  return deviations;
}

float* StatManager::getVariances() {
  float* variances = new float[numPins];

  for (int i = 0; i < numPins; ++i) {
    variances[i] = 0;
    if (counts[i] > 1) {
      float squaredDiffSum = 0;
      for (int j = 0; j < counts[i]; ++j) {
        float diff = values[i][j] - getAverages()[i];
        squaredDiffSum += diff * diff;
      }
      variances[i] = squaredDiffSum / (counts[i] - 1);
    }
  }

  return variances;
}

On Pure Data I have the basic

[comport 9600]
|
[sel 13 10]
|      /
[zl group 100]
|
[iem_alisttosym]
|
[fromsymbol]
|
[unpack f f f ...f]

(Some) data is actually being sent to PD as I can see it getting printed on the terminal but it doesn't come out of the unpack atom. We're not sure if the problem is in the arduino code, statmanager or pure data. Any ideas??

We tried removing the stat analysis and the data still doesn't unpack properly. My guess is it's an issue with how the floats are being sent to PD, because sometimes we get the error: unpack type mismatch, but i have no idea how to solve this.

1

There are 1 answers

0
umläute On

you are basically sending data like this over the serial line:

Stats for Pin A0: Avg=1 Median=1 Min=0 Max=2 StdDev=1 Var=1

Stats for Pin A1: Avg=1 Median=1 Min=0 Max=2 StdDev=1 Var=1

Stats for Pin A2: Avg=1 Median=1 Min=0 Max=2 StdDev=1 Var=1

However, on the Pd side you are expecting a simple list of numbers.

So how do you convert the input strings to the numbers? (there's no apparent code that does this magic for you).

I therefore suggest you use a simple parseable format on the sending side:

for (int i = 0; i < NB_ANALOG_PINS; i++) { 
  Serial.print("A"); 
  Serial.print(i); 
  Serial.print(" "); 
  Serial.print(averages[i]); 
  Serial.print(" ");
  Serial.print(medians[i]);
  Serial.print(" "); 
  Serial.print(minimums[i]); 
  Serial.print(" "); 
  Serial.print(maximums[i]); 
  Serial.print(" "); 
  Serial.print(deviations[i]); 
  Serial.print(" "); 
  Serial.println(variances[i]);
}

which will result in the following data to be transmitted:

A0 1 1 0 2 1 1
A1 1 1 0 2 1 1
A2 1 1 0 2 1 1

you can then parse this data on the Pd side with something like this (actually the bang outlet of [t l b] should go into [t l l], but that's complicated in ASCII-art :-)):

       [comport]
       |
       [select 13 10]
       |            [s ok]
       |
       |            +-+--+
       [list prepend] |  |
       |              |  |
[r ok] [t l l]        |  |
|      |     +--------+  |
[list  ]                 |
|                        |
[t l b]                  |
|     +------------------+
|
[fudiparse]
|
[route A0 A1 A2]
|
[unpack 0 0 0 0 0 0]
| ...

totally untested