Initial Comit

This commit is contained in:
2025-06-12 10:04:45 +02:00
commit abffaf43ec
28 changed files with 14670 additions and 0 deletions

1828
include/boot.h Normal file

File diff suppressed because it is too large Load Diff

91
include/configs/lwipopts.h Executable file
View File

@ -0,0 +1,91 @@
#ifndef _LWIPOPTS_H
#define _LWIPOPTS_H
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#ifndef MEM_SIZE
#define MEM_SIZE 16384 // 4000
#endif
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS) // (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 1
#define LWIP_IPV4 1
#define LWIP_TCP 1
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 1
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#define PBUF_DEBUG LWIP_DBG_OFF
#define API_LIB_DEBUG LWIP_DBG_OFF
#define API_MSG_DEBUG LWIP_DBG_OFF
#define SOCKETS_DEBUG LWIP_DBG_OFF
#define ICMP_DEBUG LWIP_DBG_OFF
#define INET_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#define IP_REASS_DEBUG LWIP_DBG_OFF
#define RAW_DEBUG LWIP_DBG_OFF
#define MEM_DEBUG LWIP_DBG_OFF
#define MEMP_DEBUG LWIP_DBG_OFF
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_OFF
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#define TCP_CWND_DEBUG LWIP_DBG_OFF
#define TCP_WND_DEBUG LWIP_DBG_OFF
#define TCP_FR_DEBUG LWIP_DBG_OFF
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#define TCP_RST_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_OFF
#define TCPIP_DEBUG LWIP_DBG_OFF
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
#endif /* __LWIPOPTS_H__ */

3083
include/font.hpp Normal file

File diff suppressed because it is too large Load Diff

460
include/server.h Executable file
View File

