haha, Mr. PaulS, you took my question very literally. Also, I'm from Seattle, which makes this a real community affair. First: Processing 1.5.1. Arduino 1.0.1.
Serial parts of Processing code:
Serial arduino;
int updateInterval = 5000;
void setup(){
//Serial setups *******************************************************
println(Serial.list());
arduino = new Serial(this, Serial.list()[4], 9600);
arduino.bufferUntil('\n');
}
void draw(){
if (millis() - timeLastSendCoordinates > updateInterval) {
logData("Sending off for coordinates and sending to Arduino", true);
sendCoordinates();
}
}
void sendCoordinates() {
timeLastSendCoordinates = millis();
arduinoData.update(heartbeat.xNext, heartbeat.yNext);
arduino.write("#" + arduinoData.xDir + "," + arduinoData.xSpeed + "," + arduinoData.yDir + "," + arduinoData.ySpeed + "\n");
logData("Sending Arduino more coordinates. Moving to: " + arduinoData.x + ", " + arduinoData.y + " From: " + arduinoData.xPrev + ", " + arduinoData.yPrev, true);
}
And the Arduino code:
#include <AccelStepper.h>
// Define a stepper and the pins it will use
AccelStepper xStepper(1, 2, 22);
AccelStepper yStepper(1, 3, 23);
unsigned long time;
unsigned long lastUpdateTime;
float incompleteStepsRatio;
const int updateInterval = 5000; //interval in milliseconds to ask for new coordinates. MUST BE SAME IN PROCESSING.
int xBoundaryData = 100;
int yBoundaryData = 100;
int xDir = 1;
int yDir = 1;
int xSpeed = 200;
int ySpeed = 200;
boolean xHit = false;
boolean yHit = false;
const int xStepsNeededAfterHit = 150;
const int yStepsNeededAfterHit = 350;
int xStepsAfterHit = 0;
int yStepsAfterHit = 0;
unsigned long xHitTimeCounter = 0;
unsigned long yHitTimeCounter = 0;
boolean stringComplete = false;
int inputStringIndex = 0;
char inputString[25]; //allows for a serial input of 25 chars
int dataStringIndex = 0;
char dataString[8]; //this gives us a buffer of 5 numbers
int dataIndex = 0;
int data[4]; //xDir, xSpeed, yDir, ySpeed
void setup() {
Serial.begin(9600);
memset(inputString, 0, sizeof(inputString));
memset(dataString, 0, sizeof(dataString));
time = 0;
lastUpdateTime = 0;
xStepper.setSpeed(xDir * xSpeed);
yStepper.setSpeed(yDir * ySpeed);
}
void loop() {
checkBoundaries();
processData();
updateSteppers();
}
/******
Receive serial data from processing forms:
#xDir,xSteps,yDir,ySteps\n
******/
void serialEvent() {
while (Serial.available()) {
checkBoundaries();
char inChar = (char)Serial.read();
//check that we have startred reading a string and it started at the begining
if (inputString[0] != 0 || inChar == '#') {
inputString[inputStringIndex] = inChar;
inputStringIndex++;
if (inChar != '#' && inChar != ',' && inChar != '\n') {
dataString[dataStringIndex] = inChar;
dataStringIndex++;
}
if (inChar == '#') {
memset(dataString, 0, sizeof(dataString));
dataStringIndex = 0;
dataIndex = 0;
}
if (inChar == ',' || inChar == '\n') {
dataString[dataStringIndex] = '\0';
data[dataIndex] = atoi(dataString);
memset(dataString, 0, sizeof(dataString)); //clear out the dataString
dataStringIndex = 0;
dataIndex++;
if (dataIndex > 3) {
//this is just to make sure we don't overflow the index
dataIndex = 0;
}
}
if (inChar == '\n' ) {
inputString[inputStringIndex] = '\0';
stringComplete = true;
} //if newline
} //if # or such
} //while loop
}
/******
Checks boundary data streams for a hit
******/
void checkBoundaries() {
xBoundaryData = analogRead(A0);
yBoundaryData = analogRead(A1);
time = millis();
if (xHit == true && xBoundaryData > 10) {
if (xStepsAfterHit >= xStepsNeededAfterHit) {
xHit = false;
}
xStepsAfterHit = round((float)((time - xHitTimeCounter) / 1000.0) * (float)xSpeed);
}
if (yHit == true && yBoundaryData > 10) {
if (yStepsAfterHit >= yStepsNeededAfterHit) {
yHit = false;
}
yStepsAfterHit = round((float)((time - yHitTimeCounter) / 1000.0) * (float)ySpeed);
}
if (xHit == false && xBoundaryData < 10) {
xStepsAfterHit = 0;
xHitTimeCounter = time;
xHit = true;
xDir = -xDir;
xStepper.setSpeed(xSpeed * xDir);
sendSerialHitData(0);
}
if (yHit == false && yBoundaryData < 10) {
yStepsAfterHit = 0;
yHitTimeCounter = time;
yHit = true;
yDir = -yDir;
yStepper.setSpeed(ySpeed * yDir);
sendSerialHitData(1);
}
}
/******
Process the serial data if available.
******/
void processData() {
if (stringComplete == true) {
lastUpdateTime = millis();
Serial.print("Received:");
Serial.println(inputString);
if (xHit == false) {
xDir = (abs(data[0]) == 1 ? data[0] : 1);
}
if (yHit == false) {
yDir = (abs(data[2]) == 1 ? data[2] : 1);
}
xSpeed = data[1];
ySpeed = data[3];
xStepper.setSpeed(xDir * xSpeed);
yStepper.setSpeed(yDir * ySpeed);
Serial.print("moving:");
Serial.print(xDir);
Serial.print(",");
Serial.print(xSpeed);
Serial.print(",");
Serial.print(yDir);
Serial.print(",");
Serial.println(ySpeed);
memset(inputString, 0, sizeof(inputString));
inputStringIndex = 0;
stringComplete = false;
}
}
/******
runs steppers. Checks for hits to reset
******/
void updateSteppers() {
xStepper.runSpeed();
yStepper.runSpeed();
}
/******
Sends debug info about boundary conditions
dir -> x=0, y=1 this is just so when it gets passed to processing we can create an integer array.
******/
void sendSerialHitData(int dir) {
//the reason dir is an int, is so that when Processing receives it we don't have to worry about its type being different than the other variables.
incompleteStepsRatio = (float)((lastUpdateTime + updateInterval) - millis()) / (float)updateInterval;
incompleteStepsRatio = ((int)(incompleteStepsRatio * 100)) / 100.0;
Serial.print("hit:");
Serial.print(dir);
Serial.print(",");
Serial.print(xDir);
Serial.print(",");
Serial.print(xSpeed);
Serial.print(",");
Serial.print(yDir);
Serial.print(",");
Serial.print(ySpeed);
Serial.print(",");
Serial.println(incompleteStepsRatio);
}