556 lines
14 KiB
Go
556 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/pterm/pterm"
|
|
"go.bug.st/serial"
|
|
)
|
|
|
|
type ParserState int
|
|
|
|
const (
|
|
// MISC
|
|
UART_ECHO = 0x01
|
|
UART_VERSION = 0x02
|
|
UART_CLIENT_INFO = 0x03
|
|
UART_CLIENT_INPUT = 0x04
|
|
|
|
// OTA
|
|
UART_OTA_START = 0x10
|
|
UART_OTA_PAYLOAD = 0x11
|
|
UART_OTA_END = 0x12
|
|
UART_OTA_STATUS = 0x13
|
|
UART_OTA_START_ESPNOW = 0x14
|
|
)
|
|
|
|
const (
|
|
WAITING_FOR_START_BYTE ParserState = iota
|
|
ESCAPED_MESSAGE_ID
|
|
GET_MESSAGE_ID
|
|
IN_PAYLOD
|
|
ESCAPED_PAYLOAD_BYTE
|
|
)
|
|
|
|
const (
|
|
START_BYTE = 0xAA
|
|
ESCAPE_BYTE = 0xBB
|
|
END_BYTE = 0xCC
|
|
)
|
|
|
|
type ParseError int
|
|
|
|
const (
|
|
WRONG_CHECKSUM ParseError = iota
|
|
UNEXPECETD_BYTE
|
|
)
|
|
|
|
type MessageReceive struct {
|
|
raw_message []byte
|
|
parsed_message []byte
|
|
checksum byte
|
|
error ParseError
|
|
state ParserState
|
|
write_index int
|
|
raw_write_index int
|
|
}
|
|
|
|
type OTASyncManager struct {
|
|
OTA_MessageCounter int
|
|
OTA_PayloadMessageSequence int
|
|
NewOTAMessage chan MessageReceive
|
|
TimeoutMessage time.Duration
|
|
}
|
|
|
|
func (ot *OTASyncManager) WaitForNextMessageTimeout() (*MessageReceive, error) {
|
|
select {
|
|
case msg := <-ot.NewOTAMessage:
|
|
return &msg, nil
|
|
case <-time.After(ot.TimeoutMessage):
|
|
return nil, fmt.Errorf("Message Timeout")
|
|
}
|
|
}
|
|
|
|
func initMessageReceive(mr *MessageReceive) {
|
|
mr.raw_message = make([]byte, 1024*4)
|
|
mr.parsed_message = make([]byte, 1024*4)
|
|
mr.checksum = 0
|
|
mr.error = 0
|
|
mr.write_index = 0
|
|
mr.raw_write_index = 0
|
|
mr.state = WAITING_FOR_START_BYTE
|
|
}
|
|
|
|
func addByteToRawBuffer(mr *MessageReceive, pbyte byte) {
|
|
mr.raw_message[mr.raw_write_index] = pbyte
|
|
mr.raw_write_index += 1
|
|
}
|
|
func addByteToParsedBuffer(mr *MessageReceive, pbyte byte) {
|
|
mr.parsed_message[mr.write_index] = pbyte
|
|
mr.write_index += 1
|
|
mr.checksum ^= pbyte
|
|
}
|
|
|
|
func parse_uart_ota_payload_payload(payloadBuffer []byte, payload_len int) {
|
|
//fmt.Printf("RAW BUFFER: % 02X", payloadBuffer[:payload_len])
|
|
if payload_len != 4 {
|
|
fmt.Printf("Payload should be 4 is %v", payload_len)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Sequence %v, WriteIndex %v", binary.LittleEndian.Uint16(payloadBuffer[0:1]), binary.LittleEndian.Uint16(payloadBuffer[2:3]))
|
|
}
|
|
|
|
func parse_uart_version_payload(payloadBuffer []byte, payload_len int) {
|
|
type payload_data struct {
|
|
Version uint16
|
|
BuildHash [7]uint8
|
|
}
|
|
|
|
tableHeaders := pterm.TableData{
|
|
{"Version", "Buildhash"},
|
|
}
|
|
|
|
tableData := tableHeaders
|
|
|
|
tableData = append(tableData, []string{
|
|
fmt.Sprintf("%d", binary.LittleEndian.Uint16(payloadBuffer[1:3])),
|
|
fmt.Sprintf("%s", payloadBuffer[3:10]),
|
|
})
|
|
|
|
err := pterm.DefaultTable.WithHasHeader().WithBoxed().WithData(tableData).Render()
|
|
if err != nil {
|
|
fmt.Printf("Fehler beim Rendern der Tabelle: %s\n", err)
|
|
}
|
|
|
|
}
|
|
|
|
func parse_uart_client_info_payload(payloadBuffer []byte, payload_len int) {
|
|
|
|
type payload_data struct {
|
|
ClientID uint8
|
|
IsAvailable uint8
|
|
SlotIsUsed uint8
|
|
MACAddr [6]uint8
|
|
LastPing uint32
|
|
LastSuccessfulPing uint32
|
|
Version uint16
|
|
}
|
|
|
|
tableHeaders := pterm.TableData{
|
|
{"Client ID", "Verfügbar", "Genutzt", "MAC-Adresse", "Last Ping", "Letzter Erfolg Ping", "Version"},
|
|
}
|
|
|
|
tableData := tableHeaders
|
|
|
|
currentOffset := 2
|
|
|
|
const (
|
|
ENTRY_LEN = 19
|
|
OFFSET_MAC_ADDR = 3
|
|
OFFSET_LAST_PING = 9
|
|
OFFSET_LAST_SUCCESS_PING = 13
|
|
OFFSET_VERSION = 17
|
|
)
|
|
|
|
for i := 0; i < int(payloadBuffer[1]); i++ {
|
|
if currentOffset+ENTRY_LEN > payload_len {
|
|
fmt.Printf("Fehler: Payload zu kurz für Client-Eintrag %d\n", i)
|
|
break
|
|
}
|
|
|
|
entryBytes := payloadBuffer[currentOffset : currentOffset+ENTRY_LEN]
|
|
|
|
var clientData payload_data
|
|
|
|
clientData.ClientID = entryBytes[0]
|
|
clientData.IsAvailable = entryBytes[1]
|
|
clientData.SlotIsUsed = entryBytes[2]
|
|
|
|
copy(clientData.MACAddr[:], entryBytes[OFFSET_MAC_ADDR:OFFSET_MAC_ADDR+6])
|
|
|
|
clientData.LastPing = binary.LittleEndian.Uint32(entryBytes[OFFSET_LAST_PING : OFFSET_LAST_PING+4])
|
|
clientData.LastSuccessfulPing = binary.LittleEndian.Uint32(entryBytes[OFFSET_LAST_SUCCESS_PING : OFFSET_LAST_SUCCESS_PING+4])
|
|
clientData.Version = binary.LittleEndian.Uint16(entryBytes[OFFSET_VERSION : OFFSET_VERSION+2])
|
|
|
|
// Füge die geparsten Daten als String-Slice zur Tabelle hinzu
|
|
tableData = append(tableData, []string{
|
|
fmt.Sprintf("%d", clientData.ClientID),
|
|
fmt.Sprintf("%d", clientData.IsAvailable),
|
|
fmt.Sprintf("%d", clientData.SlotIsUsed),
|
|
fmt.Sprintf("%X:%X:%X:%X:%X:%X",
|
|
clientData.MACAddr[0], clientData.MACAddr[1], clientData.MACAddr[2],
|
|
clientData.MACAddr[3], clientData.MACAddr[4], clientData.MACAddr[5]),
|
|
fmt.Sprintf("%d", clientData.LastPing),
|
|
fmt.Sprintf("%d", clientData.LastSuccessfulPing),
|
|
fmt.Sprintf("%d", clientData.Version),
|
|
})
|
|
|
|
currentOffset += ENTRY_LEN
|
|
}
|
|
err := pterm.DefaultTable.WithHasHeader().WithBoxed().WithData(tableData).Render()
|
|
if err != nil {
|
|
fmt.Printf("Fehler beim Rendern der Tabelle: %s\n", err)
|
|
}
|
|
}
|
|
|
|
func parse_uart_client_input(payloadBuffer []byte, payload_len int) {
|
|
|
|
clientCount := payloadBuffer[1]
|
|
fmt.Printf("Client Count %d\n", clientCount)
|
|
clientInputLen := 13
|
|
|
|
for i := 0; i < int(clientCount); i++ {
|
|
offset := 2 + (i * clientInputLen)
|
|
|
|
// --- Client ID (uint8) ---
|
|
clientID := payloadBuffer[offset]
|
|
offset += 1
|
|
|
|
fmt.Printf("Client: %d\n", clientID)
|
|
|
|
// --- Lage X (float32) ---
|
|
xBits := binary.LittleEndian.Uint32(payloadBuffer[offset : offset+4])
|
|
lageX := math.Float32frombits(xBits)
|
|
offset += 4
|
|
|
|
fmt.Printf("\tLAGE_X: %f\n", lageX)
|
|
|
|
// --- Lage Y (float32) ---
|
|
yBits := binary.LittleEndian.Uint32(payloadBuffer[offset : offset+4])
|
|
lageY := math.Float32frombits(yBits)
|
|
offset += 4
|
|
|
|
fmt.Printf("\tLAGE_Y: %f\n", lageY)
|
|
|
|
// --- Bitmask (int32) ---
|
|
maskBits := binary.LittleEndian.Uint32(payloadBuffer[offset : offset+4])
|
|
bitmask := uint32(maskBits)
|
|
offset += 4
|
|
|
|
fmt.Printf("\tBITMASK: %032b\n", bitmask)
|
|
}
|
|
}
|
|
|
|
func message_receive_callback(mr MessageReceive) {
|
|
log.Printf("Message Received: % 02X\n", mr.raw_message[:mr.raw_write_index])
|
|
switch mr.parsed_message[0] {
|
|
case byte(UART_ECHO):
|
|
case UART_VERSION:
|
|
parse_uart_version_payload(mr.parsed_message, mr.write_index)
|
|
case UART_CLIENT_INFO:
|
|
parse_uart_client_info_payload(mr.parsed_message, mr.write_index)
|
|
case UART_OTA_START:
|
|
OTA_UpdateHandler.NewOTAMessage <- mr
|
|
case UART_OTA_PAYLOAD:
|
|
parse_uart_ota_payload_payload(mr.parsed_message, mr.write_index)
|
|
OTA_UpdateHandler.NewOTAMessage <- mr
|
|
case UART_OTA_END:
|
|
OTA_UpdateHandler.NewOTAMessage <- mr
|
|
case UART_OTA_STATUS:
|
|
OTA_UpdateHandler.NewOTAMessage <- mr
|
|
case UART_CLIENT_INPUT:
|
|
parse_uart_client_input(mr.parsed_message, mr.write_index)
|
|
}
|
|
}
|
|
|
|
func message_receive_failed_callback(mr MessageReceive) {
|
|
log.Printf("Error Message Received: % 02X\n", mr.raw_message[:mr.raw_write_index])
|
|
}
|
|
|
|
func parseByte(mr *MessageReceive, pbyte byte) {
|
|
addByteToRawBuffer(mr, pbyte)
|
|
switch mr.state {
|
|
case WAITING_FOR_START_BYTE:
|
|
if pbyte == START_BYTE {
|
|
initMessageReceive(mr)
|
|
mr.state = GET_MESSAGE_ID
|
|
addByteToRawBuffer(mr, pbyte)
|
|
}
|
|
// ignore every other byte
|
|
case GET_MESSAGE_ID:
|
|
if pbyte == ESCAPE_BYTE {
|
|
mr.state = ESCAPED_MESSAGE_ID
|
|
} else {
|
|
addByteToParsedBuffer(mr, pbyte)
|
|
mr.state = IN_PAYLOD
|
|
}
|
|
case ESCAPED_MESSAGE_ID:
|
|
addByteToParsedBuffer(mr, pbyte)
|
|
mr.state = IN_PAYLOD
|
|
case IN_PAYLOD:
|
|
if pbyte == ESCAPE_BYTE {
|
|
mr.state = ESCAPED_PAYLOAD_BYTE
|
|
break
|
|
}
|
|
if pbyte == START_BYTE {
|
|
mr.error = UNEXPECETD_BYTE
|
|
go message_receive_failed_callback(*mr)
|
|
initMessageReceive(mr)
|
|
return
|
|
}
|
|
if pbyte == END_BYTE {
|
|
if mr.checksum != 0 { // checksum wrong
|
|
mr.error = WRONG_CHECKSUM
|
|
go message_receive_failed_callback(*mr)
|
|
initMessageReceive(mr)
|
|
return
|
|
}
|
|
go message_receive_callback(*mr)
|
|
initMessageReceive(mr)
|
|
break
|
|
}
|
|
// normal case
|
|
addByteToParsedBuffer(mr, pbyte)
|
|
case ESCAPED_PAYLOAD_BYTE:
|
|
addByteToParsedBuffer(mr, pbyte)
|
|
mr.state = IN_PAYLOD
|
|
default:
|
|
panic(fmt.Sprintf("unexpected main.ParserState: %#v", mr.state))
|
|
}
|
|
}
|
|
|
|
func buildMessage(payloadBuffer []byte, payload_len int, sendBuffer []byte) int {
|
|
var writeIndex int
|
|
checksum := byte(0x00)
|
|
writeIndex = 0
|
|
sendBuffer[writeIndex] = START_BYTE
|
|
writeIndex++
|
|
for i := range payload_len {
|
|
b := payloadBuffer[i]
|
|
if b == START_BYTE || b == ESCAPE_BYTE || b == END_BYTE {
|
|
sendBuffer[writeIndex] = ESCAPE_BYTE
|
|
writeIndex++
|
|
}
|
|
sendBuffer[writeIndex] = b
|
|
writeIndex++
|
|
checksum ^= b
|
|
}
|
|
if checksum == START_BYTE || checksum == ESCAPE_BYTE || checksum == END_BYTE {
|
|
sendBuffer[writeIndex] = ESCAPE_BYTE
|
|
writeIndex++
|
|
}
|
|
sendBuffer[writeIndex] = checksum
|
|
writeIndex++
|
|
sendBuffer[writeIndex] = END_BYTE
|
|
writeIndex++
|
|
return writeIndex
|
|
}
|
|
|
|
func sendMessage(port serial.Port, sendBuffer []byte) {
|
|
n, err := port.Write(sendBuffer)
|
|
if err != nil {
|
|
log.Printf("Could not Send %v to Serial Port", sendBuffer)
|
|
}
|
|
if n < len(sendBuffer) {
|
|
log.Printf("Did not send all data %v, only send %v", len(sendBuffer), n)
|
|
}
|
|
fmt.Printf("Send Message % 02X\n", sendBuffer[:n])
|
|
}
|
|
|
|
var (
|
|
updatePath string
|
|
OTA_UpdateHandler OTASyncManager
|
|
)
|
|
|
|
func main() {
|
|
flag.StringVar(&updatePath, "update", "", "Path to Updatefile")
|
|
flag.Parse()
|
|
|
|
OTA_UpdateHandler = OTASyncManager{
|
|
OTA_MessageCounter: 0,
|
|
OTA_PayloadMessageSequence: 0,
|
|
NewOTAMessage: make(chan MessageReceive),
|
|
TimeoutMessage: time.Millisecond * 30000,
|
|
}
|
|
|
|
mode := &serial.Mode{
|
|
//BaudRate: 115200,
|
|
BaudRate: 921600,
|
|
}
|
|
port, err := serial.Open("/dev/ttyUSB0", mode)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
ctx, cancle := context.WithCancel(context.Background())
|
|
defer cancle()
|
|
|
|
go func() {
|
|
buff := make([]byte, 1024)
|
|
mr := MessageReceive{}
|
|
initMessageReceive(&mr)
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
n, err := port.Read(buff)
|
|
if err != nil {
|
|
log.Print(err)
|
|
break
|
|
}
|
|
if n == 0 {
|
|
fmt.Println("\nEOF")
|
|
break
|
|
}
|
|
for _, b := range buff[:n] {
|
|
parseByte(&mr, b)
|
|
}
|
|
//fmt.Printf("Empfangen: % 02X\n", string(buff[:n]))
|
|
break
|
|
}
|
|
}
|
|
}()
|
|
|
|
if updatePath != "" {
|
|
// start update
|
|
update, err := os.ReadFile(updatePath)
|
|
if err != nil {
|
|
log.Printf("Could not read Update file %v", err)
|
|
return
|
|
}
|
|
log.Printf("Update Buffer read, update size %v", len(update))
|
|
log.Printf("Gonna break it down in 200 Bytes packages will send %v packages", len(update)/200)
|
|
|
|
// start
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_OTA_START
|
|
n := buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
msg, err := OTA_UpdateHandler.WaitForNextMessageTimeout()
|
|
if err != nil {
|
|
log.Printf("Error Message not acked %v", err)
|
|
} else {
|
|
if msg.parsed_message[2] != 0x00 {
|
|
log.Printf("Update Start failed %v", msg.parsed_message[2])
|
|
return
|
|
} else {
|
|
log.Printf("Update Start confirmed Updating Partition %v", msg.parsed_message[1])
|
|
}
|
|
}
|
|
|
|
update_write_index := 0
|
|
// write update parts
|
|
for update_write_index < len(update) {
|
|
payload_buffer = make([]byte, 1024)
|
|
send_buffer = make([]byte, 1024)
|
|
payload_buffer[0] = UART_OTA_PAYLOAD
|
|
|
|
write_len := min(200, len(update)-update_write_index)
|
|
|
|
//end_payload_len := min(update_write_index+200, len(update))
|
|
|
|
copy(payload_buffer[1:write_len+1], update[update_write_index:update_write_index+write_len])
|
|
n = buildMessage(payload_buffer, write_len+1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
msg, err := OTA_UpdateHandler.WaitForNextMessageTimeout()
|
|
if err != nil {
|
|
log.Printf("Error Message not acked %v", err)
|
|
return
|
|
} else {
|
|
seqCounter := binary.LittleEndian.Uint16(msg.parsed_message[1:3])
|
|
buff_write_index := binary.LittleEndian.Uint16(msg.parsed_message[3:5])
|
|
log.Printf("Sequenzce Counter: %d, Update buffer Write Index: %d", seqCounter, buff_write_index)
|
|
}
|
|
update_write_index += 200
|
|
}
|
|
|
|
log.Printf("Update übertragen beende hier!!!")
|
|
// end
|
|
payload_buffer = make([]byte, 1024)
|
|
send_buffer = make([]byte, 1024)
|
|
payload_buffer[0] = UART_OTA_END
|
|
n = buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
|
|
_, err = OTA_UpdateHandler.WaitForNextMessageTimeout()
|
|
if err != nil {
|
|
log.Printf("Error Message not acked %v", err)
|
|
return
|
|
} else {
|
|
log.Printf("Message Waiting hat funktionioert")
|
|
}
|
|
return
|
|
}
|
|
|
|
for {
|
|
var input string
|
|
var input2 string
|
|
_, err := fmt.Scanln(&input, &input2)
|
|
if err != nil {
|
|
log.Fatalf("Could not read from stdin %v", err)
|
|
return
|
|
}
|
|
fmt.Printf("Input %v", input)
|
|
|
|
switch input {
|
|
case "q":
|
|
return
|
|
case "1":
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_ECHO
|
|
n := buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
case "2":
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_VERSION
|
|
n := buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
case "3":
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_CLIENT_INFO
|
|
n := buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
case "4": // start update
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_OTA_START
|
|
n := buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
case "5": // send payload
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_OTA_PAYLOAD
|
|
for i := range 200 {
|
|
payload_buffer[i+1] = byte(i)
|
|
}
|
|
n := buildMessage(payload_buffer, 201, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
case "6": // end update
|
|
case "7": // Start OTA for ESP-NOW clients
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_OTA_START_ESPNOW
|
|
n := buildMessage(payload_buffer, 1, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
case "8": // Get Fake Client Input
|
|
payload_buffer := make([]byte, 1024)
|
|
send_buffer := make([]byte, 1024)
|
|
payload_buffer[0] = UART_CLIENT_INPUT
|
|
seed, err := strconv.Atoi(input2)
|
|
if err != nil {
|
|
log.Printf("Could not parse %v to a number", input2)
|
|
return
|
|
}
|
|
payload_buffer[1] = byte(seed)
|
|
n := buildMessage(payload_buffer, 2, send_buffer)
|
|
sendMessage(port, send_buffer[:n])
|
|
default:
|
|
fmt.Printf("Not a valid input")
|
|
}
|
|
}
|
|
}
|