commit c8f30c0caf805ca81637e1001796defada3c9914 Author: Andrey Egorov Date: Thu Jul 18 19:34:26 2024 +0300 Initial. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74750ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +out/ +Makefile +go.sum +.git/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f5942ac --- /dev/null +++ b/Makefile @@ -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 \ No newline at end of file diff --git a/annalist b/annalist new file mode 100644 index 0000000..1453cc7 Binary files /dev/null and b/annalist differ diff --git a/api/modem/at.go b/api/modem/at.go new file mode 100644 index 0000000..d7d0e86 --- /dev/null +++ b/api/modem/at.go @@ -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 +} diff --git a/api/modem/gpio.go b/api/modem/gpio.go new file mode 100644 index 0000000..e486a42 --- /dev/null +++ b/api/modem/gpio.go @@ -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 +} diff --git a/api/modem/modem.go b/api/modem/modem.go new file mode 100644 index 0000000..4fadc43 --- /dev/null +++ b/api/modem/modem.go @@ -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? +*/ + +/* + */ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..721f1aa --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c3a9fb6 --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..24b74dc --- /dev/null +++ b/main.go @@ -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() +}