Philosophy On Understanding Your Code

I consider myself to be an advanced beginner in terms of Arduino coding. My question is this. Is it necessary in your opinion to fully understand every line of code you have in your sketch, even if your sketch is working perfectly? Personally, I am not comfortable having code in my sketch that I do not understand, however, I have a project that is fully functional and works great but there is code in it I picked up from other coders that is very complex that I do not understand at all. It is a self balancing robot that uses a Kalman Filter and other complex algorithms that may as well be written in Swahili (with all due respect). Another reason to fully understand my code is, this project I am referring to was designed for the Arduino UNO (or any Atmel328 controller) and I want to move it to an ESP32. NO CAN DO because parts of the code will not compile when the ESP32 is the selected processor (which I understand because different libraries are required for different processors). I am just curious if it was considered a bad practice to ever use code that you do not fully understand. Thanks in advance for the feedback!

My philosophy is If the code does what you want, for a wide range of test cases and is not critical, then it may not be important to understand the details.

If the code does not do what you want, then understanding what it does and how is the required first step in fixing it. In that case, I learn something in the process.

There are plenty of self balancing robot projects, so for a starting point, pick one that is most similar to your setup.

No, it is not necessary if the "code" works as you require. But if it does not, better be prepared to understand what it is not doing. A good percentage of threads on the forum are from people who do not take the time to learn what their found code is really doing, but here they come hoping that someone will spend their time learning what the code does and can offer fixes. So much better to do it yourself!

Thanks for the feedback jremington. The other thing is, I do this as a hobby. If I were a design engineer that depended on my coding ability to feed my family or where the health and safety of others came into play, I would think that understanding every line would be considered a prerequisite. I was just curious if others felt this way as well.

In the world of programming are things called "libraries" which provide "abstraction layers" that are large chunks of detailed code that allow you to write in higher-level code. Like any subject, if you want to be good, know the history and foundation layers... but you do not need to know the fuel mixture to fly to the moon.

Hang on to that motivating atitude.

You should know how the code functions from a programming standpoint, like how the language works.

You should not expect to understand and prolly don't need to things like PID loops and Kalman filters… ppl get degrees, advanced sometimes, in that, and to expect yourself to know what is going on in what the code is accomplishing is unrealistic.

As would be delving to the bottom of every library you include.

So. Learn the language, read any code and if it is language orientated thing, use it to ask questions or focus your learning time, like what is a switch statement, or how do arrays work… and just let your eyes glaze when what's being expressed is without you knowledge, e.g. how Fast Fourier Transforms work.

a7

Great advice everyone. Thanks for the feedback!

No. But, you will find that 'working perfectly' is an easy condition to falsely convince yourself is the case. Until you know what it is doing, you don't know what it isn't doing.

For example, we see here many, many cases where someone indexes beyond the end of an array, because they get the loop end conditions wrong. "But it works!" is the exclamation, when they are confronted.

Well, yes, it does do what you wanted to the array contents, but that extra memory location you trounced by going beyond end-of-array may be being used for something elsewhere in your code, and it's random change of value may not come to light for many, many days - but it will eventually become a bug. A big, hairy one, because "it all works" is the firm belief.

So, when you develop that "working perfectly" belief, particularly as a novice, that's usually when you need to look more closely; it's often a mistaken belief.

you don't need to understand the code until you need to debug it.

I will cheerfully admit to looking closer at my own code and realizing that although it works, it isn't doing what I thought…

And sometimes looking really close, or slowing things down artificially you can see that perfect isn't perfect.

void loop() {
  if (digitalRead(someInputPin) == LOW) {
    digitalWrite(someLEDPin, LOW);
  }
  else {
    digitalWrite(someLEDPin, HIGH);
  }
}

may seem like it is perfectly turning on and off the LED, but looked at with sharper eyes, like with an oscilloscope or logic analyser you find that it turns on and off dozens of time when you press the button, ending up on, and dozens more when you release the button, ending up off.

a7

Of course - it's just converting the bouncing, noisy switch contact bounce into a nice clean random pulse width series of highs and lows. Just the first step towards state change detection...

Are you sure that it is the code per se that you don't understand, or are you not understanding the mathematics (and physics) that has been implemented by the code? Can you show an example of a code snippet that you "don't understand"?

Good point. Here is some of the code:

