Getting info about field (and desk) devices

Due to having quite a few Particle devices and many different applications under development, I often lose track of what application software is running on each device. I also like to (easily) know what version of system software is running on field devices. I’m usually interested in the application name, the system firmware, the SSID and the RSSI.

In the past, I’ve used several instances of Particle.variable() to let me be aware of these, but I’m pretty sure I’m wasting bandwidth and using up Particle variables that may be needed in the future. Besides I don’t need to know this ALL the time…just when I need to know!

So, I created a simple function that I can call that can provide me this information. This might make for a handy library so I can easily keep it updated across multiple applications. I guess that’s another day and will be based upon feedback I get from this topic. I’m assuming there’s a much “smarter” way of satisfying this need and someone will enlighten me.

So…here’s what I’ve done:

I create a global variable declared at the top of the program, so it’s readily visible for modification when I create or update an applications:

char gAppName[30] = "myProjectV1.ino";

In setup, I publish the function I created:

Particle.function("deviceInfo",deviceInfo);

…and here’s the function:

int deviceInfo(String command) {
  //Publishes info about the device
  String fw = System.version();
  char SystemFW[20];
  fw.toCharArray(SystemFW,15);
  char PublishString[200];
  sprintf(PublishString,"Application: %s, System firmware: %s, SSID: %s, RSSI: %i",gAppName,SystemFW,WiFi.SSID(),WiFi.RSSI());
  Particle.publish("deviceInfo",PublishString,60,PRIVATE);
  return 1;
}

I’ll likely create something similar for Electrons (sans SSID and WiFi RSSI, but with…APN?)

Constructive feedback is welcome, as I hope to put this in all my applications moving forward.

Instead of gAppName you could try out __FILE__ which is populated by the compiler.
In addition you can use __DATE__ and __TIME__ to get the build date/time of that version too.

And you could just use one Particle.variable() to hold the same string (which could even be up to 622 char), since you have more free variables than functions.
And Particle.variable()s don’t consume any bandwidth unless their contents is requested.

@ScruffR, See…that’s a “smarter” way to do it. By the way, it is __FILE__ (those are double underscores on either end).

The returned filename is fully qualified, so I may do a character search on it to get just the last portion. The date and time are great, too!

Thanks for the constructive feedback! I’ll move forward with your suggestions.

One other thing was this

  String fw = System.version();
  char SystemFW[20];
  fw.toCharArray(SystemFW,15);
  sprintf(buf, "%s", SystemFW);

can be written more concise

  sprintf(buf, "%s", (const char*)System.version());
  // or
  sprintf(buf, "%s", System.version().c_str());

Yup, double underscores.
I’m not sure but __FILENAME__ might be just the file. I’ll have to check GCC compiler variables.
Doesn’t seem to be in the standard.

I wanted to try these out, but the __DATE__and__TIME__ line is not compiling for me (was not declared in this scope error). That one works for you?

Yes, they did. I’ve simplified mine to:

char gDeviceInfo[200];
char buf[15];

  //Publishes info about the device
  sprintf(buf, "%s", (const char*)System.version());
  sprintf(gDeviceInfo,"Application: %s, Date: %s, Time: %s, System firmware: %s, SSID: %s, RSSI: %i",strstr(__FILE__,"//"),__DATE__,__TIME__,buf,WiFi.SSID(),WiFi.RSSI());
  Particle.variable("deviceInfo",gDeviceInfo);
  //

Gettin’ closer to being elegant!

Oh duh, I was reading that as one date and time string, not as two separate things
__DATE__
__TIME__

The lowercase “and” should have been a clue I guess.

How is that working? I don't have any double slash in the raw __FILE__ output.

Hmmmm…well…I’m using Particle Dev. Perhaps that’s at the root of the difference. With out that string function, I’m getting a verrrrrrry long fully-qualified app name.

Yep, that was the difference. In Dev, you get, “//src/appName.cpp” after the long 60 character string, but Build doesn’t have that “//src” in there. Good to know that.

so…what we need to find is the string function that allows us to find the last “/” in the filename and that should do it for both avenues. I thought I had it…but not yet.

Here it is,

strrchr(__FILE__,'/')

This finds the last occurrence of a character (including the terminal NULL character if you want to get the end of a string).

I’ve been using this for a while and it seems safe…

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

void setup()
{
  Particle.variable("FileName",__FILENAME__);
}
void loop()
{
  
}

1 Like

@ctmorrison, that buf was just an example :wink:
The maximum conciseness would be with this

  snprintf(gDeviceInfo, sizeof(gDeviceInfo)
          ,"Application: %s, Date: %s, Time: %s, System firmware: %s, SSID: %s, RSSI: %i"
          ,strstr(__FILE__,"//")
          ,__DATE__
          ,__TIME__
          ,(const char*)System.version()  // cast required for String
          ,(const char*)WiFi.SSID()       // cast not required but always safe when in doubt if String or char*
          ,WiFi.RSSI()
          );

(I also added the safety check to obey the buffer boundaries)

The above example also illustrates why I prefer (const char*)stringObject over the widely used alternative stringObject.c_str() since the former always works while the other would throw an error on a non-String.

Since you are looking for the last occurance of a single character you can use strrchr()

(Oops, didn’t see all these posts in between pointing out strrchr() already :blush: - got a rather slow net here)

So, wrapping it all up here with @BulldogLowell’s and @Ric’s tips and @ScruffR’s framework , I think we have:

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

char gDeviceInfo[200];

  snprintf(gDeviceInfo, sizeof(gDeviceInfo)
          ,"Application: %s, Date: %s, Time: %s, System firmware: %s, SSID: %s, RSSI: %i"
          ,__FILENAME__
          ,__DATE__
          ,__TIME__
          ,(const char*)System.version()  // cast required for String
          ,(const char*)WiFi.SSID()       // cast not required but always safe when in doubt if String or char*
          ,WiFi.RSSI()
          );

Particle.variable("deviceInfo",gDeviceInfo);

Thanks! I’ll be using this in all of my source code and a slightly different version for Electrons.

4 Likes

@ctmorrison, this is not working for me. Nothing appears after TIME.

@peekay123 Are you getting the short file name (just “projectName.cpp”), or are you getting the whole long version? His code works for me, but if I make it get the whole output of __FILE__, then it stops after the time value due to running out of room in the char array.

@Ric, I get this:
Application: PegasusSPI.cpp, Date: Feb 18 2017, Time: 02:02:22,

@peekay123 hmm… that’s weird. I just re-copied and pasted his code, to make sure mine was identical to his, and it worked fine (Photon, checked by Serial print and variable GET through CLI).

Application: epromlengthproblem.cpp, Date: Feb 18 2017, Time: 02:21:09, System firmware: 0.6.0, SSID: Ric's WiFi, RSSI: -50

@ric, how did you place your code?