Improved SMS.
This commit is contained in:
@ -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
105
api/modem/sms/setup.go
Normal 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
|
||||
}
|
@ -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)
|
||||
|
Reference in New Issue
Block a user