No USB connection after uploading a program file - Pololu Balboa 32U4 (with ATMEL MEGA32U4-MU chip) balancing robot -

Hello together,
I ask you to help me.
For a student project, I have now made two Pololu Balboa 32U4 robots incapable of connection to the PC. The Pololu Balboa 32U4 ( Pololu - Balboa 32U4 Balancing Robot Kit (No Motors or Wheels) ) using a MEGA32U4-MU chip.
This is how it came about:
I first set up the connection to my computer with the Pololu 32U4. All preinstalled programs such as BlinkLED worked perfectly. The robot had a connection to COM7, which was automatically recognized.
Afterwards I started writing my own scripts. I have successfully run a reading_encoder.ino, a reading_accelerometer.ino and a reading_angular_madgwick_filter.ino file. I was able to read the built-in encoders and the built-in IMU on the Balboa 32U4 well. I'll upload the tutorials that worked here:

reading_accelerometer.ino (940 Bytes)

reading_encoder.ino (3,1 KB)

reading_angular_madgwick_filter.ino (4,1 KB)

Since I want to design a controller that is simulated on Simulink, I have to determine the robot state (inclination angle, wheel angle and the derivatives). This already worked separately from the other in the tutorials reading_**.ino. So I prepared this data preparation in balancer-1.ino. Basically, I simply combined the individual program files from my already uploaded reading_**.ino tutorials.
And then it happened: First the program balancer-1.ino compiles and then loads the code onto the Pololu Balboa 32U4. Seconds later, when I want to open the communication window of my IDE, the message comes: no connection to COM7. So I lost the connection to my COM7 when I uploaded my program file balancer-1.ino. The uploaded program is the following:

balancer-1.ino (6,9 KB)

The error message came up:

Arduino: 1.8.19 (Windows 10), board: "Pololu A-Star 32U4"

The sketch uses 16808 bytes (58%) of program memory. The maximum is 28672 bytes.

Global variables use 610 bytes (23%) of dynamic memory, leaving 1950 bytes for local variables. The maximum is 2560 bytes.

No board could be found on the selected port. Please check the correct selection of the serial port. If this is correct, please restart the board using the reset button after the start of the upload.


This report would be more detailed if the option
"Verbose output during compilation"
in File -> Preferences would be activated.

Of course, I immediately checked what could be the problem. It is often said that it could be due to Windows updates. I think that's not the case, because I was then able to get a second Balboa 32U4 robot. The new, second robot naturally connected to COM7 again. Afterwards I was able to run BlinkLED, my own tutorials reading_**.ino. Then balancer-1.ino lost the connection again.

When I reconnect both robots to my PC, this message comes up from Windows every time:

Fehlermeldung_Arduino

In English:

The last USB device connected to this computer did not work properly and is not recognized by Windows.

Recommendation: Reconnect the device. If it is still not recognized by Windows, the device may be inoperable.

I tried it on two Windows computers with different Arduino programs 1.8.19 and 2.2.1. Always the same. The PC no longer recognizes the Arduino. I also restarted the PC several times. I am also sure that the Balboa 32U4 is not on any other COM board. In the device manager it appears on COM7 as unreachable. As in the error message.

Can someone please give me a tip? I can't get any further and would be very grateful!
Many kind regards
Jonas

If your board has a reset button

  1. Start the upload
  2. When the IDE reports the memory usage, double tap the reset button.

If your board does not have a reset button, hook one up (between reset and GND) or use a piece of wire.

Part of the code that is uploaded to boards with native USB (like yours) providers functionality for the board to be recognised by a PC and to react on the software reset issued by the IDE (opening and closing the port with a baud rate of 1200 baud). Your sketch can damage variables used by those functionalities.

Thank you @sterretje, your advice worked immediately. The board is recognized again and I can upload programs like BlinkLED again. But as soon as I try to upload my own file (see description) I get stuck in the same error again and again. What is wrong with my program?

Many kind regards
Jonas

My advice is to take the "divide and conquer" approach to troubleshooting. Start removing any extraneous code from your sketch until you have distilled it down the a program that contains only the absolute minimum of code required to produce the fault. Doing this will make the situation easier to understand by removing all irrelevant complexity from the system.

I find that often by the time I have completed this work the cause of the fault has become clear to me. Even if it doesn't, you can then share this minimal, reproducible, verifiable example (MCVE) here on the forum. It will be of value to the helpers here.


I would recommend paying extra attention to any division operations done in your code. Dividing by zero can cause the fault you are experiencing. In case you would like a demonstration, here is a "MCVE" for that:

void setup() {
  volatile byte foo = 1 / 0;  // The variable must be volatile to prevent it from being optimized out.
}
void loop() {}

