Initial.
This commit is contained in:
		
							
								
								
									
										53
									
								
								api/modem/at.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								api/modem/at.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										44
									
								
								api/modem/gpio.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										237
									
								
								api/modem/modem.go
									
									
									
									
									
										Normal 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? | ||||
| */ | ||||
|  | ||||
| /* | ||||
|  */ | ||||
		Reference in New Issue
	
	Block a user