Compare commits

..

No commits in common. "master" and "v0.1.6" have entirely different histories.

10 changed files with 100 additions and 370 deletions

View File

@ -4,4 +4,4 @@ export GOARM=6
export CGO_ENABLED=0 export CGO_ENABLED=0
build: build:
@go build -o out/out main.go @go build -o out/modem main.go

View File

@ -37,8 +37,7 @@ type Port interface {
Disconnect() error Disconnect() error
IsConnected() bool IsConnected() bool
RawSend(msg string) error RawSend(msg string, timeout time.Duration) (string, error)
RawRead(timeout time.Duration) (string, error)
Send(cmd string) (Resp, error) Send(cmd string) (Resp, error)
SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error)
@ -107,53 +106,42 @@ func (p *atPort) IsConnected() bool {
return p.port != nil return p.port != nil
} }
func (p *atPort) RawRead(timeout time.Duration) (string, error) { // Low level write/read function
func (p *atPort) RawSend(msg string, timeout time.Duration) (string, error) {
p.mutex.Lock() p.mutex.Lock()
defer p.mutex.Unlock() defer p.mutex.Unlock()
deadline := time.Now().Add(timeout) // Write
if _, err := p.port.Write([]byte(msg)); err != nil {
return "", fmt.Errorf("serial port write: %w", err)
}
time.Sleep(timeout)
// Read
outBuf := make([]byte, 0) outBuf := make([]byte, 0)
readLoop: readLoop:
for { for {
readLen, err := p.port.Read(p.inputBuf) readLen, err := p.port.Read(p.inputBuf)
if err != nil { if err != nil {
return "", fmt.Errorf("port read: %w", err) return "", fmt.Errorf("port read: %w", err)
} }
if readLen == 0 && time.Now().After(deadline) { if readLen == 0 {
break readLoop break readLoop
} }
outBuf = append(outBuf, p.inputBuf[:readLen]...) outBuf = append(outBuf, p.inputBuf[:readLen]...)
// if readLen < len(p.inputBuf) { if readLen < len(p.inputBuf) {
// break readLoop break readLoop
// } }
} }
// p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(outBuf), "\x1b[38;2;255;255;255m") // p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(outBuf), "\x1b[38;2;255;255;255m")
return string(outBuf), nil return string(outBuf), nil
} }
// Low level write/read function
func (p *atPort) RawSend(msg string) error {
p.mutex.Lock()
defer p.mutex.Unlock()
// Write
if _, err := p.port.Write([]byte(msg)); err != nil {
return fmt.Errorf("serial port write: %w", err)
}
return nil
}
func (p *atPort) Send(cmd string) (Resp, error) { func (p *atPort) Send(cmd string) (Resp, error) {
err := p.RawSend(cmd + "\r\n") rawResp, err := p.RawSend(cmd+"\r\n", time.Microsecond)
if err != nil { if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err) return RespNil, fmt.Errorf("%s request: %w", cmd, err)
} }
rawResp, err := p.RawRead(ReadTimeout)
if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err)
}
if len(rawResp) <= 4 { if len(rawResp) <= 4 {
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp)) return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
} }
@ -163,15 +151,10 @@ func (p *atPort) Send(cmd string) (Resp, error) {
} }
func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) { func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) {
err := p.RawSend(cmd + "\r\n") rawResp, err := p.RawSend(cmd+"\r\n", timeout)
if err != nil { if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err) return RespNil, fmt.Errorf("%s request: %w", cmd, err)
} }
rawResp, err := p.RawRead(timeout)
if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err)
}
if len(rawResp) <= 4 { if len(rawResp) <= 4 {
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp)) return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
} }

View File

