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

Get access to the latest features and updates before they're released. You'll have everything that's needed to unleash your robot's potential!

#105  

Hello @Athena, You wrote, "Use a Sufficient Buffer Size: If possible, configure the buffer size to accommodate the expected data rate. This can help reduce the risk of overflow."

How do I increase the Buffer Size?  Should I monitor is level?  How is that done? Thanks. Jack

PRO
Canada
#106  

Hello Jack,

Increasing the buffer size and monitoring its level can help manage data flow more effectively in your serial communication setup. Here's how you can approach these tasks:

Increasing Buffer Size

  1. Arduino Serial Buffer:

    • The default serial buffer size for Arduino is typically 64 bytes. To increase it, you need to modify the HardwareSerial.h file in the Arduino core library.
    • Locate the HardwareSerial.h file in your Arduino installation directory. The path might look something like this: Arduino/hardware/arduino/avr/cores/arduino/HardwareSerial.h.
    • Open the file and look for the line that defines the buffer size, usually something like #define SERIAL_BUFFER_SIZE 64.
    • Change the value to your desired buffer size, e.g., #define SERIAL_BUFFER_SIZE 128.
    • Save the file and restart the Arduino IDE.

    Note: Modifying core library files can affect all Arduino projects, so proceed with caution. Also, increasing the buffer size will consume more RAM, which is limited on microcontrollers.

  2. EZB Buffer:

    • The buffer size on the EZB side is typically managed by the firmware and may not be easily adjustable. However, ensuring efficient data handling and processing can help manage buffer usage.

Monitoring Buffer Level

  1. Arduino Buffer Monitoring:

    • You can monitor the buffer level by checking the number of bytes available in the buffer using Serial.available(). This function returns the number of bytes available for reading from the serial buffer.
    • Example:
      int bufferLevel = Serial.available();
      Serial.print("Buffer Level: ");
      Serial.println(bufferLevel);
      
  2. EZB Buffer Monitoring:

    • On the EZB side, you can use the UART.hardwareUartAvailable() function to check the number of bytes available in the UART receive buffer.
    • Example:
      var bufferLevel = UART.hardwareUartAvailable(0);
      print("EZB Buffer Level: " + bufferLevel);
      

Best Practices

  • Read Data Regularly: Regularly read data from the buffer to prevent it from filling up.
  • Implement Flow Control: If possible, use flow control mechanisms to manage data flow and prevent buffer overflow.
  • Optimize Data Rate: Ensure that the data rate is optimized for your application to prevent excessive data from overwhelming the buffer.

By increasing the buffer size and monitoring its level, you can better manage data flow and reduce the risk of overflow. If you have any more questions or need further assistance, feel free to ask!

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.
PRO
Synthiam
#107   — Edited

There might be a misunderstanding or a focus on details that aren't critical to the problem you're addressing. Athena, as a tool, is designed to provide feedback and guidance based on the context of your input so that her responses reflect your direction.

The "buffer" Athena mentioned refers to the Arduino's receive buffer. The suggestion arose because your original code transmits the X value in a loop at a very high frequency, even when the value hasn’t changed. This can overwhelm the Arduino's receive buffer, potentially causing performance issues or unexpected behavior.

Her recommendation to transmit the value only when it changes is rooted in good coding practices. This approach minimizes unnecessary data transmission and prevents the Arduino's receive buffer from being flooded with redundant information. While the term "buffer" might seem significant, it's not central to your implementation goals; instead, it’s part of her explanation for why optimizing your code in this way is beneficial.

Rather than focusing on the term "buffer," the key takeaway is her advice to optimize communication by sending updates only when necessary. This ensures efficient resource usage and avoids potential issues with the Arduino's ability to process incoming data.

#108  

Thanks @Athena and @DJ for your assistance. Jack

PRO
Canada
#109  

You're welcome, Jack! If you have any more questions or need further assistance in the future, feel free to reach out. Happy coding and best of 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.
PRO
Synthiam
#110  

Nice. Keep rocking on your project:)

#111  

Hello @Athena,

1.  Can you please review how I have used the MAP function for motor control and provide recommendations? 2.  I would like to ramp up the motor speed on the initial pass thru the MAP function, and after that the mapped PWM should be applied directly.  Can you please provide recommendations for ramping the speed up in the initial run? 3.  Can you please review the overall sketch and provide recommendations?

Thanks so much. Jack

ARDUINO CODE:

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 = 3;
const unsigned long RightMotFwdSensDebounceDelay = 7;
long pathTotalDistance = 0;
long adjacentRemainingDistance = 0;
long pathEndpointDistance = 0;
float oppositeDistance = 0;
float desiredCourse = 90;
long actualCourse = 90;
float diffCourse=0;
float lastDiff= 1;
int leftSpeed= 100;
int rightSpeed= 80;
int run = 0;


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

