Optimize data transmision over USB Serial

Status
Not open for further replies.

cbarreto61

New member
Hi everyone!

I am using a Teensy 3.2 for controlling the force applied by a Stepper Motor to a clamping device, first I read the clamping force from a Load cell then a PID controller outputs the number of steps the stepper Motor should move. While the teensy is in the PID loop I want to print the input of the controller, the setPoint and the force from other load cell, I want to do this every 10ms (100 Hz). It works "fine", but I noticed a decrease in the performance when I start printing data on the USB Serial Port, I read this data using a Serial Terminal on Windows 7. Could someone tell me how to transmit more efficiently? The function doStraightenTasks() is where I print the data:

Code:
#include "AccelStepper.h"
#include "PID_v1.h"
#include <stdlib.h>
#include <EEPROM.h>


#define DRIVER		1
#define HOMING		0
#define CLOSING		1
#define PULLING		2
#define OPENING		3
#define RETURNING	4
// Setpoint equation constants. y = 21968.23036x + 290.7863525
//linear regression for clamp load cell, calculated with weights from 0.5Kg to 1.5Kg
#define SLOPE		21968.23036f
#define INTERCEPT	290.7863525f
// Setpoint equation constants. y =  2241.65615x + 290.7862525
// linear regression from 4.9N ro 14.7N
#define SLOPE_N		2241.656159f
#define INTERCEPT_N	290.7863525f

//EEPROM addresses
#define DEFAULT_VALUES_FLAG		100
#define GRAMS_MSB				101
#define GRAMS_LSB				102
#define KP						103
#define KI						104
#define KD						105
#define SPEED_MSB				106
#define SPEED_3SB				107
#define SPEED_2SB				108
#define SPEED_LSB				109
#define ACCELERATION_MSB		110
#define ACCELERATION_3SB		111
#define ACCELERATION_2SB		112
#define ACCELERATION_LSB		113

//CONSTANTS
#define KP_DEFAULT				20.0f
#define KI_DEFAULT				12.0f
#define KD_DEFAULT				16.0f
#define MAX_PULL_SPEED			4000
#define MAX_PULL_ACCELERATION	(MAX_PULL_SPEED * 1.2)
#define MAX_CLAMP_ACCELERATION	1000000
#define MAX_CLAMP_SPEED			100000
#define PID_SAMPLE_TIME			20

const uint8_t switchPin = 2;
const uint8_t clampLoadCellPin = A10;
const uint8_t pullLoadCellPin = A11;
const uint8_t pullMotorStepPin = 4;
const uint8_t pullMotorDirectionPin = 5;
const uint8_t clampMotorStepPin = 6;
const uint8_t clampMotorDirectionPin = 7;
const uint8_t yMotorStepPin = 8;
const uint8_t yMotorDirectionPin = 9;
const int enablePin = 20;
const long topPosition = -12500;
const long initialClamp = 100000;		//ADC reading from clamp load cell, approx. 1Kg

//y = 21968.23036x + 290.7863525   linear trendline for clamp load cell, calculated with weights from 0.5Kg to 1.5Kg
//double setPoint = 11275.0;		//setpoint for 500g -> 4.9N -> 0.3883V
//double setPoint = 22259.0;		//setpoint for 1000g -> 9.8N -> 0.7689V
//double setPoint = 33243.0;		//setpoint for 1500g -> 14.7N -> 1.1495V
//double setPoint = 44227.0;	//setpoint for 2000g -> 19.6N -> 1.5301V
//double setPoint = 55211.0;	//setpoint for 2500g -> 24.5N -> 1.9107V
//double setPoint = 59605.0;	//setpoint for 2700g -> 26.46N -> 2.0629V
double setPoint = 22259.0;  //default value for 1000g, 9.8N

char inputString[32];
bool startCommand;
boolean stringComplete = false,
startSwitch = false;
uint8_t pullingState = 0,
status = 0,
i = 0;
uint16_t grams;
uint32_t  previous,
pullSpeed,
pullAcceleration;			// could consider using unions for saving eeprom data in byte format 
double input,
output,
error,
previousError,
pullLoad,
kp = KP_DEFAULT,
ki = KI_DEFAULT,
kd = KD_DEFAULT;

AccelStepper pullMotor(DRIVER, pullMotorStepPin, pullMotorDirectionPin);
AccelStepper clampMotor(DRIVER, clampMotorStepPin, clampMotorDirectionPin);
//AccelStepper yMotor(DRIVER, yMotorStepPin, yMotorDirectionPin);
PID clampPID(&input, &output, &setPoint, kp, ki, kd, DIRECT);

