6 Commits

15 changed files with 863 additions and 404 deletions

View File

@ -37,8 +37,9 @@ type Port interface {
Disconnect() error
IsConnected() bool
RawSend(msg string) (string, error)
RawSend(msg string, timeout time.Duration) (string, error)
Send(cmd string) (Resp, error)
SendWithTimeout(cmd string, timeout time.Duration) (Resp, error)
io.Closer
}
@ -106,7 +107,7 @@ func (p *atPort) IsConnected() bool {
}
// Low level write/read function
func (p *atPort) RawSend(msg string) (string, error) {
func (p *atPort) RawSend(msg string, timeout time.Duration) (string, error) {
p.mutex.Lock()
defer p.mutex.Unlock()
@ -114,25 +115,48 @@ func (p *atPort) RawSend(msg string) (string, error) {
if _, err := p.port.Write([]byte(msg)); err != nil {
return "", fmt.Errorf("serial port write: %w", err)
}
// time.Sleep(time.Millisecond)
time.Sleep(timeout)
// 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(outBuf), "\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", time.Microsecond)
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
return Resp(resp), nil
}
func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) {
rawResp, err := p.RawSend(cmd+"\r\n", timeout)
if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err)
}
if len(rawResp) <= 4 {
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

@ -9,12 +9,13 @@ func (resp Resp) Check() bool {
}
func (resp Resp) RmFront(str string) Resp {
if !resp.Check() {
return RespNil
}
return Resp(string(resp)[len(str):])
}
func (resp Resp) CheckFront(str string) bool {
return len(resp) >= len(str) && resp[:len(str)].String() == str
}
func (resp Resp) String() string {
return string(resp)
}

View File

@ -49,9 +49,10 @@ func (g *gps) Update() error {
if err != nil {
return fmt.Errorf("receive GPS data: %w", err)
}
if !resp.Check() {
if !resp.Check() || !resp.CheckFront("+CGPSINFO:") {
return fmt.Errorf("get GPS info: error response: %s", resp)
}
if err := g.data.decode(strings.Split(strings.Replace(resp.RmFront("+CGPSINFO:").String(), "\r", "", -1), "\n")[0]); err != nil {
g.logger.Printf("error decode: %s\n", err.Error())
}
@ -84,9 +85,9 @@ 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() {
if !resp.Check() || !resp.CheckFront("+CGPS:") {
return fmt.Errorf("get GPS mode: error response: %s", resp)
}
ans := strings.Replace(strings.Split(strings.Split(resp.RmFront("+CGPS:").String(), "\n")[0], ",")[0], " ", "", -1)
@ -97,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

@ -42,14 +42,14 @@ checkLoop:
}
st.GotResponses = true
g.logger.Println("NMEA check:", s)
// g.logger.Println("NMEA check:", s)
values := strings.Split(s, ",")
if len(values[0]) != 6 {
return StatusNil, fmt.Errorf("nmea invalid sentence: %s", s)
}
switch values[0][3:] { // Switch by content
case "GSV": // Any satelites
g.logger.Println("check GSV")
// g.logger.Println("check GSV")
// Check len
if len(values) < 17 {
g.logger.Println("GSV too small values")
@ -83,7 +83,7 @@ checkLoop:
}
st.FoundSatelitesCount = satCount
case "GSA": // Active satelites
g.logger.Println("check GSA")
// g.logger.Println("check GSA")
// Check len
if len(values) < 17 {
@ -107,7 +107,7 @@ checkLoop:
}
st.ActiveSatelitesCount = max(st.ActiveSatelitesCount, count)
case "RMC": // Minimum GPS data
g.logger.Println("check RMC")
// g.logger.Println("check RMC")
// Check len
if len(values) < 12 {
g.logger.Println("RMC too small values")
@ -125,7 +125,7 @@ checkLoop:
st.IsValidData = true
}
case "GST":
g.logger.Println("check GST")
// g.logger.Println("check GST")
// Check len
if len(values) < 8 {
g.logger.Println("GST too small values")

View File

@ -4,237 +4,152 @@ import (
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
)
var apns = map[string]string{
"Tinkoff": "m.tinkoff",
}
const pppConfigName = "annalistnet"
const pppConfig = `
# Annalist project custom internet connection
# APN:
connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T %s"
# Port:
%s
# Baudrate:
%d
noipdefault
usepeerdns
defaultroute
persist
noauth
nocrtscts
local
`
type conn struct {
logger *log.Logger
port at.Port
pppPort string
isConnectExecuted bool
isInited bool
}
type Conn interface {
Init() error
ConfigurePPP() error
Ping() bool // Is connected
Init(pppPort string) error
Connect() error
Disconnect() error
IsConnected() bool // Check interface existance
Ping() error
io.Closer
}
func New(logger *log.Logger, port at.Port) Conn {
return &conn{
logger: logger,
port: port,
logger: logger,
port: port,
isConnectExecuted: false,
isInited: false,
}
}
func (c *conn) checkPackageExist(pname string) bool {
resp, err := exec.Command("apt-mark", "showmanual", pname).Output()
c.logger.Println("CHECK:", resp)
if err != nil {
c.logger.Println("CHECK PACKAGE ERROR: ", err.Error())
return false
}
return string(resp[:len(pname)]) == pname
}
func (c *conn) ensurePackage(pname string) error {
if c.checkPackageExist(pname) {
return nil
}
return fmt.Errorf("package %s not installed", pname)
// c.logger.Println("Installing", pname, "package...")
// resp, err := exec.Command("apt-get", "install", pname).Output()
// if err != nil {
// return fmt.Errorf("execute install cmd: %w", err)
// }
// c.logger.Println(resp)
// c.logger.Println("\x1b[38;2;255;0;0mComplete\x1b[38;2;255;255;255m")
// return nil
}
// Check requirenments
func (c *conn) checkReqs() error {
// Check AT port for sure
if c.port == nil || !c.port.IsConnected() {
return fmt.Errorf("AT port is not connect or nil")
}
// Ensure all necessary packages installed
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)
// }
// Check SIM is valid
// AT+CPIN? and just check
resp, err := c.port.Send("AT+CPIN?")
if err != nil {
return fmt.Errorf("AT+CPIN? request: %w", err)
}
if !resp.Check() {
return fmt.Errorf("validate SIM: error response: %s", resp)
}
return nil
}
func (c *conn) ConfigurePPP() error {
// Get provider name and its APN
resp, err := c.port.Send("AT+CSPN?")
if err != nil {
return fmt.Errorf("AT+CSPN? request: %w", err)
}
if !resp.Check() {
return fmt.Errorf("get provider: error response: %s", resp)
}
strs := strings.Split(string(resp), "\"")
if len(strs) < 3 {
return fmt.Errorf("parse AT+CSPN response: %s", string(resp))
}
provider := strs[1]
apn := apns[provider]
if apn == "" {
return fmt.Errorf("no apn for provider: %s", provider)
}
// Make config
c.logger.Printf("Config values: %s, %s, %d", apn, c.port.GetName(), c.port.GetBaudrate())
config := fmt.Sprintf(pppConfig, apn, c.port.GetName(), c.port.GetBaudrate())
// Write to file
f, err := os.OpenFile("/etc/ppp/peers/"+pppConfigName, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return fmt.Errorf("open ppp config file %w", err)
}
defer f.Close()
if _, err := f.Write([]byte(config)); err != nil {
return fmt.Errorf("write to ppp config file: %w", err)
}
return nil
}
func (c *conn) setup() error {
c.logger.Println("Check requirenments...")
if err := c.checkReqs(); err != nil {
return fmt.Errorf("check requirenments: %w", err)
}
// DEBUG show networks and signal
resp, err := c.port.Send("AT+COPS?")
if err != nil {
return fmt.Errorf("AT+COPS? request: %w", err)
}
c.logger.Println("DEBUG networks:", resp)
resp, err = c.port.Send("AT+CSQ")
if err != nil {
return fmt.Errorf("AT+CSQ request: %w", err)
}
c.logger.Println("DEBUG signal quality:", resp)
// Configure ppp
// what is better ASK If /etc/ppp/peers/annalistnet not exists
c.logger.Println("Configure ppp...")
if err := c.ConfigurePPP(); err != nil {
return fmt.Errorf("configure ppp: %w", err)
}
return nil
}
func (c *conn) connect() error {
resp, err := exec.Command("pon", pppConfigName).Output()
if err != nil {
return fmt.Errorf("execute connect cmd: %w", err)
}
c.logger.Println("DEBUG pon response:", string(resp))
return nil
}
func (c *conn) diconnect() error {
resp, err := exec.Command("poff", pppConfigName).Output()
if err != nil {
return fmt.Errorf("execute disconnect cmd: %w", err)
}
c.logger.Println("DEBUG poff response:", string(resp))
return nil
}
func (c *conn) Init() error {
// Setup
c.logger.Println("Setup...")
func (c *conn) Init(pppPort string) error {
c.pppPort = pppPort
// Setup only setup
if err := c.setup(); err != nil {
return fmt.Errorf("setup: %w", err)
}
// Connect
c.logger.Println("Connect...")
if err := c.connect(); err != nil {
return fmt.Errorf("connect: %w", err)
}
//DEBUG
resp, err := exec.Command("ifconfig").Output()
if err != nil {
return fmt.Errorf("execute ifconfig cmd: %w", err)
}
c.logger.Println("DEBUG ifconfig resp:", string(resp))
// Test connectin using Ping
c.logger.Println("Test...")
if !c.Ping() {
return fmt.Errorf("ping failed")
}
c.isInited = true
return nil
}
func (c *conn) Ping() bool {
func (c *conn) Connect() error {
if !c.isInited {
return fmt.Errorf("internet submodule is not inited")
}
// Check is already connected
if c.isConnectExecuted {
return fmt.Errorf("already connected")
}
// Check signal
// if ok, err := utils.CheckService(c.port, c.logger); err != nil || !ok {
// if err != nil {
// return fmt.Errorf("check service: %w", err)
// }
// return fmt.Errorf("no service")
// }
resp, err := exec.Command("pon", pppConfigName).Output()
if err != nil {
return fmt.Errorf("execute pon cmd: %w", err)
}
if len(resp) > 0 {
c.logger.Println("pon response:", string(resp))
}
c.isConnectExecuted = true
// Set default route
c.logger.Println("\x1b[38;2;0;255;0mInternet connected.\x1b[38;2;255;255;255m")
return nil
}
func (c *conn) Disconnect() error {
// return nil // Temporary do not turn off inet
if !c.isConnectExecuted {
return nil
// return fmt.Errorf("internet is not connected")
}
c.isConnectExecuted = false
resp, err := exec.Command("poff", pppConfigName).Output()
if err != nil {
return fmt.Errorf("execute poff cmd: %w", err)
}
if len(resp) > 0 {
c.logger.Println("poff response:", string(resp))
}
c.isConnectExecuted = false
return nil
}
func (c *conn) Ping() error {
// Test - try to connect to Google DNS
// ping -I ppp0 8.8.8.8
resp, err := exec.Command("ping", "8.8.8.8").Output()
if err != nil {
c.logger.Println("Ping 1 cmd error:", err)
c.logger.Println("Ping default interface cmd error:", err)
}
c.logger.Println("Ping 1 resp:", string(resp))
c.logger.Println("Ping default interface resp:", string(resp))
resp, err = exec.Command("ping", "-I", "ppp0", "8.8.8.8").Output()
if err != nil {
c.logger.Println("Ping 2 cmd error:", err)
c.logger.Println("Ping ppp0 interface cmd error:", err)
}
c.logger.Println("Ping 2 resp:", string(resp))
c.logger.Println("Ping ppp0 interface resp:", string(resp))
return !(strings.Contains(string(resp), "Destination Host Unreachable") || strings.Contains(string(resp), "Destination Net Unreachable")) // tmp solution
if strings.Contains(string(resp), "Destination Host Unreachable") || strings.Contains(string(resp), "Destination Net Unreachable") {
return fmt.Errorf("ping response: %s", string(resp))
}
return nil
}
func (c *conn) IsConnected() bool {
if !c.isConnectExecuted {
return false
}
// Make "ifconfig" request
resp, err := exec.Command("ifconfig").Output()
if err != nil {
c.logger.Println("ifconfig cmd error:", err.Error())
return false
}
lines := strings.Split(string(resp), "\n")
for _, l := range lines {
if len(l) == 0 {
continue
}
if l[0] == ' ' {
continue
}
interfaceName := strings.Split(l, ":")[0]
if interfaceName == "ppp0" {
return true
}
}
return false // Did not found
}
func (c *conn) Close() error {
if err := c.diconnect(); err != nil {
c.isInited = false
if err := c.Disconnect(); err != nil {
return fmt.Errorf("diconnect: %w", err)
}
return nil

131
api/modem/internet/setup.go Normal file
View File

@ -0,0 +1,131 @@
package internet
import (
"fmt"
"os"
"os/exec"
"strings"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/utils"
)
var apns = map[string]string{
"Tinkoff": "m.tinkoff",
"Megafon": "internet",
"BeeLine": "internet.beeline.ru",
}
const pppConfigName = "annalistnet"
const pppConfig = `
# Annalist project custom internet connection
# APN:
connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T %s"
# Port:
%s
# Baudrate:
%d
noipdefault
usepeerdns
defaultroute
persist
noauth
nocrtscts
local
`
func (c *conn) setup() error {
if err := c.checkReqs(); err != nil {
return fmt.Errorf("check requirenments: %w", err)
}
// Configure ppp
// what is better ASK If /etc/ppp/peers/annalistnet not exists
if err := c.configurePPP(); err != nil {
return fmt.Errorf("configure ppp: %w", err)
}
return nil
}
// Check requirenments
func (c *conn) checkReqs() error {
// Check AT port for sure
if c.port == nil || !c.port.IsConnected() {
return fmt.Errorf("AT port is not connect or nil")
}
// Ensure all necessary packages installed
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)
// }
// Check SIM is valid
if err := utils.CheckPIN(c.port, c.logger); err != nil {
return fmt.Errorf("PIN check: %w", err)
}
return nil
}
func (c *conn) ensurePackage(pname string) error {
if c.checkPackageExist(pname) {
return nil
}
return fmt.Errorf("package %s not installed", pname)
// c.logger.Println("Installing", pname, "package...")
// resp, err := exec.Command("apt-get", "install", pname).Output()
// if err != nil {
// return fmt.Errorf("execute install cmd: %w", err)
// }
// c.logger.Println(resp)
// c.logger.Println("\x1b[38;2;255;0;0mComplete\x1b[38;2;255;255;255m")
// return nil
}
func (c *conn) checkPackageExist(pname string) bool {
resp, err := exec.Command("apt-mark", "showmanual", pname).Output()
if err != nil {
c.logger.Println("check package error: ", err.Error())
return false
}
return string(resp[:len(pname)]) == pname
}
func (c *conn) configurePPP() error {
// Get provider name and its APN
resp, err := c.port.Send("AT+CSPN?")
if err != nil {
return fmt.Errorf("AT+CSPN? request: %w", err)
}
if !resp.Check() {
return fmt.Errorf("get provider: error response: %s", resp)
}
strs := strings.Split(string(resp), "\"")
if len(strs) < 3 {
return fmt.Errorf("parse AT+CSPN response: %s", string(resp))
}
provider := strs[1]
apn := apns[provider]
if apn == "" {
return fmt.Errorf("no apn for provider: %s", provider)
}
// Make config
c.logger.Printf("Config ppp values: %s, %s, %d", apn, c.pppPort, c.port.GetBaudrate())
config := fmt.Sprintf(pppConfig, apn, c.pppPort, c.port.GetBaudrate())
// Write to file
f, err := os.OpenFile("/etc/ppp/peers/"+pppConfigName, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return fmt.Errorf("open ppp config file %w", err)
}
defer f.Close()
if _, err := f.Write([]byte(config)); err != nil {
return fmt.Errorf("write to ppp config file: %w", err)
}
return nil
}

View File

@ -6,6 +6,7 @@ import (
"log"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
@ -17,6 +18,9 @@ import (
"gitea.unprism.ru/KRBL/sim-modem/api/modem/sms"
)
// yy/MM/dd,hh:mm:ss+zzzz
const timeLayout = "06/01/02,15:04:05-0700"
type ModemData struct {
Port string `json:"Port"`
gps.Data
@ -53,14 +57,16 @@ type Modem interface {
IsConnected() bool
Update() error
GetData() ModemData
GetTime() (time.Time, error)
PowerOn() error
PowerOff() error
// Access to SMS, GPS, AT interfaces mostly for debug
At() at.Port // Send
Gps() gps.Gps // Update, GetData, GetStatus
Sms() sms.Sms // Send, ReadNew
At() at.Port // Send
Gps() gps.Gps // Update, GetData, GetStatus
Sms() sms.Sms // Send, ReadNew
Ic() internet.Conn // Connect, Disconnect
io.Closer
}
@ -80,29 +86,41 @@ func (m *modem) Init() error {
defer m.mutex.Unlock()
// Turn module on
m.logger.Println("=============================== Turn on module")
m.logger.Println("=============================== Init gpio")
if err := m.onOffPin.Init(); err != nil {
return fmt.Errorf("gpio pin init: %w", err)
}
// Search
m.logger.Println("=============================== Search")
// Soft search
if err := m.searchPort(true); err != nil {
ports, err := m.searchPort(true)
if err != nil {
return fmt.Errorf("soft port search: %w", err)
}
// Wide search
if m.port == nil {
if err := m.searchPort(false); err != nil {
return fmt.Errorf("not soft port search: %w", err)
if len(ports) == 0 {
// Wide search
ports, err := m.searchPort(true)
if err != nil {
return fmt.Errorf("wide port search: %w", err)
}
if len(ports) == 0 {
return fmt.Errorf("no AT ports found")
}
}
if m.port == nil {
return fmt.Errorf("no port is detected")
if len(ports) == 1 {
// return fmt.Errorf("only one AT port found")
m.logger.Println("!!!!! only one AT port found")
}
// !!!!
// Internet connection and serial connection on one port is impossible, so:
// port[0] is for serial
// port[1] is for internet(ppp)
m.logger.Println(ports)
// Connect
m.logger.Println("=============================== Connect")
m.port = at.New(m.logger, ports[0], m.baudrate)
if err := m.connect(); err != nil {
return fmt.Errorf("connect: %w", err)
}
@ -112,19 +130,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.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port)
// if err := m.ic.Init(ports[1]); 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.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 {
@ -170,6 +188,35 @@ func (m *modem) GetData() ModemData {
}
}
func (m *modem) GetTime() (time.Time, error) {
// Make request
resp, err := m.port.Send("AT+CCLK?")
if err != nil {
return time.Time{}, err
}
if !resp.Check() || !resp.CheckFront("+CCLK: ") {
return time.Time{}, fmt.Errorf("CCLK? error response: %s", resp.String())
}
// Extract time string
values := strings.Split(strings.Split(resp.String(), "\n")[0], "\"")
if len(values) < 2 {
return time.Time{}, fmt.Errorf("invalid values (len): [%s]", values)
}
timeStr := values[1]
if len(timeStr) != len("yy/MM/dd,hh:mm:ss+zz") {
return time.Time{}, fmt.Errorf("invalid time string: %s", timeStr)
}
// Convert time zone
timeZoneStr := timeStr[len(timeStr)-2:]
timeZone, err := strconv.Atoi(timeZoneStr)
if err != nil {
return time.Time{}, fmt.Errorf("parse time zone: %w", err)
}
timeStr = fmt.Sprintf("%s%02d%02d", timeStr[:len(timeStr)-2], timeZone/4, timeZone%4)
// Parse to golang time
return time.Parse(timeLayout, timeStr)
}
func (m *modem) PowerOn() error {
m.onOffPin.PowerOn() // DEBUG do not want to wait 30 seconds
return nil
@ -180,6 +227,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
}
@ -192,24 +246,38 @@ func (m *modem) At() at.Port {
return m.port
}
func (m *modem) Ic() internet.Conn {
return m.ic
}
func (m *modem) Close() error { // I can't return error so I log it
m.mutex.Lock()
defer m.mutex.Unlock()
if err := m.sms.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose sms error: %s\x1b[38;2;255;255;255m\n", err.Error())
// Close submodules
if m.sms != nil {
if err := m.sms.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose sms error: %s\x1b[38;2;255;255;255m\n", err.Error())
}
}
if m.ic != nil {
if err := m.ic.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose internet error: %s\x1b[38;2;255;255;255m\n", err.Error())
}
}
if m.gps != nil {
if err := m.gps.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose gps error: %s\x1b[38;2;255;255;255m\n", err.Error())
}
}
if err := m.port.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose serial port error: %s\x1b[38;2;255;255;255m\n", err.Error())
}
// Close gpio and serial
if err := m.onOffPin.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose gpio pin error: %s\x1b[38;2;255;255;255m\n", err.Error())
}
if err := m.ic.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose internet connection error: %s\x1b[38;2;255;255;255m\n", err.Error())
if err := m.port.Close(); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mclose serial port error: %s\x1b[38;2;255;255;255m\n", err.Error())
}
return nil
}
@ -269,10 +337,10 @@ func (m *modem) saveGPS(path string) error {
// Short way to send command
func (m *modem) printCmd(cmd string) {
if resp, err := m.port.Send(cmd); err != nil {
m.logger.Println("FAILED TO SEND REQ", cmd, ":", err.Error())
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 +355,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
@ -295,6 +365,16 @@ func (m *modem) setupPort() error {
return nil
}
func (m *modem) checkCurPortDead() error {
if resp, err := m.port.RawSend("AT\r\n", 20*time.Millisecond); err != nil || len(resp) == 0 {
if err != nil {
return fmt.Errorf("raw send: %w", err)
}
return fmt.Errorf("read 0")
}
return nil
}
func (m *modem) checkPort(portName string) (outErr error) {
defer func() {
if outErr != nil { // Clear port if there is error
@ -315,9 +395,12 @@ 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 {
return fmt.Errorf("ping error: %w", err)
if err := m.checkCurPortDead(); err != nil {
return fmt.Errorf("echo: %w", err)
}
if err := m.setupPort(); err != nil {
@ -353,34 +436,6 @@ func (m *modem) checkPort(portName string) (outErr error) {
return nil
}
func (m *modem) searchPort(isSoft bool) error {
// Get ports
ports := []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}
if !isSoft {
ps, err := getTtyDevices()
if err != nil {
fmt.Errorf("get serial devices: %w", err)
}
ports = ps
}
// Check ports
SearchLoop:
for _, p := range ports {
m.logger.Printf("Checking port %s ...\n", p)
if err := m.checkPort("/dev/" + p); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mCheck failed: %s\x1b[38;2;255;255;255m\n", err.Error())
continue SearchLoop
}
m.logger.Print("Found modem on port: ", p)
m.port = at.New(m.logger, "/dev/"+p, m.baudrate)
return nil
}
return nil
}
func (m *modem) ping() error {
resp, err := m.port.Send("AT")
if err != nil {
@ -392,7 +447,36 @@ func (m *modem) ping() error {
return nil
}
func getTtyDevices() ([]string, error) {
func (m *modem) searchPort(isSoft bool) ([]string, error) {
// Get ports
ports, err := getTtyPorts(isSoft)
if err != nil {
fmt.Errorf("get devices: %w", err)
}
// Check ports
return m.getAtPorts(ports)
}
func (m *modem) getAtPorts(ports []string) ([]string, error) {
outPorts := make([]string, 0)
for _, p := range ports {
m.logger.Printf("Checking port %s ...\n", p)
if err := m.checkPort("/dev/" + p); err != nil {
m.logger.Printf("\x1b[38;2;255;0;0mCheck failed: %s\x1b[38;2;255;255;255m\n", err.Error())
continue
}
m.logger.Print("Found AT port: ", p)
outPorts = append(outPorts, "/dev/"+p)
}
return outPorts, nil
}
func getTtyPorts(isSoft bool) ([]string, error) {
if isSoft {
return []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}, nil
}
// Get ports
/**/
out, err := exec.Command("ls", "--", "/dev/tty[!0-9]*").Output()

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")
}

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

@ -0,0 +1,88 @@
package sms
import (
"fmt"
"strconv"
"strings"
)
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

@ -5,8 +5,10 @@ import (
"io"
"log"
"strings"
"time"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/utils"
)
type dialer struct {
@ -30,55 +32,61 @@ 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 ping
if err := utils.CheckPIN(d.port, d.logger); err != nil {
return fmt.Errorf("check PIN: %w", err)
}
// Setup prefered message storage
// if err := d.setupMsgSt(); err != nil { // Does not use store now
// d.logger.Printf("ERROR setup msg storage: %s\n", err.Error())
// }
// Check SIM an PIN
if resp, err := d.port.Send("AT+CPIN?"); err != nil || !resp.Check() {
// Set notifications into console
if resp, err := d.port.Send("AT+CNMI=2,2"); err != nil || !resp.Check() {
if err != nil {
return fmt.Errorf("check pin: %w", err)
return err
}
return fmt.Errorf("check pin: error response: %s", resp)
return fmt.Errorf("CNMI= error response: %s", resp.String())
}
// Ensure text format
d.logger.Println(d.port.Send("AT+CMGF"))
if resp, err := d.port.Send("AT+CMGF=1"); err != nil || !resp.Check() {
// 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 err
}
return fmt.Errorf("set SIM format: error response: %s", resp)
return fmt.Errorf("CNUM error response: %s", resp.String())
}
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), time.Millisecond) // 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)

View File

@ -0,0 +1,37 @@
package utils
import (
"fmt"
"log"
"strings"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
)
func CheckService(port at.Port, logger *log.Logger) (bool, error) {
srv, err := getService(port)
if err != nil {
return false, fmt.Errorf("get service: %w", err)
}
if srv == "NO SERVICE" {
return false, nil
}
logger.Println("Current service:", srv)
return true, nil
}
// Returns service
func getService(port at.Port) (string, error) {
resp, err := port.Send("AT+CPSI?")
if err != nil {
return "", err
}
if !resp.Check() || !resp.CheckFront("+CPSI: ") {
return "", fmt.Errorf("error response: %s", resp)
}
values := strings.Split(strings.ReplaceAll(strings.Split(resp.RmFront("+CPSI: ").String(), "\n")[0], "\r", ""), ",")
if len(values) < 2 {
return "", fmt.Errorf("invalid values(len): [% s]", values)
}
return values[0], nil
}

109
api/modem/utils/signal.go Normal file
View File

@ -0,0 +1,109 @@
package utils
import (
"fmt"
"log"
"strconv"
"strings"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
)
func CheckSignal(port at.Port, logger *log.Logger) (bool, error) {
rssi, ber, err := getSignalQuality(port)
if err != nil {
return false, fmt.Errorf("get signal quality: %w", err)
}
logger.Printf("check signal: rssi=%d ber=%d\n", rssi, ber)
if err := checkRssi(rssi); err != nil {
return false, nil
}
if err := checkBer(ber); err != nil {
logger.Printf("bad ber(not critical): %s", err.Error()) // Happened not to be critical
}
return true, nil
}
func checkRssi(rssi int) error {
// rssi - Received signal strenght indicator
// 0 -113 dBm or less
// 1 -111 dBm
// 2...30 -109... - 53 dBm
// 31 -51 dBm or greater
// 99 not known or not detectable
// 100 -116 dBm or less
// 101 -115 dBm
// 102…191 -114... - 26dBm
// 191 -25 dBm or greater
// 199 not known or not detectable
// 100…199 expand to TDSCDMA, indicate RSCPreceived
if rssi <= 2 { // Too poor
return fmt.Errorf("too poor <= -109dBm")
}
if rssi > 2 && rssi <= 31 {
return nil // Can live
}
if rssi == 99 {
return fmt.Errorf("not known or not detectable")
}
if rssi >= 100 && rssi <= 102 {
return fmt.Errorf("too poor <= -114dBm")
}
if rssi > 102 && rssi <= 191 {
return nil // Can live
}
if rssi == 199 {
return fmt.Errorf("not known or not detectable")
}
return fmt.Errorf("invalid code %d", rssi)
}
func checkBer(ber int) error {
// ber - Bit error rate
// 0 <0.01%
// 1 0.01% --- 0.1%
// 2 0.1% --- 0.5%
// 3 0.5% --- 1.0%
// 4 1.0% --- 2.0%
// 5 2.0% --- 4.0%
// 6 4.0% --- 8.0%
// 7 >=8.0%
// 99 not known or not detectable
if ber >= 0 && ber <= 3 {
// ber -> [0%;1%)
// Ok, can live
return nil
}
if ber >= 4 && ber <= 7 {
return fmt.Errorf("too high: %d code", ber)
}
if ber == 99 {
return fmt.Errorf("not known or not detectable")
}
return fmt.Errorf("invalid code %d", ber)
}
// Returns rssi and ber(look above)
func getSignalQuality(port at.Port) (int, int, error) {
resp, err := port.Send("AT+CSQ")
if err != nil {
return 99, 99, err
}
if !resp.Check() || !resp.CheckFront("+CSQ: ") {
return 99, 99, fmt.Errorf("error response: %s", resp)
}
values := strings.Split(strings.ReplaceAll(strings.Split(resp.RmFront("+CSQ: ").String(), "\n")[0], "\r", ""), ",")
if len(values) != 2 {
return 99, 99, fmt.Errorf("invalid values(len): [% s]", values)
}
rssi, err := strconv.Atoi(values[0])
if err != nil {
return 99, 99, fmt.Errorf("parse rssi: %w", err)
}
ber, err := strconv.Atoi(values[1])
if err != nil {
return 99, 99, fmt.Errorf("parse ber: %w", err)
}
return rssi, ber, nil
}

26
api/modem/utils/sim.go Normal file
View File

@ -0,0 +1,26 @@
package utils
import (
"fmt"
"log"
"strings"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
)
func CheckPIN(port at.Port, logger *log.Logger) error {
// Get code
resp, err := 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)
}
logger.Println("PIN is ready")
return nil
}

162
main.go
View File

@ -1,59 +1,163 @@
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
"gitea.unprism.ru/KRBL/sim-modem/api/modem"
)
func main() {
log.Println("CGSG forever!!!")
if err := mainE(); err != nil {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
// Just for logs
go func(ctx context.Context) {
<-ctx.Done()
log.Println("GOT INTERUPT SIGNAL")
}(ctx)
if err := mainE(ctx); err != nil {
log.Println("MAIN finished with error:", err.Error())
}
log.Println("END")
}
func mainE() error {
func mainE(ctx context.Context) error {
logger := log.New(os.Stdout, "main : ", log.LstdFlags)
m := modem.New(log.New(logger.Writer(), "modem : ", log.LstdFlags))
logger.Println("||||||||||||||||| INIT |||||||||||||||")
if err := m.Init(); err != nil {
logger.Println("Init ended with error:", err.Error())
logger.Println("Try to turn on")
if err := m.PowerOn(); err != nil {
return err
}
logger.Println("Reinit")
if err := m.Init(); err != nil {
return err
initLoop:
for {
select {
case <-ctx.Done():
logger.Println("Break init loop")
return nil
default:
if err := m.Init(); err != nil {
logger.Println("Init ended with error:", err.Error())
// logger.Println("Turn on...")
// if err := m.PowerOn(); err != nil {
// logger.Println("Turn on error:", err.Error())
// }
continue initLoop
}
break initLoop
}
}
if !m.IsConnected() {
logger.Println("AAAAAAAAAAAAAAA Modem is not connected")
logger.Println("Modem is not connected")
return nil
}
logger.Println("||||||||||||||||| GET INFO |||||||||||||||||")
logger.Println(m.Update())
logger.Printf("DATA: %+v\n", m.GetData())
defer func() {
logger.Println("||||||||||||||||| CLOSE |||||||||||||||")
m.Close()
}()
logger.Println("||||||||||||||||| SEND SMS |||||||||||||||||")
logger.Println(m.At().Send("AT+CNUM"))
// if err := m.Sms().Send("+79218937173", "CGSG forever"); err != nil {
// Connect to internet
// if err := m.Ic().Connect(); err != nil {
// return fmt.Errorf("connect to internet: %w", err)
// }
logger.Println("||||||||||||||||| SMS |||||||||||||||||")
Cmd := func(cmd string) {
resp, err := m.At().SendWithTimeout(cmd, 50*time.Millisecond)
logger.Println(cmd, "===>", resp, err)
}
_ = Cmd
// Select ME PMS
// logger.Println("SEND SMS")
// logger.Println(m.Sms().Send("+79218937173", "CGSG forever!!!"))
// m.At().RawSend("\r\n\x1A\r\n")
// Cmd("AT+CREG?")
// Cmd("AT+CNMI?")
// Cmd("AT+CSQ")
// Cmd("AT+CSCA?")
// Cmd("AT+CPOL?")
// Cmd("AT+COPS?")
// // Cmd("AT+COPS=?")
// Cmd("AT+CPSI?")
// resp, err = m.At().Send("AT+CNMI=2,2")
for {
select {
case <-ctx.Done():
logger.Println("Break main loop")
return nil
default:
// Cmd("AT+CPSI?")
// Cmd("AT+CSQ")
// Cmd("AT+CCLK?")
// logger.Println(m.Gps().GetStatus())
// m.Update()
// st, _ := m.Gps().GetStatus()
// logger.Printf("GPS STATUS: %+v", st)
// logger.Printf("GPS DATA: %+v", m.GetData())
logger.Println(m.GetTime())
time.Sleep(2 * time.Second)
}
// Cmd("AT+CSQ")
// Cmd("AT+COPS?")
// if err := m.CheckSignal(); err != nil {
// logger.Println(err)
// } else {
// logger.Println("AAAAAAAAAAA THERE IS SIGNAL")
// }
// 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
// }
if ms, err := m.Sms().ReadNew(); err != nil {
return err
} else {
logger.Println("NEW:", ms)
}
logger.Println("||||||||||||||||| Checking gps status |||||||||||||||||")
st, err := m.Gps().GetStatus()
if err != nil {
return err
}
logger.Printf("GPS Status:%+v\n", st)
// logger.Printf("GPS Status:%+v\n", st)
// logger.Println("Turn off", m.PowerOff())