This commit is contained in:
Andrey Egorov 2024-07-18 19:34:26 +03:00
commit c8f30c0caf
9 changed files with 401 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
out/
Makefile
go.sum
.git/

11
Makefile Normal file
View File

@ -0,0 +1,11 @@
build:
@go build -o ./out/annalist.exe main.go
#run: build
# @./out/annalist.exe
linux:
@$Env:GOOS="linux" $Env:GOARCH="arm" $Env:GOARM=5
# .profile
# cgo_enabled 0

BIN
annalist Normal file

Binary file not shown.

53
api/modem/at.go Normal file
View File

@ -0,0 +1,53 @@
package modem
import (
"fmt"
"log"
"strings"
"go.bug.st/serial"
)
func (m *modem) makeAtReq(msg string) (string, error) {
log.Println("Write...")
if _, err := m.serialPort.Write([]byte(msg)); err != nil {
return "", fmt.Errorf("serial port write: %w", err)
}
log.Println("Read...")
readLen, err := m.serialPort.Read(m.inputBuf)
if err != nil {
return "", fmt.Errorf("port read: %w", err)
}
return strings.Split(string(m.inputBuf[:readLen]), "\n")[1], nil
}
func (m *modem) makeAtEchoReqAndCheck(msg, checkMsg string) (bool, error) {
ans, err := m.makeAtReq(msg)
log.Println(msg, checkMsg)
if err != nil {
return false, err
}
return (len(ans) >= len(checkMsg) && ans[:len(checkMsg)] == checkMsg), nil
}
func (m *modem) connect(port string) error {
log.Println("Connecting to", port, "...")
s, err := serial.Open(port, &serial.Mode{BaudRate: m.baudrate})
if err != nil {
return fmt.Errorf("open port: %w", err)
}
// s.Close() There is no open f
// s.Open()
m.serialPort = s
return nil
}
func (m *modem) disconnect() error {
defer func() {
m.serialPort = nil
}()
if err := m.serialPort.Close(); err != nil {
return fmt.Errorf("close port: %w", err)
}
return nil
}

44
api/modem/gpio.go Normal file
View File

@ -0,0 +1,44 @@
package modem
import (
"fmt"
"time"
gpio "github.com/warthog618/go-gpiocdev"
)
func (m *modem) PowerOn() error {
c, err := gpio.NewChip(m.deviceName)
if err != nil {
return fmt.Errorf("gpio new chip: %w", err)
}
l, err := c.RequestLine(m.powerKey, gpio.AsOutput(0))
if err != nil {
return fmt.Errorf("gpio request line: %w", err)
}
m.gpioLine = l
time.Sleep(100 * time.Millisecond)
if err := m.gpioLine.SetValue(1); err != nil {
return fmt.Errorf("gpio set value: %w", err)
}
time.Sleep(3 * time.Second)
if err := m.gpioLine.SetValue(0); err != nil {
return fmt.Errorf("gpio set value: %w", err)
}
time.Sleep(30 * time.Second)
return nil
}
func (m *modem) PowerOff() error {
time.Sleep(100 * time.Millisecond)
if err := m.gpioLine.SetValue(1); err != nil {
return fmt.Errorf("gpio set value: %w", err)
}
time.Sleep(3 * time.Second)
if err := m.gpioLine.SetValue(0); err != nil {
return fmt.Errorf("gpio set value: %w", err)
}
time.Sleep(30 * time.Second)
m.gpioLine = nil
return nil
}

237
api/modem/modem.go Normal file
View File

