Simple firmware to retrieve cellular info into serial monitor

Hi all

I am new in particle. Trying to check in serial monitor all cellular info using AT+UCELLINFO but my code (below) is posting only RESP_ERROR=-3 Any ideas why and how to fix it?

#pragma PARTICLE_NO_PREPROCESSOR
#include "application.h"

void RequestTowerCellID();

void setup() {
    Serial.begin(9600);
    Cellular.on();

}


/**
 * Asks the Cellular module for info about the tower it's connected to.
 **/
void RequestTowerCellID() {
    Serial.println("requesting cell tower info...");
    int ret = Cellular.command(15000, "AT+UCELLINFO\r\n");
    Serial.println("Current cell info" + String(ret));
    return;
}

void loop(){
    RequestTowerCellID();
    delay(5000);
}

This is not actually answering your question, but to clarify things first.
If you were not running SYSTEM_MODE(AUTOMATIC) (which is default) calling Cellular.on() allone would not actually connected your device to a cell tower.
To really connect you’d at least need to call Cellular.connect() too.

However, since you are not selecting an alternative SYSTEM_MODE() nor using SYSTEM_THREAD(ENABLED) your code won’t run unless a cloud connection is already established (rendering the Cellular.on() call superfluous).

Now back to topic:
In order to get some more info about the reason for your response error, you could enable some system logging.

#pragma PARTICLE_NO_PREPROCESSOR
// #include "application.h" <-- this is deprecated, rather use
#include "Particle.h"

SerialLogHandler fullLog(LOG_LEVEL_ALL);

This should provide some extra output via USB Serial.

But since you are expecting some data back from that call, you’d probably need to provide a callback function that actually catches and processes that data. The return value of the command is always only a single integer that reflects how successful the call was - it won’t ever contain the requested data (unless the success info is all you are after :wink: ).
And finally the actually request would be "AT+UCELLINFO?\r\n" or if you want to set the info mode "AT+UCELLINFO=<mode>\r\n" (<mode>: periodict reporting 0…disabled/1…enabled).

Everything ScruffR said is true. Also, AT+UCELLINFO only works on the SARA-U. It does not work on the 2G Electron (SARA-G350), the E Series LTE or Boron LTE (SARA-R410M-02-B).

1 Like

Great point. What is an AT command for cell info for SARA-G350?

AT+CGED=3 will work on the SARA-U and SARA-G, but not the SARA-R.

Also the CellularHelper library may be helpful. It can return the decoded AT+CGED information. Even if you decide not to use the actual library it shows how to decode the response:

2 Likes

This is very helpful library! I tried your example 4 (creg). My modem is 2G SARA-G350. Particle firmware is 0.8.0 The following code was compiled and flashed alright but in serial monitor (started ok) there is nothing. Any ideas?

#include <CellularHelper.h>
#include "Particle.h"


SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

SerialLogHandler logHandler;

bool testRun = false;

void setup() {
	Serial.begin(9600);
}

void loop() {
	if (!testRun) {
		testRun = true;

		delay(5000);

		Log.info("isLTE=%d", CellularHelper.isLTE());

		CellularHelperCREGResponse resp;
		CellularHelper.getCREG(resp);
		Log.info(resp.toString().c_str());
	}

}

Hi Rickkas7

I tried this code and it works but I am trying to understand why it works and how to publish what I see in serial monitor in the cloud (to be able to retrieve the position remotely)

#pragma PARTICLE_NO_PREPROCESSOR
#include "Particle.h"

SerialLogHandler fullLog(LOG_LEVEL_ALL);


SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

void RequestTowerCellID();

void setup() {
	Serial.begin(9600);
}

void RequestTowerCellID() {
    Serial.println("requesting cell tower info...");
    int ret = Cellular.command(15000, "AT+CGED=3\r\n");
    Serial.println("Current cell info" + String(ret));
    return;
}

void loop(){
    RequestTowerCellID();
    delay(5000);
}

image

The output you are seeing is generated by the internal logging features of the device OS.
While you could use an alternative logger to catch the same output, the correct way to do it is - as said further up - by implementing a callback function.
When you don’t activate logging, that output will also not be produced.

See the docs of Cellular.command()

1 Like

Hi ScruffR

My first attempt to write a code using callback in Cellular.command(). The code is just to see the AT command response in serial monitor. The code has not been tested yet but I’d like to spot any obvious mistakes. What do you think having skimmed over it?

#pragma PARTICLE_NO_PREPROCESSOR
#include "Particle.h"

#define ATTEMPT_FREQUENCY 20 * 1000

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

unsigned int lastLocationRequest = 0;

void RequestTowerCellID();
int cb(int type, const char* buf, int len, char* cellular_data);

void setup() {
    char cellular_data[200];
	Serial.begin(9600);
}


void loop(){
    
        unsigned int now = millis();

        if (((now - lastLocationRequest) > ATTEMPT_FREQUENCY) || (lastLocationRequest == 0)) {
            lastLocationRequest = now;
            RequestTowerCellID();
        }
        else {
            // any updates?
            Cellular.command(cb, &cellular_data, 15000, "");
        }
        
}