@ -0,0 +1,460 @@
#include <stdlib.h>
#include <string.h>
#include "bme280.h" // Deine BME280 Sensor-Bibliothek
#include "hardware/i2c.h" // Nötig für I2C-Definitionen, falls bme280.h sie nicht enthält
#include "lwip/pbuf.h"
#include "lwip/tcp.h"
#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"
#define TCP_PORT 80
#define DEBUG_printf printf
#define BUF_SIZE 8192 * 4 // Kann angepasst werden für größere HTML/JSON
#define POLL_TIME_S 5
static uint64_t __tcp_errors = 0;
// Deklariere den BME280 Kontext als externe globale Variable.
// Dieser wird in main.cpp initialisiert und hier verwendet.
extern bme280_ctx *ctx; // WICHTIG: Nutzt jetzt denselben Namen wie in main.cpp
struct data {
float t;
float h;
};
extern struct data _list[1000];
extern int list_index;
extern int list_count;
const char *html_page = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Dashboard oder so</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
background: #f4f6f8;
font-family: 'Montserrat', sans-serif;
padding: 20px;
color: #333;
}
h1, h2 {
font-weight: 600;
text-align: center;
}
.chart-container {
width: 100%;
max-width: 800px;
margin: 20px auto;
background: #fff;
padding: 20px;
border-radius: 12px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.live-data {
display: flex;
justify-content: center;
gap: 30px;
font-size: 1.5em;
margin-top: 10px;
}
canvas {
width: 100% !important;
height: 300px !important;
}
</style>
</head>
<body>
<h1>Dashboard oder so</h1>
<div class="chart-container live-data">
<div>🌡 Temp: <span id="liveTemp">--</span> °C</div>
<div>💧 Humidity: <span id="liveHumidity">--</span> %</div>
</div>
<div class="chart-container">
<h2>Temperature History</h2>
<canvas id="tempChart"></canvas>
</div>
<div class="chart-container">
<h2>Humidity History</h2>
<canvas id="humChart"></canvas>
</div>
<script>
let tempChart, humChart;
function initCharts() {
const tempCtx = document.getElementById('tempChart').getContext('2d');
const humCtx = document.getElementById('humChart').getContext('2d');
const options = {
type: 'line',
options: {
responsive: true,
animation: false,
scales: {
x: {
title: { display: true, text: 'Time' },
ticks: { autoSkip: true, maxTicksLimit: 10 }
},
y: { beginAtZero: false }
}
}
};
tempChart = new Chart(tempCtx, {
...options,
data: {
labels: [],
datasets: [{
label: 'Temperature (°C)',
data: [],
borderColor: '#e74c3c',
backgroundColor: 'rgba(231, 76, 60, 0.2)',
tension: 0.3,
fill: true,
pointRadius: 2
}]
}
});
humChart = new Chart(humCtx, {
...options,
data: {
labels: [],
datasets: [{
label: 'Humidity (%)',
data: [],
borderColor: '#3498db',
backgroundColor: 'rgba(52, 152, 219, 0.2)',
tension: 0.3,
fill: true,
pointRadius: 2
}]
}
});
}
function updateLiveData() {
fetch('/data')
.then(res => res.json())
.then(data => {
document.getElementById('liveTemp').textContent = data.temp.toFixed(1);
document.getElementById('liveHumidity').textContent = data.humidity.toFixed(1);
});
}
function updateCharts() {
fetch('/history')
.then(res => res.json())
.then(data => {
const labels = data.map((_, i) => {
const t = new Date(Date.now() - (data.length - 1 - i) * 10000);
return t.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
});
const temps = data.map(e => e.t);
const hums = data.map(e => e.h);
tempChart.data.labels = labels;
tempChart.data.datasets[0].data = temps;
tempChart.update();
humChart.data.labels = labels;
humChart.data.datasets[0].data = hums;
humChart.update();
});
}
initCharts();
updateCharts();
updateLiveData();
setInterval(updateCharts, 10000); // alle 10s Verlaufsdaten
setInterval(updateLiveData, 2000); // alle 2s Livewerte
</script>
</body>
</html>
)rawliteral";
typedef struct HTTP_SERVER_T_ {
struct tcp_pcb *server_pcb;
struct tcp_pcb *client_pcb;
} HTTP_SERVER_T;
static HTTP_SERVER_T *http_server_init(void) {
HTTP_SERVER_T *state = (HTTP_SERVER_T *)calloc(1, sizeof(HTTP_SERVER_T));
if (!state) {
__tcp_errors++;
DEBUG_printf("Failed to allocate HTTP server state\n");
return NULL;
}
return state;
}
static err_t http_server_close(void *arg) {
HTTP_SERVER_T *state = (HTTP_SERVER_T *)arg;
err_t err = ERR_OK;
if (state->client_pcb != NULL) {
tcp_arg(state->client_pcb, NULL);
tcp_poll(state->client_pcb, NULL, 0);
tcp_sent(state->client_pcb, NULL);
tcp_recv(state->client_pcb, NULL);
tcp_err(state->client_pcb, NULL);
err = tcp_close(state->client_pcb);
if (err != ERR_OK) {
__tcp_errors++;
DEBUG_printf("Close failed %d, calling abort\n", err);
tcp_abort(state->client_pcb);
err = ERR_ABRT;
}
state->client_pcb = NULL;
}
// Keep this off to keep the server alive
// if (state->server_pcb) {
// tcp_arg(state->server_pcb, NULL);
// tcp_close(state->server_pcb);
// state->server_pcb = NULL;
//}
return err;
}
static err_t http_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) {
DEBUG_printf("HTTP data sent, length: %u\n", len);
return http_server_close(arg); // Verbindung nach dem Senden schließen
}
static err_t http_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p,
err_t err) {
HTTP_SERVER_T *state = (HTTP_SERVER_T *)arg;
if (!p) {
// Client hat Verbindung geschlossen
return http_server_close(arg);
}
if (err != ERR_OK) {
__tcp_errors++;
DEBUG_printf("Error in TCP receive: %d\n", err);
pbuf_free(p);
return http_server_close(arg);
}
cyw43_arch_lwip_check();
// Eingehende Anfrage verarbeiten
if (p->tot_len > 0) {
char *req = (char *)malloc(p->tot_len + 1);
if (req == NULL) {
__tcp_errors++;
DEBUG_printf("Failed to allocate memory for request\n");
pbuf_free(p);
return http_server_close(arg);
}
pbuf_copy_partial(p, req, p->tot_len, 0);
req[p->tot_len] = '\0'; // Request-String nullterminieren
// DEBUG_printf("Received HTTP request:\n%s\n", req);
// Einfaches Routing basierend auf der URL
const char *http_header_ok_html =
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: "
"%d\r\nConnection: close\r\n\r\n";
const char *http_header_ok_json =
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: "
"%d\r\nConnection: close\r\n\r\n";
const char *http_header_not_found =
"HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: "
"13\r\nConnection: close\r\n\r\n";
const char *not_found_body = "404 Not Found";
char response_header[256];
const char *response_body;
size_t response_body_len;
char dynamic_json_body[256]; // Puffer für dynamische JSON-Daten
char *history_body = 0;
if (strstr(req, "GET / HTTP/1.1") ||
strstr(req, "GET /index.html HTTP/1.1")) {
response_body = html_page;
response_body_len = strlen(html_page);
sprintf(response_header, http_header_ok_html, response_body_len);
} else if (strstr(req, "GET /data HTTP/1.1")) {
// Sensordaten auslesen
float temp = 0.0;
float humidity = 0.0;
if (ctx != NULL) { // Zugriff auf die globale ctx Variable
ctx->update(ctx); // Sensorwerte aktualisieren
temp = ctx->read_temp(ctx);
humidity = ctx->read_humidity(ctx);
} else {
DEBUG_printf("BME280 Kontext ist NULL, sende Standarddaten.\n");
}
sprintf(dynamic_json_body, "{\"temp\": %.2f, \"humidity\": %.2f}", temp,
humidity);
response_body = dynamic_json_body;
response_body_len = strlen(dynamic_json_body);
sprintf(response_header, http_header_ok_json, response_body_len);
} else if (strstr(req, "GET /history HTTP/1.1")) {
history_body =
(char *)malloc(4096); // or larger, depending on expected size
if (!history_body) {
__tcp_errors++;
DEBUG_printf("Memory alloc failed for history\n");
free(req);
pbuf_free(p);
return http_server_close(arg);
}
char *ptr = history_body;
ptr += sprintf(ptr, "[");
for (int i = 0; i < list_count; i++) {
int index = (list_index + i) % list_count;
ptr += sprintf(ptr, "{\"t\":%.2f,\"h\":%.2f}%s", _list[index].t,
_list[index].h, (i < list_count - 1) ? "," : "");
}
ptr += sprintf(ptr, "]");
response_body = history_body;
response_body_len = ptr - history_body;
sprintf(response_header, http_header_ok_json, response_body_len);
} else {
response_body = not_found_body;
response_body_len = strlen(not_found_body);
sprintf(response_header, http_header_not_found, response_body_len);
}
// Header senden
err_t wr_err = tcp_write(tpcb, response_header, strlen(response_header),
TCP_WRITE_FLAG_MORE);
if (wr_err != ERR_OK) {
__tcp_errors++;
DEBUG_printf("Failed to write header %d\n", wr_err);
free(req);
pbuf_free(p);
return http_server_close(arg);
}
// Body senden
wr_err =
tcp_write(tpcb, response_body, response_body_len, TCP_WRITE_FLAG_COPY);
if (wr_err != ERR_OK) {
__tcp_errors++;
DEBUG_printf("Failed to write body %d\n", wr_err);
free(req);
pbuf_free(p);
return http_server_close(arg);
}
tcp_output(tpcb); // Sicherstellen, dass Daten gesendet werden
if (history_body) {
free(history_body);
}
free(req);
}
pbuf_free(p);
tcp_recved(tpcb, p->tot_len); // Empfang der Daten bestätigen
return ERR_OK;
}
static err_t http_server_poll(void *arg, struct tcp_pcb *tpcb) {
// DEBUG_printf("HTTP server poll function called\n");
return ERR_OK;
}
static void http_server_err(void *arg, err_t err) {
if (err != ERR_ABRT) {
__tcp_errors++;
DEBUG_printf("HTTP server error: %d\n", err);
}
}
static err_t http_server_accept(void *arg, struct tcp_pcb *client_pcb,
err_t err) {
HTTP_SERVER_T *state = (HTTP_SERVER_T *)arg;
if (err != ERR_OK || client_pcb == NULL) {
__tcp_errors++;
DEBUG_printf("Failure in accept: %d\n", err);
return ERR_VAL;
}
DEBUG_printf("Client connected\n");
state->client_pcb = client_pcb;
tcp_arg(client_pcb, state);
tcp_sent(client_pcb, http_server_sent);
tcp_recv(client_pcb, http_server_recv);
tcp_poll(client_pcb, http_server_poll, POLL_TIME_S * 2);
tcp_err(client_pcb, http_server_err);
return ERR_OK;
}
bool http_server_open(void *arg) {
HTTP_SERVER_T *state = (HTTP_SERVER_T *)arg;
DEBUG_printf("Starting HTTP server at %s on port %u\n",
ip4addr_ntoa(netif_ip4_addr(netif_list)), TCP_PORT);
struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
if (!pcb) {
__tcp_errors++;
DEBUG_printf("Failed to create pcb\n");
return false;
}
err_t err = tcp_bind(pcb, NULL, TCP_PORT);
if (err) {
__tcp_errors++;
DEBUG_printf("Failed to bind to port %u\n", TCP_PORT);
return false;
}
state->server_pcb = tcp_listen_with_backlog(pcb, 1);
if (!state->server_pcb) {
__tcp_errors++;
DEBUG_printf("Failed to listen\n");
if (pcb) {
tcp_close(pcb);
}
return false;
}
tcp_arg(state->server_pcb, state);
tcp_accept(state->server_pcb, http_server_accept);
return true;
}
// Funktion zum Initialisieren und Ausführen des HTTP-Servers in der
// Hauptschleife
void run_http_server(void) {
static HTTP_SERVER_T *state = NULL;
if (!state) {
state = http_server_init();
if (!state) {
return;
}
if (!http_server_open(state)) {
http_server_close(state);
free(state);
state = NULL;
return;
}
}
#if PICO_CYW43_ARCH_POLL
cyw43_arch_poll();
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1));
#else
sleep_ms(1);
#endif
}

