Improved SMS.

This commit is contained in:
Andrey Egorov 2024-08-09 18:17:20 +03:00
parent b3fb13b7b3
commit 75d05f197c
8 changed files with 255 additions and 137 deletions

View File

@ -116,23 +116,33 @@ func (p *atPort) RawSend(msg string) (string, error) {
}
// time.Sleep(time.Millisecond)
// Read
readLen, err := p.port.Read(p.inputBuf)
// p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(p.inputBuf[:readLen]), "\x1b[38;2;255;255;255m")
if err != nil {
return "", fmt.Errorf("port read: %w", err)
outBuf := make([]byte, 0)
readLoop:
for {
readLen, err := p.port.Read(p.inputBuf)
if err != nil {
return "", fmt.Errorf("port read: %w", err)
}
if readLen == 0 {
break readLoop
}
outBuf = append(outBuf, p.inputBuf[:readLen]...)
if readLen < len(p.inputBuf) {
break readLoop
}
}
// p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(p.inputBuf[:readLen]), "\x1b[38;2;255;255;255m")
return string(p.inputBuf[:readLen]), nil
return string(outBuf), nil
}
func (p *atPort) Send(cmd string) (Resp, error) {
cmd += "\r\n"
rawResp, err := p.RawSend(cmd)
rawResp, err := p.RawSend(cmd + "\r\n")
if err != nil {
return RespNil, fmt.Errorf("make request: %w", err)
return RespNil, fmt.Errorf("%s request: %w", cmd, err)
}
if len(rawResp) <= 4 {
return RespNil, fmt.Errorf("read too small msg: %d byte - %s", len(rawResp), string(rawResp))
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
}
resp := rawResp[2 : len(rawResp)-2] // Cut \r\n

View File

@ -85,7 +85,7 @@ func (g *gps) switchGpsMode(on bool) error {
// Check gps mode status
resp, err := g.port.Send("AT+CGPS?")
if err != nil {
return fmt.Errorf("make at ask: %w", err)
return err
}
if !resp.Check() || !resp.CheckFront("+CGPS:") {
return fmt.Errorf("get GPS mode: error response: %s", resp)
@ -98,7 +98,7 @@ func (g *gps) switchGpsMode(on bool) error {
// Modem is not in GPS mode
resp, err = g.port.Send("AT+CGPS=" + onStr)
if err != nil {
return fmt.Errorf("set GPS mode: %w", err)
return err
}
if !resp.Check() {
return fmt.Errorf("set GPS mode: error response: %s", resp)

View File

@ -56,7 +56,7 @@ func (g *gps) rawCollect(flags nmeaFlags) (string, error) {
// Set output rate to 10Hz
if resp, err := g.port.Send("AT+CGPSNMEARATE=1"); err != nil {
return "", fmt.Errorf("AT+CGPSNMEARATE= request: %w", err)
return "", err
} else {
if !resp.Check() {
return "", fmt.Errorf("set output rate: error response: %s", resp)

View File

@ -112,19 +112,19 @@ func (m *modem) Init() error {
// submodulesLogger := io.Discard // FOR less logs
m.logger.Println("=============================== Init submodules")
//m.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port)
//if err := m.ic.Init(); err != nil {
// m.logger.Printf("\x1b[38;2;255;0;0mInternet: %s\x1b[38;2;255;255;255m\n", err.Error())
//} else {
// m.logger.Println("\x1b[38;2;0;255;0mInternet OK\x1b[38;2;255;255;255m")
//}
//
//m.sms = sms.New(log.New(submodulesLogger, "modem-sms : ", log.LstdFlags), m.port)
//if err := m.sms.Init(); err != nil {
// m.logger.Printf("\x1b[38;2;255;0;0mSMS: %s\x1b[38;2;255;255;255m\n", err.Error())
//} else {
// m.logger.Println("\x1b[38;2;0;255;0mSMS OK\x1b[38;2;255;255;255m")
//}
// m.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port)
// if err := m.ic.Init(); err != nil {
// m.logger.Printf("\x1b[38;2;255;0;0mInternet: %s\x1b[38;2;255;255;255m\n", err.Error())
// } else {
// m.logger.Println("\x1b[38;2;0;255;0mInternet OK\x1b[38;2;255;255;255m")
// }
m.sms = sms.New(log.New(submodulesLogger, "modem-sms : ", log.LstdFlags), m.port)
if err := m.sms.Init(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mSMS: %s\x1b[38;2;255;255;255m\n", err.Error())
} else {
m.logger.Println("\x1b[38;2;0;255;0mSMS OK\x1b[38;2;255;255;255m")
}
m.gps = gps.New(log.New(submodulesLogger, "modem-gps : ", log.LstdFlags), m.port)
if err := m.gps.Init(); err != nil {
@ -180,6 +180,13 @@ func (m *modem) PowerOff() error {
return err
}
func (m *modem) restart() error {
m.PowerOff()
time.Sleep(10 * time.Second)
m.PowerOn()
return nil
}
func (m *modem) Sms() sms.Sms {
return m.sms
}
@ -272,7 +279,7 @@ func (m *modem) printCmd(cmd string) {
m.logger.Println("FAILED TO SEND REQ", cmd, ":", err.Error())
} else {
_ = resp
// m.logger.Println("CMD", cmd, ":", resp)
m.logger.Println("CMD", cmd, ":", resp)
}
}
@ -287,7 +294,9 @@ func (m *modem) setupPort() error {
return fmt.Errorf("reset output buffer: %w", err)
}
// m.restart()
// These commands ensure that correct modes are set
// m.port.RawSend("\r\n\x1A\r\n") // Sometimes enables echo mode
m.printCmd("ATE0") // Sometimes enables echo mode
m.printCmd("AT+CGPSFTM=0") // Sometimes does not turn off nmea
m.printCmd("AT+CMEE=2") // Turn on errors describtion
@ -315,8 +324,10 @@ func (m *modem) checkPort(portName string) (outErr error) {
}
defer m.port.Disconnect() // Do not bother about errors...
// m.restart()
// To filter dead ports
if _, err := m.port.Send("AT"); err != nil {
if str, err := m.port.RawSend("AT"); err != nil || len(str) == 0 {
return fmt.Errorf("ping error: %w", err)
}

View File

@ -1,69 +0,0 @@
package sms
import (
"fmt"
"strconv"
)
func DecodeError(code int) string {
switch code {
case 300:
return "ME failure"
case 301:
return "SMS service of ME reserved"
case 302:
return "Operation not allowed"
case 303:
return "Operation not supported"
case 304:
return "Invalid PDU mode parameter"
case 305:
return "Invalid text mode parameter"
case 310:
return "SIM not inserted"
case 311:
return "SIM PIN required"
case 312:
return "PH-SIM PIN required"
case 313:
return "SIM failure"
case 314:
return "SIM busy"
case 315:
return "SIM wrong"
case 316:
return "SIM PUK required"
case 317:
return "SIM PIN2 required"
case 318:
return "SIM PUK2 required"
case 320:
return "Memory failure"
case 321:
return "Invalid memory index"
case 322:
return "Memory full"
case 330:
return "SMSC address unknown"
case 331:
return "No network service"
case 332:
return "Network timeout"
case 340:
return "NO +CNMA ACK EXPECTED"
case 341:
return "Buffer overflow"
case 342:
return "SMS size more than expected"
case 500:
return "Unknown error"
}
return "UNDEFINED ERROR CODE"
}
func GetError(msg []byte) (int, error) {
if len(msg) >= len("+CMS ERROR: ")+3 && string(msg[:len("+CMS ERROR: ")]) == "+CMS ERROR: " {
return strconv.Atoi(string(msg[len("+CMS ERROR: ") : len("+CMS ERROR: ")+3]))
}
return 0, fmt.Errorf("failed to parse error")
}

105
api/modem/sms/setup.go Normal file
View File

@ -0,0 +1,105 @@
package sms
import (
"fmt"
"strconv"
"strings"
)
func (d *dialer) checkPIN() error {
// Get code
resp, err := d.port.Send("AT+CPIN?")
if err != nil {
return fmt.Errorf("AT+CPIN? request: %w", err)
}
if !resp.Check() || !resp.CheckFront("+CPIN:") {
return fmt.Errorf("AT+CPIN? error response: %s", resp)
}
code := strings.ReplaceAll(strings.ReplaceAll(strings.Split(resp.RmFront("+CPIN:").String(), "\n")[0], "\r", ""), " ", "")
if code != "READY" {
return fmt.Errorf("not READY code: %s", code)
}
d.logger.Println("PIN is ready")
return nil
}
func (d *dialer) setupMsgSt() error {
// Check for free space for messages
// !!! I use one! memory for all three bindings
// 1: read and delete
// 2: sending
// 3: write received
// First try SM
if _, err := d.port.Send(`AT+CPMS="SM","SM","SM"`); err != nil {
return fmt.Errorf(`AT+CPMS="SM","SM","SM" request: %w`, err)
}
st, err := d.getCurMsgStSize()
if err != nil {
return fmt.Errorf("SM: %w", err)
}
if st[0].Used < st[0].Total {
d.logger.Printf("Use SM message storage: %d/%d\n", st[0].Used, st[0].Total)
return nil // There is space
}
d.logger.Printf("SM message storage is full: %d/%d\n", st[0].Used, st[0].Total)
// Second try ME
if _, err := d.port.Send(`AT+CPMS="ME","ME","ME"`); err != nil {
return fmt.Errorf(`AT+CPMS="ME","ME","ME" request: %w`, err)
}
st, err = d.getCurMsgStSize()
if err != nil {
return fmt.Errorf("ME: %w", err)
}
if st[0].Used < st[0].Total {
d.logger.Printf("Use ME message storage: %d/%d\n", st[0].Used, st[0].Total)
return nil // There is space
}
d.logger.Printf("ME message storage is full: %d/%d\n", st[0].Used, st[0].Total)
// Otherwise error
return fmt.Errorf("all storages are full")
}
// Message storage
type msgSt struct {
Name string
Used int
Total int
}
// Get size of used and total mem of current memory storage
func (d *dialer) getCurMsgStSize() ([]msgSt, error) {
// Request
resp, err := d.port.Send("AT+CPMS?")
if err != nil {
return nil, fmt.Errorf("AT+CPMS? request: %w", err)
}
// Check start and end
if !resp.Check() && !resp.CheckFront("+CPMS:") {
return nil, fmt.Errorf("AT+CPMS")
}
// Remove front and cut to values
resp = resp.RmFront("+CPMS:")
values := strings.Split(strings.ReplaceAll(strings.Split(resp.String(), "\n")[0], "\r", ""), ",")
if len(values) != 9 {
return nil, fmt.Errorf("CPMS response: invalid values count: [%s]", values)
}
// Parse values
outMsgs := [3]msgSt{}
for i := 0; i < 3; i++ {
name := values[i]
used, err := strconv.Atoi(values[i*3+1])
if err != nil {
return nil, fmt.Errorf("parse value #%d: %w", i+1, err)
}
total, err := strconv.Atoi(values[i*3+2])
if err != nil {
return nil, fmt.Errorf("parse value #%d, %w", i+2, err)
}
outMsgs[i] = msgSt{name, used, total}
}
return outMsgs[:], nil
}

View File

@ -30,55 +30,51 @@ func New(logger *log.Logger, port at.Port) Sms {
}
func (d *dialer) Init() error {
// Ensure serial port
// Ensure serial port is connected
if !d.port.IsConnected() {
return fmt.Errorf("serial port is not connected")
}
// Check SIM an PIN
if resp, err := d.port.Send("AT+CPIN?"); err != nil || !resp.Check() {
if err != nil {
return fmt.Errorf("check pin: %w", err)
}
return fmt.Errorf("check pin: error response: %s", resp)
// Check ping
if err := d.checkPIN(); err != nil {
return fmt.Errorf("check PIN: %w", err)
}
// Ensure text format
d.logger.Println(d.port.Send("AT+CMGF"))
if resp, err := d.port.Send("AT+CMGF=1"); err != nil || !resp.Check() {
// Setup prefered message storage
if err := d.setupMsgSt(); err != nil {
return fmt.Errorf("setup msg storage: %w", err)
}
// Check number
if resp, err := d.port.Send("AT+CNUM"); err != nil || !resp.Check() {
if err != nil {
return fmt.Errorf("set to text format request: %w", err)
return fmt.Errorf("AT+CNUM request ")
}
return fmt.Errorf("set SIM format: error response: %s", resp)
}
return nil
}
func (d *dialer) Send(number, msg string) error {
d.port.Send(fmt.Sprintf(`AT+CMGS="%s"`, number)) // Because it will throw error
resp, err := d.port.RawSend(fmt.Sprintf("%s\n\r", msg)) // Add additional \r\n because there is not supposed to be
sresp, err := d.port.Send(fmt.Sprintf(`AT+CMGSEX="%s"`, number)) // Because it will throw error
if err != nil {
return err
}
d.logger.Println(sresp)
resp, err := d.port.RawSend(fmt.Sprintf("%s\x1A", msg)) // Add additional \r\n because there is not supposed to be
if err != nil {
return fmt.Errorf("message request: %w", err)
}
d.logger.Println("SEND RESPONSE:", resp)
resp, err = d.port.RawSend("\x1A")
if err != nil {
return fmt.Errorf("message request: %w", err)
d.logger.Println("Send response:", resp)
if !at.Resp(resp).Check() {
return fmt.Errorf("error response: %s", resp)
}
d.logger.Println("SEND RESPONSE:", resp)
errCode, err := GetError([]byte(resp))
if err != nil {
return fmt.Errorf("send sms failed and failed to get error: %w", err)
}
return fmt.Errorf("failed to send with SMS error: %d - %s", errCode, DecodeError(errCode))
return nil
}
// Reads all new messages
func (d *dialer) ReadNew() ([]string, error) {
resp, err := d.port.Send("AT+CMGL=\"UNREAD\"")
resp, err := d.port.Send("AT+CMGL=\"ALL\"")
if err != nil {
return nil, fmt.Errorf("AT+CMGL request: %w", err)
return nil, err
}
d.logger.Println("raw sms:", resp)
msgs := strings.Split(strings.Replace(string(resp), "\r", "", -1), "\n")
outMsgs := make([]string, 0)

91
main.go
View File

@ -5,6 +5,7 @@ import (
"os"
"gitea.unprism.ru/KRBL/sim-modem/api/modem"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
)
func main() {
@ -35,16 +36,14 @@ func mainE() error {
logger.Println("AAAAAAAAAAAAAAA Modem is not connected")
return nil
}
// m.PowerOff()
// time.Sleep(10 * time.Second)
// m.PowerOn()
logger.Println("||||||||||||||||| GET INFO |||||||||||||||||")
logger.Println(m.Update())
logger.Printf("DATA: %+v\n", m.GetData())
// logger.Println("||||||||||||||||| GET INFO |||||||||||||||||")
// logger.Println(m.Update())
// logger.Printf("DATA: %+v\n", m.GetData())
// logger.Println("||||||||||||||||| SEND SMS |||||||||||||||||")
// logger.Println(m.At().Send("AT+CNUM"))
logger.Println("||||||||||||||||| SMS |||||||||||||||||")
// resp, err := m.At().Send("AT+CNUM")
// logger.Println("CNUM:", resp, err)
// // if err := m.Sms().Send("+79218937173", "CGSG forever"); err != nil {
// // return err
// // }
@ -53,12 +52,78 @@ func mainE() error {
// } else {
// logger.Println("NEW:", ms)
// }
logger.Println("||||||||||||||||| Checking gps status |||||||||||||||||")
st, err := m.Gps().GetStatus()
if err != nil {
return err
Cmd := func(cmd string) {
resp, err := m.At().Send(cmd)
logger.Println(cmd, "===>", resp, err)
}
logger.Printf("GPS Status:%+v\n", st)
_ = Cmd
var resp at.Resp
var err error
buf := make([]byte, 256)
_ = resp
_ = err
_ = buf
// Select ME PMS
// resp, err = m.At().Send("AT+CPMS=?")
// logger.Println("Possible mem storages:", resp, err)
// resp, err = m.At().Send("AT+CPMS?")
// logger.Println("Prefered mem storage:", resp, err)
// Cmd("AT")
// logger.Println("SEND SMS")
// logger.Println(m.Sms().Send("+79218937173", "CGSG forever!!!"))
// m.At().RawSend("\r\n\x1A\r\n")
for {
readLen, err := m.At().SerialPort().Read(buf)
if err != nil {
return err
}
if readLen > 0 {
logger.Println(string(buf[:readLen]))
}
}
// resp, err = m.At().Send("AT+CPMS?")
// logger.Println("Prefered mem storage:", resp, err)
// resp, err = m.At().Send("AT+CREG?")
// logger.Println("Network registration:", resp, err)
// resp, err = m.At().Send("AT+CPMS?")
// logger.Println("Prefered mem storage:", resp, err)
// resp, err = m.At().Send("AT+CPMS=?")
// logger.Println("Possible mem storage:", resp, err)
// resp, err = m.At().Send("AT+CNMI?")
// logger.Println("New message indications:", resp, err)
// resp, err = m.At().Send("AT+CMGL=\"REC UNREAD\"")
// logger.Println("New messages:", resp, err)
// resp, err = m.At().Send("AT+CNMI=2,1")
// logger.Println("AT+CNMI=2,1:", resp, err)
// resp, err = m.At().Send("AT+CNMI?")
// logger.Println("New message indications:", resp, err)
// logger.Println("Reading port...")
// for {
// readLen, err := m.At().SerialPort().Read(buf)
// if err != nil {
// return err
// }
// if readLen > 0 {
// logger.Println(string(buf[:readLen]))
// }
// }
// for {
// resp, err = m.At().Send("AT+CSQ")
// logger.Println("AT+CSQ:", resp, err)
// time.Sleep(500 * time.Millisecond)
// }
// logger.Println("||||||||||||||||| Checking gps status |||||||||||||||||")
// st, err := m.Gps().GetStatus()
// if err != nil {
// return err
// }
// logger.Printf("GPS Status:%+v\n", st)
// logger.Println("Turn off", m.PowerOff())