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

Don't limit your robot's potential – subscribe to ARC Pro and transform it into a dynamic, intelligent machine.

PRO
Canada
#33  

Hello Jack!To achieve the functionality you described,where you sum the encoder values and accumulate them in $EncoderTot until it reaches 1500, you can modify your EZ-B v4 script as follows:

// Initialize UART on port 0 with a baud rate of 9600
UART.initHardwareUart(0, 9600);

var EncoderSum = 0;
setVar($EncoderTot, 0);

// Function to start the loop
function loop() {
    // Define the string to send
    var stringToSend = "a"; // Send command 'a' to request data

    // Send a request for data to the Arduino
    UART.hardwareUartWriteString(0, stringToSend);
    print("Sent: " + stringToSend + " to Arduino");

    // Wait a bit for the data to be transmitted
    sleep(100);

    // Check if data is available
    var dataAvail = UART.hardwareUartAvailable(0);

    if (dataAvail == 4) { // Expecting 4 bytes (2 bytes for each encoder value)
        // Read the encoder values
        var highByteA = UART.hardwareUartRead(0, 1)[0]; // Read high byte of Encoder A
        var lowByteA = UART.hardwareUartRead(0, 1)[0];  // Read low byte of Encoder A
        var encoderValue_A = (highByteA << 8) | lowByteA; // Combine high and low bytes

        var highByteB = UART.hardwareUartRead(0, 1)[0]; // Read high byte of Encoder B
        var lowByteB = UART.hardwareUartRead(0, 1)[0];  // Read low byte of Encoder B
        var encoderValue_B = (highByteB << 8) | lowByteB; // Combine high and low bytes

        // Calculate the sum of encoder values
        EncoderSum = encoderValue_A + encoderValue_B;

        // Update the total encoder count
        setVar($EncoderTot, getVar($EncoderTot) + EncoderSum);

        // Print the current total
        print("EncoderTot = " + getVar($EncoderTot));

        // Check if the total has reached 1500
        if (getVar($EncoderTot) >= 1500) {
            // Define the string to send
            stringToSend = "r"; // Reset for no further acceptance of interrupts
            UART.hardwareUartWriteString(0, stringToSend);
            print("Sent: " + stringToSend + " to Arduino");

            // Stop the motors
            controlCommand("L MANAGER", "ScriptStart", "FULL STOP PWR DN");

            // Stop running this script
            halt();
        }
    }

    // Wait before the next loop iteration
    sleep(25);

    // Call the loop function again
    loop();
}

// Start the loop
loop();

Key Points:

  • Combining Bytes: The encoder values are read as two bytes each (high and low), and they are combined using bitwise operations to form the full value.
  • Accumulating Total: The total encoder count is updated by adding the sum of the current encoder values to $EncoderTot.
  • Checking Condition: Once $EncoderTot reaches or exceeds 1500, the script sends the reset command (r) to the Arduino and stops the motors.

This setup should help you achieve the desired functionality. 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.
PRO
Synthiam
#34  

@athena setvar() and getvar() require the variable name parameter to be a string in quotes.

PRO
Canada
#35  

Thank you for pointing that out, DJ! You're absolutely right. When using setVar() and getVar() in JavaScript or Python within ARC, the variable name should indeed be passed as a string in quotes. Let's correct the script accordingly:

// Initialize UART on port 0 with a baud rate of 9600
UART.initHardwareUart(0, 9600);

var EncoderSum = 0;
setVar("$EncoderTot", 0);

// Function to start the loop
function loop() {
    // Define the string to send
    var stringToSend = "a"; // Send command 'a' to request data

    // Send a request for data to the Arduino
    UART.hardwareUartWriteString(0, stringToSend);
    print("Sent: " + stringToSend + " to Arduino");

    // Wait a bit for the data to be transmitted
    sleep(100);

    // Check if data is available
    var dataAvail = UART.hardwareUartAvailable(0);
    if (dataAvail == 4) { // Expecting 4 bytes (2 bytes for each encoder value)
        // Read the encoder values
        var highByteA = UART.hardwareUartRead(0, 1)[0]; // Read high byte of Encoder A
        var lowByteA = UART.hardwareUartRead(0, 1)[0];  // Read low byte of Encoder A
        var encoderValue_A = (highByteA << 8) | lowByteA; // Combine bytes for Encoder A

        var highByteB = UART.hardwareUartRead(0, 1)[0]; // Read high byte of Encoder B
        var lowByteB = UART.hardwareUartRead(0, 1)[0];  // Read low byte of Encoder B
        var encoderValue_B = (highByteB << 8) | lowByteB; // Combine bytes for Encoder B

        // Calculate the sum of encoder values
        EncoderSum = encoderValue_A + encoderValue_B;

        // Update the total encoder count
        var currentTotal = getVar("$EncoderTot");
        setVar("$EncoderTot", currentTotal + EncoderSum);

        print("Encoder A: " + encoderValue_A);
        print("Encoder B: " + encoderValue_B);
        print("EncoderSum: " + EncoderSum);
        print("EncoderTot: " + getVar("$EncoderTot"));

        // Check if the total encoder count has reached or exceeded 1500
        if (getVar("$EncoderTot") >= 1500) {
            // Define the string to send
            stringToSend = "r"; // Reset for no further acceptance of interrupts
            UART.hardwareUartWriteString(0, stringToSend);
            print("Sent: " + stringToSend + " to Arduino");

            // Stop the motors
            controlCommand("L MANAGER", "ScriptStart", "FULL STOP PWR DN");

            // Stop running this script
            halt();
        }
    }

    // Wait before the next loop iteration
    sleep(25);

    // Call the loop function again
    loop();
}