Just in case, this is the code by @jonasheinzler

/*
 * Control of the balancing robot Balboa 32U4 
 * as a part of the research by XY.
 * This implementation is done by XY in September 2023.
 * 
 */
// general libaries to load 
#include <Balboa32U4.h> // preprogrammed functions of the Balboa 32U4
#include <LSM6.h> // data processing of the IMU (used hardware component)
#include <MadgwickAHRS.h>

// declare general variables
// adapt factors to normalize the output of the IMU 
float adapt_factor_gyro = 28.57;
float adapt_factor_accelerometer = 8196.7213;
float adapt_factor_encoder = 1779.6;

// initialize state space memorys
float gyro_offsets[3]; // for each coordinate direction x, y, z
float imu_measurement[4]; // [roll, pitch, heading, pitch_processed] 
float encoder_measurement[10]; // [currentTime, previousTime, countsLeft, countsRight, phiLeft, phiRight, dPhiLeft, dPhiRight]
float state[4]; // [theta, phi, dtheta, dphi]

// Firstly, the inertial measurement unit (IMU) and the encoder 
// for state representation are prepared
// setup the inertial measurement unit 
LSM6 imu;
// setup the encoder 
Balboa32U4Encoders encoders;
void initializeEncoder(){
  encoder_measurement[0] = millis();
  encoder_measurement[2] = encoders.getCountsAndResetLeft();
  encoder_measurement[3] = encoders.getCountsAndResetRight();
  encoder_measurement[4] = calculate_Phi(encoder_measurement[2] ,adapt_factor_encoder, true);
  encoder_measurement[5] = calculate_Phi(encoder_measurement[3] ,adapt_factor_encoder, true);
}
// setup the Madgwick filter
Madgwick filter; 

// The angle is calulated using data from the accelerometer and gyroscope
// The Madgwick toolbox was used for this

void initializeMadgwickFilter() {
  initializeIMU();
  const float sensorRate = 208.00;
  filter.begin(sensorRate);
}
// helperfunction for offset treatment
void initializeIMU(){

  // gyro offset treatment
  float gyro_offset_x = 0; 
  float gyro_offset_y = 0; 
  float gyro_offset_z = 0; 
  
  // write the accelerometer settings 
  imu.writeReg(LSM6::CTRL1_XL, 0b01011000); // 208 Hz, +/- 4 m/s^2
  // write the gyro settings
  imu.writeReg(LSM6::CTRL2_G, 0b01011000); // 208 Hz, 1000 deg/s

  // wait for IMU readings to stabilze
  delay(1000);
  
  // calibrate the gyro 
  for (int i=0; i<1000; i++){
      imu.read();
      gyro_offset_x+=imu.g.x;
      gyro_offset_y+=imu.g.y;
      gyro_offset_z+=imu.g.z;
    }

  gyro_offset_x = gyro_offset_x/1000;
  gyro_offset_y = gyro_offset_y/1000;
  gyro_offset_z = gyro_offset_z/1000;

  // return the calibration subtrahend
  float gyro_offsets[3];
  gyro_offsets[0] = gyro_offset_x;
  gyro_offsets[1] = gyro_offset_y;
  gyro_offsets[2] = gyro_offset_z;
}

// evaluation of the inertial measurement unit (calculation of theta and dtheta)
void evaluateIMU(){
  imu.read();
  // normalize the sensor raw data
  float* gyroNormalized = normalizeGyro(imu.g.x, imu.g.y, imu.g.z, adapt_factor_gyro, gyro_offsets);
  float* accNormalized = normalizeAcc(imu.a.x, imu.a.y, imu.a.z, adapt_factor_accelerometer); 
  // run the Madgwick filter
  filter.updateIMU(gyroNormalized[0], gyroNormalized[1], gyroNormalized[2], accNormalized[0], accNormalized[1], accNormalized[2]);
  // restore the filter results
  imu_measurement[0] = filter.getRoll(); 
  imu_measurement[1] = filter.getPitch(); 
  imu_measurement[2] = filter.getYaw(); 
  imu_measurement[3] = pitch_dataprocessing(imu_measurement[1], imu_measurement[0]); // calculate the defined rest point in the state space

  // save as a part of the state space vector
  state[0] = imu_measurement[3]; // theta
  state[2] = gyroNormalized[1];  // dtheta 
}

float* normalizeGyro(float gyroX, float gyroY, float gyroZ, float adapt_factor, float offsets[3]){
  float gyroNormalized[3]; 
  gyroNormalized[0] = ((float) gyroX - offsets[0])/adapt_factor;
  gyroNormalized[1] = ((float) gyroY - offsets[1])/adapt_factor;
  gyroNormalized[2] = ((float) gyroZ - offsets[2])/adapt_factor;
  return gyroNormalized;
}

