Выполнение AT/USSD команд к /dev/ttyUSB* модему из консоли Linux

Категория: Linux

Утилиты и скрипты для коммуникации с /dev/ttyUSB* устройством из shell консоли (bash скриптов).

Теория

Отправить в /dev/ttyUSB* терминал AT команду не составляет проблем. Но результат вывода мы не увидим!

echo ATI > /dev/ttyUSB2
# или
echo ATI | sudo tee /dev/ttyUSB2

Мы можем читать поток stdout c /dev/ttyUSB2 (результат выполнения команд):

sudo cat /dev/ttyUSB2

В чем проблемы?

  1. Нужно получить только результат выполнения конкретной AT команды, а не весь поток вывода.
  2. Нужно использовать gsm7bit кодировку для кодирования параметров и получения ответа при выполнении USSD запросов:
    echo AT+CUSD=1,AA182C3602,15 | sudo tee /dev/ttyUSB2
  3. Ну и хочется простоты в работе)

Выполнение AT/USSD команд

bash

Скрипт at.sh для отправки AT команды и получения ответа в переменную:

at.sh "AT+COPS=3,0\nAT+COPS?" /dev/ttyUSB2
#!/bin/bash

AT="$1"
TTY="$2"
SLEEP=0

RESULT=$(cat $TTY & echo -e "$AT" > $TTY; sleep $SLEEP; kill %cat)
echo $RESULT

Но вы получите ответ вида:

AT+COPS? +COPS: 0,0,"lifecell",2 OK ^BOOT:36507742,0,0,0,87 ^RSSI:3 ^RSSI:3 ^RSSI:3

minicom

Можно отправить AT команду и перенаправить весь вывод в файл:

sudo minicom -D /dev/ttyUSB2 -S ./at/carrier-name.txt -C output.txt

После этого нужно закрыть терминал и разобрать/декодировать весь вывод из файла output.txt. Не круто..

ussd.py

В сети нашел python скрипт для отправки USSD запросов (немного поправил):

ussd.py *121# /dev/ttyUSB2
122 SMS po Ukraine. 3G: 1024 MB Nomer do 01.05.18.

Скрипт ussd.py:

#!/usr/bin/python
#coding: utf8

import base64, io, tty, sys
import time

ussd = sys.argv[1]
modem = '/dev/ttyUSB5'

if len(sys.argv) > 2:
    modem = sys.argv[2]
else:
    modem = '/dev/ttyUSB2'

def write2p(a):
	#sp.write(bytes(a + '\r\n', 'utf8'))
	sp.write(a + '\r\n')

def gsm7bitencode(src):
	"""
	Encode ASCII text to 7-bit encoding
	"""
	result, count, last = [], 0, 0
	for c in src:
		this = ord(c) << (8 - count)
		if count:
			result.append('%02X' % ((last >> 8) | (this & 0xFF)))
		count = (count + 1) % 8
		last = this
	result.append('%02x' % (last >> 8))
	return ''.join(result)

def gsm7bitdecode(f):
   f = ''.join(["{0:08b}".format(int(f[i:i+2], 16)) for i in range(0, len(f), 2)][::-1])
   return ''.join([chr(int(f[::-1][i:i+7][::-1], 2)) for i in range(0, len(f), 7)])

### 
sp = io.open(modem, 'w+b', 0)
tty.setraw(sp)
write2p('AT+CUSD=1,' + gsm7bitencode(ussd) + ',15')

###
start = time.time()
while (time.time() - start < 10):
	l = sp.readline()
	#print(l)
	if l.startswith('+CUSD'):
		ussd_response = l[10:l.rfind('"')]
		response_decoded = gsm7bitdecode(ussd_response)
		print(response_decoded)
		break

# @note Реализация без таймаута
# @todo Нужен таймаут для прерывания ридера потока, если ничего 
#for l in sp:
	##print(l)
	#if l.startswith('+CUSD'):
		#ussd_response = l[10:l.rfind('"')]
		##print(ussd_response)
		#response_decoded = gsm7bitdecode(ussd_response)
		#print(response_decoded)
		#break

sp.close()

Кодировка GSM 7bit в USSD запросах

Кодирование в gsm7bit

При отправке USSD запросов нужно кодировать USSD-код в кодировку GSM 7bit (как я понял из-за того, что модем не поддерживает текстовый режим).

Следующие запросы вернут ERROR:

AT+CUSD=1,"*111#","15"
AT+CUSD=1,"*121#",15

Python функция gsm7bitencode для кодирования utf8 строки в gsm7bit:

def gsm7bitencode(src):
	"""
	Encode ASCII text to 7-bit encoding
	"""
	result, count, last = [], 0, 0
	for c in src:
		this = ord(c) << (8 - count)
		if count:
			result.append('%02X' % ((last >> 8) | (this & 0xFF)))
		count = (count + 1) % 8
		last = this
	result.append('%02x' % (last >> 8))
	return ''.join(result)
Примечание

Смотрите онлайн-сервис для работы с кодировками: http://smstools3.kekekasvi.com/topic.php?id=288

Пример USSD команд в кодировке 7bit gsm проверки баланса:

AT+CUSD=1,"AA582C3602",15    # *111#
AT+CUSD=1,"AA182C3602",15    # *121#

Кодированные ответы (ответ всегда приходит на устройство /dev/ttyUSB2):

+CUSD: 0,"C2303BEC9E83662E98ED2C77B340E2B7BB3E07C15C30D859EE762914",15
+CUSD: 0,"C2303BEC9E83662E98ED2C77B340E2B7BB3E07C15C30D859EE7629542A9502442CD3C3ECB40EA48AC9622317E8299687E9697A196477A7DB6177BACC02B9DFEDB21C4466E7C320B8FC6D2FCBD7699038CC0EBBE761103B6D2E8FCB6C3648158BC5460A05",15

Декодирование из gsm7bit

Python функция gsm7bitdecode для декодирования gsm7bit в utf8:

def gsm7bitdecode(f):
   f = ''.join(["{0:08b}".format(int(f[i:i+2], 16)) for i in range(0, len(f), 2)][::-1])
   return ''.join([chr(int(f[::-1][i:i+7][::-1], 2)) for i in range(0, len(f), 7)])


#modem, #tty, #at, #bash

категория: Linux