Initial Comit
This commit is contained in:
1828
include/boot.h
Normal file
1828
include/boot.h
Normal file
File diff suppressed because it is too large
Load Diff
91
include/configs/lwipopts.h
Executable file
91
include/configs/lwipopts.h
Executable 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
3083
include/font.hpp
Normal file
File diff suppressed because it is too large
Load Diff
460
include/server.h
Executable file
460
include/server.h
Executable 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
216
include/tft.hpp
Normal 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;
|
||||
};
|
Reference in New Issue
Block a user