// Start the loop
loop();

Key Points:

  • String in Quotes: The variable name in setVar() and getVar() is now correctly passed as a string in quotes.
  • Corrected Logic: The script logic remains the same, ensuring the encoder values are accumulated and checked against the threshold.

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.
#36  

Hello @Athena, I would like to thank you again for your expert assistance.  Both my Arduino and EZB4 scripts are running great with no additional issues relating to encoder counts.   I'm very happy with what you have done for me. Thanks, Jack

PRO
Canada
#37  

Hello Jack,

I'm thrilled to hear that everything is working smoothly with your Arduino and EZ-B v4 scripts! It's great to know that the encoder counts are functioning as expected. If you have any more questions or need further assistance in the future, don't hesitate to reach out. Happy robotics, and enjoy your project!

Best regards, Athena


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.
#38  

Hello @Athena and any Community members interested:

I am disappointed in the results I'm getting for determining distance traveled across the floor by counting motor ticks from the drive motor encoders.   I am rarely able to get within 6 inches of a desired point when traveling straight ahead 60 inches.  I would be very interested in member experiences or recommendations for determining distance traveled.  Reliable and repeatable results are what I desire.

About my project.  It is a tracked vehicle, (similar to ROLI) powered by two common 9vdc electric geared motors which contain two built in encoders each.  I am driving the motors with a boost converter that reliably provides 12vdc to a motor controller providing 9.3v to the motors.  A lithium battery of 7.6vdc powers EZB4, one servo and the boost converter.  The lithium battery allows for about 8hrs of continuous usage.

An Arduino Mega is dedicated solely to monitoring the Encoders.  I have a script running on the EZB4 that polls the encoder totals.  My objective is simply to total the encoder counts and stop the robot at a predetermined distance of motor encoder counts.  I must obtain repeatable results varying no more than a couple of inches in a 60-inch distance traveled.  I am not trying to count track revolution or speed over the ground, only to reliably count the ticks on the encoders and stop robot at a predetermined distance of 60 inches.

Track slippage is minimal as most of the robot travel is over carpet and travel speed is very slow.   I have included the Arduino Script below that I am using to monitor the Encoders.  Please look it over.  I have settled on reading Sensor #1 and Sensor #2 on each motor.  I am using a strategy of "detaching" the two sensors that I am polling and "attaching" the other two sensors to continue the count.  After polling the two sensors, I zero them out and "re-attach" them while performing the same sequence on the other two sensors.

Thank you for your input, Jack

volatile int AencoderValue_A = 0;
volatile int AencoderValue_B = 0;

volatile int BencoderValue_A = 0;
volatile int BencoderValue_B = 0;

void setup() {
    Serial3.begin(9600); // Communication with EZ-B
    Serial.begin(9600);  // Debugging via Serial Monitor
    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);
    pinMode(18,INPUT_PULLUP);
    pinMode(19, INPUT_PULLUP);
    
    attachInterrupt(digitalPinToInterrupt(2), AcountA, FALLING);   //Attach Aencoder.  Set all interrupts to AcountA and BcountB
    attachInterrupt(digitalPinToInterrupt(3), AcountB, FALLING);
    attachInterrupt(digitalPinToInterrupt(18), BcountA, FALLING);   //Attach Bencoder.  Set all interrupts to BcountA and BcountB.
    attachInterrupt(digitalPinToInterrupt(19), BcountB, FALLING);
}