void loop() {

        // Wait for 8 bytes to receive running parameters from EZB4.  if true allows for the first half of loop to read, until "if run==1"
    if (Serial3.available() >= 8) {
        delay(10);
        resetAll(); //Reset encoder parameters
      
        // Read and store bytes for pathTotalDistance
        byte highBytePTD = Serial3.read();
        byte lowBytePTD = Serial3.read();
        pathTotalDistance = (highBytePTD << 8) | lowBytePTD;

        // Read and store bytes for pathEndpointDistance
        byte highBytePED = Serial3.read();
        byte lowBytePED = Serial3.read();
        pathEndpointDistance = (highBytePED << 8) | lowBytePED;

        // Read and store bytes for oppositeDistance
        byte highByteOD = Serial3.read();
        byte lowByteOD = Serial3.read();
        oppositeDistance = (highByteOD << 8) | lowByteOD;
    
        actualCourse = Serial3.read(); //  Read and store actualCourse
        desiredCourse = Serial3.read();//  Read and store desiredCourse

        //  Print the above received paramenters
        Serial.print("pathTotalDistance:  ");
        Serial.println(pathTotalDistance);
        Serial.print("pathEndpointDistance:  ");
        Serial.println(pathEndpointDistance);
        Serial.print("oppositeDistance:  ");
        Serial.println(oppositeDistance);
        Serial.print("actualCourse:  ");
        Serial.println(actualCourse);
        Serial.print("desiredCourse:  ");
        Serial.println(desiredCourse);

        startMotors(leftSpeed, rightSpeed); // Begin motor run
   }
        if (run == 1) {  // if true allows for second half of loop to run

        AB_EncoderTot += (AencoderValue_A + AencoderValue_B);  //  Total encoder value sum
    
        Serial.print("A= ");
        Serial.println(AencoderValue_A, DEC);  // Print ISR AcountA
        Serial.print("B= ");
        Serial.println(AencoderValue_B, DEC);  //Print ISR AcountB
        resetEncoders(); // Reset ISR AcountA and ISR AcountB immediately after printing
        Serial.print("AB_EncoderTot= ");
        Serial.println(AB_EncoderTot, DEC);  // Print AB_Encoder total

        if (AB_EncoderTot >= pathEndpointDistance) {   // If encoder total is >= endpoint
        stopMotors();  // Stop motors perform some resets
    }
      if (Serial3.available() > 0) {
        // Read the incoming byte
        int incomingByte = Serial3.read();

        // Check if the incoming byte is 'x'
        if (incomingByte == 'x') {
        stopMotors();
        } else {
            // Otherwise, treat it as the actualCourse
            actualCourse = incomingByte;
        }
    }   
        // Default value for desiredCourse is 90, 
       // if any other value run toa and calculate desiredCourse (offset reflector)
       if (desiredCourse != 90) { 
       toa();
       }
      //  Calculating diffCourse
      diffCourse = actualCourse-desiredCourse;

      if (diffCourse != lastDiff) { // Only map motor speeds if PWM values must change
      if (abs(diffCourse) <= 5){ // Get positive value for diffCourse

      //  leftSpeed and rightSpeed values
      leftSpeed = map(  diffCourse, -4, 4, 150, 60);
      rightSpeed= map(  diffCourse, -4, 4, 50, 120);
      }else { 
      leftSpeed = map(  diffCourse, -15, 15, 150, 60);
      rightSpeed= map(  diffCourse, -15, 15, 50, 120);
      } // Constrains the PWM values to only the map values
      leftSpeed= constrain(leftSpeed,60,150);
      rightSpeed= constrain(rightSpeed,50,120);  

      startMotors(leftSpeed, rightSpeed);  // Begin motor run

      lastDiff = diffCourse; // Assuring only a change in diffCourse will allow new motor mapping
     }
     }
    delay(50);  // Delay for smooth execution of sketch
}

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

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

void setMotorDirectionForward() {  // Motor run logic forward
    digitalWrite(13, false);
    digitalWrite(12, true);
    digitalWrite(9, true);
    digitalWrite(8, false);
}

void setMotorDirectionReverse() {  // Motor run logic reverse
    digitalWrite(13, true);
    digitalWrite(12, false);
    digitalWrite(9, false);
    digitalWrite(8, true);
}

void startMotors(int leftSpeed, int rightSpeed) {  // For motor run
    run = 1;
    analogWrite(11, leftSpeed);
    analogWrite(10, rightSpeed);    
}

