This page contains or will contain stuff you need to know when coding for smoothie. TODO : make proper intro.
Config system and checksums
When to read configuration
When coding a smoothie module, you probably want the user to be able to configure it.
In smoothie configuration is stored in a configuration file, it is read at startup time ( when your module is being loaded ), and anytime the configuration is reloaded ( for example when the config file is changed )
If you want your config values to be read only upon module loading, but not when the config is reloaded, read your config values from the on_module_loaded callback of your module : example.
However this is not recommended. You probably want your configuration to be also read when the system says the configuration has been updated.
What you want to do then is add a on_config_reload handler to your module.
That handler is called for all modules, when Smoothie wants the configuration reloaded.
And of course you also want to call that callback from on_module_loaded
void MyModule::on_module_loaded() { // Settings this->on_config_reload(this); } // Get config void MyModule::on_config_reload(void* argument){ // read configuration values }
Checksums
Say we have a setting that looks like this in our config file :
maximum_death_star_hourly_power_consumption 100000000000
Now in our code if we want to read it we'll do something like that :
void MyModule::on_config_reload(void* argument){ // Config does not actually work like that, this is just an hypothetical example to explain, Don't do it like this. this->maximum_power = this->kernel->config->value("maximum_death_star_hourly_power_consumption")->as_number(); }
See what's happening here ? Because config must know what config option name it's looking for exactly, we must store the whole name in flash.
If we do that for all config options, it's going to end up using quite a lot of flash storage space.
The solution is Checksums : we don't store the actual string, but a checksum of that string we can compare to the checksums of the configuration lines in the config file and still know if they correspond to our string.
That way we don't actually store the string.
An online tool to compute those when you need can be found here.
The actual code for the checksum calculation is in src/libs/utils.cpp :
uint16_t get_checksum(string to_check){ // From: http://en.wikipedia.org/wiki/Fletcher%27s_checksum uint16_t sum1 = 0; uint16_t sum2 = 0; for( int index = 0; index < to_check.length(); ++index ){ sum1 = (sum1 + to_check[index]) % 255; sum2 = (sum2 + sum1) % 255; } return (sum2 << 8) | sum1; }
So, now our code will look more like :
#define maximum_death_star_hourly_power_consumption_checksum CHECKSUM("maximum_death_star_hourly_power_consumption") void MyModule::on_config_reload(void* argument){ // Config works more like this this->maximum_power = this->kernel->config->value(maximum_death_star_hourly_power_consumption_checksum)->as_number(); }
There you go, tons of flash space saved that we can waste on something else.
Also allows not to care about the length on the config options' names, making them much more readable.
Config value types
There are different types of config values you can retrieve from the config file.
Actually, they are all strings, and you can always access that, but there are convenience methods that allow you to convert them into other stuff.
ConfigValue object
This is not very useful. It's what you get when you do :
this->kernel->config->value(whatever_checksum);
You get an object of type ConfigValue. It stores the string ( if the config option was found in the file ), and has all kinds of convenience methods.
String
If your value is a string, you can access it using the as_string() method :
this->whatever_option = this->kernel->config->value(whatever_checksum)->as_string();
Note : if you provided a default string, and the option is not found in the config file, that default will be returned. See bellow.
Number
That's easy, just converts your string to a double :
double trouble = this->kernel->config->value(whatever_checksum)->as_number();
Note : if you provided a default number, and the option is not found in the config file, that default will be returned. See bellow.
Boolean
This is convenient for example for stuff like :
void Laser::on_module_loaded() { if( !this->kernel->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ){ return; }
This prevents the module from being loaded if the laser_module_enable config option is not present ( defaults to false ) or set to false explicitely.
"true" and "1" are valid true values, anything else is false.
Pin
Pin objects represent a pin configuration, they store the port number, the pin number, whether the pin is inverted, and other stuff in the future, see : Pin.h.
They are generated from the result of as_string() ( making default values as strings possible ).
this->step_pin = this->kernel->config->value(extruder_step_pin_checksum )->by_default("1.22" )->as_pin()->as_output();
Defaults
As you can see in some of the examples above, you can set a default value in case that option is not specified in the config file.
This is done using the by_default() method, that can take either a string or a double.
Doubles are used as defaults only by the as_number() and as_bool() methods.
this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(1)->as_number(); this->step_pin = this->kernel->config->value(extruder_step_pin_checksum )->by_default("1.22" )->as_pin()->as_output();
If you want smoothie to die if no config value is set in the config file, you can also use ->required(), but that's mean.