void RequestTowerCellID() {
    Serial.println("requesting cell tower info...");
    int ret = Cellular.command(cb, 15000, "AT+CGED=3\r\n");
    Serial.println("Current cell info" + String(ret));
    return;
}

int cb(int type, const char* buf, int len, char* cellular_data)
{
    if ((type != TYPE_PLUS) || !cellular_data) {
        return WAIT;
    }
    
    Serial.println("got good cellinfo response " + String(buf)
    
    return WAIT;
}
1 Like

Good to see that you have invested some thought into this :+1:

However, a few things

cellular_data[200]; is a local variable which will disappear as soon you drop out of setup().

Here is a ); missing, and you don't need to wrap a const char* string in a String object. It already is a string.
So you could just do

Serial.printlnf("got good cellinfo response <%*s>", len, buf);

There also is no overload for Cellular.command() that takes the timeout as second parameter.

And I wouldn't "hammer" Cellular.command() in your else case inside of loop()

You could try out the docs sample as is and then build ontop of that

// EXAMPLE - Get the ICCID number of the inserted SIM card
int callbackICCID(int type, const char* buf, int len, char* iccid)
{
  if ((type == TYPE_PLUS) && iccid) {
    if (sscanf(buf, "\r\n+CCID: %[^\r]\r\n", iccid) == 1)
      /*nothing*/;
  }
  return WAIT;
}

void setup()
{
  Serial.begin(9600);
  char iccid[32] = "";
  if ((RESP_OK == Cellular.command(callbackICCID, iccid, 10000, "AT+CCID\r\n")) && (strcmp(iccid,"") != 0))
  {
    Serial.printlnf("SIM ICCID = %s\r\n", iccid);
  }
  else
  {
    Serial.println("SIM ICCID NOT FOUND!");
  }
}

void loop()
{
  // your loop code
}

Thanks, I changed the firmware using docs example (and your feedback). It works in serial monitor

Now I wonder how I could Particle.publish() the cellular_data char array to the Particle cloud. Never used it before. Could you suggest a code snippet / prototype?

Overall looks good and some part is already done

Serial monitor

image

#pragma PARTICLE_NO_PREPROCESSOR
#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

char cellular_data[200] = "";

int cb(int type, const char* buf, int len, char* cellular_data);

void setup() {
    
	Serial.begin(9600);
	
}


void loop(){
    
    delay(5000);
    
    if ((RESP_OK == Cellular.command(cb, cellular_data, 10000, "AT+CGED=3\r\n"))
        && (strcmp(cellular_data, "") != 0))
    {
        Serial.printlnf("Cellular info: %s\r\n", cellular_data);
    }
    else
    {
        Serial.printlnf("Cellular info not found");
    }
    
    
}


int cb(int type, const char* buf, int len, char* cellular_data)
{
    if ((type == TYPE_PLUS) && cellular_data) {
        if(sscanf(buf, "\r\n+CGED: %[^\r]\r\n", cellular_data) == 1)
        /*nothing*/;
    }
    return WAIT;
}

For that you can do something like this in loop() (to come back from the callback as quickly as possible)

  if (strlen(cellular_data) > 0) {
    Particle.publish("cellData", cellular_data, PRIVATE);
    delay(1000);             // to obey publish limit
    cellular_data[0] = '\0'; // reset string to not publish the same one again 
  }

OK, works. I see published events in console.particle and when I do curl in Ubuntu terminal. Now I would like to save those curl outputs in the terminal to a growing file but curl api_https_request >> my_file.txt does not work (the file is empty).

Any idea how to append api curl outputs I see in Linux terminal to a file? Or should I better use Webhook route?

For me this works just fine

curl -H "Authorization: Bearer {yourAccessToken}" https://api.particle.io/v1/events/cellData >> test.txt

How did you terminate the logging?
If you do it via Ctrl+C it will be empty, but via Ctrl+Z (EndOfFile) you should see the logged events.

OK, i will try. Yes I first tried Ctrl + C and it was empty. I will try your way. Thanks!

Thanks. With some modifications it worked.

Overall if it is better to keep a bash script at my server that is querying API with curl command and pipes (append) it to a file or is it better to employ webhook? Just wonder what could be hidden pros/cons of the two ways above. Thanks.

That’s entirely up to your preference.
With webhooks you are just more flexible if you should happen to also want the events directed somewhere else.

Hi ScruffR

I was having hard time to run my bash script doing continuous curl in the background so that it does not stop when I close the terminal (I know that nohug does it but still could not make it work).

I decided to set up a webhook instead. hence the 2 questions:

  • do you know any user-friendly web service with which it is easy to set up webhook and keep appending event data to a growing file? I heard that google spreadsheet can work with it

  • assume it is google spreadsheet any tips for setting up forms in webhook to append event data to it?

maybe too much to ask and I should just read the docs :slight_smile: Cheers a lot!

I do like Losant