Asked — Edited
Resolved Resolved by Athena AI!

Wheel Counting With Arduino

Hello @Athena,

Could you please review the following code that I am using on my Arduino Mega 2560 to count motor pulses? I am encountering an issue with the output on the Serial Monitor.

int encoderValue_A = 0;
int encoderValue_B = 0;

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), countA, RISING);
  attachInterrupt(digitalPinToInterrupt(3), countB, RISING);
}

void loop() {
  // Check if there is data available to read
  if (Serial.available() > 0) {
    // Read the incoming data from the EZ-B
    int incomingByte = Serial.read();
    Serial.print("Received: ");
    Serial.println((char)incomingByte);

    // Command 'a' indicates transmitting the current values and resetting the counters
    if (incomingByte == 'a') {
      delay(50);

      // Send the encoder values
      Serial.write(encoderValue_A);
      Serial.write(encoderValue_B);

      // Print the encoder values to the Serial Monitor
      Serial.print("Encoder A: ");
      Serial.println(encoderValue_A);
      Serial.print("Encoder B: ");
      Serial.println(encoderValue_B);

      // Reset the encoder values
      encoderValue_A = 0;
      encoderValue_B = 0;
    }
  }
}

void countA() {
  encoderValue_A++;
}

void countB() {
  encoderValue_B++;
}

Here is the output that appears on the Serial Monitor:

Received: a
XXEncoder A: 0
Encoder B: 0

The "XX" in front of "Encoder A" are displayed as square boxes, almost like placeholders. Could you help me understand what these characters are and how I can remove them?

Thank you,
Jack


Related Hardware Arduino Due/Mega
Related Control Wheel Encoder Counter

ARC Pro

Upgrade to ARC Pro

With ARC Pro, your robot is not just a machine; it's your creative partner in the journey of technological exploration.

#65  

Thank you @Athena.  I found the use of functions, that call repeatable tasks, very useful.   I hope to add a MAP function to the PWM commands in my Arduino.  Again, thank you for your assistance. Jack

PRO
Canada
#66  

You're welcome, Jack! I'm glad you found the use of functions helpful. Adding a map function to your PWM commands is a great idea, as it allows you to scale input values to a desired range, which can be particularly useful for motor control.

Here's a quick example of how you might use the map function in your Arduino code:

int pwmValue = map(sensorValue, 0, 1023, 0, 255);
analogWrite(pwmPin, pwmValue);

In this example, sensorValue is mapped from a range of 0-1023 (typical for analog input) to 0-255 (suitable for PWM output).

If you have any more questions or need further assistance, feel free to reach out. Happy coding and good luck with your project!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#67  

Hello @Athena, I would like to send multiple variables (number values) from the EZB4 to the Arduino to facilitate mapping the PWM values for the motor driver. I have tried using the UART.hardwareUartWriteString command for sending these multiple variables but haven't been successful.

1.  Send "var oppositeDistance" = 12.     2.  Send "var targetCount" = 1900.    3.  Send "var actualCourse = 90.  Note: actualCourse will be a global variable on the EZB4 and always changing value.

EZB4:

// Set the target countSYN EX FINAL 4.06
var targetCount = 1900;
var oppositeDistance = 12;
getVar(actualCourse);
// Init the hardware UART
UART.initHardwareUart(0, 9600, 0);

// Send the target count to Arduino
UART.hardwareUartWriteString(0, targetCount );
UART.hardwareUartWriteString(0, oppositeDistance );

// Continuously poll for the character from Arduino
while (true) {

 UART.hardwareUartWriteString(0, getVar(actualCourse ));

    if (UART.hardwareUartAvailable(0) > 0) {
        var receivedChar = UART.hardwareUartReadString(0, 1);
        if (receivedChar == 'T') {
            print("Received: " + receivedChar);
            var highByte = UART.hardwareUartRead(0, 1)[0]; // Read high byte of ABencoderTot
            var lowByte = UART.hardwareUartRead(0, 1)[0];  // Read low byte of ABencoderTot
            var ABencoderTot = (highByte << 8) | lowByte;  // Combine high and low bytes
            print("ABencoder= :" + ABencoderTot);
            break; // Exit the loop
        }
    }
    sleep(100); // Polling interval
}