float* normalizeAcc(float accX, float accY, float accZ, float adapt_factor){
  float accNormalized[3]; 
  accNormalized[0] = accX/adapt_factor; 
  accNormalized[1] = accY/adapt_factor; 
  accNormalized[2] = accZ/adapt_factor;
  return accNormalized;  
}

float pitch_dataprocessing(float pitch, float roll){
  if(abs(roll)>=90){
    return (pitch + 90); 
  }else{
    return abs(pitch)-90;
  }
}

// evaluation of the Encoders (for balance control: left and right are equal) 
void evaluateEncoders(){
  encoder_measurement[1] = encoder_measurement[0]; // previousTime becomes currentTime
  encoder_measurement[0] = millis(); // set new currentTime

  // save the current number of counts (output of the encoder)
  encoder_measurement[2] = encoders.getCountsAndResetLeft();
  encoder_measurement[3] = encoders.getCountsAndResetRight();
  
  // save the change of the angle
  float curr_dPhiLeft = calculate_Phi(encoder_measurement[2], adapt_factor_encoder, true);
  float curr_dPhiRight = calculate_Phi(encoder_measurement[3], adapt_factor_encoder, true);

  // accumulate the actual phi
  encoder_measurement[4] += curr_dPhiLeft;
  encoder_measurement[5] += curr_dPhiRight;

  // calculate the speed of the wheel rotation (radian per second) 
  encoder_measurement[6] = calculate_dPhi(curr_dPhiLeft, encoder_measurement[0], encoder_measurement[1]);
  encoder_measurement[7] = calculate_dPhi(curr_dPhiRight, encoder_measurement[0], encoder_measurement[1]);

  // save as a part of the state space vector
  state[1] = encoder_measurement[4]; // or encoder_measurement[5] (equals for the balance regulator, because the torque is applied equally)
  state[3] = encoder_measurement[6]; // or encoder_measurement[7] (equals for the balance regulator, because the torque is applied equally)
}

// calulate phi under consideration of the gearbox and 12 time counts per revolution
float calculate_Phi(float counts, float adapt_factor_encoder, bool radian){
  if (radian==true){
    return (2*PI*counts)/adapt_factor_encoder;
  }else{
    return (360*counts)/adapt_factor_encoder;
  }
}
// calulate dphi under consideration that currentTime and previousTime is measured in milliseconds
float calculate_dPhi(float current_dPhi, float currentTime, float previousTime){
  return (current_dPhi)/(currentTime-previousTime);
}


// run the programm
void setup() {
  // initialize Madgwick filter including the IMU 
  initializeMadgwickFilter(); 
  initializeEncoder();
}

void loop() {
  // update theta and dtheta (saved as state[0] and state[2])
  evaluateIMU();
  // update phi and dphi (saved as state[1] and state[3])
  evaluateEncoders();
  
  // prints to check the results
  Serial.print("State:   theta:"); 
  Serial.print(state[0]); 
  Serial.print("   phi:"); 
  Serial.print(state[1]);
  Serial.print("   dtheta:"); 
  Serial.print(state[2]); 
  Serial.print("   dphi:"); 
  Serial.println(state[3]);
}

Maybe this:

float* normalizeGyro(float gyroX, float gyroY, float gyroZ, float adapt_factor, float offsets[3]){
  float gyroNormalized[3]; 
  gyroNormalized[0] = ((float) gyroX - offsets[0])/adapt_factor;
  gyroNormalized[1] = ((float) gyroY - offsets[1])/adapt_factor;
  gyroNormalized[2] = ((float) gyroZ - offsets[2])/adapt_factor;
  return gyroNormalized;
}

float* normalizeAcc(float accX, float accY, float accZ, float adapt_factor){
  float accNormalized[3]; 
  accNormalized[0] = accX/adapt_factor; 
  accNormalized[1] = accY/adapt_factor; 
  accNormalized[2] = accZ/adapt_factor;
  return accNormalized;  
}

You return pointers to float arrays. However those float arrays go out of scope at the moment that you return from those functions and hence no longer exists. If you make those arrays static or declare them global it might solve the problem.

Example making them static

float* normalizeAcc(float accX, float accY, float accZ, float adapt_factor){
  static float accNormalized[3]; 
  accNormalized[0] = accX/adapt_factor; 
  accNormalized[1] = accY/adapt_factor; 
  accNormalized[2] = accZ/adapt_factor;
  return accNormalized;  
}