/////////////////////////////tilt angle calculation///////////////////////
void angle_calculate(int16_t ax,int16_t ay,int16_t az,int16_t gx,int16_t gy,int16_t gz,float dt,float Q_angle,float Q_gyro,float R_angle,float C_0,float K1)
{
  float Angle = -atan2(ay , az) * (180/ PI);           //Radial rotation angle calculation formula; the negative sign is direction processing.
  Gyro_x = -gx / 131;              //The X-axis angular velocity calculated by the gyroscope ; the negative sign is the direction processing.
  Kalman_Filter(Angle, Gyro_x);            // Kalman Filter
  // Z-axis angular velocity
  Gyro_z = -gz / 131;                      //speed of Z-axis 
  //accelz = az / 16.4;
 
  float angleAx = -atan2(ax, az) * (180 / PI); //calculate the included angle of X-axis  
  Gyro_y = -gy / 131.00; //angle speed of Y-axis 
  Yiorderfilter(angleAx, Gyro_y); //first-order filtering
}
////////////////////////////////////////////////////////////////
 
///////////////////////////////KalmanFilter/////////////////////
void Kalman_Filter(double angle_m, double gyro_m)
{
  angle += (gyro_m - q_bias) * dt;          //prior estimate
  angle_err = angle_m - angle;
   
  Pdot[0] = Q_angle - P[0][1] - P[1][0];    //The differential of the covariance of the prior estimate error 
  Pdot[1] = - P[1][1];
  Pdot[2] = - P[1][1];
  Pdot[3] = Q_gyro;
   
  P[0][0] += Pdot[0] * dt;    //The integral of the covariance differential of the prior estimate error
  P[0][1] += Pdot[1] * dt;
  P[1][0] += Pdot[2] * dt;
  P[1][1] += Pdot[3] * dt;
   
  //Intermediate variables in matrix multiplication
  PCt_0 = C_0 * P[0][0];
  PCt_1 = C_0 * P[1][0];
  //denominator
  E = R_angle + C_0 * PCt_0;
  //gain value
  K_0 = PCt_0 / E;
  K_1 = PCt_1 / E;
   
  t_0 = PCt_0;  //Intermediate variables in matrix multiplication 
  t_1 = C_0 * P[0][1];
   
  P[0][0] -= K_0 * t_0;    //the covariance of the prior estimate error 
  P[0][1] -= K_0 * t_1;
  P[1][0] -= K_1 * t_0;
  P[1][1] -= K_1 * t_1;
   
  q_bias += K_1 * angle_err;    //posterior estimate
  angle_speed = gyro_m - q_bias;   //The differential of the output value; get the optimal angular velocity
  angle += K_0 * angle_err; ////posterior estimate; get the optimal angular velocity
}
 
/////////////////////first-order filtering/////////////////
void Yiorderfilter(float angle_m, float gyro_m)
{
  angleY_one = K1 * angle_m + (1 - K1) * (angleY_one + gyro_m * dt);
}
 
//////////////////angle PD////////////////////
void PD()
{
  PD_pwm = kp * (angle + angle0) + kd * angle_speed; //PD angle loop control 
}
 
//////////////////speed PI////////////////////
void speedpiout()
{
  float speeds = (pulseleft + pulseright) * 1.0;      //pulse value of speed  
  pulseright = pulseleft = 0;      //Clear 
  speeds_filterold *= 0.7;         //first-order complementary filtering
  speeds_filter = speeds_filterold + speeds * 0.3;
  speeds_filterold = speeds_filter;
  positions += speeds_filter;
  positions += front;             //Forward control fusion
  positions += back;              //backward control fusion
  positions = constrain(positions, -3550,3550);    //Anti-integral saturation
  PI_pwm = ki_speed * (setp0 - positions) + kp_speed * (setp0 - speeds_filter);      //speed loop controlling PI
}
//////////////////speed PI////////////////////

How far down do you want to go?

  • Understand the code in the libraries you are using?
  • Understand what machine code the compiler generates when you add 2 variables?
  • Understand the operation of the registers in the processor?
  • Understand how thousands of transistors are interconnected to make a processor?
  • Understand how transistors work?

If you really want to go deep start with the Big Bang.

So to examine a single line of code:

Is the inscrutable part more that you are unfamiliar with the atan2 function, or more that you don't understand the mathematics of why (180°/π)·Tan–1(ay / ax) returns the angle of a vector with coordinates (ax,ay), or something else?

All of the above. I going with the idea of, it all appears to work and work quite well and leave the "how it all works" to the Mathematics professors.

I don't think it would be unreasonable to expect the person responsible for the code (you) to at least understand that atan2 is a function that returns a computed angle based on two inputs; beyond that, I think it would be fine (for the most part) to accept the form and function of this mathematical expression on faith.

but be prepared to find out and learn if needed in a pinch ! :hugs:

There are software systems made from millions of lines of code. No one person understands it all.

Taking code that someone else wrote and programming an Arduino with it, not knowing what it does. Is this actually an enjoyable hobby for you?