00 AT komutları standardı — ITU V.250
AT komutları (Hayes komut seti), 1981'de Hayes Communications tarafından geliştirilmiş ve ITU-T V.250 ile standardize edilmiştir. Bugün tüm GSM/LTE/5G modemlerin temel kontrol arayüzüdür.
AT Komut Mimarisi
────────────────────────────────────────────────
Uygulama (Python, C, bash)
↓ UART / USB ACM / USB Serial
/dev/ttyUSB0 veya /dev/ttyACM0
↓ AT komutları (ASCII metin)
Modem AT işlemcisi (EC25, SIM7600, RM500Q)
↓
Cellular RF yığını (LTE, NB-IoT, 5G NR)
AT komut formatı
Her AT komutu AT ile başlar ve \r\n (CR+LF) ile biter. Komutlar büyük/küçük harf duyarsızdır. Yanıt her zaman OK veya ERROR ile biter.
AT+CMD=?AT+CMD?AT+CMD=<val>AT+CMD# Modem cihazını bul
ls /dev/ttyUSB* /dev/ttyACM*
# /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3
# minicom ile bağlan (Ctrl+A Q ile çık)
minicom -D /dev/ttyUSB2 -b 115200
# Ya da screen
screen /dev/ttyUSB2 115200
# Python ile hızlı test
python3 -c "
import serial, time
s = serial.Serial('/dev/ttyUSB2', 115200, timeout=2)
s.write(b'AT\r\n')
time.sleep(0.2)
print(s.read(s.in_waiting).decode())
s.close()
"
# OK
01 Temel AT komutları — ATI, AT+CIMI, AT+CSQ, AT+COPS
Her LTE/5G modemin desteklediği temel AT komutları — cihaz bilgisi, SIM IMSI, sinyal gücü ve operatör seçimi.
# ATI — üretici ve model bilgisi
ATI
# Quectel
# EC25EFAR06A12M4G
# Revision: EC25EFAR06A12M4G
# OK
# AT+CGMI — üretici adı
AT+CGMI
# Quectel
# OK
# AT+CGMM — model adı
AT+CGMM
# EC25
# OK
# AT+CGSN — IMEI numarası
AT+CGSN
# 867729040000001
# OK
# AT+CIMI — SIM IMSI numarası
AT+CIMI
# 286011234567890
# OK
# AT+CCID — SIM ICCID (kart seri no)
AT+CCID
# +CCID: 89901012345678901234
# OK
# AT+CSQ — sinyal kalitesi (RSSI)
AT+CSQ
# +CSQ: 23,0
# 23 → -83 dBm (0–31 arası, 99=bilinmiyor)
# 0 → BER (0-7 arası, 99=bilinmiyor)
# OK
# AT+COPS — operatör bilgisi (mevcut)
AT+COPS?
# +COPS: 0,0,"Turkcell",7
# 0=otomatik, 0=uzun alfanümerik, "Turkcell", 7=LTE
# OK
# AT+COPS=? — mevcut operatörleri tara
AT+COPS=?
# +COPS: (2,"Turkcell","TKCL","28601",7),
# (3,"Vodafone TR","VODTR","28602",7),
# (3,"Turk Telekom","TRKCEL","28603",7)
# OK
# AT+COPS=1,2,"28601",7 — manuel operatör seç
AT+COPS=1,2,"28601",7
# OK
Sinyal kalitesi dönüşüm tablosu
02 PPP vs QMI vs MBIM — veri bağlantısı karşılaştırması
LTE modem ile Linux arasında veri bağlantısı kurmanın üç farklı yöntemi vardır. Her birinin avantaj ve dezavantajları vardır.
PPP bağlantı kurma
# /etc/ppp/peers/lte dosyası
cat /etc/ppp/peers/lte
# /dev/ttyUSB2
# 115200
# connect '/usr/sbin/chat -v -f /etc/chatscripts/gprs'
# noauth
# defaultroute
# usepeerdns
# persist
# noipdefault
# /etc/chatscripts/gprs
cat /etc/chatscripts/gprs
# ABORT BUSY ABORT ERROR ABORT 'NO CARRIER'
# '' ATZ
# OK 'AT+CGDCONT=1,"IP","internet"'
# OK ATD*99#
# CONNECT ''
# PPP bağlantısını başlat
pppd call lte
# ppp0 arayüzü oluşturulur, IP atanır
ifconfig ppp0
# ppp0: flags=4305 mtu 1500
# inet 100.65.12.45 P-t-P:10.64.64.64
# TX bytes:1234 RX bytes:5678
03 SIM yönetimi — AT+CPIN, AT+CLCK, PIN/PUK
SIM kart durumu sorgulama, PIN/PUK girişi ve SIM kilidi yönetimi — embedded sistemlerde SIM otomasyonu için kritik komutlar.
# AT+CPIN — SIM PIN durumu sorgula
AT+CPIN?
# +CPIN: READY → SIM hazır, PIN gerekmez
# +CPIN: SIM PIN → PIN bekleniyor
# +CPIN: SIM PUK → PUK bekleniyor (3 yanlış PIN)
# +CPIN: SIM PIN2 → PIN2 gerekli
# OK
# PIN gir (SIM PIN durumundayken)
AT+CPIN="1234"
# OK
# PUK ve yeni PIN gir (SIM PUK durumundayken)
AT+CPIN="12345678","1234"
# OK
# AT+CLCK — SIM kilidi sorgula/değiştir
# AT+CLCK="SC",2 → PIN kilidi durumunu sorgula
AT+CLCK="SC",2
# +CLCK: 1 → PIN kilidi aktif
# OK
# PIN kilidini kapat
AT+CLCK="SC",0,"1234"
# OK
# PIN kilidini aç
AT+CLCK="SC",1,"1234"
# OK
# AT+CPWD — PIN değiştir
AT+CPWD="SC","1234","5678"
# OK (eski PIN: 1234, yeni PIN: 5678)
# AT+CIMI — SIM'in IMSI numarasını oku
AT+CIMI
# 286011234567890
# OK
# AT+QSIMSTAT — SIM fiziksel durum (Quectel)
AT+QSIMSTAT?
# +QSIMSTAT: 0,1 → 0=otomatik rapor kapalı, 1=SIM takılı
# OK
Yanlış PIN girişinin 3 denemesi sonucu SIM PUK durumuna girer. PUK'un da 10 yanlış girişi SIM kartı kalıcı olarak kilitler. Embedded sistemlerde PIN denemelerini sayın.
Python ile PIN yönetimi
import serial
import time
class ModemAT:
def __init__(self, port='/dev/ttyUSB2', baudrate=115200):
self.ser = serial.Serial(port, baudrate, timeout=3)
time.sleep(0.5)
def send(self, cmd, wait=1.0):
self.ser.write((cmd + '\r\n').encode())
time.sleep(wait)
resp = self.ser.read(self.ser.in_waiting).decode(errors='replace')
return resp.strip()
def check_sim(self):
resp = self.send('AT+CPIN?')
if 'READY' in resp:
return 'ready'
elif 'SIM PIN' in resp:
return 'pin_required'
elif 'SIM PUK' in resp:
return 'puk_required'
return 'unknown'
def enter_pin(self, pin):
resp = self.send(f'AT+CPIN="{pin}"', wait=2.0)
return 'OK' in resp
def enter_puk(self, puk, new_pin):
resp = self.send(f'AT+CPIN="{puk}","{new_pin}"', wait=2.0)
return 'OK' in resp
def close(self):
self.ser.close()
def main():
modem = ModemAT('/dev/ttyUSB2')
status = modem.check_sim()
print(f"SIM durumu: {status}")
if status == 'pin_required':
if modem.enter_pin('1234'):
print("PIN kabul edildi")
else:
print("PIN reddedildi!")
elif status == 'puk_required':
print("PUK gerekiyor, dikkatli olun!")
if modem.enter_puk('12345678', '1234'):
print("PUK kabul edildi, yeni PIN ayarlandı")
elif status == 'ready':
resp = modem.send('AT+CIMI')
imsi = [l for l in resp.split('\n') if l.strip().isdigit()]
print(f"IMSI: {imsi[0].strip() if imsi else 'okunamadı'}")
modem.close()
if __name__ == '__main__':
main()
04 APN konfigürasyonu — AT+CGDCONT, bağlantı kurma
APN (Access Point Name), modem ile operatörün veri ağı arasındaki bağlantı noktasıdır. Doğru APN olmadan data bağlantısı kurulamaz.
# AT+CGDCONT — PDP context tanımla
# Sözdizimi: AT+CGDCONT=<cid>,<pdp_type>,<apn>
# Mevcut PDP context'leri listele
AT+CGDCONT?
# +CGDCONT: 1,"IP","super","",0,0
# OK
# APN ayarla (Turkcell için)
AT+CGDCONT=1,"IP","internet"
# OK
# IPv4/IPv6 dual-stack APN
AT+CGDCONT=1,"IPV4V6","internet"
# OK
# AT+CGACT — PDP context'i aktive et
AT+CGACT=1,1
# OK
# PDP context durumunu sorgula
AT+CGACT?
# +CGACT: 1,1 → context 1 aktif
# OK
# AT+CGPADDR — atanan IP adresini sorgula
AT+CGPADDR=1
# +CGPADDR: 1,"100.65.45.123"
# OK
# Quectel — AT+QICSGP ile APN yönetimi
AT+QICSGP=1,1,"internet","","",1
# 1=context id, 1=IPv4, "internet"=APN
# ""=kullanıcı, ""=şifre, 1=PAP/CHAP
# OK
# SIM7600 — AT+CGSOCKCONT ile APN
AT+CGSOCKCONT=1,"IP","internet"
# OK
05 Network registration — AT+CREG, AT+CEREG
GSM/GPRS kayıt durumu için AT+CREG, LTE/4G kayıt durumu için AT+CEREG kullanılır. NB-IoT için AT+CEREG özellikle önemlidir.
# AT+CREG — GSM/GPRS kayıt durumu
AT+CREG?
# +CREG: 0,1
# 0=bildirim kapalı, 1=kayıtlı (ev ağı)
# Durum değerleri:
# 0 = Kayıtsız, aramıyor
# 1 = Kayıtlı, ev ağı
# 2 = Kayıtsız, arıyor
# 3 = Kayıt reddedildi
# 4 = Bilinmiyor
# 5 = Kayıtlı, roaming
# OK
# AT+CEREG — LTE/EPS kayıt durumu
AT+CEREG?
# +CEREG: 0,1
# OK
# Detaylı kayıt bilgisi (TAC, CI)
AT+CEREG=2
AT+CEREG?
# +CEREG: 2,1,"0B13","021A4D01",7
# 2=detaylı mod, 1=kayıtlı, "0B13"=TAC, "021A4D01"=CI, 7=LTE
# OK
# AT+QNWINFO — ağ bilgisi (Quectel)
AT+QNWINFO
# +QNWINFO: "FDD LTE","28601","LTE BAND 3",1750
# OK
# AT+QENG — serving cell bilgisi (Quectel)
AT+QENG="servingcell"
# +QENG: "servingcell","NOCONN","LTE","FDD",286,01,021A4D01,
# 123,1750,3,4,4,-83,-12,-54,16,41
# OK
# AT+CEREG için unsolicited kayıt bildirimleri aç
AT+CEREG=1
# Şimdi ağ değiştiğinde otomatik bildirim gelir:
# +CEREG: 1 → kayıt oldu
# +CEREG: 0 → bağlantı kesildi
Kayıt izleme döngüsü (Python)
import serial, time, re
def wait_registered(port='/dev/ttyUSB2', timeout=60):
ser = serial.Serial(port, 115200, timeout=2)
start = time.time()
print("LTE ağına kayıt bekleniyor...")
while time.time() - start < timeout:
ser.write(b'AT+CEREG?\r\n')
time.sleep(1)
resp = ser.read(ser.in_waiting).decode(errors='replace')
m = re.search(r'\+CEREG:\s*\d,(\d)', resp)
if m:
stat = int(m.group(1))
if stat in (1, 5): # 1=ev, 5=roaming
ser.close()
return True, 'roaming' if stat == 5 else 'home'
print(f" Durum: {stat}, bekleniyor...")
ser.close()
return False, 'timeout'
ok, mode = wait_registered()
if ok:
print(f"Kayıt başarılı — mod: {mode}")
else:
print("Kayıt zaman aşımı!")
06 SMS gönderme ve alma — AT+CMGS, AT+CMGR
AT komutlarıyla SMS gönderme (PDU ve metin modu) ve alma. Embedded sistemlerde OTP, alarm bildirimi ve uzaktan komut için kullanılır.
# SMS metin moduna geç
AT+CMGF=1
# OK
# SMS gönder
AT+CMGS="+905001234567"
# > (modem prompt bekler)
Sistem alarmı: Sicaklik 85C asild!
# Ctrl+Z ile gönder (0x1A)
# +CMGS: 42
# OK (42 = mesaj referans no)
# SMS klasörlerini listele
AT+CMGL="ALL"
# +CMGL: 1,"REC READ","+905009876543","","26/01/15,10:30:00+12"
# Merhaba, test mesaji
# OK
# Belirli bir SMS'i oku (index 1)
AT+CMGR=1
# +CMGR: "REC READ","+905009876543","","26/01/15,10:30:00+12"
# Merhaba, test mesaji
# OK
# SMS sil (index 1)
AT+CMGD=1
# OK
# Tüm okunmuş SMS'leri sil
AT+CMGD=1,1
# OK
# Yeni SMS bildirimi aktif et
AT+CNMI=2,1,0,0,0
# Yeni SMS geldiğinde:
# +CMTI: "SM",2 → SM klasörü, index 2
# OK
Python ile SMS gönderme
import serial
import time
def send_sms(port, number, message):
ser = serial.Serial(port, 115200, timeout=5)
time.sleep(0.3)
def cmd(c, wait=0.5):
ser.write((c + '\r\n').encode())
time.sleep(wait)
return ser.read(ser.in_waiting).decode(errors='replace')
# Metin moduna geç
r = cmd('AT+CMGF=1')
if 'OK' not in r:
raise RuntimeError("CMGF hatası")
# Alıcı numarasını gir
ser.write(f'AT+CMGS="{number}"\r\n'.encode())
time.sleep(0.5)
r = ser.read(ser.in_waiting).decode(errors='replace')
if '>' not in r:
raise RuntimeError("SMS prompt alınamadı")
# Mesajı gönder (Ctrl+Z = 0x1A ile bitir)
ser.write(message.encode() + b'\x1a')
time.sleep(5) # SMS gönderimi için bekle
r = ser.read(ser.in_waiting).decode(errors='replace')
ser.close()
if '+CMGS:' in r:
ref = r.split('+CMGS:')[1].split('\n')[0].strip()
print(f"SMS gönderildi, ref={ref}")
return True
else:
print(f"Hata: {r}")
return False
send_sms('/dev/ttyUSB2', '+905001234567', 'Sicaklik alarmi: 85C')
07 Baud rate, flow control ve modem reset
Seri port parametreleri, donanım akış kontrolü ve modem sıfırlama prosedürleri — güvenilir embedded modem iletişimi için zorunlu bilgiler.
# AT+IPR — baud rate sorgula
AT+IPR?
# +IPR: 115200
# OK
# Baud rate değiştir (dikkatli kullan!)
AT+IPR=921600
# OK (terminal artık 921600 ile bağlanmalı)
# AT&K — akış kontrolü
AT&K0
# OK (akış kontrolü kapalı)
AT&K3
# OK (RTS/CTS donanım akış kontrolü)
AT&K4
# OK (XON/XOFF yazılım akış kontrolü)
# AT&V — mevcut ayarları görüntüle
AT&V
# ATZ — modem ayarlarını fabrika varsayılanına sıfırla
ATZ
# OK
# AT&F — fabrika ayarlarına dön
AT&F
# OK
# AT+CFUN — modem fonksiyon modu
AT+CFUN?
# +CFUN: 1 → tam fonksiyon
# OK
# Soft reset (RF kapalı, sonra tam açık)
AT+CFUN=0 # RF kapat (minimum fonksiyon)
AT+CFUN=1 # Tam fonksiyon (reset ile)
# Quectel — modem yeniden başlat
AT+QPOWD=1
# POWERED DOWN
# SIM7600 — modem yeniden başlat
AT+CRESET
# OK
# +CPIN: READY (yaklaşık 10s sonra)
Python pyserial ile doğru baud ayarı
import serial
# Doğru seri port ayarları
ser = serial.Serial(
port='/dev/ttyUSB2',
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
rtscts=False, # RTS/CTS donanım akış kontrolü
xonxoff=False, # XON/XOFF yazılım akış kontrolü
timeout=3,
write_timeout=3
)
# DTR/RTS pinlerini ayarla (bazı modemlerde önemli)
ser.dtr = True
ser.rts = True
print(f"Port açık: {ser.is_open}")
print(f"Baud: {ser.baudrate}")
ser.write(b'AT\r\n')
import time; time.sleep(0.2)
print(ser.read(ser.in_waiting).decode())
ser.close()
08 Practical — EC25/SIM7600 Python AT kontrolü
Quectel EC25 veya SIMCom SIM7600 modemi Python ile kontrol eden tam bir sınıf — bağlantı yönetimi, sinyal izleme ve data oturumu.
#!/usr/bin/env python3
"""
LTE Modem AT Kontrolcüsü
Quectel EC25 / SIMCom SIM7600 ile test edilmiştir.
Gereksinimler: pip install pyserial
"""
import serial
import time
import re
import threading
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s')
log = logging.getLogger('LTEModem')
class LTEModem:
def __init__(self, port='/dev/ttyUSB2', baudrate=115200):
self.port = port
self.baudrate = baudrate
self.ser = None
self._lock = threading.Lock()
self._urcs = [] # unsolicited result codes
def open(self):
self.ser = serial.Serial(
self.port, self.baudrate,
rtscts=False, xonxoff=False, timeout=3
)
time.sleep(0.5)
log.info(f"Port açıldı: {self.port}")
def close(self):
if self.ser and self.ser.is_open:
self.ser.close()
def send_cmd(self, cmd, timeout=5.0, expect='OK'):
"""AT komutu gönder, yanıt bekle."""
with self._lock:
self.ser.reset_input_buffer()
self.ser.write((cmd + '\r\n').encode())
deadline = time.time() + timeout
resp = ''
while time.time() < deadline:
if self.ser.in_waiting:
chunk = self.ser.read(self.ser.in_waiting).decode(errors='replace')
resp += chunk
if expect in resp or 'ERROR' in resp:
break
time.sleep(0.05)
return resp.strip()
# ── Cihaz bilgisi ─────────────────────────────────────
def get_info(self):
info = {}
for key, cmd in [('model', 'AT+CGMM'), ('imei', 'AT+CGSN'),
('imsi', 'AT+CIMI'), ('fw', 'AT+CGMR')]:
resp = self.send_cmd(cmd)
lines = [l.strip() for l in resp.split('\n')
if l.strip() and l.strip() not in ('OK', cmd.split('+')[1])]
info[key] = lines[0] if lines else ''
return info
# ── Sinyal kalitesi ───────────────────────────────────
def get_signal(self):
resp = self.send_cmd('AT+CSQ')
m = re.search(r'\+CSQ:\s*(\d+),(\d+)', resp)
if m:
rssi_raw = int(m.group(1))
rssi_dbm = -113 + rssi_raw * 2 if rssi_raw < 99 else None
return {'raw': rssi_raw, 'dbm': rssi_dbm}
return None
def get_lte_signal(self):
"""Quectel: LTE RSRP/RSRQ/SINR"""
resp = self.send_cmd('AT+QCSQ')
m = re.search(r'\+QCSQ:\s*"([^"]+)",(-?\d+),(-?\d+),(-?\d+),(-?\d+)', resp)
if m:
return {
'rat': m.group(1),
'rssi': int(m.group(2)),
'rsrp': int(m.group(3)),
'sinr': int(m.group(4)),
'rsrq': int(m.group(5)),
}
return None
# ── Ağ kaydı ──────────────────────────────────────────
def get_registration(self):
resp = self.send_cmd('AT+CEREG?')
m = re.search(r'\+CEREG:\s*\d,(\d)', resp)
if m:
stat = int(m.group(1))
return {
'registered': stat in (1, 5),
'roaming': stat == 5,
'status': stat
}
return None
def wait_registered(self, timeout=120):
start = time.time()
while time.time() - start < timeout:
reg = self.get_registration()
if reg and reg['registered']:
log.info(f"LTE kayıt OK {'(roaming)' if reg['roaming'] else '(ev ağı)'}")
return True
time.sleep(3)
return False
# ── APN ve data bağlantısı ────────────────────────────
def setup_apn(self, apn='internet', pdp_type='IP', cid=1):
resp = self.send_cmd(f'AT+CGDCONT={cid},"{pdp_type}","{apn}"')
return 'OK' in resp
def activate_pdp(self, cid=1):
resp = self.send_cmd(f'AT+CGACT=1,{cid}', timeout=30)
return 'OK' in resp
def get_ip(self, cid=1):
resp = self.send_cmd(f'AT+CGPADDR={cid}')
m = re.search(r'\+CGPADDR:\s*\d+,"([^"]+)"', resp)
return m.group(1) if m else None
# ── Tam bağlantı sekansı ──────────────────────────────
def connect(self, apn='internet'):
log.info("Modem başlatılıyor...")
# SIM kontrolü
r = self.send_cmd('AT+CPIN?')
if 'READY' not in r:
log.error(f"SIM hazır değil: {r}")
return False
# APN ayarla
self.setup_apn(apn)
# Ağ kaydı bekle
if not self.wait_registered():
log.error("Ağ kaydı başarısız")
return False
# PDP aktive et
if not self.activate_pdp():
log.error("PDP aktivasyon başarısız")
return False
ip = self.get_ip()
log.info(f"Bağlantı kuruldu. IP: {ip}")
return True
# ── Ana program ───────────────────────────────────────────
def main():
modem = LTEModem(port='/dev/ttyUSB2')
modem.open()
info = modem.get_info()
log.info(f"Model : {info.get('model','?')}")
log.info(f"IMEI : {info.get('imei','?')}")
log.info(f"IMSI : {info.get('imsi','?')}")
sig = modem.get_signal()
if sig:
log.info(f"Sinyal: {sig['raw']} ({sig['dbm']} dBm)")
lte_sig = modem.get_lte_signal()
if lte_sig:
log.info(f"LTE : {lte_sig}")
ok = modem.connect(apn='internet')
if ok:
log.info("Data bağlantısı hazır!")
# wwan0 / usb0 arayüzüne DHCP çek
import subprocess
subprocess.run(['udhcpc', '-i', 'wwan0', '-n'], timeout=15)
modem.close()
if __name__ == '__main__':
main()
EC25 modemi genellikle /dev/ttyUSB0 (diag), /dev/ttyUSB1 (GPS NMEA), /dev/ttyUSB2 (AT komutları), /dev/ttyUSB3 (modem data) portları açar. AT komutları için ttyUSB2 kullanın. Data için QMI/MBIM tercih edin.