This post is a quickly written guide that might help some other users and also an invitation for questions.
My home internet is via mobile ISP, and I recently upgraded to a Teltonika RUTX50 dual SIM card router. This replaced my older RUT950 router, and both of these Teltonika units run a modified version of OpenWRT called RutOS… so the following guide should work on most SIM routers capable of running OpenWRT, albeit with tweaks.
The issue: I lock my router to a fixed GSM band in order to prevent tower/cell hopping, to make my connection more stable. However, my ISP seems to power-down or switch off cells/towers overnight – I assume to conserve energy – and combined with my band lock this creates connection drop-outs.
So, to determine exactly what is happening, I want to log detailed info from my router’s GSM modem into Home Assistant: The primary serving band, secondary & additional bands, a record of band switching as forced by my ISP, and all subsequent RSSI, RSRP, RSRQ, SINR values per band.
OpenWRT/Teltonika by default does not pass this info to Modbus, but is here is how you can get it…
ROUTER SETUP
- Follow these instructions re: Custom Modbus Register Block…
https://wiki.teltonika-networks.com/view/Modbus_custom_register_block_RutOS
- SSH or Router UI > System > CLI and create the following script…
$ vi /bin/extramodbus
#! /bin/ash
while true
do
gsmctl -CA 'AT+QCAINFO' -o --modemtime 2 | tr '\n\r,\"' ',' > /tmp/regfile
echo '0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000' >> /tmp/regfile
sleep 8
done
- Save the script and make it executable…
$ chmod +x /bin/extramodbus
- An explanation of the above script…
4a) Use the GSMCTL command to get a bunch of info from the modem. More info about GSMCTL can be found here: https://wiki.teltonika-networks.com/view/AT_Commands …
gsmctl -CA 'AT+QCAINFO' -o --modemtime 2
4b) Use the tr
command to reformat the response of the gsmctl
into comma delimited values…
| tr '\n\r,\"' ','
4c) Write this info to a file called /tmp/regfile
> /tmp/regfile
4d) Pad out the /tmp/regfile … If a Modbus server is given data file that contains less than the expected number of characters / below the register count, it returns an error and fails. We want the /tmp/regfile to contain a minimum of 250 characters (aka a register count of 125, = 250 characters / 2) so running the echo command below appends 100x '0’s to the end of the /tmp/regfile. Hacky but it works, and I would love to know a cleaner solution…
echo '0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000' >> /tmp/regfile
EDIT: An alternative to adding zeros could be to replace the echo
line with the following printf
line instead…
printf "%250s" "#END#" >> /tmp/regfile
… where “%250s” = ‘add a string of 250 null characters’ followed by the string ‘#END#’ just to make life easier for humans … your choice, echo
or printf
… Ideally I’d use the truncate
command but this is not installed with embedded Linux on our routers.
4e) Run the script every 8 seconds…
sleep 8
done
- SSH or Router UI > System > Custom Scripts and tell the script file to run in the background, start on boot etc.
extramodbus &
exit 0
- If everything is working then /tmp/regfile should look something like this:
76543210,+QCAINFO: ,PCC,,1300,111,,LTE BAND 3,,1,222,-90,-11,-56,6,,+QCAINFO: ,SCC,,103,333,,LTE BAND 1,,1,444,-95,-10,-77,9,0,-,-,,+QCAINFO: ,SCC,,154570,1,,NR5G BAND 28,,555,,,vodafone EU,23/04/18,23:50:22,0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000
- From the Router UI, navigate to the following pages and copy the attached screenshots…
7a) Services > Modbus TCP Slave
7b) Services > Modbus TCP Master > Modbus TCP Slave Devices > Add / Slave Device Configuration
7c) Services > Modbus TCP Master > Modbus TCP Slave Devices > Edit (pencil icon) > Requests Configuration
7d) Services > Modbus TCP Master > Modbus TCP Slave Devices > Edit (pencil icon) > Requests Configuration Testing > Test … pressing Test should return the contents of /tmp/regfile truncated to 125 registers aka 250 characters. If there are errors here, most likely the script we created above is not running (try rebooting the router to launch the script).
HOME ASSISTANT SETUP
I’ll skip explanations and just refer to the required YAML. Any questions just ask. Watch YAML indentation as per usual.
- configuration.yaml…
modbus: !include modbus.yaml
template: !include templates.yaml
- modbus.yaml…
### 'RUTX: Mobile Signal Strength' and 'RUTX: System Temp' are simply to check
### if Home Assistant is receiving the standard Teltonika modbus messages.
- name: TeltonikaRutOS
type: tcp
host: 192.168.1.1
port: 502
sensors:
- name: "RUTX: Mobile Signal Strength" #aka sensor.rutx_mobile_signal_strength
scan_interval: 30
unit_of_measurement: "dBm"
device_class: signal_strength
slave: 192
address: 4
- name: "RUTX: System Temp" #aka sensor.rutx_system_temp
scan_interval: 30
unit_of_measurement: "°C"
device_class: temperature
slave: 192
address: 6
- name: "RUTX: ExtraModBus" #aka sensor.rutx_extramodbus aka /tmp/regfile
scan_interval: 8
slave: 192
address: 1024 #I have no idea why the router creates register 1025, but Home Assistant needs 1024
data_type: string
count: 125
- templates.yaml… the .split(,)[n] function in the YAML splits the incoming modbus text based on comma counts. You may need to fiddle with these [n] numbers depending on the contents of your individual /tmp/regfile…
### This yaml will parse comma delimited ASCII modbus from
### slave 192, address 1024 aka sensor.rutx_extramodbus aka /tmp/regfile
- sensors:
extramodbus_gsm_cell_id:
friendly_name: "GSM Cell ID"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[0] | int }}"
# GSM primary band
extramodbus_gsm_band:
friendly_name: "GSM Band"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[7] }}"
extramodbus_gsm_band_rsrq:
friendly_name: "GSM Band RSRQ"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[11] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band_rsrp:
friendly_name: "GSM Band RSRP"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[12] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band_rssi:
friendly_name: "GSM Band RSSI"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[13] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band_sinr:
friendly_name: "GSM Band SINR"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[14] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
# GSM secondary band2
extramodbus_gsm_band2:
friendly_name: "GSM Band2"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[22] }}"
extramodbus_gsm_band2_rsrq:
friendly_name: "GSM Band2 RSRQ"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[26] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band2_rsrp:
friendly_name: "GSM Band2 RSRP"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[27] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band2_rssi:
friendly_name: "GSM Band2 RSSI"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[28] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band2_sinr:
friendly_name: "GSM Band2 SINR"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[29] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
# GSM secondary band3
extramodbus_gsm_band3:
friendly_name: "GSM Band3"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[40] }}"
extramodbus_gsm_band3_rsrq:
friendly_name: "GSM Band3 RSRQ"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[44] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band3_rsrp:
friendly_name: "GSM Band3 RSRP"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[45] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band3_rssi:
friendly_name: "GSM Band3 RSSI"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[46] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band3_sinr:
friendly_name: "GSM Band3 SINR"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[47] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
# GSM secondary band4
extramodbus_gsm_band4:
friendly_name: "GSM Band4"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[58] }}"
extramodbus_gsm_band4_rsrq:
friendly_name: "GSM Band4 RSRQ"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[62] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band4_rsrp:
friendly_name: "GSM Band4 RSRP"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[63] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band4_rssi:
friendly_name: "GSM Band4 RSSI"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[64] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
extramodbus_gsm_band4_sinr:
friendly_name: "GSM Band4 SINR"
value_template: "{{ states('sensor.rutx_extramodbus').split(',')[65] | float(0) }}"
unit_of_measurement: "dBm"
device_class: signal_strength
- And there you have it! Make up some nice HA sensors, automation triggers, whatever you want… the screenshot at the top of the post shows a 96 hour log of my ISP switching cells & bands on me, and a history graph of primary band signal stats.
Hopefully this helps someone. Cheers, Dan
p.s: thanks to Teltonika forum member Daumantas for the detailed explanation of GSMCTL.