@ -1,7 +1,6 @@
package gpio package gpio
import ( import (
"context"
"io" "io"
"log" "log"
"time" "time"
@ -9,10 +8,6 @@ import (
gpio "github.com/stianeikeland/go-rpio/v4" gpio "github.com/stianeikeland/go-rpio/v4"
) )
const (
waitCtxTimeout = 100 * time.Microsecond
)
type gpioPin struct { type gpioPin struct {
logger *log.Logger logger *log.Logger
pin gpio.Pin pin gpio.Pin
@ -21,7 +16,7 @@ type gpioPin struct {
type Pin interface { type Pin interface {
Init() error Init() error
PowerOn() PowerOn()
PowerOnCtx(ctx context.Context) PowerOff()
io.Closer io.Closer
} }
@ -36,47 +31,32 @@ func (p gpioPin) Init() error {
return gpio.Open() return gpio.Open()
} }
func waitCtx(ctx context.Context, timeout time.Duration) { func (p gpioPin) sendOnOffSignal() {
deadline := time.Now().Add(timeout)
for {
select {
case <-ctx.Done():
return
default:
if time.Now().After(deadline) {
return
}
}
time.Sleep(waitCtxTimeout)
}
}
func (p gpioPin) sendOnOffSignal(ctx context.Context) {
p.pin.Output() p.pin.Output()
p.logger.Println("Power on 0/3 + 100ms") p.logger.Println("Power on 0/3 + 100ms")
p.pin.Low() p.pin.Low()
p.pin.Toggle() p.pin.Toggle()
waitCtx(ctx, 100*time.Millisecond) time.Sleep(100 * time.Millisecond)
p.logger.Println("Power on 1/3 + 3s") p.logger.Println("Power on 1/3 + 3s")
p.pin.High() p.pin.High()
p.pin.Toggle() p.pin.Toggle()
waitCtx(ctx, 3*time.Second) time.Sleep(3 * time.Second)
p.logger.Println("Power on 2/3 + 30s") p.logger.Println("Power on 2/3 + 30s")
p.pin.Low() p.pin.Low()
p.pin.Toggle() p.pin.Toggle()
waitCtx(ctx, 30*time.Second) time.Sleep(30 * time.Second)
p.logger.Println("Power on 3/3") p.logger.Println("Power on 3/3")
} }
func (p gpioPin) PowerOn() { func (p gpioPin) PowerOn() {
p.sendOnOffSignal(context.Background()) p.sendOnOffSignal()
} }
func (p gpioPin) PowerOnCtx(ctx context.Context) { func (p gpioPin) PowerOff() {
p.sendOnOffSignal(ctx) p.sendOnOffSignal()
} }
func (p gpioPin) Close() error { func (p gpioPin) Close() error {

View File

@ -11,10 +11,10 @@ import (
) )
type Data struct { type Data struct {
Latitude float64 `json:"Latitude"` // ddmm.mmmmmm Latitude float64 `json:"Latitude"`
Longitude float64 `json:"Longitude"` // dddmm.mmmmmm Longitude float64 `json:"Longitude"`
LatitudeIndicator string `json:"Latitude_indicator"` // N/S - North/South LatitudeIndicator string `json:"Latitude_indicator"` // North/South
LongitudeIndicator string `json:"Longitude_indicator"` // W/E - West/East LongitudeIndicator string `json:"Longitude_indicator"` // West/East
Speed float64 `json:"Speed"` Speed float64 `json:"Speed"`
Course float64 `json:"-"` Course float64 `json:"-"`
Altitude float64 `json:"-"` Altitude float64 `json:"-"`
@ -59,6 +59,8 @@ func (gps *Data) decode(str string) error {
if err != nil { if err != nil {
logger.Println("ERROR parse longitude:", err.Error()) logger.Println("ERROR parse longitude:", err.Error())
} }
newGpsInfo.Latitude /= 100
newGpsInfo.Longitude /= 100
newGpsInfo.LatitudeIndicator = strs[1] newGpsInfo.LatitudeIndicator = strs[1]
newGpsInfo.LongitudeIndicator = strs[3] newGpsInfo.LongitudeIndicator = strs[3]
newGpsInfo.Date = strs[4] newGpsInfo.Date = strs[4]

View File

@ -34,14 +34,12 @@ const (
) )
func secondCountDownTimer(title string, logger *log.Logger, t time.Duration) { func secondCountDownTimer(title string, logger *log.Logger, t time.Duration) {
_ = title
_ = logger
counter := 0 counter := 0
for { for {
if counter > int(t.Seconds()) { if counter > int(t.Seconds()) {
break break
} }
// logger.Printf("%s: %d/%f\n", title, counter, t.Seconds()) logger.Printf("%s: %d/%f\n", title, counter, t.Seconds())
time.Sleep(time.Second) time.Sleep(time.Second)
counter += 1 counter += 1
} }
@ -120,7 +118,7 @@ func (g *gps) collectNmeaReports(flags nmeaFlags) ([]string, error) {
} }
// DEBUG // DEBUG
g.logger.Println("NMEA raw collect:", resp) // g.logger.Println("NMEA raw collect:", resp)
// Right responce struct: // Right responce struct:
// \r\n // \r\n

View File

@ -1,27 +1,15 @@
package internet package internet
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"log" "log"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"time"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at" "gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
) )
const (
pingPacketsCount = 3
pingTimeout = 5
inetConnectedTimeout = 4 * time.Second
pingAddr = "8.8.8.8"
ifName = "ppp0" // Interface name
inetMetric = 2000
)
type conn struct { type conn struct {
logger *log.Logger logger *log.Logger
port at.Port port at.Port
@ -30,10 +18,6 @@ type conn struct {
isConnectExecuted bool isConnectExecuted bool
isInited bool isInited bool
isRouteSet bool
connectTime time.Time
gw string // Gateway
} }
type Conn interface { type Conn interface {
@ -42,9 +26,6 @@ type Conn interface {
Connect() error Connect() error
Disconnect() error Disconnect() error
SetDefaultRouteTable() error
UnsetDefaultRouteTable() error
IsConnected() bool // Check interface existance IsConnected() bool // Check interface existance
Ping() error Ping() error
@ -53,12 +34,10 @@ type Conn interface {
func New(logger *log.Logger, port at.Port) Conn { func New(logger *log.Logger, port at.Port) Conn {
return &conn{ return &conn{
logger: logger, logger: logger,
port: port, port: port,
isConnectExecuted: false, isConnectExecuted: false,
isInited: false, isInited: false,
isRouteSet: false,
} }
} }
@ -81,6 +60,12 @@ func (c *conn) Connect() error {
return fmt.Errorf("already connected") return fmt.Errorf("already connected")
} }
// Check signal // 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() resp, err := exec.Command("pon", pppConfigName).Output()
if err != nil { if err != nil {
@ -89,15 +74,11 @@ func (c *conn) Connect() error {
if len(resp) > 0 { if len(resp) > 0 {
c.logger.Println("pon response:", string(resp)) c.logger.Println("pon response:", string(resp))
} }
c.isConnectExecuted = true c.isConnectExecuted = true
c.connectTime = time.Now()
c.gw, err = c.GetHostIp()
if err != nil {
return fmt.Errorf("get host ip: %w", err)
}
c.logger.Println("\x1b[38;2;0;255;0mInternet connected.\x1b[38;2;255;255;255m")
// Set default route
c.logger.Println("\x1b[38;2;0;255;0mInternet connected.\x1b[38;2;255;255;255m")
return nil return nil
} }
@ -116,154 +97,28 @@ func (c *conn) Disconnect() error {
c.logger.Println("poff response:", string(resp)) c.logger.Println("poff response:", string(resp))
} }
c.isConnectExecuted = false c.isConnectExecuted = false
c.logger.Println("\x1b[38;2;0;255;0mInternet disconnected.\x1b[38;2;255;255;255m")
return nil return nil
} }
func (c *conn) SetDefaultRouteTable() error {
// route add -net default gw 10.64.64.64 metric 2000 dev ppp0
resp, err := exec.Command("route", "add", "-net", "default", "gw", c.gw, "metric", strconv.Itoa(inetMetric), "dev", ifName).Output()
if err != nil {
return fmt.Errorf("execute add cmd: %w", err)
}
// Check response
if len(resp) != 0 {
c.logger.Println("Not nil response:", string(resp))
}
c.isRouteSet = true
c.logger.Println("\x1b[38;2;0;255;0mInternet route table set.\x1b[38;2;255;255;255m")
return nil
}
func (c *conn) UnsetDefaultRouteTable() error {
if !c.isRouteSet {
return fmt.Errorf("route table is not set")
}
resp, err := exec.Command("route", "del", "-net", "default", "gw", c.gw, "metric", strconv.Itoa(inetMetric), "dev", ifName).Output()
if err != nil {
return fmt.Errorf("execute del cmd: %w", err)
}
// Check response
if len(resp) != 0 {
c.logger.Println("Not nil response:", string(resp))
}
c.logger.Println("\x1b[38;2;0;255;0mInternet route table unset.\x1b[38;2;255;255;255m")
return nil
}
func (c *conn) ping(flags []string, timeout int) error {
c.logger.Println("Ping", flags[len(flags)-1])
// Just counter
ctx, cancel := context.WithCancel(context.Background())
go func(c *conn, ctx context.Context) {
for i := 0; i < timeout; i++ {
c.logger.Printf("Ping %d/%d", i, timeout)
select {
case <-ctx.Done():
return
case <-time.After(time.Second):
}
}
}(c, ctx)
// Executing cmd
cmd := exec.Command("ping", flags...)
resp, err := cmd.Output()
cancel()
if err != nil {
c.logger.Println("Ping default interface cmd error:", err)
}
// Parse
lines := strings.Split(string(resp), "\n")
// Look for string "--- *.*.*.* ping statistics ---" by first simbol '-'
stLineI := 0
searchStLineLoop:
for i, l := range lines {
if len(l) > 0 && l[0] == '-' {
stLineI = i + 1
break searchStLineLoop
}
}
if stLineI == 0 || stLineI >= len(lines) {
return fmt.Errorf("failed to find statistics line: %d", stLineI)
}
stStr := lines[stLineI]
// Get third value "packet lost"
values := strings.Split(stStr, ",")
if len(values) < 3 {
return fmt.Errorf("invalid statistic values(len): [%s]", values)
}
// Get number
words := strings.Split(values[2], " ")
if len(words) < 2 {
return fmt.Errorf("invalid \"packets lost\" value(words count): [%s]", words)
}
// First is ''
// Second is '...%'
packetsLost, err := strconv.Atoi(words[1][:len(words[1])-1]) // Without '%' char
if err != nil {
return fmt.Errorf("parse \"packets lost\" value: %w", err)
}
if packetsLost == 100 {
return fmt.Errorf("lost all packages")
}
if packetsLost > 0 {
c.logger.Printf("lost some packets: %d%%\n", packetsLost)
}
return nil
}
func (c *conn) GetHostIp() (string, error) {
if !c.isConnectExecuted {
return "", fmt.Errorf("internet not connected")
}
// Wait some time for system to setup route table
time.Sleep(time.Until(c.connectTime.Add(inetConnectedTimeout)))
// Execute cmd
resp, err := exec.Command("route").Output()
if err != nil {
return "", fmt.Errorf("exec route cmd: %w", err)
}
// Check and split to lines
lines := strings.Split(string(resp), "\n")
if len(lines) <= 3 || lines[0] != "Kernel IP routing table" {
return "", fmt.Errorf("invalid route response: [% s]", lines)
}
// Search line about ppp interface
searchLoop:
for _, l := range lines[1:] {
words := strings.Fields(l)
if len(words) != 8 {
/// c.logger.Printf("invalid route line(words number): [%s]\n", words)
continue searchLoop
}
if words[7] == ifName {
if words[3] != "UH" {
// c.logger.Println("invalid flags")
continue searchLoop
}
return words[0], nil
}
}
return "", fmt.Errorf("found no suitable ppp interface")
}
func (c *conn) PingDefault() error {
return c.ping([]string{"-c", strconv.Itoa(pingPacketsCount), "-w", strconv.Itoa(pingTimeout), pingAddr}, pingTimeout)
}
func (c *conn) PingPPP() error {
return c.ping([]string{"-I", ifName, "-c", string(pingPacketsCount), "-w", string(pingTimeout), pingAddr}, pingTimeout)
}
func (c *conn) Ping() error { func (c *conn) Ping() error {
return c.PingDefault() // 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 default interface cmd error:", err)
}
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 ppp0 interface cmd error:", err)
}
c.logger.Println("Ping ppp0 interface resp:", string(resp))
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 { func (c *conn) IsConnected() bool {
@ -285,7 +140,7 @@ func (c *conn) IsConnected() bool {
continue continue
} }
interfaceName := strings.Split(l, ":")[0] interfaceName := strings.Split(l, ":")[0]
if interfaceName == ifName { if interfaceName == "ppp0" {
return true return true
} }
} }
@ -294,17 +149,8 @@ func (c *conn) IsConnected() bool {
func (c *conn) Close() error { func (c *conn) Close() error {
c.isInited = false c.isInited = false
// Unset route table if err := c.Disconnect(); err != nil {
if c.isRouteSet { return fmt.Errorf("diconnect: %w", err)
if err := c.UnsetDefaultRouteTable(); err != nil {
c.logger.Println("unset route table error:", err.Error())
}
}
// Disconnect
if c.isConnectExecuted {
if err := c.Disconnect(); err != nil {
c.logger.Println("diconnect error:", err.Error())
}
} }
return nil return nil
} }

View File

@ -1,7 +1,6 @@
package modem package modem
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -22,9 +21,6 @@ import (
// yy/MM/dd,hh:mm:ss+zzzz // yy/MM/dd,hh:mm:ss+zzzz
const timeLayout = "06/01/02,15:04:05-0700" const timeLayout = "06/01/02,15:04:05-0700"
var ttyPorts = []string{"ttyUSB1", "ttyUSB2", "ttyUSB3", "ttyS0", "ttyAMA2"}
var availableModels = []string{"SIMCOM_SIM7600E-H", "SIMCOM_SIM808"}
type ModemData struct { type ModemData struct {
Port string `json:"Port"` Port string `json:"Port"`
gps.Data gps.Data
@ -39,7 +35,6 @@ type modem struct {
baudrate int baudrate int
deviceName string deviceName string
port at.Port port at.Port
model string
// Gpio values // Gpio values
onOffPin gpio.Pin // For turning on and off onOffPin gpio.Pin // For turning on and off
@ -65,7 +60,6 @@ type Modem interface {
GetTime() (time.Time, error) GetTime() (time.Time, error)
PowerOn() error PowerOn() error
PowerOnCtx(ctx context.Context) error // Because it takes ~30 seconds
PowerOff() error PowerOff() error
// Access to SMS, GPS, AT interfaces mostly for debug // Access to SMS, GPS, AT interfaces mostly for debug
@ -136,12 +130,12 @@ func (m *modem) Init() error {
// submodulesLogger := io.Discard // FOR less logs // submodulesLogger := io.Discard // FOR less logs
m.logger.Println("=============================== Init submodules") m.logger.Println("=============================== Init submodules")
m.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port) // m.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port)
if err := m.ic.Init(ports[len(ports)-1]); err != nil { // 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()) // m.logger.Printf("\x1b[38;2;255;0;0mInternet: %s\x1b[38;2;255;255;255m\n", err.Error())
} else { // } else {
m.logger.Println("\x1b[38;2;0;255;0mInternet OK\x1b[38;2;255;255;255m") // 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) // m.sms = sms.New(log.New(submodulesLogger, "modem-sms : ", log.LstdFlags), m.port)
// if err := m.sms.Init(); err != nil { // if err := m.sms.Init(); err != nil {
@ -209,7 +203,6 @@ func (m *modem) GetTime() (time.Time, error) {
return time.Time{}, fmt.Errorf("invalid values (len): [%s]", values) return time.Time{}, fmt.Errorf("invalid values (len): [%s]", values)
} }
timeStr := values[1] timeStr := values[1]
m.logger.Println("Raw time:", timeStr)
if len(timeStr) != len("yy/MM/dd,hh:mm:ss+zz") { if len(timeStr) != len("yy/MM/dd,hh:mm:ss+zz") {
return time.Time{}, fmt.Errorf("invalid time string: %s", timeStr) return time.Time{}, fmt.Errorf("invalid time string: %s", timeStr)
} }
@ -229,11 +222,6 @@ func (m *modem) PowerOn() error {
return nil return nil
} }
func (m *modem) PowerOnCtx(ctx context.Context) error {
m.onOffPin.PowerOnCtx(ctx) // DEBUG do not want to wait 30 seconds
return nil
}
func (m *modem) PowerOff() error { func (m *modem) PowerOff() error {
_, err := m.At().Send("AT+CPOF") _, err := m.At().Send("AT+CPOF")
return err return err
@ -370,37 +358,17 @@ func (m *modem) setupPort() error {
// m.restart() // m.restart()
// These commands ensure that correct modes are set // These commands ensure that correct modes are set
// m.port.RawSend("\r\n\x1A\r\n") // Sometimes enables echo mode // m.port.RawSend("\r\n\x1A\r\n") // Sometimes enables echo mode
//if m.At().GetName() == "/dev/ttyUSB0" {
// buf := make([]byte, 256)
// for i := 0; i < 10; i++ {
// len, err := m.port.SerialPort().Read(buf)
// if err != nil {
// m.logger.Println("ERROR:", err.Error())
// }
// if len != 0 {
// m.logger.Println(string(buf[:len]))
// }
// time.Sleep(time.Second)
// m.logger.Println(".")
// }
//}
m.printCmd("AT") // Sometimes enables echo mode
m.printCmd("AT") // Sometimes enables echo mode
m.printCmd("ATE0") // 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+CGPSFTM=0") // Sometimes does not turn off nmea
m.printCmd("AT+CMEE=2") // Turn on errors describtion m.printCmd("AT+CMEE=2") // Turn on errors describtion
m.printCmd("AT+CTZU=1") // Turn on time update
return nil return nil
} }
func (m *modem) checkCurPortDead() error { func (m *modem) checkCurPortDead() error {
if err := m.port.RawSend("AT\r\n"); err != nil { if resp, err := m.port.RawSend("AT\r\n", 20*time.Millisecond); err != nil || len(resp) == 0 {
return fmt.Errorf("raw send: %w", err)
}
if resp, err := m.port.RawRead(time.Second); err != nil || len(resp) == 0 {
if err != nil { if err != nil {
return fmt.Errorf("raw read: %w", err) return fmt.Errorf("raw send: %w", err)
} }
return fmt.Errorf("read 0") return fmt.Errorf("read 0")
} }
@ -459,19 +427,11 @@ func (m *modem) checkPort(portName string) (outErr error) {
if err != nil { if err != nil {
return fmt.Errorf("get model: %w", err) return fmt.Errorf("get model: %w", err)
} }
rightModel := "SIMCOM_SIM7600E-H"
// Check model // m.logger.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model))
foundModel := "" if len(model) >= len(rightModel) && model[:len(rightModel)] != rightModel {
for _, rightModel := range availableModels { return fmt.Errorf("invalid modem model: %s", model)
if len(model) >= len(rightModel) && model[:len(rightModel)] == rightModel {
foundModel = rightModel
break
}
} }
if foundModel == "" {
return fmt.Errorf("invalid model: %s", model)
}
m.model = foundModel
m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m") m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m")
return nil return nil
} }
@ -515,11 +475,11 @@ func (m *modem) getAtPorts(ports []string) ([]string, error) {
func getTtyPorts(isSoft bool) ([]string, error) { func getTtyPorts(isSoft bool) ([]string, error) {
if isSoft { if isSoft {
return ttyPorts, nil return []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}, nil
} }
// Get ports // Get ports
/**/ /**/
out, err := exec.Command("ls", "/dev/tty[!0-9]*").Output() out, err := exec.Command("ls", "--", "/dev/tty[!0-9]*").Output()
if err != nil { if err != nil {
return nil, fmt.Errorf("execute ls command: %w", err) return nil, fmt.Errorf("execute ls command: %w", err)
} }

View File

@ -5,6 +5,7 @@ import (
"io" "io"
"log" "log"
"strings" "strings"
"time"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at" "gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
"gitea.unprism.ru/KRBL/sim-modem/api/modem/utils" "gitea.unprism.ru/KRBL/sim-modem/api/modem/utils"
@ -68,14 +69,9 @@ func (d *dialer) Send(number, msg string) error {
return err return err
} }
d.logger.Println(sresp) 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
// Message body
if err := d.port.RawSend(fmt.Sprintf("%s\x1A", msg)); err != nil {
return fmt.Errorf("message request: %w", err)
}
resp, err := d.port.RawRead(at.ReadTimeout)
if err != nil { if err != nil {
return fmt.Errorf("message request read: %w", err) return fmt.Errorf("message request: %w", err)
} }
d.logger.Println("Send response:", resp) d.logger.Println("Send response:", resp)
if !at.Resp(resp).Check() { if !at.Resp(resp).Check() {

View File

@ -25,7 +25,7 @@ func CheckSignal(port at.Port, logger *log.Logger) (bool, error) {
} }
func checkRssi(rssi int) error { func checkRssi(rssi int) error {
// rssi - Received signal strength indicator // rssi - Received signal strenght indicator
// 0 -113 dBm or less // 0 -113 dBm or less
// 1 -111 dBm // 1 -111 dBm
// 2...30 -109... - 53 dBm // 2...30 -109... - 53 dBm

69
main.go
View File

@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
"fmt"
"log" "log"
"os" "os"
"os/signal" "os/signal"
@ -29,78 +28,49 @@ func main() {
log.Println("END") log.Println("END")
} }
var m modem.Modem
var logger *log.Logger
func InetInit() error {
// Connect to internet
if err := m.Ic().Connect(); err != nil {
return fmt.Errorf("connect to internet: %w", err)
}
// Setup route table
// Linux now always manage to add ppp0 interface to route table in time so it is better to add ф loop here
setupRouteTableLoop:
for {
if err := m.Ic().SetDefaultRouteTable(); err != nil {
logger.Println("set route table:", err.Error())
time.Sleep(2 * time.Second)
} else {
break setupRouteTableLoop
}
}
return nil
}
func Cmd(cmd string) {
resp, err := m.At().SendWithTimeout(cmd, 10*time.Second) //50*time.Millisecond)
logger.Println(cmd, "===>", resp, err)
}
func mainE(ctx context.Context) error { func mainE(ctx context.Context) error {
logger = log.New(os.Stdout, "main : ", log.LstdFlags) logger := log.New(os.Stdout, "main : ", log.LstdFlags)
m = modem.New(log.New(logger.Writer(), "modem : ", log.LstdFlags)) m := modem.New(log.New(logger.Writer(), "modem : ", log.LstdFlags))
logger.Println("||||||||||||||||| INIT |||||||||||||||") logger.Println("||||||||||||||||| INIT |||||||||||||||")
// If power is down modem won't find suitable devices add will try to send powerOn signal and then try again
initLoop: initLoop:
for { for {
select { select {
case <-ctx.Done(): // For interupt case <-ctx.Done():
logger.Println("Break init loop") logger.Println("Break init loop")
return nil return nil
default: default:
if err := m.Init(); err != nil { if err := m.Init(); err != nil {
logger.Println("Init ended with error:", err.Error()) logger.Println("Init ended with error:", err.Error())
// logger.Println("Turn on...") // logger.Println("Turn on...")
// if err := m.PowerOnCtx(ctx); err != nil { // if err := m.PowerOn(); err != nil {
// logger.Println("Turn on error:", err.Error()) // logger.Println("Turn on error:", err.Error())
// } // }
time.Sleep(time.Second)
continue initLoop continue initLoop
} }
break initLoop break initLoop
} }
} }
// Final check for sure
if !m.IsConnected() { if !m.IsConnected() {
logger.Println("Modem is not connected") logger.Println("Modem is not connected")
return nil return nil
} }
// Close() deinits everything recursively
defer func() { defer func() {
logger.Println("||||||||||||||||| CLOSE |||||||||||||||") logger.Println("||||||||||||||||| CLOSE |||||||||||||||")
m.Close() m.Close()
}() }()
// Internet connect // Connect to internet
// if err := InetInit(); err != nil { // if err := m.Ic().Connect(); err != nil {
// return err // return fmt.Errorf("connect to internet: %w", err)
// } // }
logger.Println("||||||||||||||||| SMS |||||||||||||||||") 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 // Select ME PMS
// logger.Println("SEND SMS") // logger.Println("SEND SMS")
@ -109,7 +79,7 @@ initLoop:
// Cmd("AT+CREG?") // Cmd("AT+CREG?")
// Cmd("AT+CNMI?") // Cmd("AT+CNMI?")
Cmd("AT+CNETSCAN") // Cmd("AT+CSQ")
// Cmd("AT+CSCA?") // Cmd("AT+CSCA?")
// Cmd("AT+CPOL?") // Cmd("AT+CPOL?")
// Cmd("AT+COPS?") // Cmd("AT+COPS?")
@ -123,20 +93,15 @@ initLoop:
return nil return nil
default: default:
// Cmd("AT+CPSI?") // Cmd("AT+CPSI?")
// Cmd("AT+CIPGSMLOC=1")
// Cmd("AT+CSQ") // Cmd("AT+CSQ")
// Cmd("AT+CTZU?")
// Cmd("AT+CPIN?")
// Cmd("AT+CCLK?") // Cmd("AT+CCLK?")
// logger.Println(m.Gps().GetStatus()) // logger.Println(m.Gps().GetStatus())
// m.Update() // m.Update()
// st, _ := m.Gps().GetStatus() // st, _ := m.Gps().GetStatus()
// logger.Printf("FOUND SATELITES: %d\n", st.FoundSatelitesCount) // logger.Printf("GPS STATUS: %+v", st)
// data := m.GetData() // logger.Printf("GPS DATA: %+v", m.GetData())
// logger.Printf("GPS DATA: %f%s %f%s\n", data.Latitude, data.LatitudeIndicator, data.Longitude, data.LongitudeIndicator) logger.Println(m.GetTime())
// logger.Println(m.GetTime()) time.Sleep(2 * time.Second)
// logger.Println(m.Ic().Ping())
time.Sleep(time.Second)
} }
// Cmd("AT+CSQ") // Cmd("AT+CSQ")
// Cmd("AT+COPS?") // Cmd("AT+COPS?")