Add: power on with context, SIM808 support
This commit is contained in:
parent
3f5412fac0
commit
509006fae1
2
Makefile
2
Makefile
@ -4,4 +4,4 @@ export GOARM=6
|
||||
export CGO_ENABLED=0
|
||||
|
||||
build:
|
||||
@go build -o out/modem main.go
|
||||
@go build -o out/out main.go
|
||||
|
@ -37,7 +37,8 @@ type Port interface {
|
||||
Disconnect() error
|
||||
IsConnected() bool
|
||||
|
||||
RawSend(msg string, timeout time.Duration) (string, error)
|
||||
RawSend(msg string) error
|
||||
RawRead(timeout time.Duration) (string, error)
|
||||
Send(cmd string) (Resp, error)
|
||||
SendWithTimeout(cmd string, timeout time.Duration) (Resp, error)
|
||||
|
||||
@ -106,42 +107,53 @@ func (p *atPort) IsConnected() bool {
|
||||
return p.port != nil
|
||||
}
|
||||
|
||||
// Low level write/read function
|
||||
func (p *atPort) RawSend(msg string, timeout time.Duration) (string, error) {
|
||||
func (p *atPort) RawRead(timeout time.Duration) (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)
|
||||
}
|
||||
time.Sleep(timeout)
|
||||
// Read
|
||||
deadline := time.Now().Add(timeout)
|
||||
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 {
|
||||
if readLen == 0 && time.Now().After(deadline) {
|
||||
break readLoop
|
||||
}
|
||||
outBuf = append(outBuf, p.inputBuf[:readLen]...)
|
||||
if readLen < len(p.inputBuf) {
|
||||
break readLoop
|
||||
}
|
||||
// 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(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) {
|
||||
rawResp, err := p.RawSend(cmd+"\r\n", time.Microsecond)
|
||||
err := p.RawSend(cmd + "\r\n")
|
||||
if err != nil {
|
||||
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 {
|
||||
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
|
||||
}
|
||||
@ -151,10 +163,15 @@ func (p *atPort) Send(cmd string) (Resp, error) {
|
||||
}
|
||||
|
||||
func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) {
|
||||
rawResp, err := p.RawSend(cmd+"\r\n", timeout)
|
||||
err := p.RawSend(cmd + "\r\n")
|
||||
if err != nil {
|
||||
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 {
|
||||
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package gpio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
@ -8,6 +9,10 @@ import (
|
||||
gpio "github.com/stianeikeland/go-rpio/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
waitCtxTimeout = 100 * time.Microsecond
|
||||
)
|
||||
|
||||
type gpioPin struct {
|
||||
logger *log.Logger
|
||||
pin gpio.Pin
|
||||
@ -16,7 +21,7 @@ type gpioPin struct {
|
||||
type Pin interface {
|
||||
Init() error
|
||||
PowerOn()
|
||||
PowerOff()
|
||||
PowerOnCtx(ctx context.Context)
|
||||
io.Closer
|
||||
}
|
||||
|
||||
@ -31,32 +36,47 @@ func (p gpioPin) Init() error {
|
||||
return gpio.Open()
|
||||
}
|
||||
|
||||
func (p gpioPin) sendOnOffSignal() {
|
||||
func waitCtx(ctx context.Context, timeout time.Duration) {
|
||||
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.logger.Println("Power on 0/3 + 100ms")
|
||||
p.pin.Low()
|
||||
p.pin.Toggle()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
waitCtx(ctx, 100*time.Millisecond)
|
||||
|
||||
p.logger.Println("Power on 1/3 + 3s")
|
||||
p.pin.High()
|
||||
p.pin.Toggle()
|
||||
time.Sleep(3 * time.Second)
|
||||
waitCtx(ctx, 3*time.Second)
|
||||
|
||||
p.logger.Println("Power on 2/3 + 30s")
|
||||
p.pin.Low()
|
||||
p.pin.Toggle()
|
||||
time.Sleep(30 * time.Second)
|
||||
waitCtx(ctx, 30*time.Second)
|
||||
|
||||
p.logger.Println("Power on 3/3")
|
||||
}
|
||||
|
||||
func (p gpioPin) PowerOn() {
|
||||
p.sendOnOffSignal()
|
||||
p.sendOnOffSignal(context.Background())
|
||||
}
|
||||
|
||||
func (p gpioPin) PowerOff() {
|
||||
p.sendOnOffSignal()
|
||||
func (p gpioPin) PowerOnCtx(ctx context.Context) {
|
||||
p.sendOnOffSignal(ctx)
|
||||
}
|
||||
|
||||
func (p gpioPin) Close() error {
|
||||
|
@ -39,7 +39,7 @@ func secondCountDownTimer(title string, logger *log.Logger, t time.Duration) {
|
||||
if counter > int(t.Seconds()) {
|
||||
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)
|
||||
counter += 1
|
||||
}
|
||||
@ -118,7 +118,7 @@ func (g *gps) collectNmeaReports(flags nmeaFlags) ([]string, error) {
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
// g.logger.Println("NMEA raw collect:", resp)
|
||||
g.logger.Println("NMEA raw collect:", resp)
|
||||
|
||||
// Right responce struct:
|
||||
// \r\n
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
const (
|
||||
pingPacketsCount = 3
|
||||
pingTimeout = 5
|
||||
inetConnectedTimeout = 2 * time.Second
|
||||
inetConnectedTimeout = 4 * time.Second
|
||||
pingAddr = "8.8.8.8"
|
||||
ifName = "ppp0" // Interface name
|
||||
inetMetric = 2000
|
||||
|
@ -1,6 +1,7 @@
|
||||
package modem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@ -21,6 +22,9 @@ import (
|
||||
// yy/MM/dd,hh:mm:ss+zzzz
|
||||
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 {
|
||||
Port string `json:"Port"`
|
||||
gps.Data
|
||||
@ -35,6 +39,7 @@ type modem struct {
|
||||
baudrate int
|
||||
deviceName string
|
||||
port at.Port
|
||||
model string
|
||||
|
||||
// Gpio values
|
||||
onOffPin gpio.Pin // For turning on and off
|
||||
@ -60,6 +65,7 @@ type Modem interface {
|
||||
GetTime() (time.Time, error)
|
||||
|
||||
PowerOn() error
|
||||
PowerOnCtx(ctx context.Context) error // Because it takes ~30 seconds
|
||||
PowerOff() error
|
||||
|
||||
// Access to SMS, GPS, AT interfaces mostly for debug
|
||||
@ -131,7 +137,7 @@ func (m *modem) Init() error {
|
||||
|
||||
m.logger.Println("=============================== Init submodules")
|
||||
m.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port)
|
||||
if err := m.ic.Init(ports[1]); err != nil {
|
||||
if err := m.ic.Init(ports[len(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")
|
||||
@ -223,6 +229,11 @@ func (m *modem) PowerOn() error {
|
||||
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 {
|
||||
_, err := m.At().Send("AT+CPOF")
|
||||
return err
|
||||
@ -359,6 +370,22 @@ func (m *modem) setupPort() error {
|
||||
// m.restart()
|
||||
// These commands ensure that correct modes are set
|
||||
// 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("AT+CGPSFTM=0") // Sometimes does not turn off nmea
|
||||
m.printCmd("AT+CMEE=2") // Turn on errors describtion
|
||||
@ -368,9 +395,12 @@ func (m *modem) setupPort() error {
|
||||
}
|
||||
|
||||
func (m *modem) checkCurPortDead() error {
|
||||
if resp, err := m.port.RawSend("AT\r\n", 20*time.Millisecond); err != nil || len(resp) == 0 {
|
||||
if err := m.port.RawSend("AT\r\n"); err != nil {
|
||||
return fmt.Errorf("raw send: %w", err)
|
||||
}
|
||||
if resp, err := m.port.RawRead(time.Second); err != nil || len(resp) == 0 {
|
||||
if err != nil {
|
||||
return fmt.Errorf("raw send: %w", err)
|
||||
return fmt.Errorf("raw read: %w", err)
|
||||
}
|
||||
return fmt.Errorf("read 0")
|
||||
}
|
||||
@ -429,11 +459,19 @@ func (m *modem) checkPort(portName string) (outErr error) {
|
||||
if err != nil {
|
||||
return fmt.Errorf("get model: %w", err)
|
||||
}
|
||||
rightModel := "SIMCOM_SIM7600E-H"
|
||||
// m.logger.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model))
|
||||
if len(model) >= len(rightModel) && model[:len(rightModel)] != rightModel {
|
||||
return fmt.Errorf("invalid modem model: %s", model)
|
||||
|
||||
// Check model
|
||||
foundModel := ""
|
||||
for _, rightModel := range availableModels {
|
||||
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")
|
||||
return nil
|
||||
}
|
||||
@ -477,11 +515,11 @@ func (m *modem) getAtPorts(ports []string) ([]string, error) {
|
||||
|
||||
func getTtyPorts(isSoft bool) ([]string, error) {
|
||||
if isSoft {
|
||||
return []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}, nil
|
||||
return ttyPorts, nil
|
||||
}
|
||||
// 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 {
|
||||
return nil, fmt.Errorf("execute ls command: %w", err)
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.unprism.ru/KRBL/sim-modem/api/modem/at"
|
||||
"gitea.unprism.ru/KRBL/sim-modem/api/modem/utils"
|
||||
@ -69,10 +68,15 @@ func (d *dialer) Send(number, msg string) error {
|
||||
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 {
|
||||
|
||||
// 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 {
|
||||
return fmt.Errorf("message request read: %w", err)
|
||||
}
|
||||
d.logger.Println("Send response:", resp)
|
||||
if !at.Resp(resp).Check() {
|
||||
return fmt.Errorf("error response: %s", resp)
|
||||
|
16
main.go
16
main.go
@ -74,9 +74,10 @@ initLoop:
|
||||
if err := m.Init(); err != nil {
|
||||
logger.Println("Init ended with error:", err.Error())
|
||||
// logger.Println("Turn on...")
|
||||
// if err := m.PowerOn(); err != nil {
|
||||
// if err := m.PowerOnCtx(ctx); err != nil {
|
||||
// logger.Println("Turn on error:", err.Error())
|
||||
// }
|
||||
time.Sleep(time.Second)
|
||||
continue initLoop
|
||||
}
|
||||
break initLoop
|
||||
@ -94,6 +95,7 @@ initLoop:
|
||||
m.Close()
|
||||
}()
|
||||
|
||||
// Internet connect
|
||||
if err := InetInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -120,7 +122,8 @@ initLoop:
|
||||
logger.Println("Break main loop")
|
||||
return nil
|
||||
default:
|
||||
Cmd("AT+CPSI?")
|
||||
// Cmd("AT+CPSI?")
|
||||
// Cmd("AT+CIPGSMLOC=1")
|
||||
// Cmd("AT+CSQ")
|
||||
// Cmd("AT+CTZU?")
|
||||
// Cmd("AT+CPIN?")
|
||||
@ -128,11 +131,12 @@ initLoop:
|
||||
// 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.Printf("FOUND SATELITES: %d\n", st.FoundSatelitesCount)
|
||||
// data := 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.Ic().Ping())
|
||||
time.Sleep(2 * time.Second)
|
||||
// logger.Println(m.Ic().Ping())
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
// Cmd("AT+CSQ")
|
||||
// Cmd("AT+COPS?")
|
||||
|
Loading…
Reference in New Issue
Block a user