As Epyon mentions, you can use #ifdef's to have separate code for different processors. However, the Arduino layer makes some things harder. It is problematical to have different include files based on the processor:
Code:
#if PROCESSOR_TEENSY_3_1
#include <i2c_t3.h>
#elif PROCESSOR_ATTINY85
#include <TinyWireM.h>
#else
#include <Wire.h>
#endif
This is due to the Arduino layer trying to be user friendly, in that it collects all of the libraries mentioned in the build directory, to build everything. However, at least in the past, it did not understand
#ifdef's, and it would give an error when it couldn't find say i2c_t3.h on an AVR platform, even though due to the
#ifdef, the library wasn't used.
The way to do this is to break your libraries into the lower level machine independent parts, and then have machine dependent libraries that include the machine independent libraries. Within the machine independent library, you do not want to use
short,
int, or
long types, but instead use size types that are appropriate to your problem (i.e. signed types
int8_t,
int16_t,
int32_t, or unsigned types
uint8_t,
uint16_t, or
uint32_t). In particular,
int is 16-bits on AVR systems, and 32-bits on ARM systems.
In addition note that on AVR systems
double is the same size and representation as
float on the Teensy. Unless you need the excess precision on Teensy, you should just use
float everywhere. You should use the float version of the math libraries (i.e.
sinf instead of
sin,
cosf instead of
cos, etc.). It would also be a good habit to add a '
f' suffix to all floating point constants.
It is generally better to transmit data via text strings, rather than binary, since it avoids problems of size or endianess (most/all processors using Arduino libraries are little endian, but it is better if you can side step the issue of how bytes are laid out within a word by going through a text layer. In addition, writing binary to some serial setups can result in data loss due to low level details that are different (whether software flow control is used, whether AT commands are interpreted inline, whether tabs are converted to spaces, whether trailing spaces are preserved, what newline characters are sent, etc.).
When transmitting data, include a version number in the data. Bump this version number whenever the basic structure changes in any way, and don't handle the wrong version.
If possible in debugging messages, print the date the system was compiled and a version number. You can use the pre-defined macro
__DATE__ and
__TIME__ that are replaced with character strings giving the date and time that the last compilation was done.
One other thing, start practicing backups. Make backups at least every time you make an intermediate result that works. It is best if you use a source code control system, that would allow you to unwind changes, so you can get back to last Tuesday's version. I tend to use cvs, because I am an old dinosaur, and it was the defato system when I started. Github appears to be the system of choice today. It doesn't matter which system you use, just that you use a system.
Edit:
Be wary of tests that say if it is not a Teensy it must be an AVR. Have an error in case you get a new processor:
Code:
#if PROCESSOR_TEENSY_3_1
// ...
#elif PROCESSOR_AVR
// ...
#else
#error "Unknown processor"
#endif
Also note, the tests for PROCESSOR_TEENSY_3_1 above will fail on a Teensy 3.5 or Teensy 3.6 which has different defines. The Teensy 3.5 defines
__MK64FX512__, and the Teensy 3.6 defined
__MK66FX1M0__.