AutomationShield API Style Guide
gergelytakacs opened this issue · 3 comments
Development
Put an empty .development
in the root folder in order to edit the examples and while working. Don't itegrate the file into the Gi repo!
Build status
Naming conventions
Please stick to the Arduino API style guide that can be found here when writing the shield API's.
Namely:
- Use
begin()
to initialize a library instance, usually with some settings. Use end() to stop it. - Use
read()
to read inputs, and write() to write to outputs, e.g.digitalRead()
,analogWrite()
, etc.
According to this the AutomationShield library will use:
-
begin()
to initialize the board. -
sensorRead()
to read the outputs "y", -
referenceRead()
to read reference "r" (if there is one) and -
actuatorWrite()
to send input ''u". -
calibration()
to perform optional calibration tasks before moving into the loop. -
bool _wasCalibrated
is a Boolean flag to read calibration status.
All boards should use these basic functions so that the library remains consistent. Determine which type of input/output will be likely most used by the user and use those in these functions. If you want to use more types of sensor readings (voltage, physical units etc.) then it is suggested to get creative based on this. Such as
sensorReadVoltage()
to read the outputs "y" in V,sensorReadCelsius()
to read the outputs "y" in degrees Celsius, etc.
Pins locations for the inputs and outputs should be stored as #define
tokens. Use all capitals and begin the token with the name of the device, continue with an underscore, variable symbol of the quantity and the word pin. So, for example the output pin of the Opto shield will be OPTO_YPIN
. Other examples of these tokens are:
#define OPTO_RPIN 0 // Potentiometer runner (Reference, r)
#define OPTO_YPIN 1 // LDR (Sensor)
#define OPTO_YAUX 2 // Auxiliary LDR
#define OPTO_UPIN 3 // LED (Actuator)
Header files for hardware API are named after the hardware, contain capitals. For example for the Magneto device the full name will be MagnetoShield, thus the API for the device will be initialized in the MagnetoShield.h
header not magnetoshield.h
. Similarly, the API will have the class, methods and other things in the MagnetoShield.cpp
source file.
The API for the particular hardware will use the common header AutomationShield.h
, which in turn will load other basic headers such as Sampling.h
and PID.h
. Thus, in the end, the user should just load the particular header in the example file, such as MagnetoShield.h
.
The header (*.h
) should just contain forward declarations of programming entities (e.g. void function()
) but not the actual functions, these go into the source (*.cpp
)
For naming functions use camel case, not snake case. Avoid underscores, if possible. Thus, you should use - myAwesomeFunction()
, instead of my_awesome_function()
. This is a thing of preference and consistence.
3.3 V compatibilty
- New shields (except some older ones) have been designed to be 3.3V compatible. Thus, you have to compensate for the AVR devices, which have a 5 V logic. This can be done by setting the reference of the ADC to the external pin, which in turn should be connected to the 3.3 V pin on hardware. To do this, you can call in the
begin()
method:analogReference(EXTERNAL)
. See more here.
MATLAB
If you want to include experimental measurements, use the following naming conventions for the mat
files:
C_
- from controlID_
- from identification
use any other information next and finish with the sampling rate. Thus a control experiment by PID sampled at 3000 microseconds will beC_PID_3000us.mat
.- Careful of file naming and letter case. Windows doesn't care, Linux does.
myFile.mat
will work on Windows if you refer to it with lower casemyfile.mat
, it won't on Linux (e.g. when testing CI). - In MATLAB examples instead of
clc
andclear
usestartScript
in the beginning, otherwise the continuous integration will fail, since theclear
command clears those variables needed for CI. (startScript
defines some exceptions forclear
). - The variable to signify a CI run is
CI_Test
. You can test its existence withexist()
to run CI specific parts of your code. - Algorithms that are expected to run for long times need to be curbed in CI. For example use an existence test for the variable
CI_Test
, which will limit solver iterations, optimization runtime, etc. (this is really specific to your application). - All scripts for examples must be self-contained. Script B should not need Script A to be run since a) the user does not know this and b) neither does CI. If you want to split examples, that is fine - but save the results to a
*.mat
file. For example modeling runs, but the model is saved and loaded in control examples. Don't just assume that a variable exists in the workspace.
Library file and folder structure
- Details on the include paths and library structure are found here.
- All *.h files must have an include guard macro, according to the FILENAME_H pattern.
Printing to serial
The primary point of any example is to output dynamic measurements to the plotter or for logging. Therefore any kind of diagnostic data is subject to conditional compilation, enabled by flags of the user. This functionality inside the header and cpp files is realized by the
AutomationShield.serialPrint("Message");
method. Please DO NOT USE the regular Serial.print()
function inside header and realization files.
Shield releases
- If there are more hardware versions of particular shield being developed, in software use token
#define SHIELDRELEASE n
to distinguish between different versions. Then
should represent release order of specific version.
ATSAMD Compatibility
- Assume 10 bit ADC resolution in order not to complicate things. ATSAMD has configurable resolution.
- Assume 3.3 V ADC reference, boards should be compatible with the R3 for non-AVR boards.
Serial
works without changes. The board does not reset upon accessing serial link, unlike in the case of AVR. Push reset button manually.analogRead
works without changes (but tolerates only 3.3 V!). Unless changed, the resolution is still 10bit. So by default 1023 level is 3.3 V.- If necessary use preprocessor directives to use different portions of code for different architectures. See more here, however, this seems not to work for the Arudino Zero, since its environment variable is
ARDUINO_ARCH_SAMD
and notARDUINO_ARCH_SAM
. So the following example works
#if defined(ARDUINO_ARCH_AVR)
// AVR-specific code
#elif defined(ARDUINO_ARCH_SAMD)
// SAM-specific code
#else
// generic, non-platform specific code
#endif
or to a more C standard
#ifdef ARDUINO_ARCH_AVR
// AVR-specific code
#elif ARDUINO_ARCH_SAMD
// SAM-specific code
#else
// generic, non-platform specific code
#endif
in this case of course you may employ #ifndef
etc. preprocessor directives.
You may consult this tutorial for a decent start on C preprocessor basics for the Arduino IDE.
Others
- Include a description as a comment at the beginning of files
- Comment line-by-line in great detail
- Do not initialize
Serial
and do not useSerial
within the source code, only in examples. - If your board uses a servo do not create a separate header (
.h
) and implementation (.cpp
) file. Since the IDE tries to build every implementation file in the library, this will cache the Servo library, which will then conflict with other AS devices. The solution is, in this case, just use a header file - it will not be included anywhere else but the examples. - Commit to Git more often then once a week.
- Only commit when you've actually tested that things work or at least they build correctly.
- Use commit messages that are sensible and descriptive enough. Do not comment messages like "New" or "Latest" or "My changes" etc.
Useful Arduino API environment variables
VARIANT_MCK
Clock speed in Hz for ATSAMD, does not seem to work for AVR
Just for fun
- Each header file shall from now on include the pre-processor token
TIME_WASTED_HERE
that shall express the time in hours wasted in finding bugs. :) (Thanks @AnnaVargova)
@rkoplinger - mainly to you at the moment. :)
@EvaVargova, @corleone68, @lukasvadovic2 you should read these...
MagnetoShield: If the linearization point is defined as y0 in Arduino IDE and Arduino DUE is used we get the following compilation error: 'float y0' redeclared as different kind of symbol'. Solution: use y_0 for linearization point.