Improved Go Tool so that the OTA Update Works again

This commit is contained in:
2026-02-08 17:00:41 +01:00
parent c656d4afd7
commit 0bcacb4a53
10 changed files with 362 additions and 35 deletions
+35
View File
@@ -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
View File
@@ -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
}
+98
View File
@@ -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)
}
}
+1
View File
@@ -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)
+70
View File
@@ -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
}