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.
you are basically sending data like this over the serial line:
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:
which will result in the following data to be transmitted:
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 :-)):totally untested