216
include/tft.hpp Normal file
View File

@ -0,0 +1,216 @@
#pragma once
extern "C" {
#include <hardware/spi.h>
#include <pico/stdlib.h>
}
class TFT {
public:
TFT(uint cs, uint dc, uint rst, uint sck, uint sda)
: PinCs(cs), PinDc(dc), PinRst(rst), PinSck(sck), PinSda(sda) {}
~TFT() = default;
enum class CMD : uint8_t {
NOP = 0x00,
SWRESET = 0x01,
SLPIN = 0x10,
SLPOUT = 0x11,
INVOFF = 0x20,
INVON = 0x21,
DISPON = 0x29,
CASET = 0x2A,
RASET = 0x2B,
RAMWR = 0x2C,
MADCTL = 0x36,
COLMOD = 0x3A,
FRMCTR1 = 0xB1,
FRMCTR2 = 0xB2,
FRMCTR3 = 0xB3,
INVCTR = 0xB4,
PWCTR1 = 0xC0,
PWCTR2 = 0xC1,
PWCTR3 = 0xC2,
PWCTR4 = 0xC3,
PWCTR5 = 0xC4,
VMCTR1 = 0xC5,
GMCTRP1 = 0xE0,
GMCTRN1 = 0xE1,
NORON = 0x13,
};
void Init() {
spi_init(spi0, 20 * 1000 * 1000);
gpio_set_function(PinSck, GPIO_FUNC_SPI);
gpio_set_function(PinSda, GPIO_FUNC_SPI);
gpio_init(PinCs);
gpio_init(PinDc);
gpio_init(PinRst);
gpio_set_dir(PinCs, GPIO_OUT);
gpio_set_dir(PinDc, GPIO_OUT);
gpio_set_dir(PinRst, GPIO_OUT);
gpio_put(PinCs, 1);
gpio_put(PinDc, 1);
gpio_put(PinRst, 1);
Reset();
// Init sequence Oder so
WriteCmd(CMD::SWRESET);
sleep_ms(150);
WriteCmd(CMD::SLPOUT);
sleep_ms(150);
WriteCmd(CMD::FRMCTR1);
WriteData(0x01);
WriteData(0x2C);
WriteData(0x2D);
WriteCmd(CMD::FRMCTR2);
WriteData(0x01);
WriteData(0x2C);
WriteData(0x2D);
WriteCmd(CMD::FRMCTR3);
WriteData(0x01);
WriteData(0x2C);
WriteData(0x2D);
WriteData(0x01);
WriteData(0x2C);
WriteData(0x2D);
WriteCmd(CMD::INVCTR);
WriteData(0x07);
WriteCmd(CMD::PWCTR1);
WriteData(0xA2);
WriteData(0x02);
WriteData(0x84);
WriteCmd(CMD::PWCTR2);
WriteData(0xC5);
WriteCmd(CMD::PWCTR3);
WriteData(0x0A);
WriteData(0x00);
WriteCmd(CMD::PWCTR4);
WriteData(0x8A);
WriteData(0x2A);
WriteCmd(CMD::PWCTR5);
WriteData(0x8A);
WriteData(0xEE);
WriteCmd(CMD::VMCTR1);
WriteData(0x0E);
WriteCmd(CMD::INVOFF);
WriteCmd(CMD::MADCTL);
WriteData(0xC8);
WriteCmd(CMD::COLMOD);
WriteData(0x05); // 16-bit color
WriteCmd(CMD::CASET);
WriteData(0);
WriteData(0);
WriteData(0);
WriteData(127);
WriteCmd(CMD::RASET);
WriteData(0);
WriteData(0);
WriteData(0);
WriteData(159);
WriteCmd(CMD::GMCTRP1);
WriteData(0x02);
WriteData(0x1c);
WriteData(0x07);
WriteData(0x12);
WriteData(0x37);
WriteData(0x32);
WriteData(0x29);
WriteData(0x2d);
WriteData(0x29);
WriteData(0x25);
WriteData(0x2B);
WriteData(0x39);
WriteData(0x00);
WriteData(0x01);
WriteData(0x03);
WriteData(0x10);
WriteCmd(CMD::GMCTRN1);
WriteData(0x03);
WriteData(0x1d);
WriteData(0x07);
WriteData(0x06);
WriteData(0x2E);
WriteData(0x2C);
WriteData(0x29);
WriteData(0x2D);
WriteData(0x2E);
WriteData(0x2E);
WriteData(0x37);
WriteData(0x3F);
WriteData(0x00);
WriteData(0x00);
WriteData(0x02);
WriteData(0x10);
WriteCmd(CMD::NORON);
sleep_ms(10);
WriteCmd(CMD::DISPON);
sleep_ms(100);
}
void SetWindow(uint8_t x, uint8_t y, uint8_t x1, uint8_t y1) {
WriteCmd(CMD::CASET);
WriteData(0x00);
WriteData(x);
WriteData(0x00);
WriteData(x1);
WriteCmd(CMD::RASET);
WriteData(0x00);
WriteData(y);
WriteData(0x00);
WriteData(y1);
WriteCmd(CMD::RAMWR);
}
void DrawPixel(uint8_t x, uint8_t y, uint16_t color) {
SetWindow(x, y, x, y);
WriteData16(color);
}
void DrawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
SetWindow(x, y, w, h);
for (int i = 0; i < w * h; i++) {
WriteData16(color);
}
}
void Reset() {
gpio_put(PinRst, 0);
sleep_ms(50);
gpio_put(PinRst, 1);
sleep_ms(50);
}
void WriteCmd(CMD cmd) { WriteCmd((uint8_t)cmd); }
void WriteCmd(uint8_t cmd) {
gpio_put(PinDc, 0); // Command Mode
gpio_put(PinCs, 0);
spi_write_blocking(spi0, &cmd, 1);
gpio_put(PinCs, 1);
}
void WriteData(uint8_t data) {
gpio_put(PinDc, 1); // Data Mode
gpio_put(PinCs, 0);
spi_write_blocking(spi0, &data, 1);
gpio_put(PinCs, 1);
}
void WriteData16(uint16_t data) {
uint8_t d8[2] = {uint8_t(data >> 8), uint8_t(data & 0xff)};
gpio_put(PinDc, 1); // Data Mode
gpio_put(PinCs, 0);
spi_write_blocking(spi0, d8, 2);
gpio_put(PinCs, 1);
}
uint PinCs = 0;
uint PinDc = 0;
uint PinRst = 0;
uint PinSck = 0;
uint PinSda = 0;
};