diff --git a/goTool/api/uart.go b/goTool/api/uart.go index b5aefa4..e53b63c 100644 --- a/goTool/api/uart.go +++ b/goTool/api/uart.go @@ -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 +} diff --git a/goTool/main.go b/goTool/main.go index 8003df0..1e82a3f 100644 --- a/goTool/main.go +++ b/goTool/main.go @@ -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 } diff --git a/goTool/ota.go b/goTool/ota.go new file mode 100644 index 0000000..cd4720e --- /dev/null +++ b/goTool/ota.go @@ -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) + } +} diff --git a/goTool/uart/com.go b/goTool/uart/com.go index db3b2a7..033c8de 100644 --- a/goTool/uart/com.go +++ b/goTool/uart/com.go @@ -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) diff --git a/goTool/uart/commands.go b/goTool/uart/commands.go index 07480dd..9057434 100644 --- a/goTool/uart/commands.go +++ b/goTool/uart/commands.go @@ -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 } diff --git a/main/main.c b/main/main.c index 52f95c1..4c112b2 100644 --- a/main/main.c +++ b/main/main.c @@ -373,8 +373,9 @@ void app_main(void) { RegisterCallback(UART_CLIENT_INFO, clientInfoCallback); RegisterCallback(UART_CLIENT_INPUT, fakeDataCallback); RegisterCallback(UART_OTA_START_ESPNOW, start_ota_update_espnow); + RegisterUART_OTAFunctions(); - init_i2c_with_all_devices(); + // init_i2c_with_all_devices(); } else { ESP_LOGI(TAG, "Started in Slavemode"); diff --git a/main/message_handler.c b/main/message_handler.c index 89baab1..4827707 100644 --- a/main/message_handler.c +++ b/main/message_handler.c @@ -13,6 +13,7 @@ void InitMessageBroker() { } void RegisterCallback(uint8_t msgid, RegisterFunctionCallback callback) { + ESP_LOGI(TAG, "Registerd Uart Callback for % X", msgid); mr.FunctionList[mr.num_direct_callbacks].MSGID = msgid; mr.FunctionList[mr.num_direct_callbacks].callback = callback; mr.num_direct_callbacks++; @@ -46,11 +47,13 @@ void MessageBrokerTask(void *param) { while (1) { if (xQueueReceive(msg_queue, &received_msg, portMAX_DELAY)) { - //ESP_LOGI(TAG, "Received message from queue: MSGID=0x%02X, Length=%u", - // received_msg.msgid, received_msg.payload_len); + // ESP_LOGI(TAG, "Received message from queue: MSGID=0x%02X, Length=%u", + // received_msg.msgid, received_msg.payload_len); for (int i = 0; i < mr.num_direct_callbacks; i++) { + //ESP_LOGI(TAG, "Searching CALLBACK for %d", received_msg.msgid); if (mr.FunctionList[i].MSGID == received_msg.msgid) { + //ESP_LOGI(TAG, "FOUND CALLBACK"); mr.FunctionList[i].callback( received_msg.msgid, received_msg.data, received_msg.payload_len, send_payload_buffer, send_payload_buffer_size, diff --git a/main/ota_update.c b/main/ota_update.c index f2674db..5ae5c96 100644 --- a/main/ota_update.c +++ b/main/ota_update.c @@ -353,14 +353,16 @@ void start_uart_update(uint8_t msgid, const uint8_t *payload, int part = prepare_ota_update(); - // TODO: what does this do? maybe comment it out for now? + uart_ota_start_t *start = (uart_ota_start_t *)send_payload_buffer; + start->partition = part & 0xff; + + // TODO: Refine Errors + // Set error if (part < 0) { - send_payload_buffer[1] = (part * -1) & 0xff; - } else { - send_payload_buffer[0] = part & 0xff; + start->error = 0x01; } - int send_payload_len = 2; + int send_payload_len = sizeof(uart_ota_start_t); int len = build_message(UART_OTA_START, send_payload_buffer, send_payload_len, send_buffer, send_buffer_size); if (len < 0) { @@ -375,9 +377,9 @@ void start_uart_update(uint8_t msgid, const uint8_t *payload, } esp_err_t write_ota_update(uint32_t write_len, const uint8_t *payload) { - ESP_LOGI(TAG, "write_ota_update: write_len: %d", write_len); - ESP_LOGI(TAG, "write_ota_update: update_buffer_write_index: %d", - update_buffer_write_index); + // ESP_LOGI(TAG, "write_ota_update: write_len: %d", write_len); + // ESP_LOGI(TAG, "write_ota_update: update_buffer_write_index: %d", + // update_buffer_write_index); if (update_buffer_write_index + write_len > UPDATE_BUFFER_SIZE) { ESP_LOGI(TAG, "write_ota_update: schreib das update weg!"); esp_err_t err = @@ -412,15 +414,17 @@ void payload_uart_update(uint8_t msgid, const uint8_t *payload_data_from_uart, esp_err_t err = write_ota_update(write_len, actual_firmware_data); + uart_ota_ack_t *ack = (uart_ota_ack_t *)send_payload_buffer; + ack->sequence_counter = sequenz_counter; + ack->write_index = update_buffer_write_index; + ack->error = (err == ESP_OK) ? 0x00 : 0x01; + + size_t send_payload_len = sizeof(uart_ota_ack_t); + if (err != ESP_OK) { ESP_LOGE(TAG, "GOT ESP ERROR WRITE OTA %d", err); } - size_t send_payload_len = 4; - memcpy(send_payload_buffer, &sequenz_counter, 2); - memcpy(&send_payload_buffer[2], &update_buffer_write_index, 2); - send_payload_buffer[4] = 0x00; // error - int len = build_message(UART_OTA_PAYLOAD, send_payload_buffer, send_payload_len, send_buffer, send_buffer_size); if (len < 0) { @@ -442,8 +446,10 @@ void end_uart_update(uint8_t msgid, const uint8_t *payload, size_t payload_len, esp_err_t err = end_ota_update(); - int send_payload_len = 1; - send_payload_buffer[0] = err & 0xff; + uart_ota_end_t *end = (uart_ota_end_t *)send_payload_buffer; + end->error = err & 0xff; + int send_payload_len = sizeof(uart_ota_end_t); + int len = build_message(UART_OTA_END, send_payload_buffer, send_payload_len, send_buffer, send_buffer_size); if (len < 0) { @@ -461,7 +467,9 @@ void end_uart_update(uint8_t msgid, const uint8_t *payload, size_t payload_len, void init_ota() { ota_task_queue = xQueueCreate(50, sizeof(ota_task_queue_message_t)); +} +void RegisterUART_OTAFunctions() { RegisterCallback(UART_OTA_START, start_uart_update); RegisterCallback(UART_OTA_PAYLOAD, payload_uart_update); RegisterCallback(UART_OTA_END, end_uart_update); @@ -485,7 +493,17 @@ int prepare_ota_update() { ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); return -2; } - return 0; + + if (update_partition->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0) { + return 0; + } + + if (update_partition->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_1) { + return 1; + } + + // TODO: Unknow partition + return 2; } esp_err_t end_ota_update() { diff --git a/main/ota_update.h b/main/ota_update.h index 8a0b652..ca0b94d 100644 --- a/main/ota_update.h +++ b/main/ota_update.h @@ -33,7 +33,23 @@ typedef struct { ClientList *client_list; } MasterOTA_TaskParams_t; +typedef struct __attribute__((packed)) { + uint16_t sequence_counter; + uint16_t write_index; + uint8_t error; +} uart_ota_ack_t; + +typedef struct __attribute__((packed)) { + uint8_t partition; + uint8_t error; +} uart_ota_start_t; + +typedef struct __attribute__((packed)) { + uint8_t error; +} uart_ota_end_t; + void init_ota(); +void RegisterUART_OTAFunctions(); void MasterOTATask(void *pvParameter); void slave_ota_task(void *pvParameter); diff --git a/main/uart_handler.c b/main/uart_handler.c index 28080d2..d0670d5 100644 --- a/main/uart_handler.c +++ b/main/uart_handler.c @@ -18,7 +18,8 @@ static const char *TAG = "ALOX - UART"; static QueueHandle_t parsed_message_queue; void init_uart(QueueHandle_t msg_queue_handle) { - uart_config_t uart_config = {.baud_rate = 921600, // 921600, 115200 + uart_config_t uart_config = {// .baud_rate = 115200, // 921600, 115200 + .baud_rate = 921600, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1,