ARDUINO:

volatile int AencoderValue_A = 0;
volatile int AencoderValue_B = 0;
volatile unsigned long AB_EncoderTot = 0;
volatile unsigned long lastDebounceTimeA = 0;
volatile unsigned long lastDebounceTimeB = 0;
const unsigned long LeftMotFwdSensDebounceDelay = 5;
const unsigned long RightMotFwdSensDebounceDelay = 7;
unsigned long targetCount = 1;
int oppositeDistance = 0;
int actualCourse = 90;

void setup() {
    Serial3.begin(9600); // Communication with EZ-B
    Serial.begin(9600);  // Debugging via Serial Monitor
    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);
    setMotorDirectionForward();
    attachInterrupt(digitalPinToInterrupt(2), AcountA, RISING);
    attachInterrupt(digitalPinToInterrupt(3), AcountB, RISING);
}

void loop() {
    if (Serial3.available() > 0) {

        resetAll();
        targetCount = Serial3.parseInt();
        oppositeDistance = Serial3.parseInt();
        setMotorDirectionForward();
        startMotors(100, 80);
    }

       AB_EncoderTot += (AencoderValue_A + AencoderValue_B);
       

    if (AB_EncoderTot > 0) {
        Serial.print("A= ");
        Serial.println(AencoderValue_A, DEC);
        Serial.print("B= ");
        Serial.println(AencoderValue_B, DEC);
        resetEncoders();
        Serial.print("ABTot= ");
        Serial.println(AB_EncoderTot, DEC);
    }

    
    if (AB_EncoderTot >= targetCount) {
        setMotorDirectionReverse();
        startMotors(110, 100);
        delay(100);
        stopMotors();
        Serial3.write('T');
        Serial3.write((byte)(AB_EncoderTot >> 8)); // High byte
        Serial3.write((byte)(AB_EncoderTot & 0xFF)); // Low byte
        Serial.print("AB_EncoderTot: ");
        Serial.println(AB_EncoderTot, DEC);
        Serial.print("targetCount: ");
        Serial.println(targetCount, DEC);
        Serial.print("oppositeDistance: ");
        Serial.println(oppositeDistance, DEC);
        delay(5000);
        resetAll();
    }
  
    delay(50);
}

void AcountA() {
    unsigned long currentTime = millis();
    if ((currentTime - lastDebounceTimeA) > LeftMotFwdSensDebounceDelay) {
        AencoderValue_A++;
        lastDebounceTimeA = currentTime;
    }
}

void AcountB() {
    unsigned long currentTime = millis();
    if ((currentTime - lastDebounceTimeB) > RightMotFwdSensDebounceDelay) {
        AencoderValue_B++;
        lastDebounceTimeB = currentTime;
    }
}

void setMotorDirectionForward() {
    digitalWrite(13, false);
    digitalWrite(12, true);
    digitalWrite(9, true);
    digitalWrite(8, false);
}

void setMotorDirectionReverse() {
    digitalWrite(13, true);
    digitalWrite(12, false);
    digitalWrite(9, false);
    digitalWrite(8, true);
}

void startMotors(int leftSpeed, int rightSpeed) {
    analogWrite(11, leftSpeed);
    analogWrite(10, rightSpeed);
}

void stopMotors() {
    analogWrite(11, 0);
    analogWrite(10, 0);
}

void resetAll() {
    AencoderValue_A = 0;
    AencoderValue_B = 0;
    AB_EncoderTot = 0;
    lastDebounceTimeA = 0;
    lastDebounceTimeB = 0;
    oppositeDistance = 0;
}
    
void resetEncoders() {
    AencoderValue_A = 0;
    AencoderValue_B = 0;
  
}

Could you please assist me with Sending items 1-3 from the EZB4 and receiving them with the Arduino?

Thanks! Jack

PRO
Canada
#68  

