Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 6 of 6

Thread: Optimize data transmision over USB Serial

  1. #1
    Junior Member
    Join Date
    Jul 2016
    Posts
    3

    Optimize data transmision over USB Serial

    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!!

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,987
    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.

  3. #3
    Junior Member
    Join Date
    Jul 2016
    Posts
    3
    Quote Originally Posted by PaulStoffregen View Post
    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?

  4. #4
    Junior Member
    Join Date
    Oct 2016
    Location
    Longview, WA
    Posts
    4
    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 ) 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 by jontscott; 10-08-2016 at 10:16 PM. Reason: Corrected write_array to print_array...

  5. #5
    Junior Member
    Join Date
    Oct 2016
    Location
    Longview, WA
    Posts
    4
    Oh and just in case it is not clear the limiting factor is not the time it takes to do the write or print... 12mbit/sec USB is the limit... So if your code is not sending data at close to the limits then the Teensy will buffer the write or print and return instantly (assuming the host computer is polling fast enough.) Here are some great pages Paul has about this:

    https://www.pjrc.com/teensy/td_serial.html#txbuffer

    https://www.pjrc.com/teensy/usb_serial.html

  6. #6
    Junior Member
    Join Date
    Jul 2016
    Posts
    3
    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.

    Quote Originally Posted by jontscott View Post
    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 ) 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);
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •