SMS LTE Info

I have a hAP AC3 LTE6.

I’d like to be able to send it a text message and for it to reply back with some useful information about the LTE connection, signal strength, router uptime etc.

I’ve already got a script I can trigger by SMS to reboot the router. It replies to me to say “Okeydokey boss”:

name="smsreboot" owner="admin" 
     policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon 
     dont-require-permissions=no run-count=0 
     source=
       /tool sms send lte1 "mynumber" message="Okeydokey boss"
       :delay 5s
       /system reboot

And here is my start on the router status script:

name="smsstatus" owner="admin" 
     policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon 
     dont-require-permissions=no last-started=aug/03/2021 13:51:25 run-count=14 
     source=
       :local CurDate [/system clock get date] 
       :local CurTime [/system clock get time] 
       :local LTEInfo [/interface lte get value-name=operator lte1]
       
       /tool sms send lte1 "mynumber" message="$CurDate $CurTime $LTEInfo"

I’m stuck on how I get relevant info from the LTE interface into my SMS. The above doesn’t print any text. Ideally I want to see CQI, RSSI, Primary Band.
I tried with:

:local LTEInfo [/interface lte info lte1 once]

but that spat out too many characters to fit in an SMS.

Any help would be greatly appreciated!

Can you show the output of this?

:local LTEInfo [/interface lte info lte1 once]

You can use the pick and find command, to select the part of the string that you need.

Here is an example

{
:local test "some data CQI=32 RSI=44"
:put $test
:local startcqi ([:find $test "CQI"]+4)
:put $startcqi
:local endcqi [:find $test " " $startcqi]
:put $endcqi
:local cqi [:pick $test $startcqi $endcqi]
:put $cqi
}

When run, it will output start/stop of data field and CQI value 32

Some shorten version

{
:local test "some data CQI=32 RSI=44"
:local cqi [:pick $test ([:find $test "CQI"]+4) [:find $test " " ([:find $test "CQI"]+4)]]
:put "CQI=$cqi"
}

Or this:

{
:local test "some data CQI=32 RSI=44"
:local cqi [:pick $test ([:find $test "CQI"]+4) ([:find $test "RSI"]-1)]
:put "CQI=$cqi"
}

@jotne :wink:


@hogg101
“operator” do not exist, is “current-operator”

Obtainable values can vary with modem models.
For example, from this two model, only “R11e-LTE” support UICC (SIM serial number)
and only R11e-4G support RSSI

The correct way to obtain one value is

/interface lte
:put ([info lte1 once as-value ]->"current-operator")
:put ([info lte1 once as-value ]->"rsrp")
:put ([info lte1 once as-value ]->"rsrq")
:put ([info lte1 once as-value ]->"sinr")
:put ([info lte1 once as-value ]->"cqi")

For example I can obain those values from R11e-LTE: (real values on example)
pin-status: ok
registration-status: registered
functionality: full
manufacturer: “MikroTik”
model: “R11e-LTE”
revision: “MikroTik_CP_2.160.000_v018”
current-operator: vodafone IT
lac: 33042
current-cellid: 17471275
enb-id: 68247
sector-id: 43
phy-cellid: 414
access-technology: Evolved 3G (LTE)
session-uptime: 1d22h44m45s
imei: 3(censored)8
imsi: 2(censored)6
**uicc: 8(censored)1f**earfcn: 525 (band 1, bandwidth 15Mhz)
rsrp: -90dBm
rsrq: -10.5dB
sinr: 18dB
cqi: 14
and those from R11e-4G
pin-status: ok
registration-status: registered
functionality: full
manufacturer: MikroTik
model: R11e-4G
revision: R11e-4G_V007
current-operator: vodafone IT
lac: 33042
current-cellid: 15118879
enb-id: 59058
sector-id: 31
phy-cellid: 60
access-technology: Evolved 3G (LTE)
session-uptime: 5d1h59m4s
imei: 3(censored)6
imsi: 2(censored)7
earfcn: 1850 (band 3, bandwidth 20Mhz)
**rssi: -76dBm**rsrp: -97dBm
rsrq: -12dB
sinr: 11dB
cqi: 11

@rextended
Without a LTE interface, it was not some I could test :smiley:

:slight_smile: I imagined it, wink on purpose :unamused:

@jotne @rextended You guys are awesome! Thank you. Looking forward to trying this.

Hello chaps

I’m opening up this topic again because I’ve finally got around to attempting this again and I’ve spent most of today working through it.

It seems the “info” command isn’t supported on ROS7. It’s been replaced by “monitor”.

Here is my working script source for others to reference. The permissions required are read and test.

:local registration  [:put ([/interface lte monitor lte1 once as-value]->"registration-status")]
:local functionality  [:put ([/interface lte monitor lte1 once as-value]->"functionality")]
:local operator  [:put ([/interface lte monitor lte1 once as-value]->"current-operator")]
:local rssi [:put ([/interface lte monitor lte1 once as-value]->"rssi")]
:local rsrp  [:put ([/interface lte monitor lte1 once as-value]->"rsrp")]
:local rsrq  [:put ([/interface lte monitor lte1 once as-value]->"rsrq")]
:local cqi  [:put ([/interface lte monitor lte1 once as-value]->"cqi")]
:local curtime [/system clock get time]
:local curdate [/system clock get date]
:local uptime  [:put ([/interface lte monitor lte1 once as-value]->"session-uptime")]
/tool sms send lte1 "cellnumber_including_country_code" message="Registration: $registration\r\
\nFunctionality: $functionality\r\
\nOperator: $operator\r\
\nRSSI: $rssi\r\
\nRSRP: $rsrp\r\
\nRSRQ: $rsrq\r\
\nCQI: $cqi\r\
\nTime: $curtime\r\
\nDate: $curdate\r\
\nUptime: $uptime"

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh…

a “PUT” inside a script!!!..

{
:local phone         "+1234567890"
:local curdate       [/system clock get date]
:local curtime       [/system clock get time]
:local monitor       [/interface lte monitor lte1 once as-value]
:local registration  ($monitor->"registration-status")
:local functionality ($monitor->"functionality")
:local operator      ($monitor->"current-operator")
:local rssi          ($monitor->"rssi")
:local rsrp          ($monitor->"rsrp")
:local rsrq          ($monitor->"rsrq")
:local cqi           ($monitor->"cqi")
:local uptime        ($monitor->"session-uptime")
/tool sms send lte1 phone-number=$phone \
    message="Registration: $registration\r\n\
             Functionality: $functionality\r\n\
             Operator: $operator\r\n\
             RSSI: $rssi\r\n\
             RSRP: $rsrp\r\n\
             RSRQ: $rsrq\r\n\
             CQI: $cqi\r\n\
             Time: $curtime\r\n\
             Date: $curdate\r\n\
             Uptime: $uptime"
}

Apart from the apparent blasphemy, what's inherently wrong with that ?
Just curious...

without explanation:
compare the 2 scripts and see the different readability

with explanations:
instead of putting a value inside a variable, you put the result of a printout on the terminal inside a variable.
It’s like making an image with photoshop, and instead of saving it directly, you take a screenshot and save that…

OK, that’s a very clear analogy :laughing:

I don’t know if this helps.

But I view all commands, including :put, as special case of scripting’s “functions”. All functions return some type (or a :nothing type). Since :put’s job is to output text to the terminal, it does that, & as a function, returns the data it got to the calling code, in this case assignment to a local variable.

If the script is running in the background, like via scheduler, there is no terminal to send the output. So it should be harmless, just superfluous.

But in some cases, this property of :put is useful. e.g. Like for an interactive script run at the terminal where you may want to both output text to the terminal & log it in one line:

/log info [:put "my code is here"]
   # my code is here
/log print where topics~"script" time>5m
   # apr/22 18:13:21 script,info my code is here

In the above, :put output to the terminal, then returns the string :put got to the “/log info” command (e.g. “built-in function”).

This is a very elegant solution, thank you

How would you handle direct AT calls to the modem which return two or more lines I tried

in terminal
:put  [/interface lte at-chat lte1 input="at+CPIN?" wait=no as-value ] 
output=+CPIN: READY
OK

scripted
:local lteCommand  ( [/interface lte at-chat lte1 input="at+CPIN?" wait=no as-value ] ->"output")
:local simState      ($lteCommand->"+CPIN");
:log info $lteCommand;
:log info $simState;

returns
+CPIN: READY
OK
<blank line>

Is there an elegant way to get these result or should I write my own parser?

Thanks for sharing your wisdom
M.

Yeah that “:local simState ($lteCommand->”+CPIN");" isn’t going to work. The “at-chat” returns is just a string, so you have to parse – and that’s not entirely easy to do.

What are you trying to do? If you enable “lte” in the /system/log as a topic, you’ll see that Mikrotik does issue a lot of commands… In most cases the +CPIN is one. More generally, parsing the LTE debug logs can sometime be useful, without having to issue the at-chat yourself.

I thought so as well, but was hoping that some of the parsing may already have been processed somewhere.

What I am trying to achieve is to handle the issues with ‘failing’ SIM cards which need to be power cycled. If I can check what the state of the SIM is then I can schedule/trigger to check this and automate some possible scenarios (disable/enable, usb power cycle, reboot, swap SIM slot, check profiles, etc..) … anything to keep the connection up and running.

I have to do some control on the modem side as well (Telit LM series) so AT is the way to go for me. If only there would be API access (with CORS choice - which is frustrating - but that’s another story)

Thanks
M.

Something is wrong if you’re needing to do a AT+CPIN, that should be correctly handled by RouterOS. And if not the logs would very likely show an issue. So a script that scan the logs is what may be best, dunno. /tool/watchdog with a ping can also solve the last line of defense, no ping after X minutes, reboot – covers other things than LTE but dirt simple fail-safe

I do have some generic AT parsing code to at least wraps “at-chat” looking for OK, or “throwing” an :error. The $ATVAR is function wraps “at-chat” and returns just the result as a string. $AT will do same but output (:put) the result.

:global ATVAR
:set ATVAR do={
    :local checkok 1;
    :local atchatwait "yes";
    :local cmd [:tostr $1]
    :put $cmd
    :local r [/interface/lte/at-chat [find running] input="$cmd" wait=$atchatwait as-value];
    :set r ($r->"output");
    if (checkok>0) do={
        :local s [:find $r "OK"]; 
        :if (s>0) do={
            :local z [:pick $r 0 $s];
            :return [:tostr $z];
        } else={:error "ATVAR got error: $r"}
    } else={:return $r}
};

:global AT 
:set AT do={
    :global ATVAR;
    :local z [$ATVAR $1];
    :put $z;
    :return $z;
};

I also do have one that parses the Telit LM960s AT#RFSTS, to get the actual tower info eNB need for cellmapper. But as you can see parsing these is complex and specific to the command. e.g. the AT#RFSTS can return 15, 16, or 17 items depending on the band/connection/etc. Maybe it helps.

:global toarrayFromTelitResponse
:set toarrayFromTelitResponse do={
    :local z [ [:tostr [:pick $1 ([:find $1 ":"]+2) [:len $1] ]]]
    :return [:toarray $z];
};

:global toarrayApplyTemplate
:set toarrayApplyTemplate do={ 
    :local s [toarray "$2"];
    :local z [:toarray ""];
    :foreach i,k in=$s do={:set ($z->$k) ($1->$i)};
    :log debug message="toarrayApplyTemplate $s -> $z";
    :return $z; 
};

:global ATrfsts 
:set ATrfsts do={
    :global toarrayApplyTemplate;
    :global toarrayFromTelitResponse;
    :global ATVAR;
    :local TErfsts1 "PLMN,EARFCN,RSRP,RSSI,RSRQ,TAC,RAC,TXPWR,DRX,MM,RRC,CID,IMSI,NETNAME,SD,ABND"
    :local TErfsts2 "PLMN,EARFCN,RSRP,RSSI,RSRQ,TAC,RAC,DRX,MM,RRC,CID,IMSI,NETNAME,SD,ABND"
    :local TErfsts3 "PLMN,EARFCN,RSRP,RSSI,RSRQ,TAC,RAC,DRX,MM,RRC,CID,IMSI,SD,ABND"
    :local TErfmmstate {0= "NULL";3="LOCATION_UPDATE_INITIATED";5="WAIT_FOR_OUTGOING_MM_CONNECTION";6="CONNECTION_ACTIVE";7="IMSI_DETACH_INITIATED";8="PROCESS_CM_SERVICE_PROMPT";9="WAIT_FOR_NETWORK_COMMAND";10="LOCATION_UPDATE_REJECTED";13="WAIT_FOR_RR_CONNECTION_LU";14="WAIT_FOR_RR_CONNECTION_MM";15="WAIT_FOR_RR_CONNECTION_IMSI_DETACH";17="REESTABLISHMENT_INITIATED";18="WAIT_FOR_RR_ACTIVE";19="IDLE";20="WAIT_FOR_ADDITIONAL_OUTGOING_MM_CONNECTION";21="WAIT_FOR_RR_CONNECTION_REESTABLISHMENT";22="WAIT_FOR_REESTABLISH_DECISION";23="LOCATION_UPDATING_PENDING";24="IMSI DETACH PENDING";25="CONNECTION_RELEASE_NOT_ALLOWED"};
    :local TErfrrcstate1 {0="IDLE";2="CELL DCH"};
    :local TErfrrcstate2 {0="IDLE";2="CELL FACH";3="CELL DCH";4="CELL PCH";5="URA PCH";};
    :local TErfsdstate {0="NONE";1="CS_ONLY";2="PS_ONLY";3="CS_PS"};
    :local a [$ATVAR "AT#RFSTS"]
    :local r [$toarrayFromTelitResponse $a];
    :local z [:toarray ""];
    :local l [:len $r];
    :if ($l=16) do={ :set z [$toarrayApplyTemplate $r $TErfsts1]; }; 
    :if ($l=15) do={ :set z [$toarrayApplyTemplate $r $TErfsts2]; };
    :if ($l=14) do={ :set z [$toarrayApplyTemplate $r $TErfsts3]; };
    #:if ($l>16 || $l<14) do={ :error "Frfsts got unexpected length ($l) from: $r"};
    :set ($z->"MM") ($TErfmmstate->($z->"MM"));
    :set ($z->"RRC") ($TErfrrcstate1->($z->"RRC"));
    :set ($z->"SD") ($TErfsdstate->($z->"SD"));
    #cleanup
    :set ($z->"ABND") [:pick ($z->"ABND") 0 [find ($z->"ABND") "\n"]]
    :return $z;
}

But scripts fixing up core functionality of the modem, I’m not sure that’s the best plan. But the $ATVAR above at least solve remove the “OK” part if you wanted to try.