void setup() {
	pinMode(13, OUTPUT);
	pinMode(switchPin, INPUT_PULLUP);
	pinMode(enablePin, OUTPUT);
	Serial.begin(0);
	if (EEPROM.read(DEFAULT_VALUES_FLAG) == 0) {
		grams = EEPROM.read(GRAMS_MSB) << 8;
		grams += EEPROM.read(GRAMS_LSB);
		setPoint = calculateSetPoint(grams);
		kp = EEPROM.read(KP);
		ki = EEPROM.read(KI);
		kd = EEPROM.read(KD);
		pullSpeed = EEPROM.read(SPEED_MSB) << 24;
		pullSpeed += EEPROM.read(SPEED_3SB) << 16;
		pullSpeed += EEPROM.read(SPEED_2SB) << 8;
		pullSpeed += EEPROM.read(SPEED_LSB);
		pullAcceleration = EEPROM.read(ACCELERATION_MSB) << 24;
		pullAcceleration += EEPROM.read(ACCELERATION_3SB) << 16;
		pullAcceleration += EEPROM.read(ACCELERATION_2SB) << 8;
		pullAcceleration += EEPROM.read(ACCELERATION_LSB);
		clampPID.SetTunings(kp, ki, kd);
	} else
	{
		pullSpeed = MAX_PULL_SPEED;
		pullAcceleration = MAX_PULL_ACCELERATION;
	}
	pullMotor.setMaxSpeed(pullSpeed);
	pullMotor.setAcceleration(pullAcceleration);
	clampMotor.setMaxSpeed(MAX_CLAMP_SPEED);
	clampMotor.setAcceleration(MAX_CLAMP_ACCELERATION);
	//yMotor().setMaxSpeed(4000);
	//yMotor().setAcceleration(4800);
	clampPID.SetSampleTime(PID_SAMPLE_TIME);			//Library is modified for working with microseconds
	clampPID.SetOutputLimits(0.0, 500.0);
	clampPID.SetMode(AUTOMATIC);
	analogReference(EXTERNAL);				// Configure Teensy to read value using 2.5v reference from amplifier
	analogReadRes(16);						// Set analog resolution to 16-Bits
	analogReadAveraging(0);				// Set 16 averaging values
	delay(100);								// wait until amplifier starts
	digitalWrite(enablePin, HIGH);			// enable
	status = HOMING;
	digitalWrite(13, LOW);
}

void loop()
{
	doCommandTasks();
	doStraightenTasks();
}

void doStraightenTasks () {
	startSwitch = digitalRead(switchPin);
	if (!startSwitch || startCommand) {
		startCommand = false;
		status = CLOSING;
		closeClamp();
		status = PULLING;
		pullMotor.moveTo(topPosition);
		while (pullMotor.isRunning()) {
			pullLoad = analogRead(pullLoadCellPin);
			input = analogRead(clampLoadCellPin);
			clampMotor.run();
			pullMotor.run();
			int8_t direction = 1;
			error = setPoint - input;
			if (error > 0) direction = 1;
			else direction = -1;
			clampMotor.run();
			pullMotor.run();
			bool newOutput = clampPID.Compute();
			clampMotor.run();
			pullMotor.run();
			if (newOutput) clampMotor.move((long) (output * direction));
			clampMotor.run();
			#if 1
			if(millis() - previous > 10) {
				char msg[20];
				//sprintf(msg,"%u,%u", (uint16_t)input, (uint16_t)setPoint);
				//sprintf(msg,"%u,%u,%i", (uint16_t)input, (uint16_t)setPoint, (int16_t)(output*direction));
				sprintf(msg,"%u,%u,%u", (uint16_t)input, (uint16_t)setPoint, (uint16_t)pullLoad);
				Serial.println(msg);
				previous = millis();
			}
			#endif
			clampMotor.run();
			pullMotor.run();
		}
		status = OPENING;
		openClamp();
		status = RETURNING;
		pullMotor.moveTo(0);
		pullMotor.setSpeed(MAX_PULL_SPEED);
		while (pullMotor.isRunning()) pullMotor.run();
		startSwitch = false;
	}
}

Thanks in advance!!
 
Printing to the USB serial output should take much less CPU time than some of the other stuff, like those analogRead() calls.

You could try using Serial.print(input) instead of using sprintf(). Not sure if that'll make much difference, but it might help a little since it avoids the complicated vaarg stuff and lots of overhead in printf.
 
Printing to the USB serial output should take much less CPU time than some of the other stuff, like those analogRead() calls.

You could try using Serial.print(input) instead of using sprintf(). Not sure if that'll make much difference, but it might help a little since it avoids the complicated vaarg stuff and lots of overhead in printf.

Cool, thanks for the advice, I will try it later today.

Would it benefit to use serial.write() instead of print()? would I need to decode in the PC?
 
So I realize this is a old but I was curious and did some testing of my own that I thought I would share...

I am using Arduino 1.6.11 and Teensyduino 1.30 on Windows 7.

Here are the results for a Teensy 3.1 (same as 3.2 basically):
Code:
println() 203099 us
sprintf() 649877 us
write() 195594 us
print_array() 204301 us

Since I now have a Teensy 3.6 here are results for that too:
Code:
println() 204576 us
sprintf() 250628 us
write() 194142 us
print_array() 203583 us

Thus I conclude sprintf is slow on a Teensy 3.1, but the faster Teensy 3.6 handles it much better. Using a Serial.write() is the fastest, while Serial.print() is ever so slightly slower.