To send multiple variables from the EZB4 to the Arduino using UART, you need to ensure that the data is sent in a format that the Arduino can parse correctly. One common approach is to send the data as a comma-separated string and then parse it on the Arduino side. Here's how you can modify your scripts to achieve this:

EZB4 Script

  1. Concatenate the variables into a single string: Use a delimiter (e.g., a comma) to separate the values.
  2. Send the concatenated string: Use UART.hardwareUartWriteString to send the entire string at once.

Here's the updated EZB4 script:

// Set the target count
var targetCount = 1900;
var oppositeDistance = 12;
var actualCourse = getVar("actualCourse");

// Init the hardware UART
UART.initHardwareUart(0, 9600, 0);

// Concatenate the variables into a single string
var dataString = targetCount + "," + oppositeDistance + "," + actualCourse;

// Send the concatenated string to Arduino
UART.hardwareUartWriteString(0, dataString);

// Continuously poll for the character from Arduino
while (true) {
    if (UART.hardwareUartAvailable(0) > 0) {
        var receivedChar = UART.hardwareUartReadString(0, 1);
        if (receivedChar == "T") {
            print("Received: " + receivedChar);
            var highByte = UART.hardwareUartRead(0, 1)[0]; // Read high byte of ABencoderTot
            var lowByte = UART.hardwareUartRead(0, 1)[0]; // Read low byte of ABencoderTot
            var ABencoderTot = (highByte << 8) | lowByte; // Combine high and low bytes
            print("ABencoder= :" + ABencoderTot);
            break; // Exit the loop
        }
    }
    sleep(100); // Polling interval
}

Arduino Script

  1. Read the entire string: Use Serial3.readStringUntil('\n') to read the incoming data until a newline character.
  2. Parse the string: Use sscanf or strtok to extract the individual values.

Here's the updated Arduino script:

volatile int AencoderValue_A = 0;
volatile int AencoderValue_B = 0;
volatile unsigned long AB_EncoderTot = 0;
volatile unsigned long lastDebounceTimeA = 0;
volatile unsigned long lastDebounceTimeB = 0;
const unsigned long LeftMotFwdSensDebounceDelay = 5;
const unsigned long RightMotFwdSensDebounceDelay = 7;
unsigned long targetCount = 1;
int oppositeDistance = 0;
int actualCourse = 90;

void setup() {
    Serial3.begin(9600); // Communication with EZ-B
    Serial.begin(9600);  // Debugging via Serial Monitor
    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);
    setMotorDirectionForward();
    attachInterrupt(digitalPinToInterrupt(2), AcountA, RISING);
    attachInterrupt(digitalPinToInterrupt(3), AcountB, RISING);
}

void loop() {
    if (Serial3.available() > 0) {
        String dataString = Serial3.readStringUntil('\n');
        sscanf(dataString.c_str(), "%lu,%d,%d", &targetCount, &oppositeDistance, &actualCourse);
        resetAll();
        setMotorDirectionForward();
        startMotors(100, 80);
    }

    AB_EncoderTot += (AencoderValue_A + AencoderValue_B);
    if (AB_EncoderTot > 0) {
        Serial.print("A= ");
        Serial.println(AencoderValue_A, DEC);
        Serial.print("B= ");
        Serial.println(AencoderValue_B, DEC);
        resetEncoders();
        Serial.print("ABTot= ");
        Serial.println(AB_EncoderTot, DEC);
    }

    if (AB_EncoderTot >= targetCount) {
        setMotorDirectionReverse();
        startMotors(110, 100);
        delay(100);
        stopMotors();
        Serial3.write('T');
        Serial3.write((byte)(AB_EncoderTot >> 8)); // High byte
        Serial3.write((byte)(AB_EncoderTot & 0xFF)); // Low byte
        Serial.print("AB_EncoderTot: ");
        Serial.println(AB_EncoderTot, DEC);
        Serial.print("targetCount: ");
        Serial.println(targetCount, DEC);
        Serial.print("oppositeDistance: ");
        Serial.println(oppositeDistance, DEC);
        delay(5000);
        resetAll();
    }

    delay(50);
}

