From ebab41c5105b3b6a35f731e81c50c46e30220174 Mon Sep 17 00:00:00 2001 From: Andrey Egorov Date: Sun, 28 Jul 2024 21:01:18 +0300 Subject: [PATCH] Added draft SMS support. --- api/modem/at/response.go | 4 +++ api/modem/gps.go | 1 + api/modem/internet/ic.go | 6 ++-- api/modem/sms/errors.go | 69 ++++++++++++++++++++++++++++++++++++++++ api/modem/sms/sms.go | 61 +++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 api/modem/sms/errors.go create mode 100644 api/modem/sms/sms.go diff --git a/api/modem/at/response.go b/api/modem/at/response.go index c09199c..aa1a4a3 100644 --- a/api/modem/at/response.go +++ b/api/modem/at/response.go @@ -15,3 +15,7 @@ func (resp Resp) RmFront(str string) Resp { func (resp Resp) String() string { return string(resp) } + +func (resp Resp) Bytes() []byte { + return []byte(resp) +} diff --git a/api/modem/gps.go b/api/modem/gps.go index ed88d39..6cfd62b 100644 --- a/api/modem/gps.go +++ b/api/modem/gps.go @@ -37,6 +37,7 @@ func (gps *GpsData) calculateSpeed(newLatitude, newLongitude float64, lastUpdate gps.Speed = earthRad * c / (math.Abs(float64(time.Since(lastUpdateTime)))) } +// Parse string from AT command that contains gps data func (gps *GpsData) decode(str string) error { var err error newGpsInfo := GpsData{} diff --git a/api/modem/internet/ic.go b/api/modem/internet/ic.go index 32a65fe..29c9bef 100644 --- a/api/modem/internet/ic.go +++ b/api/modem/internet/ic.go @@ -90,9 +90,9 @@ func (c *conn) checkReqs() error { if err := c.ensurePackage("ppp"); err != nil { return fmt.Errorf("ensure ppp package: %w", err) } - if err := c.ensurePackage("net-tools"); err != nil { - return fmt.Errorf("ensure net-tools package: %w", err) - } + // if err := c.ensurePackage("net-tools"); err != nil { + // return fmt.Errorf("ensure net-tools package: %w", err) + // } // Check SIM is valid // AT+CPIN? and just check diff --git a/api/modem/sms/errors.go b/api/modem/sms/errors.go new file mode 100644 index 0000000..6fe2e19 --- /dev/null +++ b/api/modem/sms/errors.go @@ -0,0 +1,69 @@ +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") +} diff --git a/api/modem/sms/sms.go b/api/modem/sms/sms.go new file mode 100644 index 0000000..58cc9b7 --- /dev/null +++ b/api/modem/sms/sms.go @@ -0,0 +1,61 @@ +package sms + +import ( + "fmt" + "io" + "log" + "strings" + + "github.com/CGSG-2021-AE4/modem-test/api/modem/at" +) + +type dialer struct { + logger *log.Logger + port at.Port +} + +type Dialer interface { + Init() error + Send(number, msg string) error + ReadNew() ([]string, error) + io.Closer +} + +func (d *dialer) Init() error { + // Ensure serial port + if !d.port.IsConnected() { + return fmt.Errorf("serial port is not connected") + } + // Ensure text format + if resp, err := d.port.Send("AT+CMGF=1"); err != nil || !resp.Check() { + if err != nil { + return fmt.Errorf("AT+CMGF=1 request: %w", err) + } + return fmt.Errorf("failed to set SMS format") + } + return nil +} + +func (d *dialer) Send(number, msg string) error { + cmd := fmt.Sprintf("AT+CMGS=\"%s\"\r%s%c", number, msg, 26) + if resp, err := d.port.Send(cmd); err != nil || !resp.Check() { + if err != nil { + return fmt.Errorf("AT+CGMS= request: %w", err) + } + if errCode, err := GetError(resp.Bytes()); err != nil { + return fmt.Errorf("failed to send with SMS error: %s", DecodeError(errCode)) + } + return fmt.Errorf("failed to send SMS") + } + return nil +} + +// Reads all new messages +func (d *dialer) ReadNew() ([]string, error) { + resp, err := d.port.Send("AT+CMGL") + if err != nil { + return nil, fmt.Errorf("AT+CMGL request: %w", err) + } + msgs := strings.Split(strings.Replace(string(resp), "\r", "", -1), "\n") + return msgs, nil // TODO +}