Compare commits

..
52 Commits
Author SHA1 Message Date
skruecken 9b2474c261 Improved GoTool 2026-03-21 15:11:14 +01:00
skruecken 4628041b55 Updated SDK 2026-03-21 15:10:59 +01:00
skruecken 864ffbbd7f Added Network Rebuild Test 2026-03-03 17:42:51 +01:00
skruecken 22a72ee02d Adjusted new Com Connect Logic 2026-02-10 15:58:44 +01:00
skruecken f8aa19d331 Added many Eventbus Structs for the Frontend Handling 2026-02-08 23:49:22 +01:00
skruecken 7534459e73 First api draft for ui communication 2026-02-08 19:11:49 +01:00
skruecken f4f3c695af Refactored Frontend with gemini 2026-02-08 19:11:24 +01:00
skruecken 2fd19db88d Typo 2026-02-08 17:39:34 +01:00
skruecken 0b27d68c0c Installed Formatter and extracted javascript window logic from index 2026-02-08 17:14:37 +01:00
skruecken 0bcacb4a53 Improved Go Tool so that the OTA Update Works again 2026-02-08 17:00:41 +01:00
skruecken c656d4afd7 Added Naming Convetions 2026-02-08 17:00:08 +01:00
skruecken d0ff63783f Removed RAW Logs 2026-02-03 15:34:54 +01:00
skruecken 028b6feae8 Added Dummy Tests 2026-01-27 17:36:41 +01:00
skruecken 3c011e272b Added ClientInput
- refactored some magic numbers
2026-01-27 16:42:24 +01:00
skruecken 0767ddac38 Big Gotool Refactoring
- Added Event Bus
- Reworked Package Parsing
- Rewokred Frame Parsing
2026-01-27 16:23:51 +01:00
skruecken 9efef034f0 Changed to new Frontend for UI Components 2026-01-06 16:14:51 +01:00
skruecken 6edf7e6e5f Added Working Prototype for Websocket Communication 2025-12-30 16:19:43 +01:00
skruecken 2070001f4c Added Config for Tap/Acceleration Sensor 2025-12-30 14:11:33 +01:00
skruecken 50586f2b50 Moved to the bma456h lib 2025-12-30 14:11:12 +01:00
skruecken de747ef463 BMA456 HW Interrupt after 2 Steps working 2025-12-16 17:47:58 +01:00
skruecken 6521e290d6 Prototyped the first configuration and read of the BMA456 Sensor 2025-12-13 13:54:44 +01:00
skruecken f2296a33e6 Create a simpler version of the OTA Update
Using no Broadcast logic for speed but its working now.
There is to much Acks going on but for the prototyp that is okay
2025-09-28 20:52:36 +02:00
skruecken 7097e9e7ab Added Test Data Message for Game Development Testing 2025-09-27 15:57:52 +02:00
skruecken df35def702 Added Function to Read App Image size from Partition 2025-09-27 14:26:32 +02:00
skruecken 337976e637 OTA Update nearly working but could not get image correctly transefered 2025-09-14 13:07:08 +02:00
skruecken d8716c232e Updated Message Handling to new MessageBroker 2025-08-23 21:14:18 +02:00
skruecken a3e330ed77 Added Simple Switch Command Between C3 and S3 2025-08-23 21:13:56 +02:00
skruecken 672267b991 WIP First working version of registered callback esp now logic 2025-08-18 22:38:16 +02:00
skruecken 8398442544 Reworked ESPNOW MessageBrokerTask 2025-08-18 20:27:30 +02:00
Skruecken b29512d922 WIP changes to ota update 2025-08-18 19:56:14 +02:00
Skruecken 6e4525df38 Updated Readme 2025-08-15 14:19:31 +02:00
skruecken 60a304a93d Boilerplate for OTA Update over ESPNOW 2025-08-10 19:43:54 +02:00
skruecken f504553ab6 Added Header Definitions 2025-08-10 15:17:22 +02:00
skruecken bbfe61a9ed Fixed const pointer 2025-08-10 15:14:36 +02:00
skruecken 73bc078465 Moved OTA Functionality to functions to reuse it 2025-08-10 15:08:48 +02:00
skruecken 8d4f1da028 Fixed UART Version output and visualized it in go tool 2025-08-10 13:20:11 +02:00
skruecken 1d36a757c0 Added pagebreak in readme for printing 2025-08-10 13:17:08 +02:00
skruecken 648e201f5e Fixed Readme Layout 2025-08-10 13:05:33 +02:00
skruecken 400d308f4a Fixed Readme Layout 2025-08-10 13:03:40 +02:00
skruecken 1c9120a197 Updated Readme with actual UART Protocol 2025-08-10 12:22:45 +02:00
skruecken 3b560799af Working OTA Update over UART to the Master 2025-08-03 22:52:01 +02:00
skruecken 3abdd8816c Tool Adjustments for OTA Update 2025-08-02 16:13:08 +02:00
skruecken cf42e86322 First Prototype of OTA Uart Update Protkol, not working in this state!!! 2025-08-02 16:12:41 +02:00
skruecken 59dbd7b035 Adjustes UART Message Length to 512 2025-08-02 16:12:09 +02:00
skruecken d3e44125a2 Added Defines, fixed broken function call 2025-07-26 10:42:31 +02:00
skruecken ebb739a3a0 Removed old vibe coded Python Test Tool 2025-07-26 10:39:11 +02:00
skruecken 441347fc95 Added UART MSG IDs and Prep work for OTA 2025-07-26 10:38:26 +02:00
skruecken a9779cbade Added Version to Client Infos 2025-07-26 10:37:57 +02:00
skruecken 704d1c9c0b Added Test of NVS and Partion API 2025-07-26 10:36:31 +02:00
skruecken 95bfcaa4d2 Added OTA Update Strategie writedown 2025-07-26 10:35:40 +02:00
skruecken 01d0be7004 Rebuild Python Tool in Go 2025-07-26 10:35:21 +02:00
skruecken a8c7c42471 Added new Payload Structs for Preperation of OTA Update 2025-07-24 16:11:26 +02:00
53 changed files with 8769 additions and 1028 deletions
+35
View File
@@ -13,6 +13,15 @@ get_code_gen:
gen_prot: gen_prot:
./alox.protogen -i prot.json -o main/uart ./alox.protogen -i prot.json -o main/uart
switch_to_s3:
idf.py set-target esp32s3
cp sdkconfig.s3 sdkconfig
idf.py build
switch_to_c3:
idf.py set-target esp32c3
cp sdkconfig.c3 sdkconfig
idf.py build
buildIdf: buildIdf:
idf.py build idf.py build
@@ -20,9 +29,32 @@ buildIdf:
flashMini: flashMini:
idf.py flash -p /dev/ttyACM0 idf.py flash -p /dev/ttyACM0
flashMini2:
idf.py flash -p /dev/ttyACM1
flashMini3:
idf.py flash -p /dev/ttyACM2
flashCluster:
idf.py flash -p /dev/ttyACM1
idf.py flash -p /dev/ttyACM2
idf.py flash -p /dev/ttyACM3
idf.py flash -p /dev/ttyACM4
idf.py flash -p /dev/ttyACM5
idf.py flash -p /dev/ttyACM6
idf.py flash -p /dev/ttyACM7
idf.py flash -p /dev/ttyACM8
monitorMini: monitorMini:
idf.py monitor -p /dev/ttyACM0 idf.py monitor -p /dev/ttyACM0
monitorMini1:
idf.py monitor -p /dev/ttyACM1
monitorMini2:
idf.py monitor -p /dev/ttyACM2
flash0: flash0:
idf.py flash -p /dev/ttyUSB0 idf.py flash -p /dev/ttyUSB0
@@ -40,3 +72,6 @@ monitor1:
monitor2: monitor2:
idf.py monitor -p /dev/ttyUSB2 idf.py monitor -p /dev/ttyUSB2
flash_second_ota:
parttool.py --port /dev/ttyACM0 write_partition --partition-name="ota_1" --input build/espAlox.bin
+55
View File
@@ -0,0 +1,55 @@
package api
const (
TopicFrontendCmd = "front:cmd"
)
const (
CmdUpdateValue = "update_value"
CmdInitState = "init_state"
CmdConnect = "connect"
CmdDisconnect = "disconnect"
CmdSendMessage = "send"
CmdRX = "uart_rx"
CmdTX = "uart_tx"
)
var MessageReceiveRegistry = map[string]func() any{
CmdConnect: func() any { return &WsUartConnect{} },
CmdDisconnect: func() any { return &WsUartDisconnect{} },
CmdSendMessage: func() any { return &WsUartSendMessage{} },
}
type WsMessage struct {
Cmd string `json:"cmd"`
Payload []byte `json:"payload,omitempty"`
}
type SystemState struct {
Adapters []string `json:"adapters"`
SelectedAdapter string `json:"selected_adapter"`
Baudrates string `json:"baudrates"`
SelectedBaudrate string `json:"selected_baudrate"`
UartConnected bool `json:"uart_connected"`
}
type WsUartConnect struct {
SelectedAdapter string `json:"selected_adapter"`
Baudrate int `json:"baudrate"`
}
type WsUartDisconnect struct {
}
type WsUartSendMessage struct {
MsgId byte `json:"msg_id"`
Data []byte `json:"data"`
}
type WsUartRX struct {
Data []byte `json:"data"`
}
type WsUartTX struct {
Data []byte `json:"data"`
}
+110
View File
@@ -0,0 +1,110 @@
package api
// Topics
const (
TopicUARTRx = "uart:rx"
TopicUARTTx = "uart:tx"
TopicUARTError = "uart:error"
TopicUartAction = "uart:action"
TopicOTA = "ota"
)
type Frame struct {
Time uint64
ID byte
Data []byte
}
const (
CmdEcho byte = 0x01
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 (
ClientCountOffset = 1
// Payload Sizes
PayloadVersionSize = 10
PayloadClientInfoSize = 19
PayloadClientInputSize = 13
)
type PayloadVersion struct {
Version uint16
Buildhash [7]uint8
}
type PayloadClientInfo struct {
ClientID uint8
IsAvailable uint8
SlotIsUsed uint8
MACAddr [6]uint8
LastPing uint32
LastSuccessfulPing uint32
Version uint16
}
type PayloadClientInput struct {
ClientID byte
X float32
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
}
type ActionUartConnect struct {
Adapter string
Baudrate int
}
type ActionUartConnected struct {
Adapter string
Baudrate int
Error error
}
type ActionUartDisconnect struct {
}
type ActionUartDisconnected struct {
}
type ActionUartSendMessage struct {
MsgId byte
Data []byte
}
+8
View File
@@ -0,0 +1,8 @@
package main
type Config struct {
Port int
Host string
UartPort string
Baudrate int
}
+64
View File
@@ -0,0 +1,64 @@
package eventbus
import (
"log"
"sync"
)
type EventBus interface {
Subscribe(topic string) chan any
Publish(topic string, data any)
Unsubscribe(topic string, ch chan any)
}
type EBus struct {
mu sync.RWMutex
topics map[string][]chan any
}
func New() *EBus {
return &EBus{
mu: sync.RWMutex{},
topics: map[string][]chan any{},
}
}
func (eb *EBus) Subscribe(topic string) chan any {
eb.mu.Lock()
defer eb.mu.Unlock()
ch := make(chan any, 20)
eb.topics[topic] = append(eb.topics[topic], ch)
return ch
}
func (eb *EBus) Publish(topic string, data any) {
eb.mu.RLock()
defer eb.mu.RUnlock()
for _, ch := range eb.topics[topic] {
select {
case ch <- data:
default:
log.Printf("[Event Bus]: Could not pass Message %v to %v channel full", data, topic)
}
}
}
func (eb *EBus) Unsubscribe(topic string, c chan any) {
eb.mu.Lock()
defer eb.mu.Unlock()
channels, ok := eb.topics[topic]
if !ok {
return
}
for i, ch := range channels {
if ch != c {
eb.topics[topic] = append(channels[:i], channels[i+1:]...) // example: 5 channels max i=3 channels[:3] (0,1,2) + channels[3+1:] (4,5)
close(ch)
return
}
}
}
+175
View File
@@ -0,0 +1,175 @@
package frontend
import (
"context"
"embed"
"encoding/json"
"io/fs"
"log"
"net/http"
"time"
"alox.tool/api"
"alox.tool/eventbus"
"github.com/gorilla/websocket"
)
//go:embed www
var staticFiles embed.FS
var upgrader = websocket.Upgrader{}
type FServer struct {
bus eventbus.EventBus
mux *http.ServeMux
}
func New(bus eventbus.EventBus) *FServer {
fsrv := &FServer{
bus: bus,
mux: http.NewServeMux(),
}
fsrv.routes()
return fsrv
}
func (fsrv *FServer) routes() {
// Static files from the Embed-FS
// remove "www" prefix, so index.html is reachable over /
root, _ := fs.Sub(staticFiles, "www")
fsrv.mux.Handle("/", http.FileServer(http.FS(root)))
fsrv.mux.HandleFunc("/ws", fsrv.handleWS)
}
func (fs *FServer) handleWS(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Upgrade error: %v", err)
return
}
defer conn.Close()
// Context nutzen, um Goroutinen zu stoppen, wenn die Verbindung abreißt
ctx, cancel := context.WithCancel(r.Context())
defer cancel()
// WRITER: Send Events to Browser
go fs.HandleAppEvents(ctx, conn)
// READER: Commands from Browser
// This Function is Blocking
fs.GetFrontendEvents(ctx, conn)
}
func (fs *FServer) Start(addr string) error {
server := &http.Server{
Addr: addr,
Handler: fs.mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
ctx, cancle := context.WithCancel(context.Background())
defer cancle()
go fs.HandleFrontendEvents(ctx)
log.Printf("Frontend Server gestartet auf %s", addr)
return server.ListenAndServe()
}
func (fs *FServer) HandleAppEvents(ctx context.Context, conn *websocket.Conn) error {
// Kanäle für die Hardware-Events abonnieren
rxChan := fs.bus.Subscribe(api.TopicUARTRx)
txChan := fs.bus.Subscribe(api.TopicUARTTx)
UartActions := fs.bus.Subscribe(api.TopicUartAction)
for {
select {
case <-ctx.Done():
return nil
case f := <-rxChan:
if err := conn.WriteJSON(map[string]any{"type": "rx", "frame": f}); err != nil {
return nil
}
case f := <-txChan:
if err := conn.WriteJSON(map[string]any{"type": "tx", "frame": f}); err != nil {
return nil
}
case msgT := <-UartActions:
switch msg := msgT.(type) {
case api.ActionUartConnected:
// TODO: nicht hier die daten nachhaltig speichern damit sie ans frontend gesendet werden können
// TODO: das muss irgendwo central passieren nicht für jeden client
if msg.Error != nil {
}
continue
case api.ActionUartDisconnected:
continue
}
}
}
}
func (fs *FServer) GetFrontendEvents(ctx context.Context, conn *websocket.Conn) error {
for {
select {
case <-ctx.Done():
return nil
default:
var cmd api.WsMessage
if err := conn.ReadJSON(&cmd); err != nil {
log.Printf("WS Read Error: %v", err)
return err
}
val, ok := api.MessageReceiveRegistry[cmd.Cmd]
if !ok {
log.Printf("No Message Type mapped to %v", cmd.Cmd)
continue
}
valM := val()
err := json.Unmarshal(cmd.Payload, valM)
if err != nil {
log.Printf("Could not Unmarshal payload %v", cmd.Payload)
}
fs.bus.Publish(api.TopicFrontendCmd, valM)
log.Printf("Browser Action: %s auf with %v", cmd.Cmd, cmd.Payload)
}
}
}
func (fs *FServer) HandleFrontendEvents(ctx context.Context) error {
fChan := fs.bus.Subscribe(api.TopicFrontendCmd)
for {
select {
case <-ctx.Done():
return nil
case msg := <-fChan:
switch msgT := msg.(type) {
case api.WsUartSendMessage:
log.Printf("Sending Uart Data % X", msgT.Data)
fs.bus.Publish(api.TopicUartAction, api.ActionUartSendMessage{
MsgId: msgT.MsgId,
Data: msgT.Data,
})
continue
case api.WsUartConnect:
log.Printf("Connect with %s : %d", msgT.SelectedAdapter, msgT.Baudrate)
fs.bus.Publish(api.TopicUartAction, api.ActionUartConnect{
Adapter: msgT.SelectedAdapter,
Baudrate: msgT.Baudrate,
})
continue
case api.WsUartDisconnect:
log.Printf("Disconnect from Uart Adapter")
fs.bus.Publish(api.TopicUartAction, api.ActionUartDisconnect{})
continue
}
}
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+32
View File
@@ -0,0 +1,32 @@
<div x-data="windowBox('window_id' ,100, 100)"
@mousemove.window="onDrag"
@mouseup.window="stopDrag"
@mousedown="focus"
class="card shadow-lg position-absolute"
:class="{ 'w-100 h-100 m-0 shadow-none': fullscreen }"
:style="`left: ${pos.x}px; top: ${pos.y}px; z-index: ${zIndex}; width: ${fullscreen ? '100vw' : '400px'};`"
x-cloak>
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center drag-handle"
@mousedown="startDrag" @dblclick="toggleFullscreen">
<h6 class="mb-0">CAN Interface</h6>
<div class="d-flex align-items-center gap-1">
<span class="badge me-1" :class="socket.readyState === 1 ? 'bg-success' : 'bg-danger'">WS</span>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleFullscreen">
<span>▢</span>
</button>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="minimized = !minimized">
<span x-text="minimized ? '+' : ''"></span>
</button>
</div>
</div>
<div x-show="!minimized" class="flex-grow-1 overflow-auto">
<div class="card-body">
<p>HIER DER INHALT DER COMPONENT</p>
</div>
</div>
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

+241
View File
@@ -0,0 +1,241 @@
<!doctype html>
<html>
<head>
<link href="bootstrap.min.css" rel="stylesheet" />
<script src="bootstrap.bundle.min.js"></script>
<script defer src="windows.js"></script>
<script defer src="alpinejs.min.js"></script>
<style>
[x-cloak] {
display: none !important;
}
body {
background-color: #f8f9fa;
background-image:
linear-gradient(90deg, rgba(0, 0, 0, 0.03) 1px, transparent 1px),
linear-gradient(rgba(0, 0, 0, 0.03) 1px, transparent 1px);
background-size: 20px 20px;
height: 100vh;
margin: 0;
padding-top: 56px; /* Space for navbar */
overflow: hidden;
}
.drag-handle {
cursor: move;
}
.card[style*="cursor: move"] {
transition: none;
}
.navbar {
z-index: 2000; /* Above windows */
}
.ws-indicator {
width: 12px;
height: 12px;
border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
}
.glow-success {
box-shadow: 0 0 10px #198754;
}
.glow-danger {
box-shadow: 0 0 10px #dc3545;
}
</style>
<script>
document.addEventListener("alpine:init", () => {
Alpine.store("sys", {
ws_connected: false,
adapters: ["/dev/ttyUSB0"],
selected_adapter: "",
baudrates: ["115200", "916000"],
selected_baudrate: "",
uart_connected: false,
});
});
</script>
<script>
let socket;
function connectWS() {
socket = new WebSocket("ws://" + window.location.host + "/ws");
socket.onopen = () => {
console.log("[open] Connection established");
Alpine.store("sys").ws_connected = true;
};
socket.onmessage = (event) => {
try {
let mes = JSON.parse(event.data);
if (mes && mes.cmd === "value") {
Alpine.store(mes.name, mes.value);
}
} catch (e) {
console.log("Invalid JSON:", event.data);
}
};
socket.onclose = () => {
console.log("[close] Connection died");
Alpine.store("sys").ws_connected = false;
setTimeout(connectWS, 2000);
};
socket.onerror = (error) => {
console.log("[error]");
socket.close();
};
}
connectWS();
</script>
</head>
<body x-data>
<!-- Top Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top shadow">
<div class="container-fluid">
<a class="navbar-brand fw-bold" href="#">
<span class="text-primary">Alox</span> Debug Tool
</a>
<div class="d-flex align-items-center gap-2">
<span class="text-light small opacity-75">Websocket:</span>
<div
class="rounded-circle ws-indicator"
:class="$store.sys.ws_connected ? 'bg-success glow-success' : 'bg-danger glow-danger'"
></div>
</div>
</div>
</nav>
<!-- UART Configuration Window -->
<yet-window
id="uart_config"
title="UART Configuration"
x="50"
y="80"
width="400px"
>
<label class="form-label small fw-bold text-uppercase text-muted"
>Interface</label
>
<div
class="input-group mb-3"
x-data="{ open: false }"
@click.outside="open = false"
>
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
@click="open = !open"
:disabled="$store.sys.uart_connected"
>
Adapter
</button>
<ul class="dropdown-menu" :class="{ 'show': open }" x-show="open">
<template x-for="adapter in $store.sys.adapters">
<li>
<button
class="dropdown-item"
type="button"
x-text="adapter"
@click="$store.sys.selected_adapter = adapter; open = false"
></button>
</li>
</template>
</ul>
<input
type="text"
class="form-control bg-light"
readonly
:value="$store.sys.selected_adapter || 'Select Interface...'"
/>
</div>
<label class="form-label small fw-bold text-uppercase text-muted"
>Baudrate</label
>
<div
class="input-group mb-4"
x-data="{ open: false }"
@click.outside="open = false"
>
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
@click="open = !open"
:disabled="$store.sys.uart_connected"
>
Speed
</button>
<ul class="dropdown-menu" :class="{ 'show': open }" x-show="open">
<template x-for="rate in $store.sys.baudrates">
<li>
<button
class="dropdown-item"
type="button"
x-text="rate"
@click="$store.sys.selected_baudrate = rate; open = false"
></button>
</li>
</template>
</ul>
<input
type="text"
class="form-control bg-light"
readonly
:value="$store.sys.selected_baudrate || 'Select Baudrate...'"
/>
</div>
<div class="d-grid">
<button
x-show="!$store.sys.uart_connected"
class="btn btn-primary btn-lg"
type="button"
:disabled="!$store.sys.selected_adapter || !$store.sys.selected_baudrate"
@click="socket.send(JSON.stringify({cmd: 'connect', adapter: $store.sys.selected_adapter, baudrate: parseInt($store.sys.selected_baudrate)}))"
>
Connect to UART
</button>
<button
x-show="$store.sys.uart_connected"
x-cloak
class="btn btn-danger btn-lg"
type="button"
@click="socket.send(JSON.stringify({cmd: 'disconnect'}))"
>
Disconnect
</button>
</div>
</yet-window>
<!-- UART Log Window -->
<yet-window
id="uart_log"
title="UART Log"
x="500"
y="80"
width="550px"
header-class="bg-primary text-white"
>
<div
class="bg-dark text-success font-monospace small p-2 rounded shadow-inner"
style="height: 400px; overflow-y: auto"
>
<div class="text-muted small mt-2">
// UART log data will appear here...
</div>
</div>
</yet-window>
</body>
</html>
+153
View File
@@ -0,0 +1,153 @@
// windows.js
document.addEventListener("alpine:init", () => {
// 1. Globaler Store for Window Managment
Alpine.store("Yet_WM", {
topZ: 1000,
getNewZ() {
return ++this.topZ;
},
});
Alpine.data("YetWindow", (id, initialX = 50, initialY = 50) => ({
id: id,
pos: { x: parseInt(initialX), y: parseInt(initialY) },
lastPos: { x: 0, y: 0 },
dragging: false,
minimized: false,
fullscreen: false,
zIndex: 1000,
offset: { x: 0, y: 0 },
init() {
// Lade gespeicherten Zustand (einheitlicher Key: yet_win_)
const saved = JSON.parse(localStorage.getItem(`yet_win_${this.id}`));
if (saved) {
this.pos = { x: saved.x, y: saved.y };
this.minimized = saved.min;
}
this.focus();
this.keepInBounds();
},
focus() {
this.zIndex = Alpine.store("Yet_WM").getNewZ();
},
startDrag(e) {
if (e.target.closest("button") || this.fullscreen) return;
// Verhindert Text-Markierung während des Verschiebens
e.preventDefault();
this.focus();
this.dragging = true;
this.offset.x = e.clientX - this.pos.x;
this.offset.y = e.clientY - this.pos.y;
},
onDrag(e) {
if (!this.dragging) return;
let newX = e.clientX - this.offset.x;
let newY = e.clientY - this.offset.y;
const margin = 20;
this.pos.x = Math.max(margin - 350, Math.min(newX, window.innerWidth - 50));
this.pos.y = Math.max(0, Math.min(newY, window.innerHeight - 40));
},
stopDrag() {
if (this.dragging) {
this.dragging = false;
this.save();
}
},
toggleMinimize() {
this.minimized = !this.minimized;
this.save();
},
toggleFullscreen() {
if (!this.fullscreen) {
this.lastPos = { ...this.pos };
this.pos = { x: 0, y: 0 };
this.fullscreen = true;
} else {
this.pos = { ...this.lastPos };
this.fullscreen = false;
}
this.focus();
},
save() {
localStorage.setItem(
`yet_win_${this.id}`,
JSON.stringify({
x: this.pos.x,
y: this.pos.y,
min: this.minimized,
})
);
},
keepInBounds() {
if (this.pos.x > window.innerWidth) this.pos.x = 50;
if (this.pos.y > window.innerHeight) this.pos.y = 50;
},
}));
});
// Definition der Web Component
class YetWindowElement extends HTMLElement {
connectedCallback() {
const id = this.getAttribute("id") || "win_" + Math.random().toString(36).substr(2, 9);
const title = this.getAttribute("title") || "Window";
const x = this.getAttribute("x") || "50";
const y = this.getAttribute("y") || "50";
const width = this.getAttribute("width") || "450px";
const headerClass = this.getAttribute("header-class") || "bg-dark text-white";
const content = this.innerHTML;
this.innerHTML = `
<div
x-data="YetWindow('${id}', ${x}, ${y})"
@mousemove.window="onDrag"
@mouseup.window="stopDrag"
@mousedown="focus"
class="card shadow-lg position-absolute"
:class="{ 'w-100 h-100 m-0 shadow-none': fullscreen }"
:style="\`left: \${pos.x}px; top: \${pos.y}px; z-index: \${zIndex}; width: \${fullscreen ? '100vw' : '${width}'}; user-select: \${dragging ? 'none' : 'auto'};\`"
x-cloak
>
<div
class="card-header d-flex justify-content-between align-items-center drag-handle ${headerClass}"
@mousedown="startDrag"
@dblclick="toggleFullscreen"
style="user-select: none;"
>
<h6 class="mb-0">${title}</h6>
<div class="d-flex align-items-center gap-1">
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleFullscreen">
<span></span>
</button>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleMinimize">
<span x-text="minimized ? '+' : ''"></span>
</button>
</div>
</div>
<div x-show="!minimized" class="flex-grow-1 overflow-auto">
<div class="card-body">
${content}
</div>
</div>
</div>
`;
}
}
customElements.define("yet-window", YetWindowElement);
+25
View File
@@ -0,0 +1,25 @@
module alox.tool
go 1.24.5
require (
github.com/gorilla/websocket v1.5.3
github.com/pterm/pterm v0.12.81
go.bug.st/serial v1.6.4
)
require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
github.com/containerd/console v1.0.5 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
)
+126
View File
@@ -0,0 +1,126 @@
atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg=
atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ=
atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw=
atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU=
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k=
github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI=
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU=
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA=
github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A=
go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+128
View File
@@ -0,0 +1,128 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"time"
"alox.tool/api"
"alox.tool/eventbus"
"alox.tool/frontend"
"alox.tool/state"
"alox.tool/testrunner"
"alox.tool/uart"
)
var (
Tests bool
Baudrate uint
)
func main() {
flag.BoolVar(&Tests, "t", false, "Tests")
flag.UintVar(&Baudrate, "b", 921600, "Baudrate") // 115200
flag.Parse()
log.Printf("Starting with Params %v", Baudrate)
config := Config{
Port: 8000,
Host: "0.0.0.0",
UartPort: "/dev/ttyUSB0",
Baudrate: int(Baudrate),
}
if Tests {
StartTests(config)
return
}
StartApp(config)
}
func StartTests(config Config) {
bus := eventbus.New()
com, err := uart.NewCom(bus)
if err != nil {
log.Printf("Could not Create COM %v", err)
}
err = com.Connect(config.UartPort, config.Baudrate)
if err != nil {
log.Printf("Could not Connect with Uart Device %v", err)
}
defer com.Close()
tr := testrunner.New(bus, com)
testTest := make(map[string]func() error)
testTest["Echo Test"] = tr.RunEchoTest
testTest["Version Test"] = tr.RunVersionTest
testTest["Info Test"] = tr.RunClientInfoTest
testTest["Input Test"] = tr.RunClientInputTest
testTest["Rebuild Network"] = tr.RebuildNetwork
tr.RunTestSet(testTest)
}
func StartApp(config Config) {
bus := eventbus.New()
com, err := uart.NewCom(bus)
ctx, cancle := context.WithCancel(context.Background())
defer cancle()
go com.EventbusHandler(ctx)
if err != nil {
log.Printf("Could not Create Com with Uart Device %v", err)
return
}
err = com.Connect(config.UartPort, config.Baudrate)
if err != nil {
log.Printf("Could not Connect with Uart Device %v", err)
}
defer com.Close()
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)
espHandle := state.New()
espHandle.Start(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 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
}
+136
View File
@@ -0,0 +1,136 @@
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) {
RXC := om.Bus.Subscribe(api.TopicUARTRx)
defer om.Bus.Unsubscribe(api.TopicUARTRx, RXC)
go func() {
for {
select {
case <-ctx.Done():
return
case msg := <-RXC:
val, ok := msg.(api.Frame)
if !ok {
log.Printf("val is not type api.Frame its %T", val)
continue
}
log.Printf("[%d] Frame: %X, % X", val.Time, val.ID, val.Data)
om.processFrame(val)
}
}
}()
}
func (om *OTAManager) processFrame(val api.Frame) {
switch val.ID {
case api.CmdOtaStart:
msgT, err := uart.ParseFrameOtaStart(val)
if err != nil {
log.Printf("Could not Parse Client Input %v", err)
return
}
// 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.CmdOtaPayload:
msgT, err := uart.ParseFrameOtaPayload(val)
if err != nil {
log.Printf("Could not Parse Client Input %v", err)
return
}
// 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.CmdOtaStatus:
v, err := uart.ParseFrameOtaStatus(val)
if err != nil {
log.Printf("Could not Parse Client Input %v", err)
return
}
log.Printf("%v", v)
// Update State Machine
case api.CmdOtaEnd:
msgT, err := uart.ParseFrameOtaEnd(val)
if err != nil {
log.Printf("Could not Parse Client Input %v", err)
return
}
// 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.CmdOtaStartEspNow:
v, err := uart.ParseFrameOtaStartEspNow(val)
if err != nil {
log.Printf("Could not Parse Client Input %v", err)
return
}
//bus.Publish(api.TopicOTA, v)
log.Printf("%v", v)
}
}
+186
View File
@@ -0,0 +1,186 @@
package state
import (
"context"
"log"
"sync"
"alox.tool/api"
"alox.tool/eventbus"
"alox.tool/uart"
)
type ESPStateHandler struct {
RWMutex sync.RWMutex
Master MasterESP
}
type ESPVersion struct {
Version uint16
Buildhash [7]byte
}
type BaseESP struct {
ClientId byte
IsUpdating bool
UpdateProgress float32
Version ESPVersion
RunningPartition int
MAC [6]byte
}
type MasterESP struct {
BaseESP
Slaves map[byte]*SlaveESP
}
type SlaveESP struct {
BaseESP
SlotAvailable bool
SlotUsed bool
Bitmask uint32
LageX float32
LageY float32
LastPing uint32
LastSuccessfullPing uint32
}
func New() *ESPStateHandler {
return &ESPStateHandler{
RWMutex: sync.RWMutex{},
Master: NewMasterESP(),
}
}
func NewMasterESP() MasterESP {
return MasterESP{
BaseESP: BaseESP{
ClientId: 0,
IsUpdating: false,
UpdateProgress: 0,
Version: ESPVersion{
Version: 0,
Buildhash: [7]byte{},
},
RunningPartition: 0,
MAC: [6]byte{},
},
Slaves: map[byte]*SlaveESP{},
}
}
func NewSlaveESP() *SlaveESP {
return &SlaveESP{
BaseESP: BaseESP{
ClientId: 0,
IsUpdating: false,
UpdateProgress: 0,
Version: ESPVersion{
Version: 0,
Buildhash: [7]byte{},
},
RunningPartition: 0,
MAC: [6]byte{},
},
SlotAvailable: false,
SlotUsed: false,
Bitmask: 0,
LageX: 0,
LageY: 0,
LastPing: 0,
LastSuccessfullPing: 0,
}
}
func (s *ESPStateHandler) Start(ctx context.Context, bus eventbus.EventBus) {
go func() {
RXC := bus.Subscribe(api.TopicUARTRx)
defer bus.Unsubscribe(api.TopicUARTRx, RXC)
for {
select {
case <-ctx.Done():
return
case msg := <-RXC:
val, ok := msg.(api.Frame)
if !ok {
log.Printf("val is not type api.Frame its %T", val)
continue
}
log.Printf("[%d] Frame: %X, % X", val.Time, val.ID, val.Data)
s.processFrame(val)
}
}
}()
}
func (s *ESPStateHandler) processFrame(val api.Frame) {
s.RWMutex.Lock()
defer s.RWMutex.Unlock()
switch val.ID {
case api.CmdEcho:
log.Printf("Echo %v", val)
case api.CmdVersion:
v, err := uart.ParseFrameVersion(val)
if err != nil {
log.Printf("Could not Parse Version %v", err)
return
}
log.Printf("Version Info %d %s", v.Version, v.Buildhash)
// Update State
s.Master.Version.Version = v.Version
s.Master.Version.Buildhash = v.Buildhash
case api.CmdClientInfo:
v, err := uart.ParseFrameClientInfo(val)
if err != nil {
log.Printf("Could not Parse Client Info %v", err)
return
}
for _, c := range v {
log.Printf("Client ID %d", c.ClientID)
log.Printf("\tIsAvailable %d", c.IsAvailable)
log.Printf("\tLastPing %d", c.LastPing)
log.Printf("\tLastSuccessfulPing %d", c.LastSuccessfulPing)
log.Printf("\tSlotIsUsed %d", c.SlotIsUsed)
log.Printf("\tVersion %d", c.Version)
log.Printf("\tMACAddr % X", c.MACAddr)
_, ok := s.Master.Slaves[c.ClientID]
if !ok {
s.Master.Slaves[c.ClientID] = NewSlaveESP()
}
s.Master.Slaves[c.ClientID].SlotAvailable = c.IsAvailable != 0
s.Master.Slaves[c.ClientID].SlotUsed = c.SlotIsUsed != 0
s.Master.Slaves[c.ClientID].LastPing = c.LastPing
s.Master.Slaves[c.ClientID].LastSuccessfullPing = c.LastSuccessfulPing
s.Master.Slaves[c.ClientID].Version.Version = c.Version
s.Master.Slaves[c.ClientID].MAC = c.MACAddr
}
case api.CmdClientInput:
v, err := uart.ParseFrameClientInput(val)
if err != nil {
log.Printf("Could not Parse Client Input %v", err)
return
}
for _, c := range v {
log.Printf("Client ID %d", c.ClientID)
log.Printf("\tX %f", c.X)
log.Printf("\tY %f", c.Y)
log.Printf("\tBitmask %08b", c.InputMask)
_, ok := s.Master.Slaves[c.ClientID]
if !ok {
s.Master.Slaves[c.ClientID] = NewSlaveESP()
}
s.Master.Slaves[c.ClientID].LageX = c.X
s.Master.Slaves[c.ClientID].LageY = c.Y
s.Master.Slaves[c.ClientID].Bitmask = c.InputMask
}
}
}
+18
View File
@@ -0,0 +1,18 @@
package testrunner
import (
"os/exec"
)
const (
ESPTOOLPATH = "/home/simon/.espressif/python_env/idf5.5_py3.14_env/bin/esptool.py"
)
func RestartESPOnPort(port string) error {
cmd := exec.Command(ESPTOOLPATH, "--port", port, "run")
err := cmd.Run()
if err != nil {
return err
}
return nil
}
+82
View File
@@ -0,0 +1,82 @@
package testrunner
import (
"context"
"fmt"
"io"
"log"
"os"
"time"
"alox.tool/api"
"alox.tool/eventbus"
"alox.tool/uart"
)
type TestRunner struct {
bus eventbus.EventBus
com *uart.Com
}
func New(bus eventbus.EventBus, com *uart.Com) *TestRunner {
return &TestRunner{
bus: bus,
com: com,
}
}
func (tr *TestRunner) RunTestSet(TSet map[string]func() error) {
null, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
defer null.Close()
// Backups der originalen Deskriptoren
oldStdout := os.Stdout
oldStderr := os.Stderr
oldLogOut := log.Writer()
for name, f := range TSet {
// 1. Output komplett abdrehen
os.Stdout = null
os.Stderr = null
log.SetOutput(io.Discard)
err := f()
// 2. Sofort wiederherstellen für das Log-Resultat
os.Stdout = oldStdout
os.Stderr = oldStderr
log.SetOutput(oldLogOut)
if err != nil {
log.Printf("[%s]: \t\tFailed: %v", name, err)
continue
}
log.Printf("[%s]: \t\tSucceeded", name)
}
}
func (tr *TestRunner) Expect(idToSend byte, payload []byte, expectedID byte, timeout time.Duration) (*api.Frame, error) {
rxChan := tr.bus.Subscribe(api.TopicUARTRx)
defer tr.bus.Unsubscribe(api.TopicUARTRx, rxChan)
if err := tr.com.Send(idToSend, payload); err != nil {
return nil, fmt.Errorf("send error: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
for {
select {
case <-ctx.Done():
return nil, fmt.Errorf("timeout waiting for ID 0x%02X", expectedID)
case frame := <-rxChan:
f := frame.(api.Frame)
if f.ID == expectedID {
return &f, nil
}
// Ignore other IDs and Messages on the Bus
}
}
}
+139
View File
@@ -0,0 +1,139 @@
package testrunner
import (
"fmt"
"slices"
"time"
"alox.tool/api"
"alox.tool/uart"
)
func (tr *TestRunner) RunEchoTest() error {
frame, err := tr.Expect(api.CmdEcho, []byte{0x01}, api.CmdEcho, 1*time.Second)
if err != nil {
return err
}
if frame.Data[0] != 0x01 {
return fmt.Errorf("Got % X expected % X", frame.Data[0], 0x01)
}
return nil
}
func (tr *TestRunner) RunVersionTest() error {
frame, err := tr.Expect(api.CmdVersion, nil, api.CmdVersion, 1*time.Second)
if err != nil {
return err
}
// TODO: Check Version and Buildhash?
_, err = uart.ParseFrameVersion(*frame)
if err != nil {
return err
}
v, err := uart.ParseFrameVersion(*frame)
if v.Version != 1 {
return fmt.Errorf("Got %d expected %d", v.Version, 1)
}
return nil
}
func (tr *TestRunner) RunClientInfoTest() error {
_, err := tr.Expect(api.CmdClientInfo, nil, api.CmdClientInfo, 1*time.Second)
if err != nil {
return err
}
// TODO: Real Check
return nil
}
func (tr *TestRunner) RunClientInputTest() error {
_, err := tr.Expect(api.CmdClientInput, nil, api.CmdClientInput, 1*time.Second)
if err != nil {
return err
}
// TODO: Real Check
return nil
}
func (tr *TestRunner) RebuildNetwork() error {
// Hardcoded for now
Clients := map[string]string{
"C1": "/dev/ttyACM1",
"C2": "/dev/ttyACM2",
"C3": "/dev/ttyACM3",
"C4": "/dev/ttyACM4",
}
// Hardcoded for now
macAddresses := [][6]byte{
{0x50, 0x78, 0x7D, 0x18, 0x85, 0xA0},
{0x50, 0x78, 0x7D, 0x18, 0x7D, 0x74},
{0x50, 0x78, 0x7D, 0x18, 0x1D, 0x14},
{0x50, 0x78, 0x7D, 0x18, 0x00, 0x10},
}
// Hardcoded for now
err := RestartESPOnPort("/dev/ttyACM0")
if err != nil {
return fmt.Errorf("Could not Restart Master!")
}
time.Sleep(500 * time.Millisecond)
for c, p := range Clients {
fmt.Printf("Restarting Client %v", c)
err = RestartESPOnPort(p)
if err != nil {
fmt.Printf("Could not Restart Client %v on Port %v", c, p)
return err
}
time.Sleep(500 * time.Millisecond)
}
success := false
var frame *api.Frame
var clientInfos []api.PayloadClientInfo
var lastError string
for i := 0; i <= 5; i++ {
time.Sleep(1000 * time.Millisecond) // inital sleep for first network setup
var err error // for local error scope
frame, err = tr.Expect(api.CmdClientInfo, nil, api.CmdClientInfo, 1*time.Second)
if err != nil {
lastError = "No Uart Message"
continue
}
clientInfos, err = uart.ParseFrameClientInfo(*frame)
if err != nil {
lastError = fmt.Sprintf("Could not parse Message: %v", err)
continue
}
if len(clientInfos) != len(macAddresses) {
lastError = fmt.Sprintf("Want %v Clients, got %v Clients", len(macAddresses), len(clientInfos))
continue
}
success = true
break
}
if !success {
return fmt.Errorf("%v", lastError)
}
for _, client := range clientInfos {
if !slices.Contains(macAddresses, client.MACAddr) {
return fmt.Errorf("Client %v not found in expected list %v", client.MACAddr, macAddresses)
}
}
return nil
}
+138
View File
@@ -0,0 +1,138 @@
package uart
import (
"context"
"fmt"
"log"
"time"
"alox.tool/api"
"alox.tool/eventbus"
"go.bug.st/serial"
)
type Com struct {
bus eventbus.EventBus
port serial.Port
cancel context.CancelFunc
}
func NewCom(bus eventbus.EventBus) (*Com, error) {
return &Com{
bus: bus,
port: nil,
cancel: nil,
}, nil
}
func (c *Com) Connect(portName string, baudrate int) error {
if c.port != nil {
return fmt.Errorf("Port already connected")
}
mode := &serial.Mode{BaudRate: baudrate}
port, err := serial.Open(portName, mode)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(context.Background())
drv := New(c.bus)
go func() {
buff := make([]byte, 1024)
for {
select {
case <-ctx.Done():
return
default:
n, err := port.Read(buff)
if err != nil {
log.Print("[Warning]: Read Error:", err)
return // Loop beenden bei Hardware-Fehler
}
if n > 0 {
for _, b := range buff[:n] {
//log.Printf("[RAW][RX] % X", b)
drv.ParseByte(b)
}
}
}
}
}()
c.port = port
c.cancel = cancel
return nil
}
func (c *Com) Close() {
c.cancel()
c.port.Close()
}
func packFrame(id byte, payload []byte) []byte {
out := make([]byte, 0, len(payload)+5) // Guessing extra Puffer size
checksum := id
out = append(out, StartByte)
// Helper für Escaping
writeEscaped := func(b byte) {
if b == StartByte || b == EscapeByte || b == EndByte {
out = append(out, EscapeByte)
}
out = append(out, b)
}
writeEscaped(id)
for _, b := range payload {
writeEscaped(b)
checksum ^= b
}
writeEscaped(checksum)
out = append(out, EndByte)
return out
}
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)
c.bus.Publish(api.TopicUARTTx, api.Frame{
Time: uint64(time.Now().UnixNano()),
ID: id,
Data: payload,
})
return err
}
func (c *Com) EventbusHandler(ctx context.Context) error {
UActions := c.bus.Subscribe(api.TopicUartAction)
for {
select {
case <-ctx.Done():
return nil
case msgT := <-UActions:
switch msg := msgT.(type) {
case api.ActionUartConnect:
err := c.Connect(msg.Adapter, msg.Baudrate)
c.bus.Publish(api.TopicUartAction, api.ActionUartConnected{
Adapter: msg.Adapter,
Baudrate: msg.Baudrate,
Error: err,
})
case api.ActionUartDisconnect:
c.Close()
c.bus.Publish(api.TopicUartAction, api.ActionUartDisconnected{})
case api.ActionUartSendMessage:
c.Send(msg.MsgId, msg.Data)
}
}
}
}
+189
View File
@@ -0,0 +1,189 @@
package uart
import (
"encoding/binary"
"fmt"
"log"
"math"
"alox.tool/api"
)
func ParseFrameVersion(frame api.Frame) (api.PayloadVersion, error) {
if len(frame.Data) != api.PayloadVersionSize {
return api.PayloadVersion{}, fmt.Errorf("payload wrong size: got %d bytes, want 10", len(frame.Data))
}
v := api.PayloadVersion{
Version: binary.LittleEndian.Uint16(frame.Data[0:2]),
Buildhash: [7]uint8(frame.Data[2:10])}
return v, nil
}
func parseFrameClientInfoPart(data []byte) (api.PayloadClientInfo, error) {
if len(data) != api.PayloadClientInfoSize {
return api.PayloadClientInfo{}, fmt.Errorf("payload wrong size: got %d bytes, want 19", len(data))
}
v := api.PayloadClientInfo{
ClientID: data[0],
IsAvailable: data[1],
SlotIsUsed: data[2],
MACAddr: [6]uint8(data[3:9]),
LastPing: binary.LittleEndian.Uint32(data[9:13]),
LastSuccessfulPing: binary.LittleEndian.Uint32(data[13:17]),
Version: binary.LittleEndian.Uint16(data[17:19]),
}
return v, nil
}
func ParseFrameClientInfo(frame api.Frame) ([]api.PayloadClientInfo, error) {
if len(frame.Data) == 0 {
return nil, fmt.Errorf("empty frame data")
}
clientCount := int(frame.Data[0])
log.Printf("Clients %d", clientCount)
expectedLen := 1 + (clientCount * api.PayloadClientInfoSize)
if len(frame.Data) < expectedLen {
return nil, fmt.Errorf("frame data too short: got %d, want %d", len(frame.Data), expectedLen)
}
clientList := make([]api.PayloadClientInfo, 0, clientCount)
for i := 0; i < clientCount; i++ {
start := 1 + (i * api.PayloadClientInfoSize)
end := start + api.PayloadClientInfoSize
client, err := parseFrameClientInfoPart(frame.Data[start:end])
if err != nil {
log.Printf("Could not parse client part %d: %v", i, err)
continue
}
clientList = append(clientList, client)
}
return clientList, nil
}
func parseFrameClientInputPart(data []byte) (api.PayloadClientInput, error) {
if len(data) != api.PayloadClientInputSize {
return api.PayloadClientInput{}, fmt.Errorf("payload wrong size: got %d bytes, want 13", len(data))
}
v := api.PayloadClientInput{
ClientID: data[0],
X: math.Float32frombits(binary.LittleEndian.Uint32(data[1 : 1+4])),
Y: math.Float32frombits(binary.LittleEndian.Uint32(data[5 : 5+4])),
InputMask: binary.LittleEndian.Uint32(data[9 : 9+4]),
}
return v, nil
}
func ParseFrameClientInput(frame api.Frame) ([]api.PayloadClientInput, error) {
if len(frame.Data) == 0 {
return nil, fmt.Errorf("empty frame data")
}
clientCount := int(frame.Data[0])
log.Printf("Clients %d", clientCount)
expectedLen := 1 + (clientCount * api.PayloadClientInputSize)
if len(frame.Data) < expectedLen {
return nil, fmt.Errorf("frame data too short: got %d, want %d", len(frame.Data), expectedLen)
}
clientList := make([]api.PayloadClientInput, 0, clientCount)
for i := 0; i < clientCount; i++ {
start := 1 + (i * api.PayloadClientInputSize)
end := start + api.PayloadClientInputSize
client, err := parseFrameClientInputPart(frame.Data[start:end])
if err != nil {
log.Printf("Could not parse client art %d: %v", i, err)
continue
}
clientList = append(clientList, client)
}
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
}
+128
View File
@@ -0,0 +1,128 @@
package uart
import (
"fmt"
"time"
"alox.tool/api"
"alox.tool/eventbus"
)
const (
StartByte = 0xAA
EscapeByte = 0xBB
EndByte = 0xCC
)
type parserState int
const (
stateWaitingForStart parserState = iota
stateGetID
stateEscapedID
stateInPayload
stateEscapedPayload
)
type Parser struct {
bus eventbus.EventBus
state parserState
parsedData []byte
rawCapture []byte
checksum byte
}
func New(bus eventbus.EventBus) *Parser {
return &Parser{
bus: bus,
state: stateWaitingForStart,
parsedData: make([]byte, 0, 1024*4),
rawCapture: make([]byte, 0, 1024*4),
}
}
func (p *Parser) reset() {
p.state = stateWaitingForStart
p.parsedData = p.parsedData[:0]
p.rawCapture = p.rawCapture[:0]
p.checksum = 0
}
func (p *Parser) pushError(reason string) {
// Throw Error on the Bus befor resetting
p.bus.Publish(api.TopicUARTError, fmt.Errorf("%s: %02X", reason, p.rawCapture))
p.reset()
}
func (p *Parser) emitFrame() {
if len(p.parsedData) == 0 {
p.reset()
return
}
// Copy Data for Message Frame
dataCopy := make([]byte, len(p.parsedData)-1) // Exclude ID
copy(dataCopy, p.parsedData[1:])
f := api.Frame{
Time: uint64(time.Now().UnixNano()),
ID: p.parsedData[0],
Data: dataCopy,
}
p.bus.Publish(api.TopicUARTRx, f)
p.reset()
}
func (p *Parser) addByte(b byte) {
p.parsedData = append(p.parsedData, b)
p.checksum ^= b
}
func (p *Parser) ParseByte(b byte) {
p.rawCapture = append(p.rawCapture, b)
switch p.state {
case stateWaitingForStart:
if b == StartByte {
p.reset()
p.rawCapture = append(p.rawCapture, b) // Start Byte behalten
p.state = stateGetID
}
case stateGetID:
if b == EscapeByte {
p.state = stateEscapedID
} else {
p.addByte(b)
p.state = stateInPayload
}
case stateEscapedID:
p.addByte(b)
p.state = stateInPayload
case stateInPayload:
if b == EscapeByte {
p.state = stateEscapedPayload
return
}
if b == StartByte {
p.pushError("unexpected start byte")
return
}
if b == EndByte {
if p.checksum != 0 {
p.pushError("checksum mismatch")
return
}
p.emitFrame()
return
}
p.addByte(b)
case stateEscapedPayload:
p.addByte(b)
p.state = stateInPayload
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
idf_component_register(SRCS "main.c" "uart_handler.c" "communication_handler.c" "client_handler.c" "message_parser.c" "message_builder.c" "message_handler.c" idf_component_register(SRCS "main.c" "uart_handler.c" "communication_handler.c" "client_handler.c" "message_parser.c" "message_builder.c" "message_handler.c" "ota_update.c" "i2c.c"
INCLUDE_DIRS ".") INCLUDE_DIRS ".")
# Get the short Git commit hash of the current HEAD. # Get the short Git commit hash of the current HEAD.
+4 -1
View File
@@ -1,6 +1,7 @@
#include "client_handler.h" #include "client_handler.h"
#include "communication_handler.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/task.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@@ -40,6 +41,8 @@ int add_client(ClientList *list, const uint8_t *client_mac) {
list->Clients[slot].slotIsUsed = true; list->Clients[slot].slotIsUsed = true;
list->Clients[slot].isAvailable = true; list->Clients[slot].isAvailable = true;
list->Clients[slot].last_seen = xTaskGetTickCount();
list->Clients[slot].retry_counter = 0;
memcpy(list->Clients[slot].macAddr, client_mac, MAC_LENGTH); memcpy(list->Clients[slot].macAddr, client_mac, MAC_LENGTH);
list->ClientCount++; list->ClientCount++;
return CLIENT_OK; return CLIENT_OK;
+17
View File
@@ -21,6 +21,16 @@ enum ClientErrors {
CLIENT_INVALID_ID = -4, CLIENT_INVALID_ID = -4,
}; };
typedef enum {
OTA_IDLE,
OTA_AWAITING_ACK,
OTA_PREPARING,
OTA_READY,
OTA_UPDATING,
OTA_FAILED,
OTA_SUCCESS,
} ota_status_t;
typedef struct { typedef struct {
bool slotIsUsed; bool slotIsUsed;
bool isAvailable; bool isAvailable;
@@ -28,6 +38,13 @@ typedef struct {
uint8_t macAddr[MAC_LENGTH]; uint8_t macAddr[MAC_LENGTH];
TickType_t lastSuccessfullPing; TickType_t lastSuccessfullPing;
TickType_t lastPing; TickType_t lastPing;
uint16_t clientVersion;
ota_status_t ota_status;
uint16_t current_block_id;
uint32_t chunk_bitmask;
uint32_t resent_chunks_counter;
uint8_t retry_counter;
TickType_t last_seen;
} ClientInfo; } ClientInfo;
typedef struct { typedef struct {
+295 -134
View File
@@ -1,45 +1,128 @@
#include "communication_handler.h"
#include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_now.h" #include "esp_now.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "freertos/idf_additions.h" #include "freertos/idf_additions.h"
#include "freertos/task.h"
#include "message_structs.h"
#include "ota_update.h"
#include "client_handler.h" #include "client_handler.h"
#include "communication_handler.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/types.h>
uint8_t broadcast_address[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF};
static const char *TAG = "ALOX - COM"; static const char *TAG = "ALOX - COM";
QueueHandle_t messageQueue = NULL; // Warteschlange für empfangene Nachrichten static QueueHandle_t messageQueue = NULL;
bool hasMaster = false; static struct ESP_MessageBroker mr;
static QueueHandle_t ESP_recieved_message_queue;
void free_ESPNOW_MessageInfo(ESPNOW_MessageInfo *msg) {
if (msg->esp_now_info.src_addr) {
free(msg->esp_now_info.src_addr);
msg->esp_now_info.src_addr = NULL;
}
if (msg->esp_now_info.des_addr) {
free(msg->esp_now_info.des_addr);
msg->esp_now_info.des_addr = NULL;
}
if (msg->esp_now_info.rx_ctrl) {
free(msg->esp_now_info.rx_ctrl);
msg->esp_now_info.rx_ctrl = NULL;
}
if (msg->data) {
free(msg->data);
msg->data = NULL;
}
}
void ESP_InitMessageBroker(QueueHandle_t msg_queue_handle) {
mr.num_direct_callbacks = 0;
mr.num_task_callbacks = 0;
ESP_recieved_message_queue = msg_queue_handle;
return;
}
void ESP_RegisterFunction(CommandPages command,
ESP_RegisterFunctionCallback callback) {
mr.FunctionList[mr.num_direct_callbacks].MSGID = command;
mr.FunctionList[mr.num_direct_callbacks].callback = callback;
mr.num_direct_callbacks++;
return;
}
void ESP_RegisterTask(CommandPages command, ESP_RegisterTaskCallback callback) {
mr.TaskList[mr.num_task_callbacks].MSGID = command;
mr.TaskList[mr.num_task_callbacks].task = callback;
mr.num_task_callbacks++;
}
void ESP_MessageBrokerTask(void *param) {
ESPNOW_MessageInfo received_msg;
ESP_MessageBrokerTaskParams_t *task_params =
(ESP_MessageBrokerTaskParams_t *)param;
QueueHandle_t msg_queue = task_params->message_queue;
if (msg_queue == NULL) {
ESP_LOGE(TAG, "Message queue not initialized. Terminating task.");
vTaskDelete(NULL);
}
ESP_LOGI(TAG, "Message broker task started.");
while (1) {
if (xQueueReceive(msg_queue, &received_msg, portMAX_DELAY)) {
const BaseMessage *message = (const BaseMessage *)received_msg.data;
for (int i = 0; i < mr.num_direct_callbacks; i++) {
if (mr.FunctionList[i].MSGID == message->commandPage) {
mr.FunctionList[i].callback(&received_msg.esp_now_info,
received_msg.data, received_msg.data_len);
free_ESPNOW_MessageInfo(&received_msg);
}
}
}
}
}
static bool hasMaster = false;
static ClientList *esp_client_list; static ClientList *esp_client_list;
static uint8_t channelNumber = 0;
#define MAC_STRING_BUFFER_SIZE 18 int init_com(ClientList *clients, uint8_t wifi_channel) {
messageQueue = xQueueCreate(MESSAGE_QUEUE_SIZE, sizeof(ESPNOW_MessageInfo));
void init_com(ClientList *clients) {
// Initialisiere die Kommunikations-Warteschlange
messageQueue = xQueueCreate(MESSAGE_QUEUE_SIZE, sizeof(BaseMessage));
if (messageQueue == NULL) { if (messageQueue == NULL) {
ESP_LOGE(TAG, "Message queue creation failed"); ESP_LOGE(TAG, "Message queue creation failed");
return -1;
} }
esp_client_list = clients; esp_client_list = clients;
hasMaster = false; hasMaster = false;
channelNumber = wifi_channel;
return 0;
} }
void add_peer(uint8_t *macAddr) { int add_peer(uint8_t *macAddr) {
esp_now_peer_info_t peerInfo = { esp_now_peer_info_t peerInfo = {
.channel = .channel = channelNumber,
0, // Standardkanal, sollte uit den anderen Geräten übereinstimmen
.ifidx = ESP_IF_WIFI_STA, .ifidx = ESP_IF_WIFI_STA,
.encrypt = false, // Keine Verschlüsselung (kann geändert werden) .encrypt = false,
}; };
memcpy(peerInfo.peer_addr, macAddr, ESP_NOW_ETH_ALEN); memcpy(peerInfo.peer_addr, macAddr, ESP_NOW_ETH_ALEN);
esp_err_t result = esp_now_add_peer(&peerInfo); esp_err_t result = esp_now_add_peer(&peerInfo);
if (result == ESP_OK) { if (result == ESP_OK) {
ESP_LOGI(TAG, "Peer added: " MACSTR, MAC2STR(peerInfo.peer_addr)); ESP_LOGI(TAG, "Peer added");
if (!IS_BROADCAST_ADDR(macAddr)) { if (!IS_BROADCAST_ADDR(macAddr)) {
int ret = add_client(esp_client_list, peerInfo.peer_addr); int ret = add_client(esp_client_list, peerInfo.peer_addr);
@@ -47,6 +130,7 @@ void add_peer(uint8_t *macAddr) {
ESP_LOGE(TAG, "Client could not be added to client handler, removing " ESP_LOGE(TAG, "Client could not be added to client handler, removing "
"it from esp now client list!"); "it from esp now client list!");
esp_now_del_peer(peerInfo.peer_addr); esp_now_del_peer(peerInfo.peer_addr);
return -1;
} }
ESP_LOGI(TAG, "New client added."); ESP_LOGI(TAG, "New client added.");
} }
@@ -54,25 +138,24 @@ void add_peer(uint8_t *macAddr) {
ESP_LOGW(TAG, "Peer already exists."); ESP_LOGW(TAG, "Peer already exists.");
int id = get_client_id(esp_client_list, peerInfo.peer_addr); int id = get_client_id(esp_client_list, peerInfo.peer_addr);
if (id >= 0) { if (id >= 0) {
ESP_LOGI(TAG, "Client found again, welcome back!");
esp_client_list->Clients[id].isAvailable = true; esp_client_list->Clients[id].isAvailable = true;
} }
} else { } else {
ESP_LOGE(TAG, "Failed to add peer: %s", esp_err_to_name(result)); ESP_LOGE(TAG, "Failed to add peer: %s", esp_err_to_name(result));
return -1;
} }
return 0;
} }
BaseMessage MessageBuilder(CommandPages commandPage, PayloadUnion payload, BaseMessage MessageBuilder(CommandPages commandPage, PayloadUnion payload,
size_t payload_size) { size_t payload_size) {
BaseMessage message; BaseMessage message;
// Initialisierung der BaseMessage
message.commandPage = commandPage; message.commandPage = commandPage;
message.version = 1; message.version = 1;
message.length = (uint16_t)payload_size; message.length = (uint16_t)payload_size;
// Kopieren des Payloads in die Union memset(&message.payload, 0, sizeof(message.payload));
memset(&message.payload, 0, sizeof(message.payload)); // Sicherheitsmaßnahme
memcpy(&message.payload, &payload, payload_size); memcpy(&message.payload, &payload, payload_size);
return message; return message;
@@ -87,21 +170,18 @@ void master_broadcast_task(void *param) {
ESP_ERROR_CHECK(esp_now_send(broadcast_address, (uint8_t *)&message, ESP_ERROR_CHECK(esp_now_send(broadcast_address, (uint8_t *)&message,
sizeof(BaseMessage))); sizeof(BaseMessage)));
ESP_LOGI(TAG, "Broadcast Message sent");
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
} }
} }
void master_broadcast_ping(void *param) { void master_broadcast_ping(void *param) {
while (1) { while (1) {
// BroadCastPayload payload = {};
PingPayload payload = {}; PingPayload payload = {};
payload.timestamp = esp_timer_get_time(); payload.timestamp = esp_timer_get_time();
BaseMessage message = BaseMessage message =
MessageBuilder(PingPage, *(PayloadUnion *)&payload, sizeof(payload)); MessageBuilder(PingPage, *(PayloadUnion *)&payload, sizeof(payload));
ESP_ERROR_CHECK(esp_now_send(broadcast_address, (uint8_t *)&message, ESP_ERROR_CHECK(esp_now_send(broadcast_address, (uint8_t *)&message,
sizeof(BaseMessage))); sizeof(BaseMessage)));
ESP_LOGI(TAG, "Broadcast PING Message sent");
vTaskDelay(pdMS_TO_TICKS(2500)); vTaskDelay(pdMS_TO_TICKS(2500));
} }
} }
@@ -110,146 +190,204 @@ void master_ping_task(void *param) {
while (1) { while (1) {
for (size_t i = 0; i < MAX_CLIENTS; i++) { for (size_t i = 0; i < MAX_CLIENTS; i++) {
if (esp_client_list->Clients[i].isAvailable) { if (esp_client_list->Clients[i].isAvailable) {
ESP_LOGI(TAG, "SEND PING TO %zu: " MACSTR, i,
MAC2STR(esp_client_list->Clients[i].macAddr));
PingPayload payload = {}; PingPayload payload = {};
payload.timestamp = esp_timer_get_time(); payload.timestamp = esp_timer_get_time();
BaseMessage message = MessageBuilder( BaseMessage message = MessageBuilder(
PingPage, *(PayloadUnion *)&payload, sizeof(payload)); PingPage, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(esp_client_list->Clients[i].macAddr, (uint8_t *)&message, esp_now_send(esp_client_list->Clients[i].macAddr, (uint8_t *)&message,
sizeof(BaseMessage)); sizeof(BaseMessage));
ESP_LOGI(TAG, "SENDING PING!!!!");
} }
} }
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
} }
} }
void master_receive_callback(const esp_now_recv_info_t *esp_now_info, void master_StatusCallback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) { const uint8_t *data, int data_len) {
ESP_LOGI(TAG, "MASTER GOT MESSAGE");
const BaseMessage *message = (const BaseMessage *)data; const BaseMessage *message = (const BaseMessage *)data;
switch (message->commandPage) { ESP_LOGI(TAG, "SRC");
case StatusPage: ESP_LOGI(TAG,
ESP_LOGI(TAG, "GOT STATUS MESSAGE"); "Status Message Received: status: %d, runningPartition: %d, uptime: "
break; "%d, version: %d",
case PingPage: message->payload.status_payload.status,
ESP_LOGI(TAG, "GOT PING MESSAGE"); message->payload.status_payload.runningPartition,
uint32_t currentTime = esp_timer_get_time(); message->payload.status_payload.uptime,
uint32_t diff = currentTime - message->payload.ping_payload.timestamp; message->payload.status_payload.version);
ESP_LOGI(TAG, "Start: %lu, End: %lu, Diff: %lu, Ping: %lu",
message->payload.ping_payload.timestamp, currentTime, diff,
diff / 1000); // ping in ms
int id = get_client_id(esp_client_list, esp_now_info->src_addr);
if (id >= 0) {
esp_client_list->Clients[id].lastSuccessfullPing = xTaskGetTickCount();
esp_client_list->Clients[id].lastPing = (diff / 1000);
ESP_LOGI(TAG, "Updated client %d: " MACSTR " last ping time to %lu", id,
MAC2STR(esp_now_info->src_addr),
esp_client_list->Clients[id].lastSuccessfullPing);
}
break;
case BroadCastPage:
ESP_LOGI(TAG, "MASTER SHOULD NOT GET BROADCAST MESSAGE, is there another "
"master calling?");
break;
case RegisterPage:
ESP_LOGI(TAG, "WILL REGISTER DEVICE");
esp_now_peer_info_t checkPeerInfo;
esp_err_t checkPeer =
esp_now_get_peer(esp_now_info->src_addr, &checkPeerInfo);
switch (checkPeer) {
case (ESP_OK):
ESP_LOGI(TAG, "CLIENT BEKANNT");
int id = get_client_id(esp_client_list, esp_now_info->src_addr);
esp_client_list->Clients[id].isAvailable = true;
esp_client_list->Clients[id].lastSuccessfullPing = xTaskGetTickCount();
ESP_LOGI(TAG, "Updated client %d last ping time to %lu", id,
esp_client_list->Clients[id].lastSuccessfullPing);
break;
case (ESP_ERR_ESPNOW_NOT_INIT):
ESP_LOGI(TAG, "Not initalised");
break;
case (ESP_ERR_ESPNOW_ARG):
ESP_LOGI(TAG, "ESP ERR ESPNOW_ARG");
break;
case (ESP_ERR_ESPNOW_NOT_FOUND):
ESP_LOGI(TAG, "CLIENT WIRD IN DIE LISTE AUFGENOMMEN");
add_peer(esp_now_info->src_addr);
break;
default:
ESP_LOGI(TAG, "Unknown Message %i", checkPeer);
}
break;
default:
ESP_LOGI(TAG, "Unknown CommandPage %i", message->commandPage);
break;
}
} }
void client_receive_callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
ESP_LOGI(TAG, "SLAVE GOT MESSAGE");
ESP_LOGI(TAG, "Received message from: " MACSTR,
MAC2STR(esp_now_info->src_addr));
ESP_LOGI(TAG, "Message: %.*s", data_len, data);
void master_RegisterCallback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
BaseMessage replyMessage = {}; BaseMessage replyMessage = {};
const BaseMessage *message = (const BaseMessage *)data; esp_now_peer_info_t checkPeerInfo;
switch (message->commandPage) { esp_err_t checkPeer =
case StatusPage: esp_now_get_peer(esp_now_info->src_addr, &checkPeerInfo);
ESP_LOGI(TAG, "GOT STATUS MESSAGE"); switch (checkPeer) {
case (ESP_OK):
int id = get_client_id(esp_client_list, esp_now_info->src_addr);
esp_client_list->Clients[id].isAvailable = true;
esp_client_list->Clients[id].lastSuccessfullPing = xTaskGetTickCount();
break; break;
case PingPage: case (ESP_ERR_ESPNOW_NOT_FOUND):
ESP_LOGI(TAG, "GOT PING MESSAGE"); add_peer(esp_now_info->src_addr);
replyMessage = MessageBuilder(PingPage, *(PayloadUnion *)&message->payload, GetStatusPayload payload = {};
sizeof(message->payload)); replyMessage = MessageBuilder(GetStatusPage, *(PayloadUnion *)&payload,
ESP_ERROR_CHECK(esp_now_send( sizeof(payload));
esp_now_info->src_addr, (uint8_t *)&replyMessage, sizeof(BaseMessage))); esp_now_send(esp_now_info->src_addr, (uint8_t *)&replyMessage,
break; sizeof(BaseMessage));
case BroadCastPage:
ESP_LOGI(TAG, "GOT BROADCAST MESSAGE");
if (!hasMaster) {
if (IS_BROADCAST_ADDR(esp_now_info->des_addr)) {
ESP_LOGI(TAG,
"GOT BROADCAST MESSAGE ATTEMPTING TO REGISTER TO MASTER!");
add_peer(esp_now_info->src_addr);
replyMessage =
MessageBuilder(RegisterPage, *(PayloadUnion *)&message->payload,
sizeof(message->payload));
ESP_ERROR_CHECK(esp_now_send(esp_now_info->src_addr,
(uint8_t *)&replyMessage,
sizeof(BaseMessage)));
hasMaster = true;
}
}
break;
case RegisterPage:
break; break;
default: default:
ESP_LOGI(TAG, "GOT UNKONW MESSAGE");
break; break;
} }
} }
void client_data_sending_task(void *param) { void master_pingCallback(const esp_now_recv_info_t *esp_now_info,
while (1) { const uint8_t *data, int data_len) {
const char *dataToSend = "DATA:42"; const BaseMessage *message = (const BaseMessage *)data;
ESP_LOGI(TAG, "SEND DATA");
esp_now_send(NULL, (uint8_t *)dataToSend, uint32_t currentTime = esp_timer_get_time();
strlen(dataToSend)); uint32_t diff = currentTime - message->payload.ping_payload.timestamp;
vTaskDelay(pdMS_TO_TICKS(5000));
int id = get_client_id(esp_client_list, esp_now_info->src_addr);
if (id >= 0) {
esp_client_list->Clients[id].lastSuccessfullPing = xTaskGetTickCount();
esp_client_list->Clients[id].lastPing = (diff / 1000);
} }
} }
void master_broadcastCallback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {}
void ESPNOW_RegisterMasterCallbacks() {
ESP_RegisterFunction(StatusPage, master_StatusCallback);
ESP_RegisterFunction(RegisterPage, master_RegisterCallback);
ESP_RegisterFunction(PingPage, master_pingCallback);
ESP_RegisterFunction(BroadCastPage, master_broadcastCallback);
}
void slave_broadcastCallback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
if (!hasMaster) {
if (IS_BROADCAST_ADDR(esp_now_info->des_addr)) {
add_peer(esp_now_info->src_addr);
BaseMessage replyMessage = {};
replyMessage =
MessageBuilder(RegisterPage, *(PayloadUnion *)&replyMessage.payload,
sizeof(replyMessage.payload));
esp_now_send(esp_now_info->src_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
hasMaster = true;
}
}
}
void slave_getstatusCallback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
StatusPayload payload = {
.status = 1,
.runningPartition = 1,
.uptime = 100,
.version = 0x0002,
};
BaseMessage replyMessage =
MessageBuilder(StatusPage, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(esp_now_info->src_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
}
void slave_pingCallback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
if (!hasMaster)
return;
const BaseMessage *message = (const BaseMessage *)data;
BaseMessage replyMessage = MessageBuilder(
PingPage, *(PayloadUnion *)&message->payload, sizeof(message->payload));
esp_now_send(esp_now_info->src_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
}
void ESPNOW_RegisterSlaveCallbacks() {
ESP_RegisterFunction(BroadCastPage, slave_broadcastCallback);
ESP_RegisterFunction(GetStatusPage, slave_getstatusCallback);
ESP_RegisterFunction(PingPage, slave_pingCallback);
}
void master_receive_callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
uint8_t *copied_data = (uint8_t *)malloc(data_len);
if (copied_data == NULL) {
return;
}
memcpy(copied_data, data, data_len);
ESPNOW_MessageInfo msg_info;
msg_info.esp_now_info.src_addr = malloc(6);
if (msg_info.esp_now_info.src_addr) {
memcpy(msg_info.esp_now_info.src_addr, esp_now_info->src_addr, 6);
}
msg_info.esp_now_info.des_addr = malloc(6);
if (msg_info.esp_now_info.des_addr) {
memcpy(msg_info.esp_now_info.des_addr, esp_now_info->des_addr, 6);
}
msg_info.esp_now_info.rx_ctrl = malloc(sizeof(wifi_pkt_rx_ctrl_t));
if (msg_info.esp_now_info.rx_ctrl) {
memcpy(msg_info.esp_now_info.rx_ctrl, esp_now_info->rx_ctrl,
sizeof(wifi_pkt_rx_ctrl_t));
}
msg_info.data = copied_data;
msg_info.data_len = data_len;
xQueueSend(ESP_recieved_message_queue, &msg_info, portMAX_DELAY);
}
void client_receive_callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
uint8_t *copied_data = (uint8_t *)malloc(data_len);
if (copied_data == NULL) {
return;
}
memcpy(copied_data, data, data_len);
ESPNOW_MessageInfo msg_info;
// Initialize the esp_now_info struct to zeros
memset(&msg_info.esp_now_info, 0, sizeof(esp_now_recv_info_t));
// Now, allocate and copy the data pointed to by the pointers within
// esp_now_info src_addr
msg_info.esp_now_info.src_addr = malloc(6);
if (msg_info.esp_now_info.src_addr) {
memcpy(msg_info.esp_now_info.src_addr, esp_now_info->src_addr, 6);
} else {
free(copied_data);
return;
}
// des_addr
msg_info.esp_now_info.des_addr = malloc(6);
if (msg_info.esp_now_info.des_addr) {
memcpy(msg_info.esp_now_info.des_addr, esp_now_info->des_addr, 6);
} else {
free(msg_info.esp_now_info.src_addr);
free(copied_data);
return;
}
msg_info.esp_now_info.rx_ctrl = NULL; // Set to NULL as we are not copying it
msg_info.data = copied_data;
msg_info.data_len = data_len;
xQueueSend(ESP_recieved_message_queue, &msg_info, portMAX_DELAY);
}
void client_monitor_task(void *pvParameters) { void client_monitor_task(void *pvParameters) {
TickType_t timeout_ticks = TickType_t timeout_ticks = pdMS_TO_TICKS(CLIENT_TIMEOUT_MS);
pdMS_TO_TICKS(CLIENT_TIMEOUT_MS); TickType_t interval_ticks = pdMS_TO_TICKS(CHECK_INTERVAL_MS);
TickType_t interval_ticks =
pdMS_TO_TICKS(CHECK_INTERVAL_MS);
while (1) { while (1) {
TickType_t now = xTaskGetTickCount(); TickType_t now = xTaskGetTickCount();
@@ -261,11 +399,34 @@ void client_monitor_task(void *pvParameters) {
if (time_diff > timeout_ticks) { if (time_diff > timeout_ticks) {
esp_client_list->Clients[i].isAvailable = false; esp_client_list->Clients[i].isAvailable = false;
ESP_LOGW(TAG, "Client %d (MAC: " MACSTR ") is unavailable",
MAC2STR(esp_client_list->Clients[i].macAddr));
} }
} }
} }
vTaskDelay(interval_ticks); vTaskDelay(interval_ticks);
} }
} }
const esp_partition_t *ota_update_partition = NULL;
void ESPNOW_RegisterOTAMaster() {
// Observe this States for all Slaves in ClientList
// OTA_SLAVE_PREPARING
// OTA_SLAVE_READY
// OTA_SLAVE_ERROR
// OTA_SLAVE_WRITE_FINISHED
// OTA_SLAVE_FINISHED
ESP_RegisterFunction(OTA_PREPARE_ACKNOWLEDGED,
master_ota_prepare_acknowledge_callback);
ESP_RegisterFunction(OTA_READY_TO_RECEIVE,
master_ota_ready_to_recieve_callback);
ESP_RegisterFunction(OTA_UPDATE_SLAVE_ACKED,
master_ota_update_slave_acknowledge_callback);
}
void ESPNOW_RegisterOTASlave() {
ESP_RegisterFunction(OTA_PREPARE_FOR_UPDATE, slave_Prep_Upgrade_Callback);
ESP_RegisterFunction(OTA_CHUNK, slave_Update_Chunk_Callback);
ESP_RegisterFunction(OTA_FINISH_UPDATE, slave_Update_Finished_Callback);
}
+70 -34
View File
@@ -11,53 +11,42 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/types.h>
#include "esp_partition.h"
#include "message_structs.h"
#define BROADCAST_INTERVAL_MS 500 #define BROADCAST_INTERVAL_MS 500
#define CLIENT_TIMEOUT_MS 5000 // 5 Sekunden Timeout #define CLIENT_TIMEOUT_MS 5000 // 5 Sekunden Timeout
#define CHECK_INTERVAL_MS 1000 // Jede Sekunde überprüfen #define CHECK_INTERVAL_MS 1000 // Jede Sekunde überprüfen
static uint8_t broadcast_address[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF};
#define IS_BROADCAST_ADDR(addr) \
(memcmp(addr, broadcast_address, ESP_NOW_ETH_ALEN) == 0)
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] extern uint8_t broadcast_address[ESP_NOW_ETH_ALEN];
#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" #define IS_BROADCAST_ADDR(addr) (memcmp(addr, broadcast_address, ESP_NOW_ETH_ALEN) == 0)
#define MESSAGE_QUEUE_SIZE 10 #define MESSAGE_QUEUE_SIZE 10
typedef enum { typedef union __attribute__((packed)) {
BroadCastPage, OTA_PREPARE_FOR_UPDATE_Payload ota_prepare_for_update_payload;
StatusPage, OTA_PREPARE_ACKNOWLEDGED_Payload ota_prepare_acknowledged_payload;
PingPage, OTA_READY_TO_RECEIVE_Payload ota_ready_to_receive_payload;
RegisterPage, OTA_CHUNK_Payload ota_chunk_payload;
} CommandPages; OTA_REQUEST_BLOCK_STATUS_Payload ota_request_block_status_payload;
OTA_BLOCK_STATUS_REPORT_Payload ota_block_status_report_payload;
typedef struct { OTA_COMMIT_BLOCK_Payload ota_commit_block_payload;
uint32_t uptime; OTA_BLOCK_COMMITTED_Payload ota_block_committed_payload;
uint8_t status; OTA_FINISH_UPDATE_Payload ota_finish_update_payload;
} StatusPayload; OTA_UPDATE_STATUS_Payload ota_update_status_payload;
typedef struct {
uint32_t timestamp;
} PingPayload;
typedef struct {
} BroadCastPayload;
typedef struct {
bool familierClient;
} RegisterPayload;
typedef union {
StatusPayload status_payload; StatusPayload status_payload;
ConfigPayload config_payload;
PingPayload ping_payload; PingPayload ping_payload;
BroadCastPayload broadcast_payload; BroadCastPayload broadcast_payload;
RegisterPayload register_payload; RegisterPayload register_payload;
FirmwarePrepPayload firmware_prep_payload;
FirmwarePayload firmware_payload;
} PayloadUnion; } PayloadUnion;
typedef struct { typedef struct __attribute__((packed)) {
uint16_t version; uint16_t version; // protcol version
CommandPages commandPage; CommandPages commandPage;
uint16_t length; uint16_t length;
PayloadUnion payload; PayloadUnion payload;
@@ -66,9 +55,52 @@ typedef struct {
static_assert(sizeof(BaseMessage) <= 255, static_assert(sizeof(BaseMessage) <= 255,
"BaseMessage darf nicht größer als 255 sein"); "BaseMessage darf nicht größer als 255 sein");
void init_com(ClientList *clients); typedef void (*ESP_RegisterFunctionCallback)(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len);
typedef void (*ESP_RegisterTaskCallback)(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len);
struct ESP_RegisterdFunction {
CommandPages MSGID;
ESP_RegisterFunctionCallback callback;
};
struct ESP_RegisterdTask {
CommandPages MSGID;
ESP_RegisterTaskCallback task;
};
struct ESP_MessageBroker {
struct ESP_RegisterdFunction FunctionList[64];
uint8_t num_direct_callbacks;
struct ESP_RegisterdTask TaskList[64];
uint8_t num_task_callbacks;
};
typedef struct {
QueueHandle_t message_queue;
} ESP_MessageBrokerTaskParams_t;
typedef struct {
esp_now_recv_info_t esp_now_info;
uint8_t *data;
int data_len;
} ESPNOW_MessageInfo;
void ESP_InitMessageBroker(QueueHandle_t msg_queue_handle);
void ESP_RegisterFunction(CommandPages command,
ESP_RegisterFunctionCallback callback);
void ESP_RegisterTask(CommandPages command, ESP_RegisterTaskCallback callback);
void ESP_MessageBrokerTask(void *param);
void ESPNOW_RegisterMasterCallbacks();
void ESPNOW_RegisterSlaveCallbacks();
void ESPNOW_RegisterOTAMaster();
void ESPNOW_RegisterOTASlave();
int init_com(ClientList *clients, uint8_t wifi_channel);
int getNextFreeClientId(); int getNextFreeClientId();
void add_peer(uint8_t *macAddr); int add_peer(uint8_t *macAddr);
BaseMessage MessageBuilder(CommandPages commandPage, PayloadUnion payload, BaseMessage MessageBuilder(CommandPages commandPage, PayloadUnion payload,
size_t payload_size); size_t payload_size);
@@ -81,6 +113,10 @@ void master_receive_callback(const esp_now_recv_info_t *esp_now_info,
void client_receive_callback(const esp_now_recv_info_t *esp_now_info, void client_receive_callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len); const uint8_t *data, int data_len);
void client_data_sending_task(void *param); void client_data_sending_task(void *param);
void client_send_random_data_task(void *param);
void client_monitor_task(void *pvParameters); void client_monitor_task(void *pvParameters);
void send_ota_block_chunks(uint8_t client_id, uint16_t block_id);
#endif #endif
View File
View File
+278
View File
@@ -0,0 +1,278 @@
#include "i2c.h"
#include "bma4.h"
#include "bma456h.h"
#include "bma4_defs.h"
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
#include "hal/gpio_types.h"
#include "ota_update.h"
#include <rom/ets_sys.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static i2c_master_bus_handle_t bus_handle;
static i2c_master_dev_handle_t bma456_dev_handle;
static struct bma4_dev bma456_struct;
volatile uint8_t interrupt_status = 0;
uint8_t int_line;
struct bma4_int_pin_config pin_config = {0};
uint16_t int_status = 0;
#define BMA4_READ_WRITE_LEN UINT8_C(46)
#define BMA456W_INT_PIN 7
static void interrupt_callback(void *) {
interrupt_status = 1;
// ESP_LOGI("INTERRUPT", "STEP DETECTED");
}
/******************************************************************************/
/*! User interface functions */
/*!
* I2C read function map to ESP platform
*/
BMA4_INTF_RET_TYPE bma4_i2c_read(uint8_t reg_addr, uint8_t *reg_data,
uint32_t len, void *intf_ptr) {
// ESP_ERROR_CHECK(i2c_master_receive(bma456_dev_handle, reg_data, len, -1));
esp_err_t err = i2c_master_transmit_receive(bma456_dev_handle, &reg_addr, 1,
reg_data, len, -1);
return (err == ESP_OK) ? BMA4_OK : BMA4_E_COM_FAIL;
// return BMA4_OK;
}
/*!
* I2C write function map to ESP platform
*/
BMA4_INTF_RET_TYPE bma4_i2c_write(uint8_t reg_addr, const uint8_t *reg_data,
uint32_t len, void *intf_ptr) {
// ESP_ERROR_CHECK(i2c_master_transmit(bma456_dev_handle, reg_data, len, -1));
// Bei Bosch muss zuerst das Register, dann die Daten in einem Transfer
// gesendet werden
uint8_t *buffer = malloc(len + 1);
if (!buffer)
return BMA4_E_NULL_PTR;
buffer[0] = reg_addr;
ESP_LOGI("I2CWrite", "Message Length: %d", len);
memcpy(&buffer[1], reg_data, len);
esp_err_t err = i2c_master_transmit(bma456_dev_handle, buffer, len + 1, -1);
free(buffer);
return (err == ESP_OK) ? BMA4_OK : BMA4_E_COM_FAIL;
// return BMA4_OK;
}
/*!
* Delay function map to ESP platform
*/
void bma4_delay_us(uint32_t period, void *intf_ptr) {
uint32_t wait_ms = period / 1000;
uint32_t wait_us = period % 1000;
if (wait_ms) {
vTaskDelay(pdMS_TO_TICKS(wait_ms));
}
ets_delay_us(wait_us);
}
/*!
* @brief Prints the execution status of the APIs.
*/
void bma4_error_codes_print_result(const char api_name[], int8_t rslt) {
if (rslt != BMA4_OK) {
ESP_LOGI("BMA4_I2C", "%s\t", api_name);
if (rslt == BMA4_E_NULL_PTR) {
ESP_LOGI("BMA4_I2C", "Error [%d] : Null pointer\r\n", rslt);
} else if (rslt == BMA4_E_COM_FAIL) {
ESP_LOGI("BMA4_I2C", "Error [%d] : Communication failure\r\n", rslt);
} else if (rslt == BMA4_E_CONFIG_STREAM_ERROR) {
ESP_LOGI("BMA4_I2C", "Error [%d] : Invalid configuration stream\r\n",
rslt);
} else if (rslt == BMA4_E_SELF_TEST_FAIL) {
ESP_LOGI("BMA4_I2C", "Error [%d] : Self test failed\r\n", rslt);
} else if (rslt == BMA4_E_INVALID_SENSOR) {
ESP_LOGI("BMA4_I2C", "Error [%d] : Device not found\r\n", rslt);
} else if (rslt == BMA4_E_OUT_OF_RANGE) {
ESP_LOGI("BMA4_I2C", "Error [%d] : Out of Range\r\n", rslt);
} else if (rslt == BMA4_E_AVG_MODE_INVALID_CONF) {
ESP_LOGI("BMA4_I2C",
"Error [%d] : Invalid bandwidth and ODR combination in Accel "
"Averaging mode\r\n",
rslt);
} else {
/* For more error codes refer "*_defs.h" */
ESP_LOGI("BMA4_I2C", "Error [%d] : Unknown error code\r\n", rslt);
}
}
}
void init_i2c() {
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.glitch_ignore_cnt = 7,
//.flags.enable_internal_pullup = true,
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
}
void read_sensor_task(void *params) {
int8_t ret;
struct bma4_accel sens_data = {0};
while (1) {
ret = bma4_read_accel_xyz(&sens_data, &bma456_struct);
bma4_error_codes_print_result("bma4_read_accel_xyz", ret);
ESP_LOGI("ACC", "X: %d, Y: %d, Z: %d", sens_data.x, sens_data.y,
sens_data.z);
if (interrupt_status) {
ESP_LOGI("INTERRUPT", "Da war der Interrupt resetting");
interrupt_status = 0;
ret = bma456h_read_int_status(&int_status, &bma456_struct);
bma4_error_codes_print_result("bma456w_step_counter_output status", ret);
int8_t rslt;
struct bma456h_out_state tap_out = {0};
rslt = bma456h_output_state(&tap_out, &bma456_struct);
if (BMA4_OK == rslt) {
/* Enters only if the obtained interrupt is single-tap */
if (tap_out.single_tap) {
ESP_LOGI("INTERRUPT", "Single Tap interrupt occurred\n");
}
/* Enters only if the obtained interrupt is double-tap */
else if (tap_out.double_tap) {
ESP_LOGI("INTERRUPT", "Double Tap interrupt occurred\n");
}
/* Enters only if the obtained interrupt is triple-tap */
else if (tap_out.triple_tap) {
ESP_LOGI("INTERRUPT", "Triple Tap interrupt occurred\n");
}
}
}
// ESP_LOGI("i2c", "X:%d, Y%d, Z%d", sens_data.x, sens_data.y, sens_data.z);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void init_bma456() {
i2c_device_config_t dev_cfg_bma456 = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = BMA456_ADDRESS,
.scl_speed_hz = 100000,
};
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg_bma456,
&bma456_dev_handle));
bma456_struct.intf = BMA4_I2C_INTF;
bma456_struct.bus_read = bma4_i2c_read;
bma456_struct.bus_write = bma4_i2c_write;
bma456_struct.delay_us = bma4_delay_us;
bma456_struct.read_write_len = BMA4_READ_WRITE_LEN;
bma456_struct.intf_ptr = &bma456_dev_handle;
int8_t ret;
bma456_struct.chip_id = 0;
ret = bma456h_init(&bma456_struct);
bma4_error_codes_print_result("I2C Init", ret);
ESP_LOGI("I2C", "Chip Init ausgelesene CHIP ID %d", bma456_struct.chip_id);
ret = bma4_soft_reset(&bma456_struct);
bma4_error_codes_print_result("bma4_soft_reset", ret);
vTaskDelay(pdMS_TO_TICKS(20)); // Wartezeit nach Reset
ret = bma4_set_advance_power_save(BMA4_DISABLE, &bma456_struct);
bma4_error_codes_print_result("bma4_set_advance_power_save", ret);
vTaskDelay(pdMS_TO_TICKS(10));
ESP_LOGI("I2C", "Config SIZE %d", bma456_struct.config_size);
ESP_LOGI("I2C", "Config Pointer %p", bma456_struct.config_file_ptr);
ESP_LOGI("I2C", "Starte Config-File Upload...");
ret = bma456h_write_config_file(&bma456_struct);
bma4_error_codes_print_result("bma4_write_config_file", ret);
struct bma4_accel_config accel_config;
bma4_get_accel_config(&accel_config, &bma456_struct);
accel_config.range = BMA4_ACCEL_RANGE_2G;
ret = bma4_set_accel_config(&accel_config, &bma456_struct);
bma4_error_codes_print_result("bma4_set_accel_config status", ret);
/* Enable the accelerometer */
ret = bma4_set_accel_enable(BMA4_ENABLE, &bma456_struct);
bma4_error_codes_print_result("bma4_set_accel_enable status", ret);
struct bma456h_multitap_settings tap_settings = {0};
ret = bma456h_tap_get_parameter(&tap_settings, &bma456_struct);
bma4_error_codes_print_result("bma456h_tap_get_parameter status", ret);
tap_settings.tap_sens_thres = 0;
ret = bma456h_tap_set_parameter(&tap_settings, &bma456_struct);
bma4_error_codes_print_result("bma456h_tap_set_parameter status", ret);
ret = bma456h_feature_enable(
(BMA456H_SINGLE_TAP_EN | BMA456H_DOUBLE_TAP_EN | BMA456H_TRIPLE_TAP_EN),
BMA4_ENABLE, &bma456_struct);
bma4_error_codes_print_result("bma456w_feature_enable status", ret);
/* Setting watermark level 1, the output step resolution is 20 steps.
* Eg: 1 means, 1 * 20 = 20. Every 20 steps once output triggers
*/
ret = bma456h_step_counter_set_watermark(1, &bma456_struct);
bma4_error_codes_print_result("bma456w_step_counter_set_watermark status",
ret);
/* Hardware interrupt configuration */
int_line = BMA4_INTR2_MAP;
ret = bma4_get_int_pin_config(&pin_config, int_line, &bma456_struct);
bma4_error_codes_print_result("bma4_get_int_pin_config status", ret);
ret = bma456h_map_interrupt(int_line, BMA456H_TAP_OUT_INT, BMA4_ENABLE,
&bma456_struct);
bma4_error_codes_print_result("bma456w_map_interrupt status", ret);
pin_config.edge_ctrl = BMA4_EDGE_TRIGGER;
pin_config.output_en = BMA4_OUTPUT_ENABLE;
pin_config.lvl = BMA4_ACTIVE_HIGH;
pin_config.od = BMA4_PUSH_PULL;
pin_config.input_en = BMA4_INPUT_DISABLE;
ret = bma4_set_int_pin_config(&pin_config, int_line, &bma456_struct);
bma4_error_codes_print_result("bma4_set_int_pin_config status", ret);
gpio_reset_pin(BMA456W_INT_PIN);
gpio_set_direction(BMA456W_INT_PIN, GPIO_MODE_INPUT);
gpio_set_pull_mode(BMA456W_INT_PIN, GPIO_PULLDOWN_ONLY);
gpio_set_intr_type(BMA456W_INT_PIN, GPIO_INTR_POSEDGE);
gpio_intr_enable(BMA456W_INT_PIN);
gpio_install_isr_service(0);
gpio_isr_handler_add(BMA456W_INT_PIN, interrupt_callback,
(void *)BMA456W_INT_PIN);
xTaskCreate(read_sensor_task, "READ_SENSOR", 4096, NULL, 1, NULL);
}
void init_i2c_with_all_devices() {
init_i2c();
init_bma456();
}
+8
View File
@@ -0,0 +1,8 @@
#define I2C_PORT 0
#define I2C_MASTER_SCL_IO 5
#define I2C_MASTER_SDA_IO 6
#define BMA456_ADDRESS 0x18
void init_i2c();
void init_bma456();
void init_i2c_with_all_devices();
+205 -44
View File
@@ -1,7 +1,15 @@
#include "client_handler.h" #include "client_handler.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "esp_app_desc.h"
#include "esp_app_format.h"
#include "esp_err.h"
#include "esp_flash_partitions.h"
#include "esp_image_format.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_log_buffer.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_phy_init.h" #include "esp_phy_init.h"
#include "esp_rom_gpio.h" #include "esp_rom_gpio.h"
#include "esp_timer.h" #include "esp_timer.h"
@@ -10,34 +18,102 @@
#include "hal/uart_types.h" #include "hal/uart_types.h"
#include "message_handler.h" #include "message_handler.h"
#include "message_parser.h" #include "message_parser.h"
#include "nvs.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "communication_handler.h" #include "communication_handler.h"
#include "main.h" #include "main.h"
#include "ota_update.h"
#include "uart_handler.h" #include "uart_handler.h"
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include "message_builder.h" #include "message_builder.h"
#include "uart_msg_ids.h"
#include "i2c.h"
static const char *TAG = "ALOX - MAIN"; static const char *TAG = "ALOX - MAIN";
static const uint16_t version = 0x0001; static const uint16_t version = 0x0001;
static uint8_t send_message_buffer[1024]; static uint8_t send_message_buffer[1024];
static uint8_t send_message_payload_buffer[512 - 4]; static uint8_t send_message_payload_buffer[512];
uint32_t g_uart_firmware_total_size = 0;
static MessageBrokerTaskParams_t broker_task_params; static MessageBrokerTaskParams_t broker_task_params;
static MasterOTA_TaskParams_t master_ota_task_params;
static ESP_MessageBrokerTaskParams_t esp_broker_task_params;
ClientList clientList = {.Clients = {{0}}, .ClientCount = 0}; ClientList clientList = {.Clients = {{0}}, .ClientCount = 0};
size_t build_ClientInfoPart(uint8_t clientid, float_t lagex, float_t lagey,
int32_t bitmask, uint8_t *outputArray,
size_t outputArrayOffset, size_t outputArraySize) {
size_t offset = outputArrayOffset;
memcpy(&outputArray[offset], &clientid, sizeof(clientid));
offset += sizeof(clientid);
// lagex (typischerweise 4 Bytes)
memcpy(&outputArray[offset], &lagex, sizeof(lagex));
offset += sizeof(lagex);
// lagey (typischerweise 4 Bytes)
memcpy(&outputArray[offset], &lagey, sizeof(lagey));
offset += sizeof(lagey);
// bitmask (4 Bytes)
memcpy(&outputArray[offset], &bitmask, sizeof(bitmask));
offset += sizeof(bitmask);
return offset - outputArrayOffset;
}
void fakeDataCallback(uint8_t msgid, const uint8_t *payload, size_t payload_len,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
uint8_t seed = payload[1];
ESP_LOGI(TAG, "Sending Fake Client Infos with seed %d", seed);
srand(seed);
size_t offset = 1;
send_payload_buffer[0] = 3; // Client Count
offset +=
build_ClientInfoPart(1, rand() * 1.0, rand() * 1.0, rand() * 1,
send_payload_buffer, 1, send_payload_buffer_size);
offset += build_ClientInfoPart(2, rand() * 2.0, rand() * 2.0, rand() * 2,
send_payload_buffer, offset,
send_payload_buffer_size);
offset += build_ClientInfoPart(3, rand() * 3.0, rand() * 3.0, rand() * 3,
send_payload_buffer, offset,
send_payload_buffer_size);
int len = build_message(UART_CLIENT_INPUT, send_payload_buffer, offset,
send_buffer, send_buffer_size);
if (len < 0) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
payload_len, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
}
void echoCallback(uint8_t msgid, const uint8_t *payload, size_t payload_len, void echoCallback(uint8_t msgid, const uint8_t *payload, size_t payload_len,
uint8_t *send_payload_buffer, size_t send_payload_buffer_size, uint8_t *send_payload_buffer, size_t send_payload_buffer_size,
uint8_t *send_buffer, size_t send_buffer_size) { uint8_t *send_buffer, size_t send_buffer_size) {
ESP_LOGI(TAG, "Echo command 0x01..."); ESP_LOGI(TAG, "Echo command 0x01...");
int len = int len = build_message(UART_ECHO, payload, payload_len, send_buffer,
build_message(0x01, payload, payload_len, send_buffer, send_buffer_size); send_buffer_size);
if (len < 0) { if (len < 0) {
ESP_LOGE(TAG, ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: " "Error Building UART Message: payload_len, %d, sendbuffer_size: "
@@ -67,7 +143,7 @@ void versionCallback(uint8_t msgid, const uint8_t *payload, size_t payload_len,
send_payload_buffer[1] = (uint8_t)((version >> 8) & 0xFF); send_payload_buffer[1] = (uint8_t)((version >> 8) & 0xFF);
memcpy(&send_payload_buffer[2], &BUILD_GIT_HASH, git_build_hash_len); memcpy(&send_payload_buffer[2], &BUILD_GIT_HASH, git_build_hash_len);
int len = build_message(0x02, send_payload_buffer, needed_buffer_size, int len = build_message(UART_VERSION, send_payload_buffer, needed_buffer_size,
send_buffer, send_buffer_size); send_buffer, send_buffer_size);
if (len < 0) { if (len < 0) {
ESP_LOGE(TAG, ESP_LOGE(TAG,
@@ -84,7 +160,7 @@ void clientInfoCallback(uint8_t msgid, const uint8_t *payload,
size_t send_payload_buffer_size, uint8_t *send_buffer, size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) { size_t send_buffer_size) {
ESP_LOGI(TAG, "Client Info Command 0x03..."); ESP_LOGI(TAG, "Client Info Command 0x03...");
static uint8_t entryLength = 17; static uint8_t entryLength = 19;
uint8_t needed_buffer_size = 1 + entryLength * clientList.ClientCount; uint8_t needed_buffer_size = 1 + entryLength * clientList.ClientCount;
if (send_payload_buffer_size < needed_buffer_size) { if (send_payload_buffer_size < needed_buffer_size) {
@@ -96,27 +172,9 @@ void clientInfoCallback(uint8_t msgid, const uint8_t *payload,
send_payload_buffer[0] = clientList.ClientCount; send_payload_buffer[0] = clientList.ClientCount;
uint8_t offsetMult = 0; uint8_t offsetMult = 0;
uint8_t used_slots = 0;
for (int i = 0; i < MAX_CLIENTS; i++) { for (int i = 0; i < MAX_CLIENTS; i++) {
if (clientList.Clients[i].slotIsUsed) { if (clientList.Clients[i].slotIsUsed) {
used_slots++;
}
}
uint8_t loop_sanity_counter = 0;
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clientList.Clients[i].slotIsUsed) {
loop_sanity_counter++;
if (loop_sanity_counter > clientList.ClientCount) {
ESP_LOGE("SPECIAL",
"ERROR SANITY CHECK FAILED: loop_sanity_count: %d, "
"client_count: %d",
loop_sanity_counter, clientList.ClientCount);
}
size_t offset = 1 + (entryLength * offsetMult++); size_t offset = 1 + (entryLength * offsetMult++);
ESP_LOGE("SPECIAL", "OFFSET %d", offset);
send_payload_buffer[offset] = i; send_payload_buffer[offset] = i;
send_payload_buffer[offset + 1] = clientList.Clients[i].isAvailable; send_payload_buffer[offset + 1] = clientList.Clients[i].isAvailable;
send_payload_buffer[offset + 2] = clientList.Clients[i].slotIsUsed; send_payload_buffer[offset + 2] = clientList.Clients[i].slotIsUsed;
@@ -126,11 +184,14 @@ void clientInfoCallback(uint8_t msgid, const uint8_t *payload,
4); 4);
memcpy(&send_payload_buffer[offset + 13], memcpy(&send_payload_buffer[offset + 13],
&clientList.Clients[i].lastSuccessfullPing, 4); &clientList.Clients[i].lastSuccessfullPing, 4);
memcpy(&send_payload_buffer[offset + 17],
&clientList.Clients[i].clientVersion, 2);
} }
} }
int len = build_message(0x04, send_payload_buffer, needed_buffer_size, int len = build_message(UART_CLIENT_INFO, send_payload_buffer,
send_buffer, send_buffer_size); needed_buffer_size, send_buffer, send_buffer_size);
if (len < 0) { if (len < 0) {
ESP_LOGE(TAG, ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: " "Error Building UART Message: payload_len, %d, sendbuffer_size: "
@@ -141,6 +202,69 @@ void clientInfoCallback(uint8_t msgid, const uint8_t *payload,
uart_write_bytes(MASTER_UART, send_buffer, len); uart_write_bytes(MASTER_UART, send_buffer, len);
} }
bool g_ota_in_progress = false;
void ota_monitor_task(void *param) {
const TickType_t timeout = pdMS_TO_TICKS(10000); // 10 seconds
while (g_ota_in_progress) {
bool all_done = true;
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clientList.Clients[i].slotIsUsed) {
if (clientList.Clients[i].ota_status != OTA_SUCCESS &&
clientList.Clients[i].ota_status != OTA_FAILED) {
all_done = false;
if ((xTaskGetTickCount() - clientList.Clients[i].last_seen) >
timeout) {
ESP_LOGE(TAG, "Client %d timed out", i);
clientList.Clients[i].ota_status = OTA_FAILED;
}
}
}
}
if (all_done) {
g_ota_in_progress = false;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(TAG, "OTA Operation Finished.");
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clientList.Clients[i].slotIsUsed) {
ESP_LOGI(TAG, "Client %d [MAC: " MACSTR "]: %s, Resent Chunks: %d", i,
MAC2STR(clientList.Clients[i].macAddr),
clientList.Clients[i].ota_status == OTA_SUCCESS ? "SUCCESS"
: "FAILED",
clientList.Clients[i].resent_chunks_counter);
}
}
vTaskDelete(NULL);
}
void send_client_ota_start_message(uint8_t clientID, uint32_t app_size) {
BaseMessage message = {};
OTA_PREPARE_FOR_UPDATE_Payload ota_payload = {
.total_size = app_size,
};
message = MessageBuilder(OTA_PREPARE_FOR_UPDATE,
*(PayloadUnion *)&ota_payload, sizeof(ota_payload));
esp_err_t err = esp_now_send(clientList.Clients[clientID].macAddr,
(uint8_t *)&message, sizeof(BaseMessage));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Could not send OTA PREPARE FOR UPDATE to " MACSTR ", %s",
MAC2STR(clientList.Clients[clientID].macAddr),
esp_err_to_name(err));
} else {
ESP_LOGI(TAG, "Sent OTA PREPARE FOR UPDATE to " MACSTR,
MAC2STR(clientList.Clients[clientID].macAddr));
}
}
void app_main(void) { void app_main(void) {
ESP_LOGI(TAG, "Starting Alox Powerpod Version %d Build: %s", version, ESP_LOGI(TAG, "Starting Alox Powerpod Version %d Build: %s", version,
BUILD_GIT_HASH); BUILD_GIT_HASH);
@@ -153,12 +277,10 @@ void app_main(void) {
} }
ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(ret);
// GPIO-Pin für Moduserkennung
gpio_reset_pin(MASTER_MODE_PIN); gpio_reset_pin(MASTER_MODE_PIN);
gpio_set_direction(MASTER_MODE_PIN, GPIO_MODE_INPUT); gpio_set_direction(MASTER_MODE_PIN, GPIO_MODE_INPUT);
bool isMaster = (gpio_get_level(MASTER_MODE_PIN) == 0); bool isMaster = (gpio_get_level(MASTER_MODE_PIN) == 0);
// ESP-NOW Initialisieren
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
@@ -166,8 +288,7 @@ void app_main(void) {
wifi_config_t wifi_config = { wifi_config_t wifi_config = {
.sta = .sta =
{ {
.channel = 1, // Kanal 1, stelle sicher, dass alle Geräte .channel = 1,
// denselben Kanal verwenden
}, },
}; };
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
@@ -181,25 +302,58 @@ void app_main(void) {
ESP_ERROR_CHECK(esp_now_register_recv_cb(client_receive_callback)); ESP_ERROR_CHECK(esp_now_register_recv_cb(client_receive_callback));
} }
init_com(&clientList); ret = init_com(&clientList, 1);
if (ret < 0) {
ESP_LOGE(TAG, "Could not Init ESP NOW Communication!");
}
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "OTA: Running Partition: %s", running->label);
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
bool diagnostic_is_ok = true; // TODO build in valid diagnostics
if (diagnostic_is_ok) {
esp_ota_mark_app_valid_cancel_rollback();
} else {
// esp_ota_mark_app_invalid_rollback(); Put this function at the start
// so when the esp crashes it can rollback
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
const esp_partition_t *next_ota_partition =
esp_ota_get_next_update_partition(NULL);
int app_size = get_app_size(next_ota_partition);
ESP_LOGE(TAG, "App Size in Other Partition %d", app_size);
QueueHandle_t espnow_message_queue =
xQueueCreate(10, sizeof(ESPNOW_MessageInfo));
ESP_InitMessageBroker(espnow_message_queue);
esp_broker_task_params.message_queue = espnow_message_queue;
xTaskCreate(ESP_MessageBrokerTask, "espnow_message_broker_task", 4096,
(void *)&esp_broker_task_params, 4, NULL);
init_ota();
// Tasks starten basierend auf Master/Client
if (isMaster) { if (isMaster) {
ESP_LOGI(TAG, "Started in Mastermode"); ESP_LOGI(TAG, "Started in Mastermode");
ESPNOW_RegisterMasterCallbacks();
ESPNOW_RegisterOTAMaster();
add_peer(broadcast_address); add_peer(broadcast_address);
xTaskCreate(master_broadcast_task, "MasterBroadcast", 4096, NULL, 1, NULL); xTaskCreate(master_broadcast_task, "MasterBroadcast", 4096, NULL, 1, NULL);
// xTaskCreate(master_ping_task, "MasterPing", 4096, NULL, 1, NULL);
xTaskCreate(master_broadcast_ping, "MasterBroadcastPing", 4096, NULL, 1, xTaskCreate(master_broadcast_ping, "MasterBroadcastPing", 4096, NULL, 1,
NULL); NULL);
// xTaskCreate(client_monitor_task, "MonitorClientTask", 4096, NULL, 1,
// NULL);
QueueHandle_t parsed_message_queue = QueueHandle_t parsed_message_queue =
xQueueCreate(10, sizeof(ParsedMessage_t)); xQueueCreate(10, sizeof(ParsedMessage_t));
init_uart(parsed_message_queue); init_uart(parsed_message_queue);
InitMessageBroker(); InitMessageBroker();
// Initialisiere die Parameterstruktur
broker_task_params.message_queue = parsed_message_queue; broker_task_params.message_queue = parsed_message_queue;
broker_task_params.send_buffer = send_message_buffer; broker_task_params.send_buffer = send_message_buffer;
broker_task_params.send_buffer_size = sizeof(send_message_buffer); broker_task_params.send_buffer_size = sizeof(send_message_buffer);
@@ -207,19 +361,26 @@ void app_main(void) {
broker_task_params.payload_buffer_size = broker_task_params.payload_buffer_size =
sizeof(send_message_payload_buffer); sizeof(send_message_payload_buffer);
xTaskCreate(MessageBrokerTask, "message_handler_task", 4096, xTaskCreate(MessageBrokerTask, "MessageHandler", 4096,
(void *)&broker_task_params, 5, NULL); (void *)&broker_task_params, 5, NULL);
RegisterCallback(0x01, echoCallback); master_ota_task_params.client_list = &clientList;
RegisterCallback(0x02, versionCallback); xTaskCreate(MasterOTATask, "MasterOTATask", 4096,
RegisterCallback(0x03, clientInfoCallback); (void *)&master_ota_task_params, 4, NULL);
RegisterCallback(UART_ECHO, echoCallback);
RegisterCallback(UART_VERSION, versionCallback);
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();
// xTaskCreate(uart_status_task, "MasterUartStatusTask", 4096, NULL, 1,
// NULL); xTaskCreate(SendClientInfoTask, "SendCientInfo", 4096, NULL, 1,
// NULL);
} else { } else {
ESP_LOGI(TAG, "Started in Slavemode"); ESP_LOGI(TAG, "Started in Slavemode");
xTaskCreate(client_data_sending_task, "ClientDataSending", 4096, NULL, 1, ESPNOW_RegisterSlaveCallbacks();
NULL); xTaskCreate(slave_ota_task, "SlaveOTATask", 4096, NULL, 4, NULL);
ESPNOW_RegisterOTASlave();
} }
} }
-1
View File
@@ -19,5 +19,4 @@
#elif CONFIG_IDF_TARGET_ESP32C3 #elif CONFIG_IDF_TARGET_ESP32C3
#define MASTER_MODE_PIN GPIO_NUM_0 // Jumper-Erkennungspin #define MASTER_MODE_PIN GPIO_NUM_0 // Jumper-Erkennungspin
#endif #endif
#endif #endif
+2 -3
View File
@@ -20,8 +20,8 @@ bool add_byte_with_length_check(uint8_t byte, size_t write_index, uint8_t *data,
int build_message(uint8_t msgid, const uint8_t *payload, size_t payload_len, int build_message(uint8_t msgid, const uint8_t *payload, size_t payload_len,
uint8_t *msg_buffer, size_t msg_buffer_size) { uint8_t *msg_buffer, size_t msg_buffer_size) {
ESP_LOGE("BM", "payload_len %d, msg_buffer_size %d", payload_len + 4, //ESP_LOGE("BM", "payload_len %d, msg_buffer_size %d", payload_len + 4,
msg_buffer_size); // msg_buffer_size);
if (payload_len + 4 > msg_buffer_size) { if (payload_len + 4 > msg_buffer_size) {
return PayloadBiggerThenBuffer; return PayloadBiggerThenBuffer;
} }
@@ -75,6 +75,5 @@ int build_message(uint8_t msgid, const uint8_t *payload, size_t payload_len,
msg_buffer[write_index++] = checksum; msg_buffer[write_index++] = checksum;
msg_buffer[write_index++] = EndByte; msg_buffer[write_index++] = EndByte;
ESP_LOGE("BM", "MESSAGE FERTIG GEBAUT");
return write_index; return write_index;
} }
+5 -2
View File
@@ -13,6 +13,7 @@ void InitMessageBroker() {
} }
void RegisterCallback(uint8_t msgid, RegisterFunctionCallback callback) { 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].MSGID = msgid;
mr.FunctionList[mr.num_direct_callbacks].callback = callback; mr.FunctionList[mr.num_direct_callbacks].callback = callback;
mr.num_direct_callbacks++; mr.num_direct_callbacks++;
@@ -46,11 +47,13 @@ void MessageBrokerTask(void *param) {
while (1) { while (1) {
if (xQueueReceive(msg_queue, &received_msg, portMAX_DELAY)) { if (xQueueReceive(msg_queue, &received_msg, portMAX_DELAY)) {
ESP_LOGI(TAG, "Received message from queue: MSGID=0x%02X, Length=%u", // ESP_LOGI(TAG, "Received message from queue: MSGID=0x%02X, Length=%u",
received_msg.msgid, received_msg.payload_len); // received_msg.msgid, received_msg.payload_len);
for (int i = 0; i < mr.num_direct_callbacks; i++) { 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) { if (mr.FunctionList[i].MSGID == received_msg.msgid) {
//ESP_LOGI(TAG, "FOUND CALLBACK");
mr.FunctionList[i].callback( mr.FunctionList[i].callback(
received_msg.msgid, received_msg.data, received_msg.payload_len, received_msg.msgid, received_msg.data, received_msg.payload_len,
send_payload_buffer, send_payload_buffer_size, send_payload_buffer, send_payload_buffer_size,
+2 -2
View File
@@ -4,7 +4,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#define MAX_MESSAGE_PAYLOAD_LENGTH 128 #define MAX_MESSAGE_PAYLOAD_LENGTH 512
#define MAX_TOTAL_CONTENT_LENGTH (MAX_MESSAGE_PAYLOAD_LENGTH + 1) #define MAX_TOTAL_CONTENT_LENGTH (MAX_MESSAGE_PAYLOAD_LENGTH + 1)
enum ParserState { enum ParserState {
@@ -33,7 +33,7 @@ struct MessageReceive {
enum ParserError error; enum ParserError error;
uint8_t messageid; uint8_t messageid;
uint8_t message[MAX_MESSAGE_PAYLOAD_LENGTH]; uint8_t message[MAX_MESSAGE_PAYLOAD_LENGTH];
uint8_t index; uint16_t index;
uint8_t checksum; uint8_t checksum;
}; };
+124
View File
@@ -0,0 +1,124 @@
#ifndef MESSAGE_STRUCTS_H
#define MESSAGE_STRUCTS_H
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X"
typedef enum {
OTA_PREPARE_FOR_UPDATE,
OTA_PREPARE_ACKNOWLEDGED,
OTA_READY_TO_RECEIVE,
OTA_CHUNK,
OTA_REQUEST_BLOCK_STATUS,
OTA_BLOCK_STATUS_REPORT,
OTA_COMMIT_BLOCK,
OTA_BLOCK_COMMITTED,
OTA_FINISH_UPDATE,
OTA_UPDATE_STATUS,
OTA_UPDATE_SLAVE_ACKED,
MASTER_READY_TO_SEND_CHUNKS,
StatusPage,
GetStatusPage,
ConfigPage,
PingPage,
BroadCastPage,
RegisterPage,
} CommandPages;
typedef struct __attribute__((packed)) {
uint32_t total_size;
uint32_t block_size;
} OTA_PREPARE_FOR_UPDATE_Payload;
typedef struct __attribute__((packed)) {
// Empty
} OTA_PREPARE_ACKNOWLEDGED_Payload;
typedef struct __attribute__((packed)) {
uint8_t status; // 0 = READY, 1 = ERROR
} OTA_READY_TO_RECEIVE_Payload;
typedef struct __attribute__((packed)) {
uint16_t block_id;
uint8_t chunk_id;
uint8_t data_len;
uint8_t data[200];
} OTA_CHUNK_Payload;
typedef struct __attribute__((packed)) {
uint16_t block_id;
} OTA_REQUEST_BLOCK_STATUS_Payload;
typedef struct __attribute__((packed)) {
uint16_t block_id;
uint32_t chunk_bitmask;
} OTA_BLOCK_STATUS_REPORT_Payload;
typedef struct __attribute__((packed)) {
uint16_t block_id;
} OTA_COMMIT_BLOCK_Payload;
typedef struct __attribute__((packed)) {
uint16_t block_id;
} OTA_BLOCK_COMMITTED_Payload;
typedef struct __attribute__((packed)) {
// Empty
} OTA_FINISH_UPDATE_Payload;
typedef struct __attribute__((packed)) {
uint8_t status; // 0 = SUCCESS, 1 = FAILED
} OTA_UPDATE_STATUS_Payload;
typedef struct __attribute__((packed)) {
uint16_t current_block_id;
uint16_t update_buffer_write_index;
uint32_t update_size;
uint16_t sequenz_counter; // how often the update buffer gets written
uint8_t status; // 0 = SUCCESS, 1 = FAILED
} OTA_UPDATE_ACK_Payload;
typedef struct __attribute__((packed)) {
uint16_t version; // software version
uint8_t runningPartition;
uint8_t status;
uint32_t uptime;
} StatusPayload;
typedef struct __attribute__((packed)) {
} GetStatusPayload;
typedef struct __attribute__((packed)) {
uint8_t timeslot;
} ConfigPayload;
typedef struct __attribute__((packed)) {
uint32_t timestamp;
} PingPayload;
typedef struct __attribute__((packed)) {
} BroadCastPayload;
typedef struct __attribute__((packed)) {
bool familierClient;
} RegisterPayload;
// TODO: Check checksum fields
typedef struct __attribute__((packed)) {
uint16_t length; // length of complete firmware
uint8_t checksum; // checksum of firmware
} FirmwarePrepPayload;
// TODO: Check checksum fields
typedef struct __attribute__((packed)) {
uint8_t length;
uint8_t checksum;
uint32_t address;
uint8_t payload[240]; // TODO: need a way to figure out a safe value for this
} FirmwarePayload;
#endif // MESSAGE_STRUCTS_H
+678
View File
@@ -0,0 +1,678 @@
#include "ota_update.h"
#include "client_handler.h"
#include "communication_handler.h"
#include "driver/uart.h"
#include "esp_app_format.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_log_buffer.h"
#include "esp_now.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "message_builder.h"
#include "message_handler.h"
#include "message_structs.h"
#include "uart_handler.h"
#include "uart_msg_ids.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
static const char *TAG = "ALOX - OTA";
static QueueHandle_t ota_task_queue = NULL;
static esp_ota_handle_t update_handle = 0;
static uint8_t update_buffer[UPDATE_BUFFER_SIZE];
static uint8_t update_buffer_chunk[250];
static uint8_t update_buffer_chunk_len;
static uint32_t chunk_bitmask;
static uint16_t current_block_id;
static uint16_t update_buffer_write_index;
static uint32_t update_size;
static uint16_t sequenz_counter; // how often the update buffer gets written
static esp_partition_t partition_to_read_update_from;
static uint32_t partition_to_read_from_read_index;
static ClientList *client_list;
static bool all_chunks_send;
static bool finished;
uint32_t get_app_size(const esp_partition_t *app_size_partition) {
esp_app_desc_t app_desc;
esp_ota_get_partition_description(app_size_partition, &app_desc);
esp_image_header_t header;
esp_partition_read(app_size_partition, 0, &header, sizeof(header));
if (header.magic != ESP_IMAGE_HEADER_MAGIC) {
ESP_LOGE(TAG, "KEIN VALIDER HEADER");
return 0;
}
uint32_t data_len = sizeof(header);
for (int i = 0; i < header.segment_count; i++) {
esp_image_segment_header_t segment_header;
esp_partition_read(app_size_partition, data_len, &segment_header,
sizeof(segment_header));
ESP_LOGI(TAG, "SEGMENT %d Address %d, Segment DataLen %d", i,
segment_header.load_addr, segment_header.data_len);
uint32_t padded_len = (segment_header.data_len + 3) & ~3;
data_len += padded_len + sizeof(segment_header);
}
data_len += 1;
data_len += 32;
uint32_t padding = (16 - (data_len % 16)) % 16;
data_len += padding;
return data_len;
}
int ota_send_finish(ClientList *client_list) {
// read flash and send data
BaseMessage replyMessage = {};
OTA_FINISH_UPDATE_Payload payload = {};
ESP_LOGI(TAG, "OTA SEND FINISH");
replyMessage = MessageBuilder(OTA_FINISH_UPDATE, *(PayloadUnion *)&payload,
sizeof(payload));
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
if (client_list->Clients[i].ota_status == OTA_UPDATING) {
esp_now_send(client_list->Clients[i].macAddr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
client_list->Clients[i].ota_status = OTA_AWAITING_ACK;
}
}
}
finished = true;
return 0;
}
int ota_send_next_update_chunk(ClientList *client_list) {
// read flash and send data
BaseMessage replyMessage = {};
OTA_CHUNK_Payload payload = {};
size_t actual_read = 200;
if (partition_to_read_from_read_index + actual_read > update_size) {
actual_read = update_size - partition_to_read_from_read_index;
}
esp_err_t err = esp_partition_read(&partition_to_read_update_from,
partition_to_read_from_read_index,
payload.data, actual_read);
if (actual_read < 200) {
ESP_LOG_BUFFER_HEX(TAG, payload.data, actual_read);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "Could not read partition");
}
partition_to_read_from_read_index += actual_read;
payload.data_len = actual_read;
ESP_LOGI(TAG, "READ %d Bytes Sendig it to all Clients waiting", actual_read);
ESP_LOGI(TAG, "READ PARTITION AT %d Bytes MAX Bytes %d",
partition_to_read_from_read_index, update_size);
replyMessage =
MessageBuilder(OTA_CHUNK, *(PayloadUnion *)&payload, sizeof(payload));
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
if (client_list->Clients[i].ota_status == OTA_UPDATING) {
esp_now_send(client_list->Clients[i].macAddr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
client_list->Clients[i].ota_status = OTA_AWAITING_ACK;
}
}
}
if (partition_to_read_from_read_index == update_size)
return 1; // last update chunk send now finish it!
return 0;
}
void MasterOTATask(void *pvParameter) {
ESP_LOGI(TAG, "master_ota_task started");
ota_task_queue_message_t msg;
MasterOTA_TaskParams_t task_params = *(MasterOTA_TaskParams_t *)pvParameter;
client_list = task_params.client_list;
while (1) {
if (xQueueReceive(ota_task_queue, &msg, portMAX_DELAY)) {
ESP_LOGI(TAG, "master ota_task received command: %d", msg.command);
BaseMessage replyMessage = {};
switch (msg.command) {
case OTA_SEND_SLAVES_PREPARE_MESSAGE: {
}
case OTA_SLAVE_WILL_PREPARE: {
int id = get_client_id(client_list, msg.mac_addr);
if (id < 0) {
// error
ESP_LOGE(TAG, "Error set OTA_PREPARE could not get client id");
}
// just wait
// mark client that it will wait
ESP_LOGI(TAG, "MASTER OTA TASK: Marking Client %d as OTA_PREPARING",
id);
client_list->Clients[id].ota_status = OTA_PREPARING;
break;
}
case OTA_SLAVE_IS_PREPARED: {
// client is prepared check if all clients are preapred to send chunks
int id = get_client_id(client_list, msg.mac_addr);
if (id < 0) {
// error
}
if (client_list->Clients[id].ota_status == OTA_PREPARING) {
ESP_LOGI(TAG, "MASTER OTA TASK: Marking Client %d as OTA_UPDATING",
id);
client_list->Clients[id].ota_status = OTA_UPDATING;
} else {
ESP_LOGE(TAG, "MASTER OTA TASK: Client this should not happend");
}
bool start = true;
// check if all clients are prepared
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
if (client_list->Clients[i].ota_status != OTA_UPDATING)
start = false;
}
}
if (start)
ota_send_next_update_chunk(client_list);
break;
}
case OTA_SLAVE_ACKED: {
// mark client as acked check if all clients acked to send next message
int id = get_client_id(client_list, msg.mac_addr);
if (id < 0) {
// error
}
if (client_list->Clients[id].ota_status == OTA_AWAITING_ACK) {
ESP_LOGI(TAG, "OTA_SLAVE_ACKED Client %d Status Update OTA_UPDATING",
id);
client_list->Clients[id].ota_status = OTA_UPDATING;
} else {
ESP_LOGE(TAG, "OTA_SLAVE_ACKED Client %d Status Should not HAPPEND",
id);
}
bool start = true;
// check if all clients are prepared
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
ESP_LOGI(TAG, "SLOT %d is USED", i);
if (client_list->Clients[i].ota_status != OTA_UPDATING)
start = false;
}
}
if (start) {
if (finished)
break; // dont need to send anything else
if (all_chunks_send) {
ota_send_finish(client_list);
break;
}
ESP_LOGE(TAG, "OTA_SLAVE_ACKED all clients have the status "
"OTA_UPDATING SENDING NEXT CHUNK");
int end = ota_send_next_update_chunk(client_list);
if (end) {
all_chunks_send = true;
}
}
break;
}
case OTA_SLAVE_ERROR:
break;
// mark client as error
case OTA_MASTER_SEND_PREAPRE_REQUEST:
break;
case OTA_MASTER_SEND_CHUNK:
break;
case OTA_MASTER_SEND_FINISH:
break;
}
}
}
}
void slave_ota_task(void *pvParameter) {
ESP_LOGI(TAG, "slave_ota_task started");
ota_task_queue_message_t msg;
BaseMessage replyMessage = {};
while (1) {
if (xQueueReceive(ota_task_queue, &msg, portMAX_DELAY)) {
ESP_LOGI(TAG, "slave ota_task received command: %d", msg.command);
switch (msg.command) {
case OTA_SEND_SLAVES_PREPARE_MESSAGE:
break;
case OTA_SLAVE_WILL_PREPARE:
break;
case OTA_SLAVE_IS_PREPARED:
break;
case OTA_SLAVE_ACKED:
break;
case OTA_SLAVE_ERROR:
break;
case OTA_MASTER_SEND_PREAPRE_REQUEST: {
ESP_LOGE(TAG, "START PREAPRE CALL");
int part = prepare_ota_update(); // this part function is blocking
OTA_READY_TO_RECEIVE_Payload payload = {};
if (part < 0) {
payload.status = 1; // ERROR
} else {
payload.status = 0; // READY
}
ESP_LOGE(TAG, "PREPARED %d", part);
replyMessage = MessageBuilder(
OTA_READY_TO_RECEIVE, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(msg.mac_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
break;
}
case OTA_MASTER_SEND_CHUNK: {
// TODO: Move Update_buffer_chunk in normal update buffer no need for
// the extra step...
// TODO: at the moment its just so i can use the write_ota_update
// function unmodified
ESP_LOGI(TAG, "Master send Chunk writing it!");
write_ota_update(update_buffer_chunk_len, update_buffer_chunk);
ESP_LOGI(TAG, "AFTER WRITE_OTA_UPDATE!");
OTA_UPDATE_ACK_Payload payload = {
.update_buffer_write_index = update_buffer_write_index,
.current_block_id = current_block_id,
.sequenz_counter = sequenz_counter,
.status = 0,
};
replyMessage = MessageBuilder(
OTA_UPDATE_SLAVE_ACKED, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(msg.mac_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
break;
}
case OTA_MASTER_SEND_FINISH: {
esp_err_t err = end_ota_update();
int status = 0;
if (err != ESP_OK) {
status = 1; // TODO: Set real error
}
ESP_LOGI(TAG, "UPDATE FERTIG STATUS %d should be 0", status);
OTA_UPDATE_ACK_Payload payload = {
.update_buffer_write_index = update_buffer_write_index,
.current_block_id = current_block_id,
.sequenz_counter = sequenz_counter,
.status = status,
};
replyMessage = MessageBuilder(
OTA_UPDATE_SLAVE_ACKED, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(msg.mac_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
break;
}
}
}
}
}
void start_uart_update(uint8_t msgid, const uint8_t *payload,
size_t payload_len, uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
ESP_LOGI(TAG, "OTA Update Start Uart Command");
vTaskPrioritySet(NULL, 2);
update_size = 0;
update_buffer_write_index = 0;
sequenz_counter = 0;
all_chunks_send = false;
finished = false;
int part = prepare_ota_update();
uart_ota_start_t *start = (uart_ota_start_t *)send_payload_buffer;
start->partition = part & 0xff;
// TODO: Refine Errors
// Set error
if (part < 0) {
start->error = 0x01;
}
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) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
payload_len, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
}
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);
if (update_buffer_write_index + write_len > UPDATE_BUFFER_SIZE) {
ESP_LOGI(TAG, "write_ota_update: schreib das update weg!");
esp_err_t err =
esp_ota_write(update_handle, update_buffer, update_buffer_write_index);
if (err != ESP_OK) {
return err;
}
update_buffer_write_index = 0;
sequenz_counter++;
}
memcpy(&update_buffer[update_buffer_write_index], payload, write_len);
update_buffer_write_index += write_len;
return ESP_OK;
}
void payload_uart_update(uint8_t msgid, const uint8_t *payload_data_from_uart,
size_t total_payload_len_from_uart,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
const uint8_t *actual_firmware_data = payload_data_from_uart;
uint32_t write_len = total_payload_len_from_uart;
if (update_size == 0) {
ESP_LOGI(TAG, "First chunk received. write_len: %d", write_len);
}
update_size += write_len;
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);
}
int len = build_message(UART_OTA_PAYLOAD, send_payload_buffer,
send_payload_len, send_buffer, send_buffer_size);
if (len < 0) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
total_payload_len_from_uart, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
}
void end_uart_update(uint8_t msgid, const uint8_t *payload, size_t payload_len,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
ESP_LOGI(TAG, "OTA Update End Uart Command");
esp_err_t err = end_ota_update();
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) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
payload_len, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
vTaskPrioritySet(NULL, 1);
}
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);
}
int prepare_ota_update() {
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running Partition: %s", running->label);
const esp_partition_t *update_partition =
esp_ota_get_next_update_partition(NULL);
if (update_partition == NULL) {
ESP_LOGE(TAG, "Failed to find OTA partition.");
return -1;
}
ESP_LOGI(TAG, "Writing OTA Update to Partition: %s", update_partition->label);
esp_err_t err =
esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
return -2;
}
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() {
if (update_buffer_write_index > 0) {
ESP_LOG_BUFFER_HEX(TAG, update_buffer, update_buffer_write_index);
esp_err_t err =
esp_ota_write(update_handle, update_buffer, update_buffer_write_index);
vTaskDelay(1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error writing remaining data to partition: %s",
esp_err_to_name(err));
return err;
}
}
esp_err_t err = esp_ota_end(update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(err));
ESP_LOGI(TAG, "Total blocks written: %u, Last partial block size: %u",
sequenz_counter, update_buffer_write_index);
return err;
}
const esp_partition_t *update_partition =
esp_ota_get_next_update_partition(NULL);
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s",
esp_err_to_name(err));
}
return err;
}
// Acknoledge that the slave should prepare for an update
// Queues the Prepare Task beacuse it takes like 30 seconds
void slave_Prep_Upgrade_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
ESP_LOGE(TAG, "SLAVE PREPARE FOR UPDATE Callback");
update_size = 0;
update_buffer_write_index = 0;
sequenz_counter = 0;
const BaseMessage *message = (const BaseMessage *)data;
const OTA_PREPARE_FOR_UPDATE_Payload *payload =
&message->payload.ota_prepare_for_update_payload;
// total_update_size = payload->total_size;
// Queue Command for Task to call the prepare method
ota_task_queue_message_t msg = {.command = OTA_MASTER_SEND_PREAPRE_REQUEST};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
ESP_LOGE(TAG, "SLAVE PREPARE CALLBACK AFTER QUEUE SEND");
// Tell the master that the slave will preapre
OTA_PREPARE_ACKNOWLEDGED_Payload *reply_payload;
BaseMessage reply_message =
MessageBuilder(OTA_PREPARE_ACKNOWLEDGED, *(PayloadUnion *)&reply_payload,
sizeof(OTA_PREPARE_ACKNOWLEDGED_Payload));
ESP_ERROR_CHECK(esp_now_send(esp_now_info->src_addr,
(uint8_t *)&reply_message, sizeof(BaseMessage)));
}
void slave_Update_Chunk_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
const BaseMessage *message = (const BaseMessage *)data;
const OTA_CHUNK_Payload *payload = &message->payload.ota_chunk_payload;
// copy data to update_buffer_chunk so that the write method can write it
// back later
memcpy(update_buffer_chunk, payload->data, payload->data_len);
update_buffer_chunk_len = payload->data_len;
ESP_LOGI(TAG, "slave_update_Chunk_Callback got %d bytes from Master",
payload->data_len);
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_MASTER_SEND_CHUNK};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void slave_Update_Finished_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
const BaseMessage *message = (const BaseMessage *)data;
const OTA_FINISH_UPDATE_Payload *payload =
&message->payload.ota_finish_update_payload;
ESP_LOGI(TAG, "slave_Update_Finished_Callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_MASTER_SEND_FINISH};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void start_ota_update_espnow(uint8_t msgid, const uint8_t *payload,
size_t payload_len, uint8_t *send_payload_buffer,
size_t send_payload_buffer_size,
uint8_t *send_buffer, size_t send_buffer_size) {
ESP_LOGI(TAG, "Starting OTA update for all clients");
const esp_partition_t *ota_update_partition;
ota_update_partition = esp_ota_get_next_update_partition(NULL);
if (ota_update_partition == NULL) {
ESP_LOGE(TAG, "Failed to get update partition");
return;
}
update_size = get_app_size(ota_update_partition);
partition_to_read_update_from = *ota_update_partition;
partition_to_read_from_read_index = 0;
BaseMessage replyMessage = {};
OTA_PREPARE_FOR_UPDATE_Payload replyPayload = {};
replyMessage =
MessageBuilder(OTA_PREPARE_FOR_UPDATE, *(PayloadUnion *)&replyPayload,
sizeof(replyPayload));
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
esp_now_send(client_list->Clients[i].macAddr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
client_list->Clients[i].ota_status = OTA_READY;
}
}
}
void master_ota_prepare_acknowledge_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data,
int data_len) {
ESP_LOGI(TAG, "entering master_ota_prepare_acknowledge_callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_SLAVE_WILL_PREPARE};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void master_ota_ready_to_recieve_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data,
int data_len) {
ESP_LOGI(TAG, "entering master_ota_ready_to_recieve_callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_SLAVE_IS_PREPARED};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void master_ota_update_slave_acknowledge_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data,
int data_len) {
ESP_LOGI(TAG, "entering master_ota_update_slave_acknowledge_callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_SLAVE_ACKED};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
+97
View File
@@ -0,0 +1,97 @@
#ifndef OTA_UPDATE_H
#define OTA_UPDATE_H
#include "client_handler.h"
#include "esp_err.h"
#include "esp_now.h"
#include "esp_partition.h"
#include "message_structs.h"
#include <stdint.h>
#include <sys/types.h>
#define UPDATE_BUFFER_SIZE 4096
#define UPDATE_PAYLOAD_SIZE 200
#define UPDATE_MAX_SEQUENZES (UPDATE_BUFFER_SIZE / UPDATE_PAYLOAD_SIZE)
typedef enum {
OTA_SEND_SLAVES_PREPARE_MESSAGE,
OTA_SLAVE_WILL_PREPARE,
OTA_SLAVE_IS_PREPARED,
OTA_SLAVE_ACKED,
OTA_SLAVE_ERROR,
OTA_MASTER_SEND_PREAPRE_REQUEST,
OTA_MASTER_SEND_CHUNK,
OTA_MASTER_SEND_FINISH,
} ota_command_t;
typedef struct {
ota_command_t command;
uint8_t mac_addr[ESP_NOW_ETH_ALEN];
} ota_task_queue_message_t;
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);
u_int32_t get_app_size(const esp_partition_t *app_size_partition);
int prepare_ota_update();
esp_err_t write_ota_update(uint32_t write_len, const uint8_t *payload);
esp_err_t end_ota_update();
void slave_Prep_Upgrade_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len);
void slave_Update_Chunk_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len);
void slave_Update_Finished_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len);
void master_ota_prepare_acknowledge_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len);
void master_ota_ready_to_recieve_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len);
void master_ota_update_slave_acknowledge_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len);
void start_uart_update(uint8_t msgid, const uint8_t *payload,
size_t payload_len, uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size);
void payload_uart_update(uint8_t msgid, const uint8_t *payload_data_from_uart,
size_t total_payload_len_from_uart,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size);
void end_uart_update(uint8_t msgid, const uint8_t *payload, size_t payload_len,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size);
void start_ota_update_espnow(uint8_t msgid, const uint8_t *payload,
size_t payload_len, uint8_t *send_payload_buffer,
size_t send_payload_buffer_size,
uint8_t *send_buffer, size_t send_buffer_size);
#endif
+4 -3
View File
@@ -18,7 +18,8 @@ static const char *TAG = "ALOX - UART";
static QueueHandle_t parsed_message_queue; static QueueHandle_t parsed_message_queue;
void init_uart(QueueHandle_t msg_queue_handle) { void init_uart(QueueHandle_t msg_queue_handle) {
uart_config_t uart_config = {.baud_rate = 115200, uart_config_t uart_config = {// .baud_rate = 115200, // 921600, 115200
.baud_rate = 921600,
.data_bits = UART_DATA_8_BITS, .data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE, .parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1, .stop_bits = UART_STOP_BITS_1,
@@ -61,9 +62,9 @@ void send_message_hook(const uint8_t *buffer, size_t length) {
void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload, void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload,
size_t payload_len) { size_t payload_len) {
ESP_LOGI(TAG, "GOT UART MESSAGE MSGID: %02X, Len: %u bytes \nMSG: ", msgid, /*ESP_LOGI(TAG, "GOT UART MESSAGE MSGID: %02X, Len: %u bytes \nMSG: ", msgid,
payload_len, payload); payload_len, payload);
ESP_LOG_BUFFER_HEX(TAG, payload, payload_len); ESP_LOG_BUFFER_HEX(TAG, payload, payload_len);*/
ParsedMessage_t msg_to_send; ParsedMessage_t msg_to_send;
msg_to_send.msgid = msgid; msg_to_send.msgid = msgid;
+19
View File
@@ -0,0 +1,19 @@
#ifndef UART_MSG_IDS_H
#define UART_MSG_IDS_H
enum UART_MSG_IDS {
// 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,
};
#endif
+182 -35
View File
@@ -1,55 +1,150 @@
# Conventions
## Naming
### Filenames: snake_case (ota_master.c, ota_slave.c, com_handler.c)
### Functions: module_submodule_action()
- OTA Master = ota_m_send_chunk(), ota_m_init()
- OTA Slave = ota_s_handle_chunk(), ota_s_prepare_flash()
- com_init(), msg_parse()
### Variables:
- Global: g_ (g_client_list)
- Statics: s_ (s_update_handle)
- Constants: UPPER_SNAKE_CASE (UPDATE_BUFFER_SIZE)
### Types:
- Typedefs: Ends with _t (ota_payload_t)
- Struct-Tags: Ends with _t or spezifik name (struct ota_context)
- Unions: Ends with _u (msg_payload_u)
- Callbacks: End with _cb (com_m_rx_cb)
### FreeRTOS-Objects
- x for Basetypes (x_ota_queue, x_status_semaphor)
- v for void return (v_ota_task)
# UART Protokoll # UART Protokoll
## Struktur einer Nachricht ## Struktur einer Nachricht
0xAA = Startbyte - Control Bytes:
checksum = XOR über alle Bytes (ohne Startbyte und Checksum-Byte) - 0xAA = Startbyte
- 0xBB = EscapeByte
- 0xCC = EndByte
## Nachrichtenaufbau (Message Frame) checksum = XOR über alle Bytes (ohne Control Bytes und Checksum-Byte)
[ Startbyte ] [ Length ] [ CommandPage ] [ Payload (variabel) ] [ Checksum ] Command, Payload und Checksum werden Escaped sollten sie einem Control Byte ensteprechend
| Startbyte | Command | Payload (variable) | Checksum | Endbyte |
|-----------|---------|--------------------|----------|---------|
### Felder im Detail: ### Felder im Detail:
- **Length** (`uint8_t`): - **Command** (`uint8_t`):
Gibt die Gesamtlänge der Nachricht **ab `CommandPage` bis einschließlich `Payload`** an. Gibt an, welcher Nachrichtentyp gesendet wird.
- **CommandPage** (`uint8_t`):
Gibt an, welcher Nachrichtentyp oder Befehl gesendet wird.
- **Payload** (`variabel`): - **Payload** (`variabel`):
Datenfeld mit variabler Länge, abhängig vom `CommandPage`. Datenfeld mit variabler Länge, abhängig vom `Command`.
- **Checksum** (`uint8_t`): - **Checksum** (`uint8_t`):
XOR über alle Bytes ab `Length` bis einschließlich `Payload`. XOR über aller Bytes von `Command` und `Payload`.
### Nachrichten von PC zu ESP:
clientid: 0x00 für master, 0xFF für broadcast, ansonsten 0xA0-0xB3 // 19 Clients
### RequestPing 0xE1
Payload: byte: clientid
### RequestInfo 0xE2
Payload: byte: clientid
### RequestRestart 0xE3
Payload: byte: clientid
### PrepareFirmwareUpdate 0xF1
Payload: none
### FirmwareUpdateLine 0xF2
Payload: firmware line 240Bytes MAX
### ExecuteFirmwareUpdate 0xF3
Payload: none
### Nachrichten von ESP zu PC:
### Messages
--- Command:
- UART_ECHO = 0x01
- UART_VERSION = 0x02
- UART_CLIENT_INFO = 0x03
# Roadmap Grundlegend sind alle Zahlenwerte im LittleEndian format!
- [ ] SEND STATUS OF DEVICE OVER UART
- [ ] CONFIGURE PEERS OVER MASTER #### UART_ECHO:
- [ ] SAVE PIN CONFIG ON PEERS
- Send Message: AA 01 01 CC
- Message Received: AA 01 01 CC
Sendet zurück was geschickt wird.
#### UART_VERSION:
| Offset | Länge (Bytes) | Bezeichnung | Beschreibung |
|--------|---------------|-------------|------------------|
| 0 | 2 | Version | Software Version |
| 2 | 7 | BuildHash | Git Hash |
- Send Message: AA 02 02 CC
- Message Received: AA 02 01 00 33 62 35 36 30 37 39 6F CC
| Version | Buildhash |
|---------|-----------|
| 1 | 3b56078 |
Sendet die Version und den Buildhash vom Master zurück.
#### UART_CLIENT_INFO:
Das erste Datenbyte nach dem Commando gibt an wie viele Client Infos in dieser Nachricht vorhanden sind.
Danach teilt sich ein Eintrag wie Folgt auf:
| Offset | Länge (Bytes) | Bezeichnung | Beschreibung |
|--------|---------------|----------------------------|---------------------------------------------------------------|
| 0 | 1 | Client ID | Eindeutige ID des Clients. |
| 1 | 1 | Ist verfügbar | Boolean-Wert (0 = nein, 1 = ja), ob der Client verfügbar ist. |
| 2 | 1 | Slot genutzt | Boolean-Wert (0 = nein, 1 = ja), ob der Slot belegt ist. |
| 3 | 6 | MAC-Adresse | Die Hardware-Adresse des Clients. |
| 9 | 4 | Letzter Ping | Zeit in Millisekunden seit dem letzten Ping. |
| 13 | 4 | Letzter erfolgreicher Ping | Zeit in Millisekunden seit dem letzten erfolgreichen Ping. |
| 17 | 2 | Version | Versionsnummer des Clients. |
##### Ein Client
- Send Message: AA 03 03 CC
- Message Received: AA 03 01 00 01 01 50 78 7D 18 89 F8 34 00 00 00 61 1F 00 00 02 00 76 CC
| Client ID | Verfügbar | Genutzt | MAC-Adresse | Last Ping | Last Successful Ping | Version |
|-----------|-----------|---------|-------------------|-----------|----------------------|---------|
| 0 | 1 | 1 | 50:78:7D:18:89:F8 | 52 | 8033 | 2 |
##### Zwei Clients
- Send Message: AA 03 03 CC
- Message Received: AA 03 02 00 01 01 50 78 7D 18 89 F8 22 00 00 00 F4 2A 01 00 02 00 01 01 01 50 78 7D 18 0C B4 10 00 00 00 F1 2A 01 00 02 00 FE CC
| Client ID | Verfügbar | Genutzt | MAC-Adresse | Last Ping | Last Successful Ping | Version |
|-----------|-----------|---------|-------------------|-----------|----------------------|---------|
| 0 | 1 | 1 | 50:78:7D:18:89:F8 | 34 | 76532 | 2 |
| 1 | 1 | 1 | 50:78:7D:18:C:B4 | 16 | 76529 | 2 |
#### UART_CLIENT_INPUT:
Die Identifizierung wird hier anhand der vorher gesendeten ClientID gemacht also muss einmal vorher `UART_CLIENT_INFO` aufgerufen werden.
Das erste Datenbyte nach dem Commando gibt an wie viele Client Infos in dieser Nachricht vorhanden sind.
Danach teilt sich ein Eintrag wie Folgt auf:
| Offset | Länge (Bytes) | Bezeichnung | Beschreibung |
|--------|---------------|-------------|----------------------------------------------------------------------------------|
| 0 | 1 | Client ID | Eindeutige ID des Clients. |
| 1 | 4 | LageX | Float Wert von der X Lage. |
| 5 | 4 | LageY | Float Wert von der Y Lage. |
| 9 | 4 | InputMaske | Int32 Wert der als Bitmaske genutzt wird um bis zu 32 Boolische Werte anzugeben. |
Inputmaske:
Taster1, Taster2, IOError1, IOErro2, AkkuStand1, AkkuStand2 (2 Bit kodiert für 25%,50%,75%,100%), rest unbelegt, default 0
| Bit1 | Bit2 | Akkustand |
|------|------|-----------|
| 0 | 0 | 25% |
| 0 | 1 | 50% |
| 1 | 0 | 75% |
| 1 | 1 | 100% |
<div style="page-break-after: always;"></div>
# Machbarkeits-Studie # Machbarkeits-Studie
@@ -177,3 +272,55 @@ techn. Anforderungen hinreichend gut umsetzen lassen.
und ob diese den ursprünglichen Anforderungen entsprechen. und ob diese den ursprünglichen Anforderungen entsprechen.
## OTA-Update Technische Umsetzung:
### Vorrausetzung:
- Update File steht bereit und ist unter 2MB groß.
- UART Verbindung steht
- ESP Funktioniert einwandfrei
- ESP Läuft auf Partition A, Partition B soll geupdated werden
### Erste Schritt:
- Update in 200Byte stücke zerhacken und stück für stück per UART an den Master schicken
- Uart Protokol hat schon eine fehlercheck für die Übertragung drinnen
- Firmware wird in Partition B geschrieben
- OTA API Validiert Firmware am ende
#### Hier könnte man schon einen Neustart machen und Validieren ob die Firmware für den Master läuft!
#### Denn angeblich kann man beim ESP die aktuell laufende Partition auslesen
### Zweiter Schritt:
- Master liest in 200Byte stücken die Firmware aus seiner Partition B aus
- Und schickt per Broadcast die ersten 20 Packete an die Clients
- Die clients haben 4KB Buffer vorgesehen wo sie die 20 Packete unterbringen können
- Master Forder Ack Bitmaske an zur Validierung das alle 20 Packete da sind
- Sollten in der Bitmaske zeilen fehlen gibt der Master per unicast die fehlenden Zeilen an die Entsprechend Clients erneut
- ESP NOW kümmert sich hier um die Datenintigrität
- Wenn alle ihre ersten 20 packete haben gibt der master das go und alle schreiben die ersten 20 packete weg.
- Der master aktuallisiert den fortschritt für alle clients -> dann kann man das auch abfragen per uart und hat eine Fortschrittsanzeige
- Alle melden sich zurück wenn sie fertig sind mit dem schreiben per ota und der buffer leer ist.
- Repeat bis alle Daten da sind
Hier hab ich mal grob gerechnet:
2MB in 200Byte Schritten -> 10.000 Packete
10.000 Packete in 20er Schritten -> 500 Sequencen
Retries und Acks mal aussen vor hab ich leider keinen richtigen anhaltspunkt wie lang das dauern kann.
Aber 500* ca 300ms => ist schonmal 150sekunden nur für das acken das die Packete da sind. Annahme hier das die maximal latenz beim Ping mit 16 Clients ca 300ms sind.
Entsprechend mit Daten und retries... ja kp, Gemini schätzt max 10min. Wird sich zeigen. Da addiert sich zu viel auf.
- Alle Clients validieren ihren firmware
- Sollte das bei einem nicht klappen muss man hier nochmal gucken ob man den ganzen process nochmal von vorne anstößt nur mit dem fehlenden client...
### Dritter Schritt:
- Alle Clients rebooten
- Clients geben rückmeldung ob das Update funktioniert
#### Hier müssten war noch entscheiden was passiert wenn das Update bei nur ein paar funktionert hat?
#### Was passiert wenn das Update garnicht funktioniert hat, behält der master dann auch seinen stand?
#### Entsprechend hätte man ihn vorher auch nicht neustarten dürfen
- Sollten alle Clients ihr go geben startet der Master auch neu
#### Wenn das Master Update jetzt fehlschlägt sagt er den clients bescheid und die booten auch wieder um?
Gibt halt noch ein zwar sachen die man sich überlegen muss aber ich denke den rest hab ich soweit ausgearbeitet
+122 -32
View File
@@ -1,6 +1,6 @@
# #
# Automatically generated file. DO NOT EDIT. # Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Configuration # Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration
# #
CONFIG_SOC_ADC_SUPPORTED=y CONFIG_SOC_ADC_SUPPORTED=y
CONFIG_SOC_UART_SUPPORTED=y CONFIG_SOC_UART_SUPPORTED=y
@@ -9,9 +9,11 @@ CONFIG_SOC_PHY_SUPPORTED=y
CONFIG_SOC_WIFI_SUPPORTED=y CONFIG_SOC_WIFI_SUPPORTED=y
CONFIG_SOC_TWAI_SUPPORTED=y CONFIG_SOC_TWAI_SUPPORTED=y
CONFIG_SOC_GDMA_SUPPORTED=y CONFIG_SOC_GDMA_SUPPORTED=y
CONFIG_SOC_UHCI_SUPPORTED=y
CONFIG_SOC_AHB_GDMA_SUPPORTED=y CONFIG_SOC_AHB_GDMA_SUPPORTED=y
CONFIG_SOC_GPTIMER_SUPPORTED=y CONFIG_SOC_GPTIMER_SUPPORTED=y
CONFIG_SOC_LCDCAM_SUPPORTED=y CONFIG_SOC_LCDCAM_SUPPORTED=y
CONFIG_SOC_LCDCAM_CAM_SUPPORTED=y
CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y
CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y
CONFIG_SOC_MCPWM_SUPPORTED=y CONFIG_SOC_MCPWM_SUPPORTED=y
@@ -100,7 +102,7 @@ CONFIG_SOC_CPU_HAS_FPU=y
CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y
CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 CONFIG_SOC_CPU_BREAKPOINTS_NUM=2
CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 CONFIG_SOC_CPU_WATCHPOINTS_NUM=2
CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=64 CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40
CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16 CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16
CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096 CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096
CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16
@@ -211,7 +213,7 @@ CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y
CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y
CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y
CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y
CONFIG_SOC_SDM_GROUPS=y CONFIG_SOC_SDM_GROUPS=1
CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8
CONFIG_SOC_SDM_CLK_SUPPORT_APB=y CONFIG_SOC_SDM_CLK_SUPPORT_APB=y
CONFIG_SOC_SPI_PERIPH_NUM=3 CONFIG_SOC_SPI_PERIPH_NUM=3
@@ -255,7 +257,7 @@ CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32
CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16
CONFIG_SOC_TOUCH_SENSOR_VERSION=2 CONFIG_SOC_TOUCH_SENSOR_VERSION=2
CONFIG_SOC_TOUCH_SENSOR_NUM=15 CONFIG_SOC_TOUCH_SENSOR_NUM=15
CONFIG_SOC_TOUCH_MIN_CHAN_ID=y CONFIG_SOC_TOUCH_MIN_CHAN_ID=1
CONFIG_SOC_TOUCH_MAX_CHAN_ID=14 CONFIG_SOC_TOUCH_MAX_CHAN_ID=14
CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y
CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y
@@ -266,6 +268,7 @@ CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3
CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y
CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1
CONFIG_SOC_TWAI_CONTROLLER_NUM=1 CONFIG_SOC_TWAI_CONTROLLER_NUM=1
CONFIG_SOC_TWAI_MASK_FILTER_NUM=1
CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y
CONFIG_SOC_TWAI_BRP_MIN=2 CONFIG_SOC_TWAI_BRP_MIN=2
CONFIG_SOC_TWAI_BRP_MAX=16384 CONFIG_SOC_TWAI_BRP_MAX=16384
@@ -280,6 +283,7 @@ CONFIG_SOC_UART_SUPPORT_APB_CLK=y
CONFIG_SOC_UART_SUPPORT_RTC_CLK=y CONFIG_SOC_UART_SUPPORT_RTC_CLK=y
CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y
CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y
CONFIG_SOC_UHCI_NUM=1
CONFIG_SOC_USB_OTG_PERIPH_NUM=1 CONFIG_SOC_USB_OTG_PERIPH_NUM=1
CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968
CONFIG_SOC_SHA_SUPPORT_DMA=y CONFIG_SOC_SHA_SUPPORT_DMA=y
@@ -349,7 +353,7 @@ CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y
CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y
CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y
CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y
CONFIG_SOC_SPI_MEM_SUPPORT_OPI_MODE=y CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE=y
CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y
CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y
CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y
@@ -378,6 +382,9 @@ CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y
CONFIG_SOC_BLUFI_SUPPORTED=y CONFIG_SOC_BLUFI_SUPPORTED=y
CONFIG_SOC_ULP_HAS_ADC=y CONFIG_SOC_ULP_HAS_ADC=y
CONFIG_SOC_PHY_COMBO_MODULE=y CONFIG_SOC_PHY_COMBO_MODULE=y
CONFIG_SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV=y
CONFIG_SOC_LCDCAM_CAM_PERIPH_NUM=1
CONFIG_SOC_LCDCAM_CAM_DATA_WIDTH_MAX=16
CONFIG_IDF_CMAKE=y CONFIG_IDF_CMAKE=y
CONFIG_IDF_TOOLCHAIN="gcc" CONFIG_IDF_TOOLCHAIN="gcc"
CONFIG_IDF_TOOLCHAIN_GCC=y CONFIG_IDF_TOOLCHAIN_GCC=y
@@ -418,9 +425,9 @@ CONFIG_BOOTLOADER_PROJECT_VER=1
# end of Application Rollback # end of Application Rollback
# #
# Bootloader Rollback # Recovery Bootloader and Rollback
# #
# end of Bootloader Rollback # end of Recovery Bootloader and Rollback
CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
@@ -436,10 +443,10 @@ CONFIG_BOOTLOADER_LOG_VERSION=1
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y # CONFIG_BOOTLOADER_LOG_LEVEL_INFO is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y
# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set
CONFIG_BOOTLOADER_LOG_LEVEL=3 CONFIG_BOOTLOADER_LOG_LEVEL=4
# #
# Format # Format
@@ -447,6 +454,13 @@ CONFIG_BOOTLOADER_LOG_LEVEL=3
# CONFIG_BOOTLOADER_LOG_COLORS is not set # CONFIG_BOOTLOADER_LOG_COLORS is not set
CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y
# end of Format # end of Format
#
# Settings
#
CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y
CONFIG_BOOTLOADER_LOG_MODE_TEXT=y
# end of Settings
# end of Log # end of Log
# #
@@ -506,6 +520,7 @@ CONFIG_ESP_ROM_HAS_HAL_WDT=y
CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y
CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y
CONFIG_ESP_ROM_HAS_SPI_FLASH=y CONFIG_ESP_ROM_HAS_SPI_FLASH=y
CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y
CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y
CONFIG_ESP_ROM_HAS_NEWLIB=y CONFIG_ESP_ROM_HAS_NEWLIB=y
CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y
@@ -641,6 +656,7 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
# Common Options # Common Options
# #
# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set # CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
# CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED is not set
# end of Common Options # end of Common Options
# end of Bluetooth # end of Bluetooth
@@ -655,11 +671,11 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
# #
# #
# TWAI Configuration # Legacy TWAI Driver Configurations
# #
# CONFIG_TWAI_ISR_IN_IRAM is not set # CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set
CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# end of TWAI Configuration # end of Legacy TWAI Driver Configurations
# #
# Legacy ADC Driver Configuration # Legacy ADC Driver Configuration
@@ -757,6 +773,7 @@ CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set # CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set # CONFIG_ESP_TLS_PSK_VERIFICATION is not set
# CONFIG_ESP_TLS_INSECURE is not set # CONFIG_ESP_TLS_INSECURE is not set
CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
# end of ESP-TLS # end of ESP-TLS
# #
@@ -782,6 +799,12 @@ CONFIG_ESP_COEX_ENABLED=y
CONFIG_ESP_ERR_TO_NAME_LOOKUP=y CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
# end of Common ESP-related # end of Common ESP-related
#
# ESP-Driver:Camera Controller Configurations
#
# CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE is not set
# end of ESP-Driver:Camera Controller Configurations
# #
# ESP-Driver:GPIO Configurations # ESP-Driver:GPIO Configurations
# #
@@ -823,8 +846,10 @@ CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
# #
# ESP-Driver:MCPWM Configurations # ESP-Driver:MCPWM Configurations
# #
# CONFIG_MCPWM_ISR_IRAM_SAFE is not set CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y
# CONFIG_MCPWM_ISR_CACHE_SAFE is not set
# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set # CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set
CONFIG_MCPWM_OBJ_CACHE_SAFE=y
# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set # CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:MCPWM Configurations # end of ESP-Driver:MCPWM Configurations
@@ -839,11 +864,15 @@ CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
# #
# ESP-Driver:RMT Configurations # ESP-Driver:RMT Configurations
# #
CONFIG_RMT_ISR_HANDLER_IN_IRAM=y CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y
CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y
CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y
# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set # CONFIG_RMT_RECV_FUNC_IN_IRAM is not set
# CONFIG_RMT_ISR_CACHE_SAFE is not set # CONFIG_RMT_TX_ISR_CACHE_SAFE is not set
# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set
CONFIG_RMT_OBJ_CACHE_SAFE=y CONFIG_RMT_OBJ_CACHE_SAFE=y
# CONFIG_RMT_ENABLE_DEBUG_LOG is not set # CONFIG_RMT_ENABLE_DEBUG_LOG is not set
# CONFIG_RMT_ISR_IRAM_SAFE is not set
# end of ESP-Driver:RMT Configurations # end of ESP-Driver:RMT Configurations
# #
@@ -877,12 +906,28 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set # CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:Temperature Sensor Configurations # end of ESP-Driver:Temperature Sensor Configurations
#
# ESP-Driver:TWAI Configurations
#
# CONFIG_TWAI_ISR_IN_IRAM is not set
# CONFIG_TWAI_ISR_CACHE_SAFE is not set
# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:TWAI Configurations
# #
# ESP-Driver:UART Configurations # ESP-Driver:UART Configurations
# #
# CONFIG_UART_ISR_IN_IRAM is not set # CONFIG_UART_ISR_IN_IRAM is not set
# end of ESP-Driver:UART Configurations # end of ESP-Driver:UART Configurations
#
# ESP-Driver:UHCI Configurations
#
# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set
# CONFIG_UHCI_ISR_CACHE_SAFE is not set
# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:UHCI Configurations
# #
# ESP-Driver:USB Serial/JTAG Configuration # ESP-Driver:USB Serial/JTAG Configuration
# #
@@ -1032,16 +1077,18 @@ CONFIG_RTC_CLK_CAL_CYCLES=1024
# #
# Peripheral Control # Peripheral Control
# #
# CONFIG_PERIPH_CTRL_FUNC_IN_IRAM is not set CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y
CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y
# end of Peripheral Control # end of Peripheral Control
# #
# GDMA Configurations # GDMA Configurations
# #
CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y
# CONFIG_GDMA_ISR_IRAM_SAFE is not set CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y
CONFIG_GDMA_OBJ_DRAM_SAFE=y CONFIG_GDMA_OBJ_DRAM_SAFE=y
# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set # CONFIG_GDMA_ENABLE_DEBUG_LOG is not set
# CONFIG_GDMA_ISR_IRAM_SAFE is not set
# end of GDMA Configurations # end of GDMA Configurations
# #
@@ -1072,6 +1119,7 @@ CONFIG_ESP_BROWNOUT_USE_INTR=y
# end of Power Supplier # end of Power Supplier
CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
CONFIG_ESP_INTR_IN_IRAM=y
# end of Hardware Settings # end of Hardware Settings
# #
@@ -1123,15 +1171,19 @@ CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
# CONFIG_ESP_PHY_RF_CAL_NONE is not set # CONFIG_ESP_PHY_RF_CAL_NONE is not set
# CONFIG_ESP_PHY_RF_CAL_FULL is not set # CONFIG_ESP_PHY_RF_CAL_FULL is not set
CONFIG_ESP_PHY_CALIBRATION_MODE=0 CONFIG_ESP_PHY_CALIBRATION_MODE=0
CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000
# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set # CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set
# CONFIG_ESP_PHY_RECORD_USED_TIME is not set # CONFIG_ESP_PHY_RECORD_USED_TIME is not set
CONFIG_ESP_PHY_IRAM_OPT=y
# CONFIG_ESP_PHY_DEBUG is not set
# end of PHY # end of PHY
# #
# Power Management # Power Management
# #
CONFIG_PM_SLEEP_FUNC_IN_IRAM=y
# CONFIG_PM_ENABLE is not set # CONFIG_PM_ENABLE is not set
# CONFIG_PM_SLP_IRAM_OPT is not set CONFIG_PM_SLP_IRAM_OPT=y
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y
# end of Power Management # end of Power Management
@@ -1148,6 +1200,12 @@ CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y
# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set # CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set
# end of ESP Ringbuf # end of ESP Ringbuf
#
# ESP-ROM
#
CONFIG_ESP_ROM_PRINT_IN_IRAM=y
# end of ESP-ROM
# #
# ESP Security Specific # ESP Security Specific
# #
@@ -1200,6 +1258,7 @@ CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32
CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0 CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0
# end of Trace memory # end of Trace memory
CONFIG_ESP_SYSTEM_IN_IRAM=y
# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set # CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set # CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
@@ -1254,6 +1313,7 @@ CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y
# #
# IPC (Inter-Processor Call) # IPC (Inter-Processor Call)
# #
CONFIG_ESP_IPC_ENABLE=y
CONFIG_ESP_IPC_TASK_STACK_SIZE=1280 CONFIG_ESP_IPC_TASK_STACK_SIZE=1280
CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y
CONFIG_ESP_IPC_ISR_ENABLE=y CONFIG_ESP_IPC_ISR_ENABLE=y
@@ -1262,6 +1322,7 @@ CONFIG_ESP_IPC_ISR_ENABLE=y
# #
# ESP Timer (High Resolution Timer) # ESP Timer (High Resolution Timer)
# #
CONFIG_ESP_TIMER_IN_IRAM=y
# CONFIG_ESP_TIMER_PROFILING is not set # CONFIG_ESP_TIMER_PROFILING is not set
CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y
CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y
@@ -1304,10 +1365,12 @@ CONFIG_ESP_WIFI_IRAM_OPT=y
CONFIG_ESP_WIFI_RX_IRAM_OPT=y CONFIG_ESP_WIFI_RX_IRAM_OPT=y
CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y
CONFIG_ESP_WIFI_ENABLE_SAE_PK=y CONFIG_ESP_WIFI_ENABLE_SAE_PK=y
CONFIG_ESP_WIFI_ENABLE_SAE_H2E=y
CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y
CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y
# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set # CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set
CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50 CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50
# CONFIG_ESP_WIFI_BSS_MAX_IDLE_SUPPORT is not set
CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10 CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10
CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15 CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15
# CONFIG_ESP_WIFI_FTM_ENABLE is not set # CONFIG_ESP_WIFI_FTM_ENABLE is not set
@@ -1391,6 +1454,14 @@ CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0
# CONFIG_FATFS_IMMEDIATE_FSYNC is not set # CONFIG_FATFS_IMMEDIATE_FSYNC is not set
# CONFIG_FATFS_USE_LABEL is not set # CONFIG_FATFS_USE_LABEL is not set
CONFIG_FATFS_LINK_LOCK=y CONFIG_FATFS_LINK_LOCK=y
# CONFIG_FATFS_USE_DYN_BUFFERS is not set
#
# File system free space calculation behavior
#
CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT=0
CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0
# end of File system free space calculation behavior
# end of FAT Filesystem support # end of FAT Filesystem support
# #
@@ -1461,6 +1532,7 @@ CONFIG_FREERTOS_DEBUG_OCDAWARE=y
CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y
CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
CONFIG_FREERTOS_NUMBER_OF_CORES=2 CONFIG_FREERTOS_NUMBER_OF_CORES=2
CONFIG_FREERTOS_IN_IRAM=y
# end of FreeRTOS # end of FreeRTOS
# #
@@ -1532,6 +1604,15 @@ CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set
# end of Format # end of Format
#
# Settings
#
CONFIG_LOG_MODE_TEXT_EN=y
CONFIG_LOG_MODE_TEXT=y
# end of Settings
CONFIG_LOG_IN_IRAM=y
# end of Log # end of Log
# #
@@ -1575,7 +1656,7 @@ CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set # CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set # CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set
CONFIG_LWIP_DHCP_OPTIONS_LEN=68 CONFIG_LWIP_DHCP_OPTIONS_LEN=69
CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0 CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0
CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1
@@ -1705,6 +1786,9 @@ CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y
CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y
# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set # CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set
# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set # CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set
CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_NONE=y
# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_DEFAULT is not set
# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_CUSTOM is not set
CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y
# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set # CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set
# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set # CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set
@@ -1738,6 +1822,7 @@ CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096
# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set # CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set
# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set # CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set
CONFIG_MBEDTLS_PKCS7_C=y CONFIG_MBEDTLS_PKCS7_C=y
# end of mbedTLS v3.x related # end of mbedTLS v3.x related
@@ -1771,6 +1856,7 @@ CONFIG_MBEDTLS_HAVE_TIME=y
# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set # CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set
# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set
CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y
CONFIG_MBEDTLS_SHA1_C=y
CONFIG_MBEDTLS_SHA512_C=y CONFIG_MBEDTLS_SHA512_C=y
# CONFIG_MBEDTLS_SHA3_C is not set # CONFIG_MBEDTLS_SHA3_C is not set
CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y
@@ -1852,6 +1938,7 @@ CONFIG_MBEDTLS_ECP_NIST_OPTIM=y
# CONFIG_MBEDTLS_THREADING_C is not set # CONFIG_MBEDTLS_THREADING_C is not set
CONFIG_MBEDTLS_ERROR_STRINGS=y CONFIG_MBEDTLS_ERROR_STRINGS=y
CONFIG_MBEDTLS_FS_IO=y CONFIG_MBEDTLS_FS_IO=y
# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set
# end of mbedTLS # end of mbedTLS
# #
@@ -1874,6 +1961,7 @@ CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
# LibC # LibC
# #
CONFIG_LIBC_NEWLIB=y CONFIG_LIBC_NEWLIB=y
CONFIG_LIBC_MISC_IN_IRAM=y
CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y
CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y
# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set # CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set
@@ -1992,13 +2080,13 @@ CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192
# #
# Auto-detect flash chips # Auto-detect flash chips
# #
CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_VENDOR_GD_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_VENDOR_TH_SUPPORTED=y CONFIG_SPI_FLASH_VENDOR_TH_SUPPORT_ENABLED=y
CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y
CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y
CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y
@@ -2081,6 +2169,7 @@ CONFIG_UNITY_ENABLE_DOUBLE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
# CONFIG_UNITY_ENABLE_FIXTURE is not set # CONFIG_UNITY_ENABLE_FIXTURE is not set
# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set # CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set
# CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE is not set
# end of Unity unit testing library # end of Unity unit testing library
# #
@@ -2158,10 +2247,10 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y # CONFIG_LOG_BOOTLOADER_LEVEL_INFO is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG=y
# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
CONFIG_LOG_BOOTLOADER_LEVEL=3 CONFIG_LOG_BOOTLOADER_LEVEL=4
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set # CONFIG_FLASH_ENCRYPTION_ENABLED is not set
# CONFIG_FLASHMODE_QIO is not set # CONFIG_FLASHMODE_QIO is not set
# CONFIG_FLASHMODE_QOUT is not set # CONFIG_FLASHMODE_QOUT is not set
@@ -2188,9 +2277,9 @@ CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
# CONFIG_EXTERNAL_COEX_ENABLE is not set # CONFIG_EXTERNAL_COEX_ENABLE is not set
# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set # CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
# CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE is not set
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set # CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
# CONFIG_MCPWM_ISR_IN_IRAM is not set # CONFIG_MCPWM_ISR_IRAM_SAFE is not set
# CONFIG_RMT_ISR_IRAM_SAFE is not set
# CONFIG_EVENT_LOOP_PROFILING is not set # CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
@@ -2205,6 +2294,7 @@ CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set # CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set
# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set # CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set
CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024 CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024
CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
CONFIG_BROWNOUT_DET=y CONFIG_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_7=y CONFIG_BROWNOUT_DET_LVL_SEL_7=y
+2098
View File
File diff suppressed because it is too large Load Diff
+13 -10
View File
@@ -436,10 +436,10 @@ CONFIG_BOOTLOADER_LOG_VERSION=1
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y # CONFIG_BOOTLOADER_LOG_LEVEL_INFO is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y
# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set
CONFIG_BOOTLOADER_LOG_LEVEL=3 CONFIG_BOOTLOADER_LOG_LEVEL=4
# #
# Format # Format
@@ -548,14 +548,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set
CONFIG_ESPTOOLPY_FLASHFREQ="80m" CONFIG_ESPTOOLPY_FLASHFREQ="80m"
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set
CONFIG_ESPTOOLPY_FLASHSIZE="2MB" CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set # CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set
CONFIG_ESPTOOLPY_BEFORE_RESET=y CONFIG_ESPTOOLPY_BEFORE_RESET=y
# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
@@ -1499,17 +1499,20 @@ CONFIG_LOG_VERSION=1
# #
# Log Level # Log Level
# #
# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set CONFIG_LOG_DEFAULT_LEVEL_NONE=y
# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set
# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set
CONFIG_LOG_DEFAULT_LEVEL_INFO=y # CONFIG_LOG_DEFAULT_LEVEL_INFO is not set
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_DEFAULT_LEVEL=0
CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y
# CONFIG_LOG_MAXIMUM_LEVEL_ERROR is not set
# CONFIG_LOG_MAXIMUM_LEVEL_WARN is not set
# CONFIG_LOG_MAXIMUM_LEVEL_INFO is not set
# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set # CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set
# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set # CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set
CONFIG_LOG_MAXIMUM_LEVEL=3 CONFIG_LOG_MAXIMUM_LEVEL=0
# #
# Level Settings # Level Settings
+2357
View File
File diff suppressed because it is too large Load Diff
-248
View File
@@ -1,248 +0,0 @@
import queue # Zum sicheren Datenaustausch zwischen Threads
import serial
import time
import threading
import sys
from parser import UartMessageParser, ParserError
from message_builder import MessageBuilder, MessageBuilderError, PayloadTooLargeError, BufferOverflowError
import payload_parser
from rich.console import Console
from rich.table import Table
SERIAL_PORT = "/dev/ttyUSB0"
BAUDRATE = 115200
WRITE_TIMEOUT = 1.5
READ_TIMEOUT = 2.0
payload_parser = payload_parser.PayloadParser()
def on_message_received_from_uart(parsed_message):
print(f"[CALLBACK] Nachricht empfangen: MSGID=0x{
parsed_message.msgid:02X}, Length={parsed_message.payload_len}")
received_message_queue.put(parsed_message)
def on_message_fail_from_uart(error_message):
print(f"[CALLBACK] Fehler beim Parsen: {error_message}")
class ParsedMessage:
def __init__(self, msgid, payload_len):
self.msgid = msgid
self.payload_len = payload_len
received_message_queue = queue.Queue()
class SerialReader(threading.Thread):
# Ändere den Konstruktor, um eine bereits geöffnete serielle Instanz zu akzeptieren
def __init__(self, ser_instance, read_timeout, parser):
super().__init__()
# Speichere die übergebene serielle Instanz
self.ser = ser_instance
self.read_timeout = read_timeout
self.parser = parser
self.running = False
self.daemon = True # Thread beendet sich mit dem Hauptprogramm
def run(self):
# Überprüfe, ob die serielle Schnittstelle wirklich offen ist, bevor du beginnst
if not self.ser or not self.ser.is_open:
print(
f"[{self.name}] Fehler: Serielle Schnittstelle ist nicht geöffnet.")
return
print(f"[{self.name}] Lese-Thread gestartet. Überwache {self.ser.port}...")
self.ser.timeout = self.read_timeout # Setze den Timeout für byteweises Lesen
self.running = True
while self.running:
try:
byte = self.ser.read(1)
if byte:
self.parser.parse_byte(byte[0])
else:
pass # Timeout, kein Byte verfügbar, Thread läuft weiter
except serial.SerialException as e:
print(f"[{self.name}] Lesefehler: {e}")
self.running = False
except Exception as e:
print(f"[{self.name}] Unerwarteter Fehler im Lese-Thread: {e}")
self.running = False
# Der Thread schließt den Port NICHT mehr, das ist Aufgabe des Hauptprogramms.
print(f"[{self.name}] Lese-Thread beendet.")
def stop(self):
self.running = False
print(f"[{self.name}] Lese-Thread wird beendet...")
def on_message_received_from_uart(message_id: int, payload: bytes, payload_length: int):
"""
Callback-Funktion, die aufgerufen wird, wenn der Parser eine vollständige,
gültige Nachricht empfangen hat.
"""
print(f"\n[MAIN] Nachricht erfolgreich empfangen! ID: 0x{
message_id:02X}")
print(f"[MAIN] Payload ({payload_length} Bytes): {
payload[:payload_length].hex().upper()}")
parsed_object = payload_parser.parse_payload(
message_id, payload[:payload_length])
if message_id == 0x04:
print(parsed_object)
table = Table(title="Clients")
columns = ["ClientId", "IsAvailable",
"IsSlotUsed", "MAC", "LastPing", "LastSuccesfullPing"]
rows = []
for x in parsed_object.clients:
mac_string = ':'.join(f'{byte:02x}' for byte in x.mac_address)
rows.append([str(x.client_id), str(x.is_available), str(x.is_slot_used), mac_string,
str(x.last_ping), str(x.last_successfull_ping)])
for column in columns:
table.add_column(column)
for row in rows:
table.add_row(*row, style='bright_green')
console = Console()
console.print(table)
def on_message_fail_from_uart(message_id: int, current_message_buffer: bytes,
current_index: int, error_type: ParserError):
"""
Callback-Funktion, die aufgerufen wird, wenn der Parser einen Fehler
beim Empfang einer Nachricht feststellt.
"""
print(f"\n[MAIN] Fehler beim Parsen der Nachricht! ID: 0x{
message_id:02X}")
print(f"[MAIN] Fehler: {error_type.name}")
print(f"[MAIN] Bisheriger Puffer ({current_index} Bytes): {
current_message_buffer[:current_index].hex().upper()}")
def run_uart_test():
"""
Führt den UART-Test durch: Sendet eine Nachricht und liest alle Antworten.
"""
ser = None
parser = UartMessageParser(
on_message_received_callback=on_message_received_from_uart,
on_message_fail_callback=on_message_fail_from_uart
)
message_builder = MessageBuilder()
try:
ser = serial.Serial(
port=SERIAL_PORT,
baudrate=BAUDRATE,
timeout=READ_TIMEOUT,
write_timeout=WRITE_TIMEOUT
)
print(f"Serielle Schnittstelle {
SERIAL_PORT} mit Baudrate {BAUDRATE} geöffnet.")
reader_thread = SerialReader(
ser_instance=ser,
read_timeout=10,
parser=parser
)
reader_thread.start() # Starte den Lese-Thread
while not reader_thread.running:
time.sleep(0.1)
print("\n--- UART Testkonsole ---")
print("Gib eine Zahl (1-10) ein, um eine Nachricht zu senden.")
print("Gib 'q' oder 'exit' ein, um das Programm zu beenden.")
while True:
# Warte auf Benutzereingabe
user_input = sys.stdin.readline().strip().lower()
if user_input in ('q', 'exit'):
break
try:
choice = int(user_input)
if choice in MESSAGES:
msg_info = MESSAGES[choice]
print(f"\n[MAIN] Sende Nachricht für Option {
choice} (MSGID: 0x{msg_info['msg_id']:02X})...")
try:
message_to_send = message_builder.build_message(
msg_info["msg_id"],
msg_info["payload"],
255 # Max Payload Length
)
print(f"[MAIN] Gebaute Nachricht zum Senden: {
message_to_send.hex().upper()}")
bytes_written = ser.write(message_to_send)
print(f"[MAIN] {bytes_written} Bytes gesendet.")
except (PayloadTooLargeError, BufferOverflowError) as e:
print(f"[MAIN] Fehler beim Bauen der Nachricht: {e}")
except Exception as e:
print(
f"[MAIN] Ein unerwarteter Fehler beim Senden der Nachricht ist aufgetreten: {e}")
else:
print(
"Ungültige Option. Bitte gib eine Zahl zwischen 1 und 10 ein.")
except ValueError:
print("Ungültige Eingabe. Bitte gib eine Zahl oder 'q' ein.")
except Exception as e:
print(
f"[MAIN] Ein unerwarteter Fehler bei der Eingabeverarbeitung ist aufgetreten: {e}")
# Verarbeite empfangene Nachrichten, die sich in der Queue angesammelt haben
while not received_message_queue.empty():
msg = received_message_queue.get()
print(
f" > [MAIN-Loop] Verarbeitet: MSGID=0x{msg.msgid:02X}, Length={msg.payload_len}")
received_message_queue.task_done()
except serial.SerialException as e:
print(f"Fehler beim Zugriff auf die serielle Schnittstelle: {e}")
print(f"Stelle sicher, dass '{
SERIAL_PORT}' der korrekte Port ist und nicht von einer anderen Anwendung verwendet wird.")
except KeyboardInterrupt:
print("\n[MAIN] Test durch Benutzer abgebrochen (Ctrl+C).")
except Exception as e:
print(f"Ein unerwarteter Fehler im Hauptprogramm ist aufgetreten: {e}")
finally:
if 'reader_thread' in locals() and reader_thread.is_alive():
reader_thread.stop()
reader_thread.join(timeout=5)
if reader_thread.is_alive():
print(
"[MAIN] Warnung: Lese-Thread konnte nicht sauber beendet werden.")
if ser and ser.is_open:
ser.close()
print("Serielle Schnittstelle geschlossen.")
print("[MAIN] Programm beendet.")
# Nachrichten-Mapping
MESSAGES = {
1: {"msg_id": 0x01, "payload": b"Echo Message 1"},
2: {"msg_id": 0x02, "payload": b"Version Request"},
3: {"msg_id": 0x03, "payload": b"Client Info Request"},
4: {"msg_id": 0x04, "payload": b"Custom Data 4"},
5: {"msg_id": 0x05, "payload": b"Custom Data 5"},
6: {"msg_id": 0x06, "payload": b"Custom Data 6"},
7: {"msg_id": 0x07, "payload": b"Custom Data 7"},
8: {"msg_id": 0x08, "payload": b"Custom Data 8"},
9: {"msg_id": 0x09, "payload": b"Custom Data 9"},
10: {"msg_id": 0x0A, "payload": b"Custom Data 10 - Last One!"},
}
# Führe den Test aus
if __name__ == "__main__":
run_uart_test()
-115
View File
@@ -1,115 +0,0 @@
import enum
START_BYTE = 0xAA
ESCAPE_BYTE = 0xBB
END_BYTE = 0xCC
class MessageBuilderError(Exception):
"""Basisklasse für Fehler des Message Builders."""
pass
class PayloadTooLargeError(MessageBuilderError):
"""Ausnahme, wenn der Payload zu groß für den Puffer ist."""
def __init__(self, required_size, buffer_size):
super().__init__(f"Payload ({
required_size} bytes) ist größer als der verfügbare Puffer ({buffer_size} bytes).")
self.required_size = required_size
self.buffer_size = buffer_size
class BufferOverflowError(MessageBuilderError):
"""Ausnahme, wenn der Puffer während des Bauens überläuft."""
def __init__(self, current_size, max_size, byte_to_add=None):
msg = f"Pufferüberlauf: Aktuelle Größe {
current_size}, Max. Größe {max_size}."
if byte_to_add is not None:
msg += f" Versuch, Byte 0x{byte_to_add:02X} hinzuzufügen."
super().__init__(msg)
self.current_size = current_size
self.max_size = max_size
self.byte_to_add = byte_to_add
class MessageBuilder:
"""
Klasse zum Aufbau von UART-Nachrichten gemäß dem definierten Protokoll,
inklusive Stuffing und Checksummenberechnung.
"""
def __init__(self):
pass
def _needs_stuffing_byte(self, byte: int) -> bool:
"""
Prüft, ob ein Byte ein Stuffing-Byte benötigt (d.h. ob es ein Steuerbyte ist).
"""
return (byte == START_BYTE or byte == ESCAPE_BYTE or byte == END_BYTE)
def _add_byte_with_length_check(self, byte: int, buffer: bytearray, max_length: int):
"""
Fügt ein Byte zum Puffer hinzu und prüft auf Pufferüberlauf.
Löst BufferOverflowError aus, wenn der Puffer voll ist.
"""
if len(buffer) >= max_length:
raise BufferOverflowError(len(buffer), max_length, byte)
buffer.append(byte)
def build_message(self, msgid: int, payload: bytes, msg_buffer_size: int) -> bytes:
"""
Baut eine vollständige UART-Nachricht.
Args:
msgid (int): Die Message ID (0-255).
payload (bytes): Die Nutzdaten der Nachricht als Byte-Objekt.
msg_buffer_size (int): Die maximale Größe des Ausgabepuffers.
Dies ist die maximale Länge der *fertigen* Nachricht.
Returns:
bytes: Die fertig aufgebaute Nachricht als Byte-Objekt.
Raises:
PayloadTooLargeError: Wenn der Payload (mit Overhead) den Puffer überschreiten würde.
BufferOverflowError: Wenn während des Bauens ein Pufferüberlauf auftritt.
"""
if len(payload) + 4 > msg_buffer_size:
raise PayloadTooLargeError(len(payload) + 4, msg_buffer_size)
checksum = 0
msg_buffer = bytearray()
# 1. StartByte hinzufügen
self._add_byte_with_length_check(
START_BYTE, msg_buffer, msg_buffer_size)
# 2. Message ID hinzufügen (mit Stuffing)
if self._needs_stuffing_byte(msgid):
self._add_byte_with_length_check(
ESCAPE_BYTE, msg_buffer, msg_buffer_size)
self._add_byte_with_length_check(msgid, msg_buffer, msg_buffer_size)
checksum ^= msgid
# 3. Payload-Bytes hinzufügen (mit Stuffing)
for byte_val in payload:
if self._needs_stuffing_byte(byte_val):
self._add_byte_with_length_check(
ESCAPE_BYTE, msg_buffer, msg_buffer_size)
self._add_byte_with_length_check(
byte_val, msg_buffer, msg_buffer_size)
checksum ^= byte_val
# 4. Checksumme hinzufügen (mit Stuffing)
if self._needs_stuffing_byte(checksum):
self._add_byte_with_length_check(
ESCAPE_BYTE, msg_buffer, msg_buffer_size)
self._add_byte_with_length_check(checksum, msg_buffer, msg_buffer_size)
# 5. EndByte hinzufügen
self._add_byte_with_length_check(END_BYTE, msg_buffer, msg_buffer_size)
# Konvertiere bytearray zu unveränderlichem bytes-Objekt
return bytes(msg_buffer)
-170
View File
@@ -1,170 +0,0 @@
import enum
# --- Konstanten für das UART-Protokoll ---
# Diese Werte müssen mit denen auf deinem Embedded-System übereinstimmen
START_BYTE = 0xAA
END_BYTE = 0xCC
ESCAPE_BYTE = 0x7D # Beispielwert, bitte an dein Protokoll anpassen
MAX_PAYLOAD_LENGTH = 255 # Maximale Länge des Nachrichten-Payloads (ohne Message ID und Checksumme)
# MAX_TOTAL_CONTENT_LENGTH in C beinhaltet Message ID, Payload und Checksumme.
# Hier definieren wir MAX_PAYLOAD_LENGTH, da der Parser den Payload sammelt.
# Die Gesamtgröße des empfangenen Puffers (message + checksum) darf MAX_PAYLOAD_LENGTH + 1 nicht überschreiten,
# da die Checksumme als letztes Byte des Payloads behandelt wird.
# --- Enumerationen für Parser-Zustände und Fehler ---
class ParserState(enum.Enum):
WAITING_FOR_START_BYTE = 0
GET_MESSAGE_TYPE = 1
ESCAPED_MESSAGE_TYPE = 2
IN_PAYLOAD = 3
ESCAPE_PAYLOAD_BYTE = 4
class ParserError(enum.Enum):
NO_ERROR = 0
UNEXPECTED_COMMAND_BYTE = 1
WRONG_CHECKSUM = 2
MESSAGE_TOO_LONG = 3
class UartMessageParser:
"""
Ein State-Machine-Parser für UART-Nachrichten basierend auf der bereitgestellten C-Logik.
Nachrichtenformat (angenommen):
[START_BYTE] [MESSAGE_ID] [PAYLOAD_BYTES...] [CHECKSUM_BYTE] [END_BYTE]
Escape-Sequenzen:
Wenn START_BYTE, END_BYTE oder ESCAPE_BYTE im MESSAGE_ID oder PAYLOAD vorkommen,
werden sie durch ESCAPE_BYTE gefolgt vom ursprünglichen Byte (nicht XORed) ersetzt.
Die Checksumme wird über die unescaped Bytes berechnet.
"""
def __init__(self, on_message_received_callback=None, on_message_fail_callback=None):
"""
Initialisiert den UART-Nachrichten-Parser.
Args:
on_message_received_callback (callable, optional): Eine Funktion, die aufgerufen wird,
wenn eine gültige Nachricht empfangen wurde.
Signatur: on_message_received(message_id: int, payload: bytes, payload_length: int)
on_message_fail_callback (callable, optional): Eine Funktion, die aufgerufen wird,
wenn ein Nachrichtenfehler auftritt.
Signatur: on_message_fail(message_id: int, current_message_buffer: bytes,
current_index: int, error_type: ParserError)
"""
self.state = ParserState.WAITING_FOR_START_BYTE
self.index = 0
self.checksum = 0
self.message_id = 0
self.message_buffer = bytearray(MAX_PAYLOAD_LENGTH + 1) # +1 für Checksummen-Byte
self.error = ParserError.NO_ERROR
# Callbacks für die Anwendung. Standardmäßig None oder einfache Print-Funktionen.
self.on_message_received = on_message_received_callback if on_message_received_callback else self._default_on_message_received
self.on_message_fail = on_message_fail_callback if on_message_fail_callback else self._default_on_message_fail
def _default_on_message_received(self, message_id, payload, payload_length):
"""Standard-Callback für empfangene Nachrichten, falls keiner angegeben ist."""
print(f"Parser: Nachricht empfangen! ID: 0x{message_id:02X}, "
f"Payload ({payload_length} Bytes): {payload[:payload_length].hex().upper()}")
def _default_on_message_fail(self, message_id, current_message_buffer, current_index, error_type):
"""Standard-Callback für Nachrichtenfehler, falls keiner angegeben ist."""
print(f"Parser: Fehler bei Nachricht! ID: 0x{message_id:02X}, "
f"Fehler: {error_type.name}, "
f"Bisheriger Puffer ({current_index} Bytes): {current_message_buffer[:current_index].hex().upper()}")
def parse_byte(self, pbyte: int):
"""
Verarbeitet ein einzelnes empfangenes Byte.
Args:
pbyte (int): Das empfangene Byte (0-255).
"""
# Sicherstellen, dass pbyte ein Integer im Bereich 0-255 ist
if not isinstance(pbyte, int) or not (0 <= pbyte <= 255):
print(f"Parser: Ungültiges Byte empfangen: {pbyte}. Muss ein Integer von 0-255 sein.")
return
current_state = self.state # Für bessere Lesbarkeit
if current_state == ParserState.WAITING_FOR_START_BYTE:
if pbyte == START_BYTE:
self.index = 0
self.checksum = 0
self.message_id = 0 # Reset message_id
self.error = ParserError.NO_ERROR # Reset error
self.state = ParserState.GET_MESSAGE_TYPE
# Andernfalls ignorieren wir Bytes, bis ein Start-Byte gefunden wird
elif current_state == ParserState.ESCAPED_MESSAGE_TYPE:
self.message_id = pbyte
self.checksum ^= pbyte
self.state = ParserState.IN_PAYLOAD
elif current_state == ParserState.GET_MESSAGE_TYPE:
if pbyte == ESCAPE_BYTE:
self.state = ParserState.ESCAPED_MESSAGE_TYPE
return # Dieses Byte wurde als Escape-Sequenz verarbeitet, nicht zum Payload hinzufügen
if pbyte == START_BYTE or pbyte == END_BYTE:
self.state = ParserState.WAITING_FOR_START_BYTE
self.error = ParserError.UNEXPECTED_COMMAND_BYTE
self.on_message_fail(self.message_id, self.message_buffer, self.index, self.error)
return
self.message_id = pbyte
self.checksum ^= pbyte
self.state = ParserState.IN_PAYLOAD
elif current_state == ParserState.ESCAPE_PAYLOAD_BYTE:
# Das escapte Byte ist Teil des Payloads
if self.index < MAX_PAYLOAD_LENGTH + 1: # +1 für Checksummen-Byte
self.message_buffer[self.index] = pbyte
self.index += 1
self.checksum ^= pbyte
self.state = ParserState.IN_PAYLOAD
else:
self.state = ParserState.WAITING_FOR_START_BYTE
self.error = ParserError.MESSAGE_TOO_LONG
self.on_message_fail(self.message_id, self.message_buffer, self.index, self.error)
return
elif current_state == ParserState.IN_PAYLOAD:
if pbyte == ESCAPE_BYTE:
self.state = ParserState.ESCAPE_PAYLOAD_BYTE
return # Dieses Byte wurde als Escape-Sequenz verarbeitet
if pbyte == START_BYTE:
self.state = ParserState.WAITING_FOR_START_BYTE
self.error = ParserError.UNEXPECTED_COMMAND_BYTE
self.on_message_fail(self.message_id, self.message_buffer, self.index, self.error)
return
if pbyte == END_BYTE:
if self.checksum != 0x00:
# Checksummenfehler: Die Checksumme wurde bis zum End-Byte XORed.
# Wenn die empfangene Checksumme korrekt war, sollte das Ergebnis 0 sein.
self.state = ParserState.WAITING_FOR_START_BYTE
self.error = ParserError.WRONG_CHECKSUM
self.on_message_fail(self.message_id, self.message_buffer, self.index, self.error)
return
# Erfolgreich empfangen! Die Checksumme ist das letzte Byte im Puffer.
# Die Länge des Payloads ist index - 1 (da das letzte Byte die Checksumme war).
payload_length = self.index - 1
if payload_length < 0: # Falls nur Message ID und Checksumme, aber kein Payload
payload_length = 0
self.on_message_received(self.message_id, self.message_buffer, payload_length)
self.state = ParserState.WAITING_FOR_START_BYTE
return # EndByte wurde verarbeitet, nicht zum Payload hinzufügen
# Normales Payload-Byte
if self.index < MAX_PAYLOAD_LENGTH + 1: # +1 für Checksummen-Byte
self.message_buffer[self.index] = pbyte
self.index += 1
self.checksum ^= pbyte
else:
# Nachricht zu lang
self.state = ParserState.WAITING_FOR_START_BYTE
self.error = ParserError.MESSAGE_TOO_LONG
self.on_message_fail(self.message_id, self.message_buffer, self.index, self.error)
return
-192
View File
@@ -1,192 +0,0 @@
import dataclasses
import struct
from typing import Optional, Union, List
@dataclasses.dataclass
class StatusMessage:
"""
Repräsentiert eine Status-Nachricht (z.B. Message ID 0x01).
Payload-Format: [status_code: uint8], [battery_level: uint8], [uptime_seconds: uint16]
"""
status_code: int
battery_level: int # 0-100%
uptime_seconds: int
@dataclasses.dataclass
class SensorDataMessage:
"""
Repräsentiert eine Sensor-Daten-Nachricht (z.B. Message ID 0x02).
Payload-Format: [temperature_celsius: int16], [humidity_percent: uint16]
"""
temperature_celsius: int # signed short
humidity_percent: int # unsigned short
@dataclasses.dataclass
class ClientEntry:
"""
Repräsentiert die Informationen für einen einzelnen Client innerhalb der ClientInfoMessage.
Payload-Format:
[client_id: uint8]
[is_available: uint8] (0=false, >0=true)
[is_slot_used: uint8] (0=false, >0=true)
[mac_address: bytes (6)]
[unoccupied_value_1: uint32]
[unoccupied_value_2: uint32]
Gesamt: 1 + 1 + 1 + 6 + 4 + 4 = 17 Bytes pro Eintrag.
"""
client_id: int
is_available: bool
is_slot_used: bool
mac_address: bytes # 6 Bytes MAC-Adresse
last_ping: int # 4 Bytes, unbelegt
last_successfull_ping: int # 4 Bytes, unbelegt
@dataclasses.dataclass
class ClientInfoMessage:
"""
Repräsentiert eine Nachricht mit Client-Informationen (Message ID 0x03).
Payload-Format:
[num_clients: uint8]
[client_entry_1: ClientEntry]
[client_entry_2: ClientEntry]
...
"""
num_clients: int
clients: List[ClientEntry]
@dataclasses.dataclass
class UnknownMessage:
"""
Repräsentiert eine Nachricht mit unbekannter ID oder fehlerhaftem Payload.
"""
message_id: int
raw_payload: bytes
error_message: str
# --- Payload Parser Klasse ---
class PayloadParser:
"""
Interpretiert den Payload einer UART-Nachricht basierend auf ihrer Message ID
und wandelt ihn in ein strukturiertes Python-Objekt (dataclass) um.
"""
def __init__(self):
# Ein Dictionary, das Message IDs auf ihre entsprechenden Parsing-Funktionen abbildet.
self._parser_map = {
0x01: self._parse_status_message,
0x02: self._parse_sensor_data_message,
# Aktualisiert für die neue 0x03 Struktur
0x03: self._parse_client_info_message,
0x04: self._parse_client_info_message,
# Füge hier weitere Message IDs und ihre Parsing-Funktionen hinzu
}
def _parse_status_message(self, payload: bytes) -> Union[StatusMessage, UnknownMessage]:
"""Parsen des Payloads für Message ID 0x01 (StatusMessage)."""
# Erwartetes Format: 1 Byte Status, 1 Byte Battery, 2 Bytes Uptime (Little-Endian)
if len(payload) != 4:
return UnknownMessage(0x01, payload, f"Falsche Payload-Länge für StatusMessage: Erwartet 4, Got {len(payload)}")
try:
# '<BBH' bedeutet: Little-Endian, Byte (unsigned char), Byte (unsigned char), Half-word (unsigned short)
status_code, battery_level, uptime_seconds = struct.unpack(
'<BBH', payload)
return StatusMessage(status_code, battery_level, uptime_seconds)
except struct.error as e:
return UnknownMessage(0x01, payload, f"Fehler beim Entpacken der StatusMessage: {e}")
def _parse_sensor_data_message(self, payload: bytes) -> Union[SensorDataMessage, UnknownMessage]:
"""Parsen des Payloads für Message ID 0x02 (SensorDataMessage)."""
# Erwartetes Format: 2 Bytes Temperatur (signed short), 2 Bytes Feuchtigkeit (unsigned short) (Little-Endian)
if len(payload) != 4:
return UnknownMessage(0x02, payload, f"Falsche Payload-Länge für SensorDataMessage: Erwartet 4, Got {len(payload)}")
try:
# '<hH' bedeutet: Little-Endian, short (signed), unsigned short
temperature_celsius, humidity_percent = struct.unpack(
'<hH', payload)
return SensorDataMessage(temperature_celsius, humidity_percent)
except struct.error as e:
return UnknownMessage(0x02, payload, f"Fehler beim Entpacken der SensorDataMessage: {e}")
def _parse_client_info_message(self, payload: bytes) -> Union[ClientInfoMessage, UnknownMessage]:
"""Parsen des Payloads für Message ID 0x03 (ClientInfoMessage)."""
if not payload:
# Wenn der Payload leer ist, aber num_clients erwartet wird, ist das ein Fehler
return UnknownMessage(0x03, payload, "Payload für ClientInfoMessage ist leer, aber num_clients erwartet.")
try:
# Das erste Byte ist die Anzahl der Clients
num_clients = payload[0]
# Die restlichen Bytes sind die Client-Einträge
client_data_bytes = payload[1:]
# 1 (ID) + 1 (Avail) + 1 (Used) + 6 (MAC) + 4 (Val1) + 4 (Val2)
EXPECTED_CLIENT_ENTRY_SIZE = 17
if len(client_data_bytes) != num_clients * EXPECTED_CLIENT_ENTRY_SIZE:
return UnknownMessage(0x03, payload,
f"Falsche Payload-Länge für Client-Einträge: Erwartet {
num_clients * EXPECTED_CLIENT_ENTRY_SIZE}, "
f"Got {len(client_data_bytes)} nach num_clients.")
clients_list: List[ClientEntry] = []
# Formatstring für einen Client-Eintrag:
# < : Little-Endian
# B : uint8 (client_id, is_available, is_slot_used)
# 6s: 6 Bytes (mac_address)
# I : uint32 (unoccupied_value_1, unoccupied_value_2)
CLIENT_ENTRY_FORMAT = '<BBB6sII'
for i in range(num_clients):
start_index = i * EXPECTED_CLIENT_ENTRY_SIZE
end_index = start_index + EXPECTED_CLIENT_ENTRY_SIZE
entry_bytes = client_data_bytes[start_index:end_index]
# Entpacke die Daten für einen Client-Eintrag
client_id, is_available_byte, is_slot_used_byte, mac_address, val1, val2 = \
struct.unpack(CLIENT_ENTRY_FORMAT, entry_bytes)
# Konvertiere 0/1 Bytes zu boolschen Werten
is_available = bool(is_available_byte)
is_slot_used = bool(is_slot_used_byte)
clients_list.append(ClientEntry(
client_id=client_id,
is_available=is_available,
is_slot_used=is_slot_used,
mac_address=mac_address,
last_ping=val1,
last_successfull_ping=val2
))
return ClientInfoMessage(num_clients=num_clients, clients=clients_list)
except struct.error as e:
return UnknownMessage(0x03, payload, f"Fehler beim Entpacken der ClientInfoMessage-Einträge: {e}")
except Exception as e:
return UnknownMessage(0x03, payload, f"Unerwarteter Fehler beim Parsen der ClientInfoMessage: {e}")
def parse_payload(self, message_id: int, payload: bytes) -> Union[StatusMessage, SensorDataMessage, ClientInfoMessage, UnknownMessage]:
"""
Interpretiert den gegebenen Payload basierend auf der Message ID.
Args:
message_id (int): Die ID der Nachricht.
payload (bytes): Die rohen Nutzdaten der Nachricht.
Returns:
Union[StatusMessage, SensorDataMessage, ClientInfoMessage, UnknownMessage]:
Ein dataclass-Objekt, das die dekodierten Daten repräsentiert,
oder ein UnknownMessage-Objekt bei unbekannter ID oder Parsing-Fehler.
"""
parser_func = self._parser_map.get(message_id)
if parser_func:
return parser_func(payload)
else:
return UnknownMessage(message_id, payload, "Unbekannte Message ID.")