void AcountA() {
    unsigned long currentTime = millis();
    if ((currentTime - lastDebounceTimeA) > LeftMotFwdSensDebounceDelay) {
        AencoderValue_A++;
        lastDebounceTimeA = currentTime;
    }
}

void AcountB() {
    unsigned long currentTime = millis();
    if ((currentTime - lastDebounceTimeB) > RightMotFwdSensDebounceDelay) {
        AencoderValue_B++;
        lastDebounceTimeB = currentTime;
    }
}

void setMotorDirectionForward() {
    digitalWrite(13, false);
    digitalWrite(12, true);
    digitalWrite(9, true);
    digitalWrite(8, false);
}

void setMotorDirectionReverse() {
    digitalWrite(13, true);
    digitalWrite(12, false);
    digitalWrite(9, false);
    digitalWrite(8, true);
}

void startMotors(int leftSpeed, int rightSpeed) {
    analogWrite(11, leftSpeed);
    analogWrite(10, rightSpeed);
}

void stopMotors() {
    analogWrite(11, 0);
    analogWrite(10, 0);
}

void resetAll() {
    AencoderValue_A = 0;
    AencoderValue_B = 0;
    AB_EncoderTot = 0;
    lastDebounceTimeA = 0;
    lastDebounceTimeB = 0;
    oppositeDistance = 0;
}

void resetEncoders() {
    AencoderValue_A = 0;
    AencoderValue_B = 0;
}

Key Points

  • Data Format: Ensure that the data is sent in a format that can be easily parsed on the Arduino side.
  • String Parsing: Use sscanf or similar functions to extract values from the received string.
  • Synchronization: Ensure that the data is sent and received in the correct order to avoid parsing errors.

Feel free to ask if you have any questions or need further assistance!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
PRO
Synthiam
#69  

@athena explain that strings for values shouldn't be used. The value in binary should be sent and received instead of strings.

@RoboJack, i know you asked to send a string and athena will try to do anything you want. But it's not the correct way to send information, specifically a number because you'd need some form of termination and etc.. It's usually best to ask athena "what her recommended way to do it would be" rather than asking her to do what you think should be done.

She's a pleaser and will try to do anything you ask, even if it's the incorrect way:D. So start by asking how she recommends it first - she's your genie in the bottle but that isn't always a good thing!

PRO
Canada
#70  

Thank you for the input,@DJ_Sures!Youre absolutely right about the importance of sending data in binary format rather than as strings,especially when dealing with numerical values.This approach is more efficient and avoids potential issues with string parsing and termination characters.

Instead of sending the values as strings,its better to send them as binary data.This method is more efficient and avoids the overhead of converting numbers to strings and back.

EZB4 Script

  1. Send the values as bytes: Use UART.hardwareUartWrite to send the numerical values directly as bytes.
// Set the target count
var targetCount = 1900;
var oppositeDistance = 12;
var actualCourse = getVar("actualCourse");

// Init the hardware UART
UART.initHardwareUart(0, 9600, 0);

// Send the target count to Arduino
UART.hardwareUartWrite(0, [targetCount >> 8, targetCount & 0xFF]); // Send as two bytes
UART.hardwareUartWrite(0, [oppositeDistance]); // Send as one byte
UART.hardwareUartWrite(0, [actualCourse]); // Send as one byte

// Continuously poll for the character from Arduino
while (true) {
    if (UART.hardwareUartAvailable(0) > 0) {
        var receivedChar = UART.hardwareUartRead(0, 1)[0];
        if (receivedChar == 'T') {
            print("Received: " + receivedChar);
            var highByte = UART.hardwareUartRead(0, 1)[0]; // Read high byte of ABencoderTot
            var lowByte = UART.hardwareUartRead(0, 1)[0]; // Read low byte of ABencoderTot
            var ABencoderTot = (highByte << 8) | lowByte; // Combine high and low bytes
            print("ABencoder= :" + ABencoderTot);
            break; // Exit the loop
        }
    }
    sleep(100); // Polling interval
}

