2

I am tracking a bug in the Marlin source code.

Background

Just for those who is not familiar with Reprap 3D printer and G-code: Marlin is firmware that controls a RepRap 3D printer. It receives G-code from a host computer (or read from SD card). Here the important thing is that if you send the "M114" command, it would report information about axis positions. Here, I extended this command to make it print some internal variable as well.

The Problem

In Marlin.ino file there is a line that says:

volatile int extrudemultiply=100; //100->1 200->2

However my RepRap keeps extruding (and worse, ejecting back) a huge amount of filament. So I added some debug output for M114 command to output this value. It turns out this value is not the default value, 100, but something like 12374.

I thought somewhere inside the setup() function this value might be changed. So I defined another variable,

volatile int orig_extrudemultiply = 100;

And then at the first line of setup() function, I added the following line.

orig_extrudemultiply = extrudemultiply;

Finally, I output this value for the M114 command. Still, it is not 100. So it looks like the default value is not effective.

The Question

Although I have experience in C programming, I am new to Arduino development. How is a global variable being initialized in Arduino? In my case, what is the proper way to initialize this value?

Versioning

I originally posted the official Marlin link for the reference. However, the version I actually use is this fork from the RepRap Pro team. I was just following the Melzi entry of the RepRap wiki, because my board is Melzi. I am not sure whether the latest version of Marlin works for my board.

Further details

As the commenter said, the first thing I thought of was memory corruption. So I wanted to narrow the problem down. Since that value is configurable through M302 SXXX command, I tested this command, and it works without difficulty. This means there is nothing in the loop function or the ISRs can affect this value when it is running stably. So I consider the problem must be in the setup function or earlier.

Further tests shows that setting this value at the end of setup or even at the beginning of setup still works fine. This means the setup function may not be the problematic one. The test in "The Problem" session was the last step, which, in my opinion, means the course of the problem must being run before entering setup.

UPDATE

Further examination shows that although it looks like it works when I set the default value in the first line of setup(), but that it is just luckily being 100. So once I add more padding variables it stops working until I move the default setting after the following line in setup():

EEPROM_RetrieveSettings(); // Loads data from EEPROM if available

However, since I greped for extrudemultiply before any test, and I didn't figure out this function, this function should not change this value. I guess there is some data overflow inside this function, so the value was changed expectationally.

I got to figure out what is wrong in this function.

3
  • 4
    Problems like this are almost always the variable being corrupted by other code rather than not being initialized properly. Apr 29, 2014 at 13:25
  • 1
    Initial values of global variables like that are typically setup by toolchain provided initializers, and it actually is possible for toolchain usage mistakes to leave them out, but you'd probably notice other problems as well. Ignacio's corruption hunch is a good one - it can be interesting to try declaring a dummy variable before or after the subject one, and see if the nature of the problem changes by thus steering the mistaken writes differently - though that should not be mistaken for a fix! Apr 29, 2014 at 15:36
  • 1
    It seems you use a pretty old version of Marlin, as this declaration of extrudemultiply has changed Dec 3rd 2012! I suggest you update to a more recent version.
    – jfpoilpret
    Apr 29, 2014 at 19:00

2 Answers 2

2

This variable is declared volatile which is generally meaning that it can be modified by an Interrupt Service Routine (ISR).

So it is perfectly possible that this ISR gets called before your setup() function gets called and then it may modify this variable.

In C and C++, variables defined as type variable = xxx; are guaranteed to be initialized before any of your code starts to run.

So this means you must look for the root of the problem somewhere else.

Don't forget that it is perfectly possible that some code gets run before setup() is called.

You may try to search for the code that runs before setup(); unfortunately it can be hard to find out this code:

Indeed, in C++, there are a lot of possibilities for code to execute even before the main() function gets called; for instance, declaring an instance of a class as a global variable, will call the class constructor before main().

So you would have to find out all class instances declared as global variables in all source files, and then check the class constructor that gets called for each instance.

Another way could be to check the assembly produced for your program and find all calls occurring be fore main(), but it can be harder if you can't read assembly.

5
  • Thanks your advise. I have updated the question to include some of my observations and opinions. Can you please have a look. Most importantly, where can I find the code that get run before setup() being called? Apr 30, 2014 at 10:16
  • I've updated my answer to include hints on finding out that code. Even though you have already found the real source of the issue (I'm glad you found it!), I think it was useful to edit my answer anyway.
    – jfpoilpret
    Apr 30, 2014 at 18:49
  • I accept this answer because although my own answer solves the problem, this answer actual answers the question. The new knowledge I learn from this answer is that Arduino IDE just another C IDE. Except the fact it is to be compiled as Arduino machine code there is not much different with other C/C++ environment. So my common sense about C/C++ is still good. May 1, 2014 at 11:03
  • Thanks for this, @Earth-Engine, I'm glad my answer could bring you useful knowledge. However, considering people having a similar issue as yours in the future, I would suggest you accept your own answer as it is the right answer to your question. Cheers.
    – jfpoilpret
    May 3, 2014 at 5:45
  • My theory is that here is the place to ask about Arduino, not ask about software/firmware bug. So people get to this page because they want to know how global variables being set in Arduino, not because they have problem with Marlin. If people wanted to find solution about bugs in Marlin, they should look at the Reprap pro github page for issues (I have created a pull request for this so people will find it). May 3, 2014 at 12:17
2

The problem of this version of Marlin can be shown in the following piece of code (which is inside EEPROM_RetrieveSettings function):

  for (short i=0;i<4;i++) 
  {
    axis_steps_per_unit[i]=tmp1[i];  
    max_feedrate[i]=tmp2[i];
    max_acceleration_units_per_sq_second[i]=tmp3[i];
    max_length[i]=tmp4[i];
  }

Here max_length is an array of 3 elements, not 4 (the X,Y and Z axes have a max length to avoid hitting the edge, but the extrude axis does not have such a limit - you should be able to extrude as many filament as you can supply), so the last statement in the loop overrides other variable(s).

The solution is

  for (short i=0;i<4;i++) 
  {
    axis_steps_per_unit[i]=tmp1[i];  
    max_feedrate[i]=tmp2[i];
    max_acceleration_units_per_sq_second[i]=tmp3[i];
    if(i<3)
        max_length[i]=tmp4[i];
  }

Or

  for (short i=0;i<4;i++) 
  {
    axis_steps_per_unit[i]=tmp1[i];  
    max_feedrate[i]=tmp2[i];
    max_acceleration_units_per_sq_second[i]=tmp3[i];
    if(i < sizeof(tmp4)/sizeof(tmp4[0]))
        max_length[i]=tmp4[i];
  }

Greate thanks to the answer writer and commenter. Without your help I couldn't follow the right track.

1
  • 1
    Buffer Overrun strikes again. Make sure you don't need that fourth element though. Apr 30, 2014 at 18:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.