Hi @josephh, @glv, @PhilHaw, @TfGuy44,
I’ve included you specifically because I’ve seen you reply to Proceesing+Arduino Serial questions, but anyone else is welcome to join in.
I’m sure you’ve noticed that the same problems come up again and again. There must be about twenty common ways of getting the comms not to work. Little chances of anyone unfamiliar getting it to work quickly. The sketches in Examples are not bi-directional and not easily extensible with real values.
I’m wondering if we could make an example that would always work first time. Maybe pinned at the top of the section. As I see it the spec. is:
- Values passed Processing to Arduino and Arduino to Processing.
- Integers and floats.
- Code as simple as possible.
- Simple instructions.
- Minimum user edits to make it work (COM port name in Processing).
- Clear error message if COM port name is wrong.
- Messages are text not binary, for easy printing.
- Works first time, does not lock up, overrun, etc.
- Some checking of received string (but not checksum/acknowledge/repeat).
- No libraries required.
- No need for extra hardware connected to Ardino.
- Easy to add more values.
Instructions (not done yet)
- how to run it the first time
- how to add a value Processing-to-Ard
- how to add a value Ard-to-Processing.
- comment about merging it into your application.
- comment about performance and speeding it up.
Please look through the code, maybe try it, suggest improvements. The Arduino code is more clunky that I wanted in places. I usually use sprintf and sscanf but on this occasion sscanf would not compile. A little Googling says it’s unreliable and makes the sketch significantly bigger.
Richard.
Processing:
// Serial Demo
import processing.serial.*;
// Change this com port name to the Arduino port name
// The same name as the port set in the Arduino IDE.
final String portName = "COM4";
/*
Do not have this sketch running while the Arduino IDE is loading
the sketch into the Arduino, or the Serial Monitor is open.
*/
final boolean T = true;
final boolean F = false;
color colrText = color(225, 205, 0);
color colrBgnd = color(20, 60, 80);
boolean ArdSent = F; // true after something received from Arduino
Serial port;
void setup()
{
frameRate(20);
size(330, 240);
port = new Serial(this, portName, 9600);
}
void draw()
{
String exArduino = ""; // Received from Arduino
String sAry[]; // Received mess split into strings.
// Values received from Arduino...
long ArdMillis; // Millis
long ArdLoopCount; // LoopCount
int ArdTemp; // Temperature
// Values from Processing returned from Arduino...
int ArdFrameCount; // frameCount from Processing
int ArdMouseX; // mouseX
int ArdMouseY; // mouseY
float ArdSine; // Sine
String sfC; // Temporary string frameCount
int mX, mY; // Temporary copies of mouseX,Y
float fSine; // Sine wave
String sSine; // Sine value for display.
String msg = ""; // Message to Arduino.
int iGX, iGY; // coordinates for value display
while (port.available() > 0)
{
// Get message from Arduino
exArduino = port.readStringUntil('Z');
if (exArduino == null) break;
ArdSent = T;
exArduino = trim(exArduino); // remove the CR.
// Print what we recived.
println(String.format("%8d From Arduino: %s", frameCount, exArduino));
// Process the message from Arduino
sAry = split(exArduino, ",");
if (sAry.length == 9 && sAry[0].equals("A") && sAry[8].equals("Z"))
{
ArdMillis = parseInt(sAry[1]);
ArdLoopCount = parseInt(sAry[2]);
ArdTemp = parseInt(sAry[3]);
ArdFrameCount = parseInt(sAry[4]);
ArdMouseX = parseInt(sAry[5]);
ArdMouseY = parseInt(sAry[6]);
ArdSine = parseFloat(sAry[7]);
// Display the Arduino Values
iGX = 310;
iGY = 40;
fill(colrBgnd);
rect(220, iGY - 25, 100, 200);
fill(colrText);
text("Arduino" , iGX, iGY); iGY += 30;
text(String.format("%d", ArdMillis) , iGX, iGY); iGY += 20;
text(String.format("%d", ArdLoopCount) , iGX, iGY); iGY += 20;
text(ArdTemp , iGX, iGY); iGY += 30;
text(ArdFrameCount , iGX, iGY); iGY += 20;
text(ArdMouseX , iGX, iGY); iGY += 20;
text(ArdMouseY , iGX, iGY); iGY += 20;
text(String.format("%.1f", ArdSine) , iGX, iGY); iGY += 20;
}
}
// Display the data labels...
iGY = 40;
fill(colrBgnd);
rect(5, iGY - 25, 210, 200);
fill(colrText);
textAlign(LEFT);
textSize(16);
text("Processing" , 120, iGY); iGY += 30;
text("Millis()" , 10, iGY); iGY += 20;
text("Loop Count" , 10, iGY); iGY += 20;
text("Temperature", 10, iGY); iGY += 30;
text("FrameCount" , 10, iGY); iGY += 20;
text("Mouse X" , 10, iGY); iGY += 20;
text("Mouse Y" , 10, iGY); iGY += 20;
text("Sine" , 10, iGY); iGY += 20;
textAlign(RIGHT);
sfC = String.format("%d", frameCount);
mX = mouseX;
mY = mouseY;
fSine = 50 * sin(frameCount * PI/3600);
// (sine wave deliberately slow to see the decimals separately)
sSine = String.format("%6.1f", fSine);
// Display the Processing values...
iGX = 200;
iGY = 140;
text(sfC , iGX, iGY); iGY += 20;
text(mX , iGX, iGY); iGY += 20;
text(mY , iGX, iGY); iGY += 20;
text(sSine, iGX, iGY); iGY += 20;
// Send to the Arduino.
// Send half as often as checking for receive, so there is no overrun.
if (ArdSent && frameCount % 2 == 0)
{
msg = String.format("A,%d,%d,%d,%.1f,Z", frameCount, mX, mY, fSine);
print(String.format("%8d", frameCount)); print(" To Arduino: "); print(msg); print(" "); println();
port.write(msg);
}
}
Arduino:
#define nProcessingVals 4 // The number of values we're expecting from Processing
#define pM pinMode
#define dW digitalWrite
#define buffsize 50
char buffer[buffsize];
int led1 = 13; // Led shows recived message accepted.
long frameCount; // Values from Processing...
int mouseX;
int mouseY;
float sine;
long loopCount; // Ardino loopCount.
// https://theorycircuit.com/arduino-internal-temperature-sensor/
// (copied from there and split to two functions)
double GetTemp0(void)
{
// The internal temperature has to be used
// with the internal reference of 1.1V.
// Channel 8 can not be selected with
// the analogRead function yet.
// Set the internal reference and mux.
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
ADCSRA |= _BV(ADEN); // enable the ADC
//delay(20); // wait for voltages to become stable.
}
double GetTemp1(void)
{
unsigned int wADC;
double t;
ADCSRA |= _BV(ADSC); // Start the ADC
// Detect end-of-conversion
while (bit_is_set(ADCSRA,ADSC));
// Reading register "ADCW" takes care of how to read ADCL and ADCH.
wADC = ADCW;
// The offset of 324.31 could be wrong. It is just an indication.
t = (wADC - 324.31 ) / 1.22;
// The returned temperature is in degrees Celcius.
return (t);
}
void setup()
{
pM(led1, OUTPUT);
loopCount = 0;
Serial.begin(9600);
Serial.setTimeout(100);
delay(20);
Serial.println(__FILE__);
Serial.println(__DATE__);
Serial.println(__TIME__);
GetTemp0();
}
void loop()
{
int nRcvd;
int index;
int temp;
boolean startA;
int commaCount = 0;
if (Serial.available() > 0)
{
nRcvd = Serial.readBytesUntil('Z', buffer, buffsize);
if (nRcvd > 0)
{
// Check the message begins with A, has expected commas
// (we know it ends in Z as we read until Z.
index = 0;
startA = buffer[index] == 'A';
while ((commaCount < (nProcessingVals + 1)) && (index < nRcvd))
{
if (buffer[index++] == ',') {commaCount++;}
}
if (startA && commaCount == (nProcessingVals + 1))
{
index = 2; // index of start of first value
frameCount = atol(&buffer[index]); while (buffer[index++] != ',');
mouseX = atoi(&buffer[index]); while (buffer[index++] != ',');
mouseY = atoi(&buffer[index]); while (buffer[index++] != ',');
sine = atof(&buffer[index]); while (buffer[index++] != ',');
dW(led1, 1);
}
else {dW(led1, 0);}
}
else {dW(led1, 0);}
}
else {dW(led1, 0);}
if (loopCount % 2 == 0)
{
temp = GetTemp1();
Serial.print("A") ; Serial.print(",");
Serial.print(millis()) ; Serial.print(",");
Serial.print(loopCount) ; Serial.print(",");
Serial.print(temp) ; Serial.print(",");
Serial.print(frameCount); Serial.print(",");
Serial.print(mouseX) ; Serial.print(",");
Serial.print(mouseY) ; Serial.print(",");
Serial.print(sine) ; Serial.print(",");
Serial.print("Z");
Serial.println(); // only for better format on serial monitor
}
delay(50);
loopCount++;
}