void stopMotors() {  //  For motor stop 
        setMotorDirectionReverse();  // Set motor logic reverse to provide for precise, no drift stop
        leftSpeed= 100;
        rightSpeed= 80;
        delay(65);
        analogWrite(11, 0);
        analogWrite(10, 0);
        run = 0; 
        Serial3.write('T');  //  Send commands to EZB4 to alert other scripts of motor stop
        Serial3.write((byte)(AB_EncoderTot >> 8)); // High byte
        Serial3.write((byte)(AB_EncoderTot & 0xFF)); // Low byte
        Serial3.end();  // Stop and empty Serial3 buffer to assure only valid data will be read 
        Serial3.begin(9600);  // Restart Serial3 buffer
        resetFoundationVariables();  // Reset some variables
}

void resetAll() {  //  Reset some variables
    AencoderValue_A = 0;
    AencoderValue_B = 0;
    AB_EncoderTot = 0;
    lastDebounceTimeA = 0;
    lastDebounceTimeB = 0;
    run = 0;
    lastDiff= 1;
}

void resetEncoders() {  //  Emptying encoder value registers  
    AencoderValue_A = 0;
    AencoderValue_B = 0; 
}

void toa() {  //  Trigonometry calculation of a right triangle.  Used for offset reflector
    adjacentRemainingDistance = (pathTotalDistance - AB_EncoderTot);
    // Use atan2 to calculate the angle correctly
    desiredCourse = (atan2(oppositeDistance, adjacentRemainingDistance) * 57.32) - 90;
    // a positive angle is required, use the absolute value
    desiredCourse = abs(desiredCourse); 
}

void resetFoundationVariables() {  // Reset some variables
    setMotorDirectionForward();  //  Set motor logic forward
    pathTotalDistance = 0;
    adjacentRemainingDistance = 0;
    pathEndpointDistance = 0;
    oppositeDistance = 0;
    diffCourse= 0;
    desiredCourse= 90;
    actualCourse= 90;
    run= 0;
}


PRO
Canada
#112  

Hello Jack! Let's go through your code and address your questions:

1. Review of the MAP Function Usage

The map() function in Arduino is used to re-map a number from one range to another. In your code, you are using it to adjust motor speeds based on the course difference. Here's a quick review:

leftSpeed = map(diffCourse, -90, 90, 0, 255);
rightSpeed = map(diffCourse, -90, 90, 255, 0);
  • Range: You are mapping diffCourse from -90 to 90 to a PWM range of 0 to 255. This is a common approach to adjust motor speeds based on directional error.
  • Recommendation: Ensure that diffCourse is always within the -90 to 90 range to avoid unexpected behavior. You might want to add a constrain() function to keep diffCourse within this range.

2. Ramping Up Motor Speed

To ramp up the motor speed on the initial pass, you can gradually increase the PWM value over a set period. Here's a simple way to implement this:

void rampUpMotors(int targetLeftSpeed, int targetRightSpeed) {
    int currentLeftSpeed = 0;
    int currentRightSpeed = 0;
    int step = 5; // Adjust step size for smoother or faster ramp-up

    while (currentLeftSpeed < targetLeftSpeed || currentRightSpeed < targetRightSpeed) {
        if (currentLeftSpeed < targetLeftSpeed) {
            currentLeftSpeed += step;
            if (currentLeftSpeed > targetLeftSpeed) currentLeftSpeed = targetLeftSpeed;
        }
        if (currentRightSpeed < targetRightSpeed) {
            currentRightSpeed += step;
            if (currentRightSpeed > targetRightSpeed) currentRightSpeed = targetRightSpeed;
        }
        analogWrite(11, currentLeftSpeed);
        analogWrite(10, currentRightSpeed);
        delay(50); // Adjust delay for smoother or faster ramp-up
    }
}
  • Usage: Call rampUpMotors(leftSpeed, rightSpeed); instead of startMotors(leftSpeed, rightSpeed); during the initial run.

3. Overall Sketch Review

  • Interrupts: You are using interrupts to count encoder pulses, which is a good approach. Ensure that your debounce logic is working as expected to avoid false counts.
  • Serial Communication: You are using Serial3 for communication with the EZ-B. Make sure the baud rate and data format match on both ends.
  • Motor Control: The logic for setting motor direction and speed is clear. Consider adding comments for each function to improve readability.
  • Code Organization: Consider breaking down the loop() function into smaller functions for better readability and maintainability.
  • Error Handling: Add checks to handle unexpected values or states, especially in communication and motor control.

If you have any specific areas you'd like more detailed feedback on, 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.