@ -0,0 +1,237 @@
package modem
import (
"errors"
"fmt"
"log"
"math"
"os"
"os/exec"
"strings"
"time"
gpio "github.com/warthog618/go-gpiocdev"
"go.bug.st/serial"
)
type modem struct {
// Serial stuff
port string
deviceName string
baudrate int
inputBuf []byte
serialPort serial.Port
isAvailable bool
// Gpio stuff
powerKey int
gpioLine *gpio.Line
// Other values
speed float64
latitude_ns float64
latitude float64
longitude_we float64
longitude float64
lastUpdateTime time.Time
}
type Modem interface {
Init() error
SearchPort(isSoft bool) error
PowerOn() error
PowerOff() error
Connect() error
Ping() (bool, error)
SwitchToGpsMode() error
CalculateSpeed(newLatitude, newlongitude float64)
Update() error
GetInfo() string
TestGPS() error
}
func New() Modem {
return &modem{
baudrate: 115200,
powerKey: 6,
lastUpdateTime: time.Now(),
inputBuf: make([]byte, 128),
}
}
func (m *modem) Init() error {
if err := m.SearchPort(true); err != nil {
return fmt.Errorf("soft port search: %w", err)
}
if m.port == "" {
if err := m.SearchPort(false); err != nil {
return fmt.Errorf("not soft port search: %w", err)
}
}
if m.port == "" {
return errors.New("no port is detected")
}
if err := m.Connect(); err != nil {
return fmt.Errorf("connect: %w", err)
}
if err := m.TestGPS(); err != nil {
return fmt.Errorf("testGPS: %w", err)
}
return nil
}
func (m *modem) SearchPort(isSoft bool) error {
ports := []string{}
// Get ports
if isSoft {
ports = []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}
} else {
/**/
log.Print("Search for ports...")
out, err := exec.Command("/bin/ls", "/dev").Output()
if err != nil {
return fmt.Errorf("execute ls command: %w", err)
}
allPorts := strings.Split(string(out), "\n")
for _, p := range allPorts {
if len(p) > 2 && p[:3] == "tty" {
ports = append(ports, p)
}
}
// slices.Reverse(ports) // TODO why
}
// Check ports
log.Println("Found ports: ", ports)
log.Println("Check...")
defer func() {
if m.serialPort != nil {
m.disconnect() // TODO maybe handle
}
}()
for _, p := range ports {
if m.serialPort != nil {
if err := m.disconnect(); err != nil {
return fmt.Errorf("disconnect: %w", err)
}
}
log.Println("Checking port: ", p)
if _, err := os.Stat("/dev/" + p); err != nil {
continue
}
if err := m.connect("/dev/" + p); err != nil {
log.Println("Error:", fmt.Errorf("connect: %w", err).Error())
continue
}
// Ping
log.Println("Ping...")
if ok, err := m.Ping(); err != nil || !ok {
if err != nil {
return fmt.Errorf("ping error: %w", err)
}
return errors.New("modem does not ping")
}
// Check model
{
ok, err := m.makeAtEchoReqAndCheck("AT+CGMM\r\n", "SIMCOM_SIM7600E-H")
if err != nil {
return fmt.Errorf("make serial request: %w", err)
}
if !ok {
continue
}
}
log.Print("Found modem on port: ", p)
m.port = "/dev/" + p
m.isAvailable = true
return nil
}
// return errors.New("no a compatible modem port found")
return nil
}
func (m *modem) Connect() error {
return m.connect(m.port)
}
func (m *modem) Ping() (bool, error) {
return m.makeAtEchoReqAndCheck("AT\r\n", "OK")
}
func (m *modem) SwitchToGpsMode() error {
if err := m.serialPort.ResetInputBuffer(); err != nil {
return fmt.Errorf("reset input buffer: %w", err)
}
ans, err := m.makeAtReq("AT+CGPS?\r\n")
if err != nil {
return fmt.Errorf("make serial request: %w", err)
}
resp := strings.Split(ans, ":")
if !(len(resp) > 1 && resp[0] == "+CGPS") {
return errors.New("lost connection while checking gps status")
}
switch resp[1][1] {
case '1':
log.Println("GPS already enabled")
return nil
case '0':
ok, err := m.makeAtEchoReqAndCheck("AT+CGPS=1", "OK")
if err != nil {
return fmt.Errorf("try to switch to gps mode echo reqest: %w", err)
}
if !ok {
return errors.New("lost connection while trying to switch to gps mode")
}
default:
return errors.New("unexpected response of gps status")
}
return nil
}
func deg2rad(deg float64) float64 {
return deg * (math.Pi / 180)
}
func (m *modem) CalculateSpeed(newLatitude, newLongitude float64) {
log.Println("Calculate speed")
earthRad := 6371.0 // TODO ?
dLat := deg2rad(math.Abs(newLatitude - m.latitude))
dLon := deg2rad(math.Abs(newLongitude - m.longitude))
a := math.Sin(dLat/2)*math.Sin(dLat/2) +
math.Cos(deg2rad(newLatitude))*math.Cos(deg2rad(m.latitude))*math.Sin(dLon/2)*math.Sin(dLon/2)
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
m.speed = earthRad * c / (math.Abs(float64(time.Since(m.lastUpdateTime))))
}
func (m *modem) Update() error {
log.Println("Update")
if !m.isAvailable {
log.Println("No connection to module")
return nil
}
m.serialPort.ResetInputBuffer()
// MAKE
}
func (m *modem) GetInfo() string {
return ""
}
func (m *modem) TestGPS() error {
return nil
}
/*
TODOs:
maybe to store read/write buf in obj
QUESTIONS:
do many threads?
*/
/*
*/

13
go.mod Normal file
View File

@ -0,0 +1,13 @@
module annalist
go 1.22.5
require (
github.com/warthog618/go-gpiocdev v0.9.0
go.bug.st/serial v1.6.2
)
require (
github.com/creack/goselect v0.1.2 // indirect
golang.org/x/sys v0.18.0 // indirect
)

20
go.sum Normal file
View File

@ -0,0 +1,20 @@
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/warthog618/go-gpiocdev v0.9.0 h1:AZWUq1WObgKCO9cJCACFpwWQw6yu8vJbIE6fRZ+6cbY=
github.com/warthog618/go-gpiocdev v0.9.0/go.mod h1:GV4NZC82fWJERqk7Gu0+KfLSDIBEDNm6aPGiHlmT5fY=
github.com/warthog618/go-gpiosim v0.1.0 h1:2rTMTcKUVZxpUuvRKsagnKAbKpd3Bwffp87xywEDVGI=
github.com/warthog618/go-gpiosim v0.1.0/go.mod h1:Ngx/LYI5toxHr4E+Vm6vTgCnt0of0tktsSuMUEJ2wCI=
go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

19
main.go Normal file
View File

@ -0,0 +1,19 @@
package main
import (
"annalist/api/modem"
"log"
)
func main() {
log.Println("CGSG forever!!!")
if err := mainE(); err != nil {
log.Println("MAIN finished with error:", err.Error())
}
log.Println("END")
}
func mainE() error {
m := modem.New()
return m.Init()
}