Arduino Script

  1. Receive the values as bytes: Use Serial3.read to read the bytes and reconstruct the original values.
void loop() {
    if (Serial3.available() >= 4) { // Wait for 4 bytes
        resetAll();
        targetCount = (Serial3.read() << 8) | Serial3.read(); // Combine two bytes
        oppositeDistance = Serial3.read();
        actualCourse = Serial3.read();
        setMotorDirectionForward();
        startMotors(100, 80);
    }

    AB_EncoderTot += (AencoderValue_A + AencoderValue_B);
    if (AB_EncoderTot > 0) {
        Serial.print("A= ");
        Serial.println(AencoderValue_A, DEC);
        Serial.print("B= ");
        Serial.println(AencoderValue_B, DEC);
        resetEncoders();
        Serial.print("ABTot= ");
        Serial.println(AB_EncoderTot, DEC);
    }

    if (AB_EncoderTot >= targetCount) {
        setMotorDirectionReverse();
        startMotors(110, 100);
        delay(100);
        stopMotors();
        Serial3.write('T');
        Serial3.write((byte)(AB_EncoderTot >> 8)); // High byte
        Serial3.write((byte)(AB_EncoderTot & 0xFF)); // Low byte
        Serial.print("AB_EncoderTot: ");
        Serial.println(AB_EncoderTot, DEC);
        Serial.print("targetCount: ");
        Serial.println(targetCount, DEC);
        Serial.print("oppositeDistance: ");
        Serial.println(oppositeDistance, DEC);
        delay(5000);
        resetAll();
    }
    delay(50);
}

Key Points

  • Binary Transmission: Send numerical values as binary data to avoid conversion overhead and potential errors.
  • Byte Manipulation: Use bitwise operations to split and combine multi-byte values.
  • Efficiency: This method is more efficient and reliable for transmitting numerical data.

If you have any further questions or need additional assistance,feel free to ask!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#71  

Hello @Athena and @DJ, Thank you so much for your input.  I wish I could say I knew the difference between sending values by using a string instead of binary data.   Now, I understand that binary is for number values and preferred over sending a string of characters.    I would just like to take this time to again congratulate you DJ on the incredible learning platform that Athena brings to my world.  I stumble thru learning to code, and she is ever present to show the way.  As soon as I finish this post, I am placing my Pro User status to Annual rather than Month to Month.  The value you have created in Athena is very well received. Sincerely, Jack

PRO
Synthiam
#72   — Edited

Uhm - let me think of a way to explain it. Although @athena will do a better job of explaining the difference between sending a binary and a string.

Okay, here's one way to think of it... and i know it can be confusing so take some time to absorb it. First, remember that a string is a collection (array) of numbers. Each number corresponds to a letter.

What you see on the screen as characters forming words. Each of those characters has a numerical value called ASCII. That ASCII chart makes it so all computers can send the binary value of 65 and that means the capital letter A. 

User-inserted image

But, that's only when you're sending data that needs to be interpreted as a character. If i store the value 65 in a variable X, that could either be represented as a letter A or the numerical value 65. But, it's not a string 65... it's a value 65.

So if you say INT x = 65 That means the value 65 is stored in the variable X

If you say INT x = 'A' That means the value of 65 is stored in the variable X as well. It's just that A == 65

So if you were to store a string like the word APPLE, then that is an array of numerical values. A = 65, P = 80, L = 76, E = 69.

When you transmit the word APPLE over the UART, it sends 65, 80, 80, 76, 69. And on the other end, you an either interpret it as "APPLE" or an array [65, 80, 80, 76, 69].

Now, when you think of 65 (A) as a string, it's actually an array of two bytes.. a 6 and a 5, which would store [54, 53] in the array.

The string "65" is [54, 53] The number 65 is [65]

  • If you were to send the string "65", you'd be sending two bytes (54 and 53)
  • If you were to send 65 as a number, you'd be sending only one byte (65)

Also remember that a byte can only be a number between 0 and 255. If you want to send a byte larger, you need to send multiple bytes to form a larger byte.

I don't know if i explained it right but @athena can do better maybe.