Improved Go Tool so that the OTA Update Works again
This commit is contained in:
@@ -5,6 +5,8 @@ const (
|
||||
TopicUARTRx = "uart:rx"
|
||||
TopicUARTTx = "uart:tx"
|
||||
TopicUARTError = "uart:error"
|
||||
|
||||
TopicOTA = "ota"
|
||||
)
|
||||
|
||||
type Frame struct {
|
||||
@@ -18,6 +20,12 @@ const (
|
||||
CmdVersion byte = 0x02
|
||||
CmdClientInfo byte = 0x03
|
||||
CmdClientInput byte = 0x04
|
||||
|
||||
CmdOtaStart byte = 0x10
|
||||
CmdOtaPayload byte = 0x11
|
||||
CmdOtaEnd byte = 0x12
|
||||
CmdOtaStatus byte = 0x13
|
||||
CmdOtaStartEspNow byte = 0x14
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -50,3 +58,30 @@ type PayloadClientInput struct {
|
||||
Y float32
|
||||
InputMask uint32
|
||||
}
|
||||
|
||||
type PayloadOtaStatus struct {
|
||||
SequenzCounter uint16
|
||||
WriteIndex uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type PayloadOtaStart struct {
|
||||
Data []byte
|
||||
Parition byte
|
||||
Error byte
|
||||
}
|
||||
|
||||
type PayloadOtaEnd struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type PayloadOtaPayload struct {
|
||||
SequenzCounter uint16
|
||||
WriteIndex uint16
|
||||
Data []byte
|
||||
Error byte
|
||||
}
|
||||
|
||||
type PayloadOtaStartEspNow struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
+99
-15
@@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"alox.tool/api"
|
||||
@@ -25,7 +27,8 @@ func main() {
|
||||
Port: 8000,
|
||||
Host: "0.0.0.0",
|
||||
UartPort: "/dev/ttyUSB0",
|
||||
Baudrate: 115200,
|
||||
//Baudrate: 115200,
|
||||
Baudrate: 921600,
|
||||
}
|
||||
if Tests {
|
||||
StartTests(config)
|
||||
@@ -61,15 +64,55 @@ func StartApp(config Config) {
|
||||
}
|
||||
defer com.Close()
|
||||
|
||||
tr := testrunner.New(bus, com)
|
||||
tr.RunVersionTest()
|
||||
update, err := os.ReadFile("../espAlox.bin")
|
||||
if err != nil {
|
||||
log.Printf("Could not read Update file %v", err)
|
||||
return
|
||||
}
|
||||
updateSlices := SliceUpdate(update, 200)
|
||||
|
||||
oManager := NewOTAManager(bus, com, updateSlices)
|
||||
ctx, cancle := context.WithCancel(context.Background())
|
||||
defer cancle()
|
||||
|
||||
StartMessageHandling(ctx, bus)
|
||||
oManager.StartUpdateHandler(ctx)
|
||||
|
||||
time.Sleep(time.Millisecond * 5)
|
||||
|
||||
//tr := testrunner.New(bus, com)
|
||||
//tr.RunVersionTest()
|
||||
|
||||
time.Sleep(time.Millisecond * 5)
|
||||
|
||||
com.Send(api.CmdEcho, make([]byte, 0))
|
||||
com.Send(api.CmdVersion, make([]byte, 0))
|
||||
com.Send(api.CmdClientInfo, make([]byte, 0))
|
||||
com.Send(api.CmdClientInput, make([]byte, 0))
|
||||
|
||||
//com.Send(api.CmdOtaStart, make([]byte, 0))
|
||||
com.Send(api.CmdOtaStartEspNow, make([]byte, 0))
|
||||
|
||||
url := fmt.Sprintf("%s:%d", config.Host, config.Port)
|
||||
fserver := frontend.New(bus)
|
||||
fserver.Start(url)
|
||||
}
|
||||
|
||||
func StartMessageHandling(ctx context.Context, bus eventbus.EventBus) {
|
||||
|
||||
RXC := bus.Subscribe(api.TopicUARTRx)
|
||||
defer bus.Unsubscribe(api.TopicUARTRx, RXC)
|
||||
TXC := bus.Subscribe(api.TopicUARTTx)
|
||||
defer bus.Unsubscribe(api.TopicUARTTx, TXC)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case _ = <-bus.Subscribe(api.TopicUARTTx):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case _ = <-TXC:
|
||||
//log.Printf("MSG[TX]: % X", msg)
|
||||
case msg := <-bus.Subscribe(api.TopicUARTRx):
|
||||
case msg := <-RXC:
|
||||
//log.Printf("MSG[RX]: % X", msg)
|
||||
val, ok := msg.(api.Frame)
|
||||
if !ok {
|
||||
@@ -80,6 +123,7 @@ func StartApp(config Config) {
|
||||
|
||||
switch val.ID {
|
||||
case api.CmdEcho:
|
||||
log.Printf("Echo %v", val)
|
||||
case api.CmdVersion:
|
||||
v, err := uart.ParseFrameVersion(val)
|
||||
if err != nil {
|
||||
@@ -114,19 +158,59 @@ func StartApp(config Config) {
|
||||
log.Printf("\tY %f", c.Y)
|
||||
log.Printf("\tBitmask %08b", c.InputMask)
|
||||
}
|
||||
case api.CmdOtaPayload:
|
||||
v, err := uart.ParseFrameOtaPayload(val)
|
||||
if err != nil {
|
||||
log.Printf("Could not Parse Client Input %v", err)
|
||||
continue
|
||||
}
|
||||
bus.Publish(api.TopicOTA, v)
|
||||
log.Printf("%v", v)
|
||||
case api.CmdOtaStatus:
|
||||
v, err := uart.ParseFrameOtaStatus(val)
|
||||
if err != nil {
|
||||
log.Printf("Could not Parse Client Input %v", err)
|
||||
continue
|
||||
}
|
||||
bus.Publish(api.TopicOTA, v)
|
||||
log.Printf("%v", v)
|
||||
// Update State Machine
|
||||
|
||||
case api.CmdOtaStart:
|
||||
v, err := uart.ParseFrameOtaStart(val)
|
||||
if err != nil {
|
||||
log.Printf("Could not Parse Client Input %v", err)
|
||||
continue
|
||||
}
|
||||
bus.Publish(api.TopicOTA, v)
|
||||
log.Printf("%v", v)
|
||||
case api.CmdOtaEnd:
|
||||
v, err := uart.ParseFrameOtaEnd(val)
|
||||
if err != nil {
|
||||
log.Printf("Could not Parse Client Input %v", err)
|
||||
continue
|
||||
}
|
||||
bus.Publish(api.TopicOTA, v)
|
||||
log.Printf("%v", v)
|
||||
case api.CmdOtaStartEspNow:
|
||||
v, err := uart.ParseFrameOtaStartEspNow(val)
|
||||
if err != nil {
|
||||
log.Printf("Could not Parse Client Input %v", err)
|
||||
continue
|
||||
}
|
||||
bus.Publish(api.TopicOTA, v)
|
||||
log.Printf("%v", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 5)
|
||||
|
||||
com.Send(api.CmdEcho, make([]byte, 0))
|
||||
com.Send(api.CmdVersion, make([]byte, 0))
|
||||
com.Send(api.CmdClientInfo, make([]byte, 0))
|
||||
com.Send(api.CmdClientInput, make([]byte, 0))
|
||||
|
||||
url := fmt.Sprintf("%s:%d", config.Host, config.Port)
|
||||
fserver := frontend.New(bus)
|
||||
fserver.Start(url)
|
||||
func SliceUpdate(update []byte, maxlen int) [][]byte {
|
||||
updateSlices := [][]byte{}
|
||||
for i := 0; i < len(update); i += 200 {
|
||||
end := min(i+200, len(update))
|
||||
updateSlices = append(updateSlices, update[i:end])
|
||||
}
|
||||
return updateSlices
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"alox.tool/api"
|
||||
"alox.tool/eventbus"
|
||||
"alox.tool/uart"
|
||||
)
|
||||
|
||||
type OTAManager struct {
|
||||
Bus eventbus.EventBus
|
||||
Com *uart.Com
|
||||
Update [][]byte
|
||||
CurrentSlice uint16
|
||||
Partition byte
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
func NewOTAManager(bus eventbus.EventBus, com *uart.Com, update [][]byte) OTAManager {
|
||||
return OTAManager{
|
||||
Bus: bus,
|
||||
Com: com,
|
||||
Update: update,
|
||||
CurrentSlice: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (om *OTAManager) StartUpdateHandler(ctx context.Context) {
|
||||
OtaChanel := om.Bus.Subscribe(api.TopicOTA)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case msg := <-OtaChanel:
|
||||
om.handleOtaMessage(msg)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (om *OTAManager) handleOtaMessage(msg any) {
|
||||
switch msgT := msg.(type) {
|
||||
case api.PayloadOtaStart:
|
||||
// Send First Payload
|
||||
om.StartTime = time.Now()
|
||||
om.Partition = msgT.Parition
|
||||
err := om.Com.Send(api.CmdOtaPayload, om.Update[om.CurrentSlice])
|
||||
if err != nil {
|
||||
log.Printf("Error Sending Update Step!: %v", err)
|
||||
return
|
||||
}
|
||||
om.CurrentSlice = om.CurrentSlice + 1
|
||||
log.Printf("First Update Step %d", om.CurrentSlice)
|
||||
log.Printf("%v", msgT)
|
||||
case api.PayloadOtaPayload:
|
||||
// Send Next Payload until there is no more then send end package
|
||||
|
||||
log.Printf("msgT %v", msgT)
|
||||
|
||||
if msgT.Error != 0x00 {
|
||||
log.Printf("Error in Sending Update! Check ESP Log")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("NEXT PAYLOAD")
|
||||
if om.CurrentSlice == uint16(len(om.Update)) {
|
||||
log.Printf("LAST PAYLOAD SEND ENDING")
|
||||
om.Com.Send(api.CmdOtaEnd, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
err := om.Com.Send(api.CmdOtaPayload, om.Update[om.CurrentSlice])
|
||||
if err != nil {
|
||||
log.Printf("Error Sending Update Step!: %v", err)
|
||||
return
|
||||
}
|
||||
om.CurrentSlice = om.CurrentSlice + 1
|
||||
log.Printf("UPDATE CURRENT SLICE %d/%d", om.CurrentSlice, len(om.Update))
|
||||
log.Printf("UPDATE Part/WriteIndex %d/%d", msgT.SequenzCounter, msgT.WriteIndex)
|
||||
log.Printf("Progress: %05.2f%%", (float32(om.CurrentSlice)/float32(len(om.Update)))*100)
|
||||
|
||||
case api.PayloadOtaEnd:
|
||||
// End bestätigung
|
||||
om.EndTime = time.Now()
|
||||
duration := om.EndTime.Sub(om.StartTime)
|
||||
log.Printf("Partition %d Update done in %f.2s!", om.Partition, duration.Seconds())
|
||||
log.Printf("%v", msgT)
|
||||
case api.PayloadOtaStatus:
|
||||
log.Printf("%v", msgT)
|
||||
case api.PayloadOtaStartEspNow:
|
||||
log.Printf("%v", msgT)
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,7 @@ func packFrame(id byte, payload []byte) []byte {
|
||||
|
||||
func (c *Com) Send(id byte, payload []byte) error {
|
||||
raw := packFrame(id, payload)
|
||||
log.Printf("[RAW]: %v", raw)
|
||||
|
||||
//log.Printf("RAW: % X", raw)
|
||||
_, err := c.port.Write(raw)
|
||||
|
||||
@@ -115,5 +115,75 @@ func ParseFrameClientInput(frame api.Frame) ([]api.PayloadClientInput, error) {
|
||||
}
|
||||
|
||||
return clientList, nil
|
||||
}
|
||||
|
||||
// Dummy for now Just get Data
|
||||
func ParseFrameOtaPayload(frame api.Frame) (api.PayloadOtaPayload, error) {
|
||||
if len(frame.Data) == 0 {
|
||||
return api.PayloadOtaPayload{}, fmt.Errorf("empty frame data")
|
||||
}
|
||||
|
||||
status := api.PayloadOtaPayload{
|
||||
Data: frame.Data,
|
||||
SequenzCounter: binary.LittleEndian.Uint16(frame.Data[0:2]),
|
||||
WriteIndex: binary.LittleEndian.Uint16(frame.Data[2:4]),
|
||||
Error: frame.Data[4],
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// Dummy for now Just get Data
|
||||
func ParseFrameOtaStatus(frame api.Frame) (api.PayloadOtaStatus, error) {
|
||||
if len(frame.Data) == 0 {
|
||||
return api.PayloadOtaStatus{}, fmt.Errorf("empty frame data")
|
||||
}
|
||||
|
||||
status := api.PayloadOtaStatus{
|
||||
Data: frame.Data,
|
||||
SequenzCounter: binary.LittleEndian.Uint16(frame.Data[0:2]),
|
||||
WriteIndex: binary.LittleEndian.Uint16(frame.Data[2:4]),
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// Dummy for now Just get Data
|
||||
func ParseFrameOtaStart(frame api.Frame) (api.PayloadOtaStart, error) {
|
||||
if len(frame.Data) == 0 {
|
||||
return api.PayloadOtaStart{}, fmt.Errorf("empty frame data")
|
||||
}
|
||||
|
||||
status := api.PayloadOtaStart{
|
||||
Data: frame.Data,
|
||||
Parition: frame.Data[0],
|
||||
Error: frame.Data[1],
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// Dummy for now Just get Data
|
||||
func ParseFrameOtaEnd(frame api.Frame) (api.PayloadOtaEnd, error) {
|
||||
if len(frame.Data) == 0 {
|
||||
return api.PayloadOtaEnd{}, fmt.Errorf("empty frame data")
|
||||
}
|
||||
|
||||
status := api.PayloadOtaEnd{
|
||||
Data: frame.Data,
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// Dummy for now Just get Data
|
||||
func ParseFrameOtaStartEspNow(frame api.Frame) (api.PayloadOtaStartEspNow, error) {
|
||||
if len(frame.Data) == 0 {
|
||||
return api.PayloadOtaStartEspNow{}, fmt.Errorf("empty frame data")
|
||||
}
|
||||
|
||||
status := api.PayloadOtaStartEspNow{
|
||||
Data: frame.Data,
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user