Compare commits
11 Commits
781d9ccda4
...
fd9e999b5a
Author | SHA1 | Date | |
---|---|---|---|
|
fd9e999b5a | ||
|
d05e2205d6 | ||
|
1b741c7dab | ||
|
e0110c558e | ||
|
225a0d2264 | ||
|
2eb21228e3 | ||
|
ebab41c510 | ||
|
92d42d413a | ||
|
b9056d2299 | ||
|
12555f2f46 | ||
|
eb899f0b45 |
@ -4,6 +4,11 @@ WORKDIR /app
|
|||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y iputils-ping
|
||||||
|
RUN apt-get install -y ppp
|
||||||
|
RUN apt-get install -y net-tools
|
||||||
|
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /modem-test
|
RUN CGO_ENABLED=0 GOOS=linux go build -o /modem-test
|
||||||
|
|
||||||
|
@ -26,12 +26,14 @@ type atPort struct {
|
|||||||
|
|
||||||
type Port interface {
|
type Port interface {
|
||||||
GetName() string
|
GetName() string
|
||||||
|
GetBaudrate() int
|
||||||
GetSerialPort() serial.Port // For extra need
|
GetSerialPort() serial.Port // For extra need
|
||||||
|
|
||||||
Connect() error
|
Connect() error
|
||||||
Disconnect() error
|
Disconnect() error
|
||||||
IsConnected() bool
|
IsConnected() bool
|
||||||
|
|
||||||
|
RawSend(msg string) (string, error)
|
||||||
Send(cmd string) (Resp, error)
|
Send(cmd string) (Resp, error)
|
||||||
|
|
||||||
io.Closer
|
io.Closer
|
||||||
@ -50,6 +52,10 @@ func (p *atPort) GetName() string {
|
|||||||
return p.portName
|
return p.portName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *atPort) GetBaudrate() int {
|
||||||
|
return p.baudrate
|
||||||
|
}
|
||||||
|
|
||||||
func (p *atPort) GetSerialPort() serial.Port {
|
func (p *atPort) GetSerialPort() serial.Port {
|
||||||
return p.port
|
return p.port
|
||||||
}
|
}
|
||||||
@ -84,15 +90,15 @@ func (p *atPort) IsConnected() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Low level write/read function
|
// Low level write/read function
|
||||||
func (p *atPort) makeReq(msg string) (string, error) {
|
func (p *atPort) RawSend(msg string) (string, error) {
|
||||||
// Write
|
// Write
|
||||||
p.port.ResetInputBuffer()
|
|
||||||
if _, err := p.port.Write([]byte(msg)); err != nil {
|
if _, err := p.port.Write([]byte(msg)); err != nil {
|
||||||
return "", fmt.Errorf("serial port write: %w", err)
|
return "", fmt.Errorf("serial port write: %w", err)
|
||||||
}
|
}
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
// Read
|
// Read
|
||||||
readLen, err := p.port.Read(p.inputBuf)
|
readLen, err := p.port.Read(p.inputBuf)
|
||||||
p.logger.Println(msg, "RAWREAD:", string(p.inputBuf[:readLen]))
|
// p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(p.inputBuf[:readLen]), "\x1b[38;2;255;255;255m")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("port read: %w", err)
|
return "", fmt.Errorf("port read: %w", err)
|
||||||
}
|
}
|
||||||
@ -102,12 +108,12 @@ func (p *atPort) makeReq(msg string) (string, error) {
|
|||||||
|
|
||||||
func (p *atPort) Send(cmd string) (Resp, error) {
|
func (p *atPort) Send(cmd string) (Resp, error) {
|
||||||
cmd += "\r\n"
|
cmd += "\r\n"
|
||||||
rawResp, err := p.makeReq(cmd)
|
rawResp, err := p.RawSend(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return RespNil, fmt.Errorf("make request: %w", err)
|
return RespNil, fmt.Errorf("make request: %w", err)
|
||||||
}
|
}
|
||||||
if len(rawResp) <= 4 {
|
if len(rawResp) <= 4 {
|
||||||
return RespNil, fmt.Errorf("read too small msg: %d byte", len(rawResp))
|
return RespNil, fmt.Errorf("read too small msg: %d byte - %s", len(rawResp), string(rawResp))
|
||||||
}
|
}
|
||||||
resp := rawResp[2 : len(rawResp)-2] // Cut \r\n
|
resp := rawResp[2 : len(rawResp)-2] // Cut \r\n
|
||||||
|
|
||||||
|
@ -15,3 +15,7 @@ func (resp Resp) RmFront(str string) Resp {
|
|||||||
func (resp Resp) String() string {
|
func (resp Resp) String() string {
|
||||||
return string(resp)
|
return string(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (resp Resp) Bytes() []byte {
|
||||||
|
return []byte(resp)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package modem
|
package gpio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -9,7 +10,21 @@ import (
|
|||||||
|
|
||||||
type gpioPin struct {
|
type gpioPin struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
Pin gpio.Pin
|
pin gpio.Pin
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pin interface {
|
||||||
|
Init() error
|
||||||
|
PowerOn()
|
||||||
|
PowerOff()
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger *log.Logger, pin uint8) Pin {
|
||||||
|
return gpioPin{
|
||||||
|
logger: logger,
|
||||||
|
pin: gpio.Pin(pin),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p gpioPin) Init() error {
|
func (p gpioPin) Init() error {
|
||||||
@ -17,20 +32,20 @@ func (p gpioPin) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p gpioPin) sendOnOffSignal() {
|
func (p gpioPin) sendOnOffSignal() {
|
||||||
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()
|
||||||
time.Sleep(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()
|
||||||
time.Sleep(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()
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
p.logger.Println("Power on 3/3")
|
p.logger.Println("Power on 3/3")
|
||||||
@ -40,10 +55,10 @@ func (p gpioPin) PowerOn() {
|
|||||||
p.sendOnOffSignal()
|
p.sendOnOffSignal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *gpioPin) PowerOff() {
|
func (p gpioPin) PowerOff() {
|
||||||
p.sendOnOffSignal()
|
p.sendOnOffSignal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *gpioPin) Close() error {
|
func (p gpioPin) Close() error {
|
||||||
return gpio.Close()
|
return gpio.Close()
|
||||||
}
|
}
|
@ -6,6 +6,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/CGSG-2021-AE4/modem-test/api/modem/at"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GpsData struct {
|
type GpsData struct {
|
||||||
@ -37,6 +39,7 @@ func (gps *GpsData) calculateSpeed(newLatitude, newLongitude float64, lastUpdate
|
|||||||
gps.Speed = earthRad * c / (math.Abs(float64(time.Since(lastUpdateTime))))
|
gps.Speed = earthRad * c / (math.Abs(float64(time.Since(lastUpdateTime))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse string from AT command that contains gps data
|
||||||
func (gps *GpsData) decode(str string) error {
|
func (gps *GpsData) decode(str string) error {
|
||||||
var err error
|
var err error
|
||||||
newGpsInfo := GpsData{}
|
newGpsInfo := GpsData{}
|
||||||
@ -50,8 +53,10 @@ func (gps *GpsData) decode(str string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parse longitude: %w", err)
|
return fmt.Errorf("parse longitude: %w", err)
|
||||||
}
|
}
|
||||||
|
newGpsInfo.Latitude /= 100
|
||||||
|
newGpsInfo.Longitude /= 100
|
||||||
newGpsInfo.LatitudeIndicator = strs[1]
|
newGpsInfo.LatitudeIndicator = strs[1]
|
||||||
newGpsInfo.LatitudeIndicator = strs[3]
|
newGpsInfo.LongitudeIndicator = strs[3]
|
||||||
newGpsInfo.Date = strs[4]
|
newGpsInfo.Date = strs[4]
|
||||||
newGpsInfo.Time = strs[5]
|
newGpsInfo.Time = strs[5]
|
||||||
newGpsInfo.Altitude, err = strconv.ParseFloat(strs[6], 64)
|
newGpsInfo.Altitude, err = strconv.ParseFloat(strs[6], 64)
|
||||||
@ -72,3 +77,61 @@ func (gps *GpsData) decode(str string) error {
|
|||||||
*gps = newGpsInfo
|
*gps = newGpsInfo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gps *GpsData) Update(port at.Port) error {
|
||||||
|
if err := switchGpsMode(port, true); err != nil {
|
||||||
|
return fmt.Errorf("try to GPS mode: %w", err)
|
||||||
|
}
|
||||||
|
defer switchGpsMode(port, false)
|
||||||
|
|
||||||
|
resp, err := port.Send("AT+CGPSINFO")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("receive GPS data: %w", err)
|
||||||
|
}
|
||||||
|
if !resp.Check() {
|
||||||
|
return fmt.Errorf("error response")
|
||||||
|
}
|
||||||
|
if err := gps.decode(strings.Split(strings.Replace(resp.RmFront("+CGPSINFO:").String(), "\r", "", -1), "\n")[0]); err != nil {
|
||||||
|
return fmt.Errorf("decode: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func switchGpsMode(port at.Port, on bool) error {
|
||||||
|
onStr := "0"
|
||||||
|
if on {
|
||||||
|
onStr = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset input
|
||||||
|
if err := port.GetSerialPort().ResetInputBuffer(); err != nil {
|
||||||
|
return fmt.Errorf("reset input buffer: %w", err)
|
||||||
|
}
|
||||||
|
// Reset output
|
||||||
|
if err := port.GetSerialPort().ResetOutputBuffer(); err != nil {
|
||||||
|
return fmt.Errorf("reset output buffer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check gps mode status
|
||||||
|
resp, err := port.Send("AT+CGPS?")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("make at ask: %w", err)
|
||||||
|
}
|
||||||
|
if !resp.Check() {
|
||||||
|
return fmt.Errorf("error response")
|
||||||
|
}
|
||||||
|
ans := strings.Replace(strings.Split(strings.Split(resp.RmFront("+CGPS:").String(), "\n")[0], ",")[0], " ", "", -1)
|
||||||
|
if ans == onStr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modem is not in GPS mode
|
||||||
|
resp, err = port.Send("AT+CGPS=" + onStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("try to switch to gps: %w", err)
|
||||||
|
}
|
||||||
|
if !resp.Check() {
|
||||||
|
return fmt.Errorf("switch tp GPS failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
242
api/modem/internet/ic.go
Normal file
242
api/modem/internet/ic.go
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
package internet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/CGSG-2021-AE4/modem-test/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
|
||||||
|
}
|
||||||
|
|
||||||
|
type Conn interface {
|
||||||
|
Init() error
|
||||||
|
ConfigurePPP() error
|
||||||
|
Ping() bool // Is connected
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger *log.Logger, port at.Port) Conn {
|
||||||
|
return &conn{
|
||||||
|
logger: logger,
|
||||||
|
port: port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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("SIM card is not inserted")
|
||||||
|
}
|
||||||
|
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("failed to check SIM provider")
|
||||||
|
}
|
||||||
|
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...")
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Ping() bool {
|
||||||
|
// 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 1 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 2 resp:", string(resp))
|
||||||
|
|
||||||
|
|
||||||
|
return !strings.Contains(string(resp), "Destination Host Unreachable") // tmp solution
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Close() error {
|
||||||
|
if err := c.diconnect(); err != nil {
|
||||||
|
return fmt.Errorf("diconnect: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -10,6 +10,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CGSG-2021-AE4/modem-test/api/modem/at"
|
"github.com/CGSG-2021-AE4/modem-test/api/modem/at"
|
||||||
|
"github.com/CGSG-2021-AE4/modem-test/api/modem/gpio"
|
||||||
|
"github.com/CGSG-2021-AE4/modem-test/api/modem/internet"
|
||||||
|
"github.com/CGSG-2021-AE4/modem-test/api/modem/sms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ModemData struct {
|
type ModemData struct {
|
||||||
@ -28,11 +31,19 @@ type modem struct {
|
|||||||
port at.Port
|
port at.Port
|
||||||
|
|
||||||
// Gpio values
|
// Gpio values
|
||||||
onOffPin gpioPin
|
onOffPin gpio.Pin
|
||||||
|
|
||||||
// Other values
|
// Other values
|
||||||
gpsInfo GpsData
|
|
||||||
lastUpdateTime time.Time
|
lastUpdateTime time.Time
|
||||||
|
|
||||||
|
// GPS
|
||||||
|
gpsInfo GpsData
|
||||||
|
|
||||||
|
// Internet connection
|
||||||
|
ic internet.Conn
|
||||||
|
|
||||||
|
// Sms and calls
|
||||||
|
sms sms.Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
type Modem interface {
|
type Modem interface {
|
||||||
@ -40,6 +51,11 @@ type Modem interface {
|
|||||||
Validate() bool
|
Validate() bool
|
||||||
Update() error
|
Update() error
|
||||||
GetInfo() ModemData
|
GetInfo() ModemData
|
||||||
|
|
||||||
|
// Temp access to SMS and AT interface
|
||||||
|
Sms() sms.Dialer
|
||||||
|
At() at.Port
|
||||||
|
|
||||||
io.Closer
|
io.Closer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,18 +63,18 @@ func New(logger *log.Logger) Modem {
|
|||||||
return &modem{
|
return &modem{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
baudrate: 115200,
|
baudrate: 115200,
|
||||||
onOffPin: gpioPin{Pin: 6},
|
onOffPin: gpio.New(log.New(logger.Writer(), "gpio", log.LstdFlags), 6),
|
||||||
lastUpdateTime: time.Now(),
|
lastUpdateTime: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *modem) Init() error {
|
func (m *modem) Init() error {
|
||||||
// Turn module on
|
// Turn module on
|
||||||
// log.Println("=============================== Turn on module")
|
m.logger.Println("=============================== Turn on module")
|
||||||
// if err := m.onOffPin.Init(); err != nil {
|
if err := m.onOffPin.Init(); err != nil {
|
||||||
// return fmt.Errorf("gpio pin init: %w", err)
|
return fmt.Errorf("gpio pin init: %w", err)
|
||||||
// }
|
}
|
||||||
// onOffPin.PowerOn()
|
// m.onOffPin.PowerOn()
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
m.logger.Println("=============================== Search")
|
m.logger.Println("=============================== Search")
|
||||||
@ -87,6 +103,19 @@ func (m *modem) Init() error {
|
|||||||
if err := m.testGPS(); err != nil {
|
if err := m.testGPS(); err != nil {
|
||||||
return fmt.Errorf("testGPS: %w", err)
|
return fmt.Errorf("testGPS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Establish internet connection
|
||||||
|
// m.logger.Println("=============================== Internet")
|
||||||
|
// m.ic = internet.New(log.New(m.logger.Writer(), "internet", log.LstdFlags), m.port)
|
||||||
|
// if err := m.ic.Init(); err != nil {
|
||||||
|
// return fmt.Errorf("internet connection init: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Init sms dialer
|
||||||
|
m.sms = sms.New(log.New(m.logger.Writer(), "sms", log.LstdFlags), m.port)
|
||||||
|
if err := m.sms.Init(); err != nil {
|
||||||
|
return fmt.Errorf("sms dialer init %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,26 +170,42 @@ func (m *modem) GetInfo() ModemData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *modem) Sms() sms.Dialer {
|
||||||
|
return m.sms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modem) At() at.Port {
|
||||||
|
return m.port
|
||||||
|
}
|
||||||
|
|
||||||
func (m *modem) Close() error {
|
func (m *modem) Close() error {
|
||||||
|
if err := m.sms.Close(); err != nil {
|
||||||
|
return fmt.Errorf("sms: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Not right way I think
|
// Not right way I think
|
||||||
if err := m.port.Close(); err != nil {
|
if err := m.port.Close(); err != nil {
|
||||||
return err
|
return fmt.Errorf("serial port: %w", err)
|
||||||
}
|
}
|
||||||
if err := m.onOffPin.Close(); err != nil {
|
if err := m.onOffPin.Close(); err != nil {
|
||||||
return err
|
return fmt.Errorf("gpio pin: %w", err)
|
||||||
}
|
}
|
||||||
|
if err := m.ic.Close(); err != nil {
|
||||||
|
return fmt.Errorf("internet connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *modem) connect() error {
|
func (m *modem) connect() error {
|
||||||
if !m.Validate() {
|
if m.port == nil {
|
||||||
return fmt.Errorf("port is not defined")
|
return fmt.Errorf("port is not defined")
|
||||||
}
|
}
|
||||||
return m.port.Connect()
|
return m.port.Connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *modem) disconnect() error {
|
func (m *modem) disconnect() error {
|
||||||
if !m.Validate() {
|
if m.port == nil {
|
||||||
return fmt.Errorf("port is not defined")
|
return fmt.Errorf("port is not defined")
|
||||||
}
|
}
|
||||||
return m.port.Disconnect()
|
return m.port.Disconnect()
|
||||||
@ -176,10 +221,6 @@ func (m *modem) isConnected() bool {
|
|||||||
func (m *modem) testGPS() error {
|
func (m *modem) testGPS() error {
|
||||||
m.logger.Println("Testing GPS")
|
m.logger.Println("Testing GPS")
|
||||||
|
|
||||||
if err := m.switchToGpsMode(); err != nil {
|
|
||||||
return fmt.Errorf("switch to GPS: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.Update(); err != nil {
|
if err := m.Update(); err != nil {
|
||||||
return fmt.Errorf("update: %w", err)
|
return fmt.Errorf("update: %w", err)
|
||||||
}
|
}
|
||||||
@ -198,7 +239,6 @@ func (m *modem) saveGPS(path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open file: %w", err)
|
return fmt.Errorf("open file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
if _, err = f.WriteString(m.getShortInfo()); err != nil {
|
if _, err = f.WriteString(m.getShortInfo()); err != nil {
|
||||||
@ -207,6 +247,22 @@ func (m *modem) saveGPS(path string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *modem) testConsole() {
|
||||||
|
for {
|
||||||
|
var inStr string
|
||||||
|
fmt.Scanln(&inStr)
|
||||||
|
if inStr == "exit" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := m.port.Send(inStr)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Println("ERROR:", err.Error())
|
||||||
|
}
|
||||||
|
m.logger.Println(resp)
|
||||||
|
m.logger.Println("------------------")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *modem) checkPort(portName string) (outErr error) {
|
func (m *modem) checkPort(portName string) (outErr error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if outErr != nil { // Clear port if there is error
|
if outErr != nil { // Clear port if there is error
|
||||||
@ -227,16 +283,26 @@ func (m *modem) checkPort(portName string) (outErr error) {
|
|||||||
}
|
}
|
||||||
defer m.port.Disconnect() // Do not bother about errors...
|
defer m.port.Disconnect() // Do not bother about errors...
|
||||||
|
|
||||||
|
// Reset input
|
||||||
|
if err := m.port.GetSerialPort().ResetInputBuffer(); err != nil {
|
||||||
|
return fmt.Errorf("reset input buffer: %w", err)
|
||||||
|
}
|
||||||
|
// Reset output
|
||||||
|
if err := m.port.GetSerialPort().ResetOutputBuffer(); err != nil {
|
||||||
|
return fmt.Errorf("reset output buffer: %w", err)
|
||||||
|
}
|
||||||
|
m.port.Send("ATE0") // This shit sometimes enables echo mode... why... when... but it can
|
||||||
|
// m.port.Send("\r\n")
|
||||||
|
|
||||||
// Ping
|
// Ping
|
||||||
m.logger.Println("Ping...")
|
m.logger.Println("Ping...")
|
||||||
|
|
||||||
if err := m.ping(); err != nil {
|
if err := m.ping(); err != nil {
|
||||||
return fmt.Errorf("ping error: %w", err)
|
return fmt.Errorf("ping error: %w", err)
|
||||||
}
|
}
|
||||||
|
m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m")
|
||||||
|
|
||||||
// Check model
|
// Check model
|
||||||
m.logger.Println("Check model...")
|
m.logger.Println("Check model...")
|
||||||
|
|
||||||
resp, err := m.port.Send("AT+CGMM")
|
resp, err := m.port.Send("AT+CGMM")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get model: %w", err)
|
return fmt.Errorf("get model: %w", err)
|
||||||
@ -250,10 +316,10 @@ func (m *modem) checkPort(portName string) (outErr error) {
|
|||||||
}
|
}
|
||||||
rightModel := "SIMCOM_SIM7600E-H"
|
rightModel := "SIMCOM_SIM7600E-H"
|
||||||
// m.logger.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model))
|
// m.logger.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model))
|
||||||
if model[:len(rightModel)] != rightModel {
|
if len(model) >= len(rightModel) && model[:len(rightModel)] != rightModel {
|
||||||
return fmt.Errorf("invalid modem model: %s", model)
|
return fmt.Errorf("invalid modem model: %s", model)
|
||||||
}
|
}
|
||||||
m.logger.Println("Model right")
|
m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +340,7 @@ SearchLoop:
|
|||||||
m.logger.Printf("Checking port %s ...\n", p)
|
m.logger.Printf("Checking port %s ...\n", p)
|
||||||
|
|
||||||
if err := m.checkPort("/dev/" + p); err != nil {
|
if err := m.checkPort("/dev/" + p); err != nil {
|
||||||
m.logger.Printf("Check failed: %s\n", err.Error())
|
m.logger.Printf("\x1b[38;2;255;0;0mCheck failed: %s\x1b[38;2;255;255;255m\n", err.Error())
|
||||||
continue SearchLoop
|
continue SearchLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,40 +362,6 @@ func (m *modem) ping() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *modem) switchToGpsMode() error {
|
|
||||||
m.logger.Println("Enabling GPS mode...")
|
|
||||||
// Reset intput
|
|
||||||
if err := m.port.GetSerialPort().ResetInputBuffer(); err != nil {
|
|
||||||
return fmt.Errorf("reset input buffer: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check gps mode status
|
|
||||||
resp, err := m.port.Send("AT+CGPS?")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("make at ask: %w", err)
|
|
||||||
}
|
|
||||||
if !resp.Check() {
|
|
||||||
return fmt.Errorf("error response")
|
|
||||||
}
|
|
||||||
ans := strings.Replace(strings.Split(strings.Split(resp.RmFront("+CGPS:").String(), "\n")[0], ",")[0], " ", "", -1)
|
|
||||||
if ans == "1" {
|
|
||||||
m.logger.Println("GPS already enabled")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
m.logger.Println(ans)
|
|
||||||
|
|
||||||
// Modem is not in GPS mode
|
|
||||||
resp, err = m.port.Send("AT+CGPS=1")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("try to switch to gps: %w", err)
|
|
||||||
}
|
|
||||||
if !resp.Check() {
|
|
||||||
return fmt.Errorf("switch tp GPS failed")
|
|
||||||
}
|
|
||||||
m.logger.Println("GPS mode enabled")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTtyDevices() ([]string, error) {
|
func getTtyDevices() ([]string, error) {
|
||||||
// Get ports
|
// Get ports
|
||||||
/**/
|
/**/
|
||||||
|
69
api/modem/sms/errors.go
Normal file
69
api/modem/sms/errors.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
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")
|
||||||
|
}
|
85
api/modem/sms/sms.go
Normal file
85
api/modem/sms/sms.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package sms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/CGSG-2021-AE4/modem-test/api/modem/at"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dialer struct {
|
||||||
|
logger *log.Logger
|
||||||
|
port at.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dialer interface {
|
||||||
|
Init() error
|
||||||
|
Send(number, msg string) error
|
||||||
|
ReadNew() ([]string, error)
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger *log.Logger, port at.Port) Dialer {
|
||||||
|
return &dialer{
|
||||||
|
logger: logger,
|
||||||
|
port: port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dialer) Init() error {
|
||||||
|
// Ensure serial port
|
||||||
|
if !d.port.IsConnected() {
|
||||||
|
return fmt.Errorf("serial port is not connected")
|
||||||
|
}
|
||||||
|
// Ensure text format
|
||||||
|
if resp, err := d.port.Send("AT+CMGF=1"); err != nil || !resp.Check() {
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AT+CMGF=1 request: %w", err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to set SMS format")
|
||||||
|
}
|
||||||
|
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\x1A", msg)) // Add additional \r\n because there is not supposed to be
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("message request: %w", err)
|
||||||
|
}
|
||||||
|
if at.Resp(resp).Check() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads all new messages
|
||||||
|
func (d *dialer) ReadNew() ([]string, error) {
|
||||||
|
resp, err := d.port.Send("AT+CMGL")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AT+CMGL request: %w", err)
|
||||||
|
}
|
||||||
|
msgs := strings.Split(strings.Replace(string(resp), "\r", "", -1), "\n")
|
||||||
|
|
||||||
|
outMsgs := make([]string, 0)
|
||||||
|
for _, s := range msgs {
|
||||||
|
if len(s) >= len("+CMGL:") && s[:len("+CMGL:")] == "+CMGL:" {
|
||||||
|
params := strings.Split(s[len("+CMGL:"):], ",")
|
||||||
|
d.logger.Println("GET MSG:", params)
|
||||||
|
} else {
|
||||||
|
outMsgs = append(outMsgs, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outMsgs, nil // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dialer) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
21
main.go
21
main.go
@ -17,14 +17,27 @@ func main() {
|
|||||||
|
|
||||||
func mainE() error {
|
func mainE() error {
|
||||||
m := modem.New(log.New(os.Stdout, "modem:", log.LstdFlags))
|
m := modem.New(log.New(os.Stdout, "modem:", log.LstdFlags))
|
||||||
|
log.Println("||||||||||||||||| INIT |||||||||||||||")
|
||||||
if err := m.Init(); err != nil {
|
if err := m.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !m.Validate() {
|
||||||
|
log.Println("AAAAAAAAAAAAAAA Validation failed")
|
||||||
return nil
|
return nil
|
||||||
// ports, err := serial.GetPortsList()
|
}
|
||||||
// if err != nil {
|
log.Println("||||||||||||||||| GET INFO |||||||||||||||||")
|
||||||
|
log.Println(m.GetInfo())
|
||||||
|
|
||||||
|
log.Println("||||||||||||||||| SEND SMS |||||||||||||||||")
|
||||||
|
log.Println(m.At().Send("AT+CNUM"))
|
||||||
|
// if err := m.Sms().Send("+79218937173", "CGSG forever"); err != nil {
|
||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
// log.Println(ports)
|
if ms, err := m.Sms().ReadNew(); err != nil {
|
||||||
// return nil
|
return err
|
||||||
|
} else {
|
||||||
|
log.Println("NEW:", ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user