Flashing multiple devices at once, but knowing which device at compile time (like deviceID() but at compile time)

Goal

I would like to be able to identify which device I am compiling for. I do not want to waste time with run-time checks.

Ideas I’ve considered

  • Using a #include to include a header file that #define 's the device.
  • Using separate main.ino files for each device.

Status Quo

Separate h/cpp files per device

// #define ENABLE_COMRAD
#define ENABLE_MATT
#define ENABLE_PARKER

#ifdef ENABLE_COMRAD
#include "comrad.h"
#endif

#ifdef ENABLE_MATT
#include "matt.h"
#endif

#ifdef ENABLE_PARKER
#include "parker.h"
#endif

Runtime #defines

#define IS_COMRAD ('5' == System.deviceID().charAt(0))
#define IS_MATT ('3' == System.deviceID().charAt(0))
#define IS_PARKER ('4' == System.deviceID().charAt(0))

Function Routing using systemID() and Enum

enum Device {COMRAD=0, MATT=1, PARKER=2, NOT_DEFINED, UNKNOWN};
enum Device device;
int id_device(); // device = static_cast<enum Device>(id_device());

int id_device(){
    char first_unique = System.deviceID().charAt(0);
    switch(first_unique){
        case '3':
            return MATT;
        case '4':
            return PARKER;
        case '5':
            return COMRAD;
        default:
            return UNKNOWN;
    }
}

void device_setup(){
    // DEVICE_SPECIFIC_SETUP {
    device = static_cast<enum Device>(id_device());
    if(device == UNKNOWN){
        error(1005);
    #ifdef ENABLE_PARKER
    } else if(device == PARKER){
        parker_setup();
    #endif // ENABLE_PARKER
    
    #ifdef ENABLE_MATT
    } else if(device == MATT){
        matt_setup();
    #endif // ENABLE_MATT
    
    #ifdef ENABLE_COMRAD
    } else if(device == COMRAD){
        comrad_setup();
    #endif // ENABLE_COMRAD
    
    } else {
        error(1006);
    }
    // } DEVICE_SPECIFIC_SETUP
}

void device_loop(){
     // DEVICE_SPECIFIC_LOOP {
    if(device == UNKNOWN){
        error(1005);
    #ifdef ENABLE_PARKER
    } else if(device == PARKER){
        parker_loop();
    #endif // ENABLE_PARKER
    
    #ifdef ENABLE_MATT
    } else if(device == MATT){
        matt_loop();
    #endif // ENABLE_MATT
    
    #ifdef ENABLE_COMRAD
    } else if(device == COMRAD){
        comrad_loop();
    #endif // ENABLE_COMRAD
    
    } else {
        error(1006);
        delay(2000);
    }
    // } DEVICE_SPECIFIC_LOOP
}

@ngenetzky, it’s not clear what your question is exactly. Can you clarify how we could help? :grinning:

I would like to use a shared code base for my Particle projects. For some projects, the devices will have to cooperate. I would like to be able to distinguish which device I am compiling for when I create my bash command to flash the device (“particle flash $files”, where files is a list of files included in the project.).

Previously I was performing checks at runtime, but now that I have 5 devices, and one of them being an Electron; I would like to avoid run-time checks.

The workaround I thought of after posting this was moving all of my main.ino (which performs a lot of setup) to other files and then using a separate main.ino (such as comrad_main.ino) for each of the devices. Can you think of any better way to do this?

@ngenetzky, in your code, use compile flags to include/exclude chucks of code based on device type. The different PLATFORM_ID devices include:

#define PLATFORM_SPARK_CORE_HD              2
#define PLATFORM_PHOTON_PRODUCTION          6
#define PLATFORM_P1                         8
#define PLATFORM_ELECTRON_PRODUCTION        10

So, in you code, you would use these like this:

#if (PLATFORM_ID == PLATFORM_PHOTON_PRODUCTION)
  // code for Photon
#elif (PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION)
  // ccode for Electron
#else
  #error "An invalid device was specified!"
#endif

You add these wherever the code differs by platform. :grinning:

Well I appreciate that information. I was using very unreadable #defines for that functionality.

#if defined (STM32F10X_MD)
// Core hardware specific code.
#endif
#if defined (STM32F2XX)
// Photon hardware specific code.
// Particle Core does not support any code utilizing Software Timers.
#endif

However I think there is a miscommunication. I am trying to determine code choose code that will run on Matt vs code that will run on Parker even though they are both Photons.

I can obtain the desired behavior using run-time resources, as seen in device_loop() above. When I wrote the above #ifdef ENABLE_PARKER I was hoping I could find some way to #define ENABLE_PARKER only when I was flashing to Parker. Currently the only ways I can think to do it are very ugly.

Since you seem to use CLI for building and it lacks a way to “inject” a compiler switch into the makefile you’d need a workaround.
But since you indicated you might use a bash script there are tons of possible solutions.
The prerequisite is wrap your different code blocks in #ifdef ... #endif blocks (as you do already) and include a dedicated header file like #include "codeSelector.h" which only contains the desired #define statements in all your other source files.
In order to “inject” the desired compile switches, just copy/rename for instance codeSelectorMatt.h to codeSelector.h and you should be good - or just create it via bash.

If you need to select/deselect a bunch of files you could use subdirectories for each set of files and just do the same with a particle.include file selected from a bunch of particle.include.XXX files (or inversely using particle.ignore)

The breaking point is that CLI (or Dev or Build) don’t have a way to provide compile directives any other way than via a file and hence you will IMHO need an “ugly” workaround via such a file.

I am having a similar issue. I have 14 Photons. They all are running new sensors that we are developing. Each board has a set of constants that defines the calibration data for that sensor. The code on the Photon are identical for each board other than three constants that are unique for each board. To speed up flashing these (especially OTA), I want to have a directive that loads the correct constants. So I’ll have three constants that will be compiled and varies based on which device id I use. Its cumbersome, but its better than copying and pasting the correct constants between each flash or maintaining the same file for 14 (and growing) Photons. Anyone have a better idea? (Also, these guys have no access to the internet, unfortunately).

@elec3647, why not use a Particle.function() to set the constants which you can store in EEPROM. I would suggest making two EEPROM copies, each with their own CRC which you can verify against. If one is bad, the other is most likely ok. A variation on this is you have a set of factory default constants in code. You send “variance factors” via the Particle functions. If you get an EEPROM failure, the factory constants will still be there. You could send a “lock” command to prevent changes until an unlock command is received. You get the idea. :wink:

BTW, this could allow in-field tweaks as sensors age or conditions change.

2 Likes

I like what I’m hearing! So you’re saying that I should write a particle function which I can call via the internet to load the calibration data for each particle individually?

I like this idea! I could write a little Python program that pushes the correct calibration settings to the Particle and stores it in EEPROM?

On to reading up more documentation!

@elec3647, several of my applications use configuration parameters that I can configure via a Particle.function() and store in EEPROM. I use a single function with a command parser (*)so I can easily add more commands later. For example, I set the baudrate of Serial1 with “BAUD 57600” for example. I can even string several commands in a row. I store all parameters in a compact structure which I can easily store in EEPROM. You can even leave empty “slots” in the structure for future use!

(*) I use ArduinoSerialCommand without the serial part!

1 Like