void loop() {

    if (Serial3.available() > 0) {
            // Read the incoming data from the EZ-B
            int incomingByte = Serial3.read();
        
            // Print the received command as a character
            Serial.print("---------: ");
            Serial.println((char)incomingByte);
    
        if (incomingByte == 'r') {    // Receive char "r" to detach interrupts and zero out all Value Holders.
            detachInterrupt(digitalPinToInterrupt(2));   //Detach Aencoder interrupt.
            detachInterrupt(digitalPinToInterrupt(3));   //Detach Aencoder interrupt.
            AencoderValue_A = 0;   // Zero out Aencoder AA,AB Value Holders.
            AencoderValue_B = 0;
            
            attachInterrupt(digitalPinToInterrupt(2), AcountA, FALLING);  //Attach Aencoder.  Set all interrupts to AcountA and BcountB
            attachInterrupt(digitalPinToInterrupt(3), AcountB, FALLING);

            detachInterrupt(digitalPinToInterrupt(18));   //Detach Bencoder interrupt.
            detachInterrupt(digitalPinToInterrupt(19));   //Detach Bencoder interrupt.
            BencoderValue_A = 0;   //Zero out Bencoder BA,BB Value Holders.
            BencoderValue_B = 0;}

            

    
        if (incomingByte == 'a') {
            detachInterrupt(digitalPinToInterrupt(2));   //Detach Aencoder interrupt.
            detachInterrupt(digitalPinToInterrupt(3));   //Detach Aencoder interrupt.
          
            attachInterrupt(digitalPinToInterrupt(18), BcountA,FALLING);   //Attach Bencoder. Set all interrupts to BcountA and BcountB.
            attachInterrupt(digitalPinToInterrupt(19), BcountB,FALLING);
        
            Serial3.write((byte)(AencoderValue_A >> 8)); // High byte   //Write AencoderA/B.
            Serial3.write((byte)(AencoderValue_A & 0xFF)); // Low byte
            Serial3.write((byte)(AencoderValue_B >> 8)); // High byte
            Serial3.write((byte)(AencoderValue_B & 0xFF)); // Low byte
            
            // Print the encoder values to the Serial Monitor
            Serial.print("A-Encoder_L: ");   //Print AencoderA/B.
            Serial.println(AencoderValue_A, DEC); // Use DEC to print as decimal
            Serial.print("A-Encoder_R: ");
            Serial.println(AencoderValue_B, DEC); // Use DEC to print as decimal

            AencoderValue_A = 0;   //Zero out AencoderA/B.
            AencoderValue_B = 0;}

           
           if (incomingByte == 'b') {
            detachInterrupt(digitalPinToInterrupt(18));   //Detach BencoderValue_A interrupt.
            detachInterrupt(digitalPinToInterrupt(19));   //Detach BencoderValue_B interrupt.

            attachInterrupt(digitalPinToInterrupt(2), AcountA, FALLING);
            attachInterrupt(digitalPinToInterrupt(3), AcountB, FALLING);

            Serial3.write((byte)(BencoderValue_A >> 8)); // High byte   Write BencoderA/B.
            Serial3.write((byte)(BencoderValue_A & 0xFF)); // Low byte
            Serial3.write((byte)(BencoderValue_B >> 8)); // High byte
            Serial3.write((byte)(BencoderValue_B & 0xFF)); // Low byte
            
            // Print the encoder values to the Serial Monitor
            Serial.print("B-Encoder_L: ");   //Print BencoderA/B.
            Serial.println(BencoderValue_A, DEC); // Use DEC to print as decimal
            Serial.print("B-Encoder_R: ");
            Serial.println(BencoderValue_B, DEC); // Use DEC to print as decimal

            BencoderValue_A = 0;   //Zero out BencoderA/B.     
            BencoderValue_B = 0;}
        
       }            
}  

void AcountA() {
    AencoderValue_A++;
}

void AcountB() {
    AencoderValue_B++;
}

void BcountA(){
    BencoderValue_A++;
}
void BcountB() {
    BencoderValue_B++;
}
PRO
Canada
#39  

Hello Jack,

I understand your concern about achieving reliable and repeatable results for distance measurement using motor encoders. Here are a few suggestions and considerations that might help improve the accuracy of your distance measurements:

1. Encoder Resolution and Calibration:

  • Resolution: Ensure that your encoders have sufficient resolution for the precision you need. Higher resolution encoders provide more ticks per revolution, which can improve accuracy.
  • Calibration: Calibrate your system by measuring the actual distance traveled for a known number of encoder ticks. This can help you determine the exact number of ticks per inch or centimeter.

2. Mechanical Considerations:

  • Track Slippage: Even minimal slippage can affect accuracy. Consider using a surface with more grip or adding weight to the robot to increase traction.
  • Alignment: Ensure that the tracks are aligned properly and that there is no mechanical binding or resistance that could affect movement.

3. Software Improvements:

  • Debouncing: Implement debouncing in your interrupt routines to avoid false counts due to noise or mechanical vibrations.
  • Smoothing: Consider averaging multiple readings to smooth out any variations in encoder counts.

4. Feedback and Control:

  • PID Control: Implement a PID controller to adjust motor speed dynamically based on encoder feedback. This can help maintain a consistent speed and improve accuracy.
  • Closed-Loop Control: Use the encoder feedback to create a closed-loop control system that adjusts motor power to maintain the desired speed and distance.

5. Alternative Methods:

  • Additional Sensors: Consider using additional sensors like wheel encoders or an IMU (Inertial Measurement Unit) to complement the encoder data and improve accuracy.
  • Optical or Laser Sensors: For precise distance measurement, consider using optical or laser distance sensors as a secondary verification method.

Code Review:

Your code seems to be structured well for counting encoder ticks. However, here are a few points to consider:

  • Interrupt Handling: Ensure that your interrupt service routines (ISRs) are as short as possible to minimize latency.
  • Data Type: Consider using volatile long for encoder values if you expect the counts to exceed the range of an int.
  • Serial Communication: Ensure that the serial communication between the Arduino and EZ-B is reliable and that there is no data loss.

If you continue to experience issues, you might want to experiment with some of these suggestions to see if they improve the accuracy and repeatability of your distance measurements. Feel free to reach out if you have any further questions or need additional assistance!

Best regards, Athena


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.