So avoid sprintf (or get a Teensy 3.6 :p) would be my 2c...

Here is the simple code I used to test this:
Code:
const int LED=13;
const int loops=10000;

void setup() {
  pinMode(LED,OUTPUT);
  digitalWrite(LED,HIGH);
  Serial.begin(115200);
  while(!Serial);  // Wait for USB Serial to connect
  digitalWrite(LED,LOW);
  Serial.println("Test will begin shortly...");
  delay(500);
}

void loop() {
  Serial.println("println()");
  unsigned long normal_s=micros();
  for(int i=0; i<loops; i++){
    Serial.print("ABCDE");
    Serial.print(12345);
    Serial.print(703710,HEX);
    Serial.println(56789);
  }
  unsigned long normal_e=micros();
  Serial.send_now();
  delay(50);
  char msg[25];
  Serial.println("sprintf()");
  unsigned long sprintf_s=micros();
  for(int i=0; i<loops; i++){
    sprintf(msg,"ABCDE%u%X%u\n\r", 12345, 703710, 56789);
    Serial.write(msg,21);
  }
  unsigned long sprintf_e=micros();
  Serial.send_now();
  delay(50);
  Serial.println("write()");
  unsigned long write_s=micros();
  for(int i=0; i<loops; i++){
    Serial.write(msg,21);
  }
  unsigned long write_e=micros();
  Serial.send_now();
  delay(50);
  msg[22]=0;
  Serial.println("print_array()");
  unsigned long array_s=micros();
  for(int i=0; i<loops; i++){
    Serial.print(msg);
  }
  unsigned long array_e=micros();
  Serial.send_now();
  delay(50);
  Serial.println("println()");
  Serial.println(normal_e-normal_s);
  Serial.println("sprintf()");
  Serial.println(sprintf_e-sprintf_s);
  Serial.println("write()");
  Serial.println(write_e-write_s);
  Serial.println("print_array()");
  Serial.println(array_e-array_s);
  delay(10000);
}
 
Last edited:
Grat thanks for this,
right now I am doing another project with teensy and this is good to know,, will avoid the use ofsprintf(). Also I received my teensy 3.6 and 3.5 today, so would be great to do a comparison by my own.

Thanks!

Carlos.

So I realize this is a old but I was curious and did some testing of my own that I thought I would share...

I am using Arduino 1.6.11 and Teensyduino 1.30 on Windows 7.

Here are the results for a Teensy 3.1 (same as 3.2 basically):
Code:
println() 203099 us
sprintf() 649877 us
write() 195594 us
print_array() 204301 us

Since I now have a Teensy 3.6 here are results for that too:
Code:
println() 204576 us
sprintf() 250628 us
write() 194142 us
print_array() 203583 us

Thus I conclude sprintf is slow on a Teensy 3.1, but the faster Teensy 3.6 handles it much better. Using a Serial.write() is the fastest, while Serial.print() is ever so slightly slower.

So avoid sprintf (or get a Teensy 3.6 :p) would be my 2c...

Here is the simple code I used to test this:
Code:
const int LED=13;
const int loops=10000;

void setup() {
  pinMode(LED,OUTPUT);
  digitalWrite(LED,HIGH);
  Serial.begin(115200);
  while(!Serial);  // Wait for USB Serial to connect
  digitalWrite(LED,LOW);
  Serial.println("Test will begin shortly...");
  delay(500);
}

void loop() {
  Serial.println("println()");
  unsigned long normal_s=micros();
  for(int i=0; i<loops; i++){
    Serial.print("ABCDE");
    Serial.print(12345);
    Serial.print(703710,HEX);
    Serial.println(56789);
  }
  unsigned long normal_e=micros();
  Serial.send_now();
  delay(50);
  char msg[25];
  Serial.println("sprintf()");
  unsigned long sprintf_s=micros();
  for(int i=0; i<loops; i++){
    sprintf(msg,"ABCDE%u%X%u\n\r", 12345, 703710, 56789);
    Serial.write(msg,21);
  }
  unsigned long sprintf_e=micros();
  Serial.send_now();
  delay(50);
  Serial.println("write()");
  unsigned long write_s=micros();
  for(int i=0; i<loops; i++){
    Serial.write(msg,21);
  }
  unsigned long write_e=micros();
  Serial.send_now();
  delay(50);
  msg[22]=0;
  Serial.println("print_array()");
  unsigned long array_s=micros();
  for(int i=0; i<loops; i++){
    Serial.print(msg);
  }
  unsigned long array_e=micros();
  Serial.send_now();
  delay(50);
  Serial.println("println()");
  Serial.println(normal_e-normal_s);
  Serial.println("sprintf()");
  Serial.println(sprintf_e-sprintf_s);
  Serial.println("write()");
  Serial.println(write_e-write_s);
  Serial.println("print_array()");
  Serial.println(array_e-array_s);
  delay(10000);
}
 
Status
Not open for further replies.
Back
Top