From 2126ef934efc5ae40f324fffaf9b30ca713e1267 Mon Sep 17 00:00:00 2001 From: rasmus Date: Mon, 23 Mar 2026 23:30:49 +0100 Subject: [PATCH] Made webserver work at 192.168.4.1 --- lib/dhcp_server/CMakeLists.txt | 18 ++ lib/dhcp_server/dhcp_server.c | 324 +++++++++++++++++++ lib/dhcp_server/dhcp_server.h | 58 ++++ mein_projekt/main.cpp | 32 -- {mein_projekt => sensor-pico}/CMakeLists.txt | 23 +- sensor-pico/lwipopts.h | 34 ++ sensor-pico/main.cpp | 29 ++ sensor-pico/webserver.c | 1 + 8 files changed, 480 insertions(+), 39 deletions(-) create mode 100644 lib/dhcp_server/CMakeLists.txt create mode 100644 lib/dhcp_server/dhcp_server.c create mode 100644 lib/dhcp_server/dhcp_server.h delete mode 100644 mein_projekt/main.cpp rename {mein_projekt => sensor-pico}/CMakeLists.txt (54%) create mode 100644 sensor-pico/lwipopts.h create mode 100644 sensor-pico/main.cpp create mode 100644 sensor-pico/webserver.c diff --git a/lib/dhcp_server/CMakeLists.txt b/lib/dhcp_server/CMakeLists.txt new file mode 100644 index 0000000..a74160a --- /dev/null +++ b/lib/dhcp_server/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.13) + +add_library(dhcp_server STATIC + dhcp_server.c +) + +target_include_directories(dhcp_server + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + ${CMAKE_SOURCE_DIR}/../sensor-pico +) + +target_link_libraries(dhcp_server + pico_stdlib + pico_lwip + pico_cyw43_arch_lwip_threadsafe_background +) diff --git a/lib/dhcp_server/dhcp_server.c b/lib/dhcp_server/dhcp_server.c new file mode 100644 index 0000000..a871f1c --- /dev/null +++ b/lib/dhcp_server/dhcp_server.c @@ -0,0 +1,324 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// For DHCP specs see: +// https://www.ietf.org/rfc/rfc2131.txt +// https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor +// Extensions + +#include +#include +#include + +#include "cyw43_config.h" +#include "dhcp_server.h" +#include "lwip/udp.h" + +#define DHCPDISCOVER (1) +#define DHCPOFFER (2) +#define DHCPREQUEST (3) +#define DHCPDECLINE (4) +#define DHCPACK (5) +#define DHCPNACK (6) +#define DHCPRELEASE (7) +#define DHCPINFORM (8) + +#define DHCP_OPT_PAD (0) +#define DHCP_OPT_SUBNET_MASK (1) +#define DHCP_OPT_ROUTER (3) +#define DHCP_OPT_DNS (6) +#define DHCP_OPT_HOST_NAME (12) +#define DHCP_OPT_REQUESTED_IP (50) +#define DHCP_OPT_IP_LEASE_TIME (51) +#define DHCP_OPT_MSG_TYPE (53) +#define DHCP_OPT_SERVER_ID (54) +#define DHCP_OPT_PARAM_REQUEST_LIST (55) +#define DHCP_OPT_MAX_MSG_SIZE (57) +#define DHCP_OPT_VENDOR_CLASS_ID (60) +#define DHCP_OPT_CLIENT_ID (61) +#define DHCP_OPT_END (255) + +#define PORT_DHCP_SERVER (67) +#define PORT_DHCP_CLIENT (68) + +#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds + +#define MAC_LEN (6) +#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + +typedef struct { + uint8_t op; // message opcode + uint8_t htype; // hardware address type + uint8_t hlen; // hardware address length + uint8_t hops; + uint32_t xid; // transaction id, chosen by client + uint16_t secs; // client seconds elapsed + uint16_t flags; + uint8_t ciaddr[4]; // client IP address + uint8_t yiaddr[4]; // your IP address + uint8_t siaddr[4]; // next server IP address + uint8_t giaddr[4]; // relay agent IP address + uint8_t chaddr[16]; // client hardware address + uint8_t sname[64]; // server host name + uint8_t file[128]; // boot file name + uint8_t options[312]; // optional parameters, variable, starts with magic +} dhcp_msg_t; + +static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, + udp_recv_fn cb_udp_recv) { + // family is AF_INET + // type is SOCK_DGRAM + + *udp = udp_new(); + if (*udp == NULL) { + return -ENOMEM; + } + + // Register callback + udp_recv(*udp, cb_udp_recv, (void *)cb_data); + + return 0; // success +} + +static void dhcp_socket_free(struct udp_pcb **udp) { + if (*udp != NULL) { + udp_remove(*udp); + *udp = NULL; + } +} + +static int dhcp_socket_bind(struct udp_pcb **udp, uint16_t port) { + // TODO convert lwIP errors to errno + return udp_bind(*udp, IP_ANY_TYPE, port); +} + +static int dhcp_socket_sendto(struct udp_pcb **udp, struct netif *nif, + const void *buf, size_t len, uint32_t ip, + uint16_t port) { + if (len > 0xffff) { + len = 0xffff; + } + + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (p == NULL) { + return -ENOMEM; + } + + memcpy(p->payload, buf, len); + + ip_addr_t dest; + IP4_ADDR(ip_2_ip4(&dest), ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, + ip & 0xff); + err_t err; + if (nif != NULL) { + err = udp_sendto_if(*udp, p, &dest, port, nif); + } else { + err = udp_sendto(*udp, p, &dest, port); + } + + pbuf_free(p); + + if (err != ERR_OK) { + return err; + } + + return len; +} + +static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) { + for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) { + if (opt[i] == cmd) { + return &opt[i]; + } + i += 2 + opt[i + 1]; + } + return NULL; +} + +static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, + const void *data) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = n; + memcpy(o, data, n); + *opt = o + n; +} + +static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 1; + *o++ = val; + *opt = o; +} + +static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 4; + *o++ = val >> 24; + *o++ = val >> 16; + *o++ = val >> 8; + *o++ = val; + *opt = o; +} + +static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, + const ip_addr_t *src_addr, u16_t src_port) { + dhcp_server_t *d = arg; + (void)upcb; + (void)src_addr; + (void)src_port; + + // This is around 548 bytes + dhcp_msg_t dhcp_msg; + +#define DHCP_MIN_SIZE (240 + 3) + if (p->tot_len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0); + if (len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + dhcp_msg.op = DHCPOFFER; + memcpy(&dhcp_msg.yiaddr, &ip4_addr_get_u32(ip_2_ip4(&d->ip)), 4); + + uint8_t *opt = (uint8_t *)&dhcp_msg.options; + opt += 4; // assume magic cookie: 99, 130, 83, 99 + + uint8_t *msgtype = opt_find(opt, DHCP_OPT_MSG_TYPE); + if (msgtype == NULL) { + // A DHCP package without MSG_TYPE? + goto ignore_request; + } + + switch (msgtype[2]) { + case DHCPDISCOVER: { + int yi = DHCPS_MAX_IP; + for (int i = 0; i < DHCPS_MAX_IP; ++i) { + if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, use this IP address + yi = i; + break; + } + if (yi == DHCPS_MAX_IP) { + // Look for a free IP address + if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP available + yi = i; + } + uint32_t expiry = d->lease[i].expiry << 16 | 0xffff; + if ((int32_t)(expiry - cyw43_hal_ticks_ms()) < 0) { + // IP expired, reuse it + memset(d->lease[i].mac, 0, MAC_LEN); + yi = i; + } + } + } + if (yi == DHCPS_MAX_IP) { + // No more IP addresses left + goto ignore_request; + } + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER); + break; + } + + case DHCPREQUEST: { + uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP); + if (o == NULL) { + // Should be NACK + goto ignore_request; + } + if (memcmp(o + 2, &ip4_addr_get_u32(ip_2_ip4(&d->ip)), 3) != 0) { + // Should be NACK + goto ignore_request; + } + uint8_t yi = o[5] - DHCPS_BASE_IP; + if (yi >= DHCPS_MAX_IP) { + // Should be NACK + goto ignore_request; + } + if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, ok to use this IP address + } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == + 0) { + // IP unused, ok to use this IP address + memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN); + } else { + // IP already in use + // Should be NACK + goto ignore_request; + } + d->lease[yi].expiry = + (cyw43_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); + printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x " + "IP=%u.%u.%u.%u\n", + dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], + dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], + dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], + dhcp_msg.yiaddr[3]); + break; + } + + default: + goto ignore_request; + } + + opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); + opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, + &ip4_addr_get_u32(ip_2_ip4(&d->nm))); + opt_write_n(&opt, DHCP_OPT_ROUTER, 4, + &ip4_addr_get_u32(ip_2_ip4( + &d->ip))); // aka gateway; can have multiple addresses + opt_write_n(&opt, DHCP_OPT_DNS, 4, + &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // this server is the dns + opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); + *opt++ = DHCP_OPT_END; + struct netif *nif = ip_current_input_netif(); + dhcp_socket_sendto(&d->udp, nif, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, + 0xffffffff, PORT_DHCP_CLIENT); + +ignore_request: + pbuf_free(p); +} + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { + ip_addr_copy(d->ip, *ip); + ip_addr_copy(d->nm, *nm); + memset(d->lease, 0, sizeof(d->lease)); + if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { + return; + } + dhcp_socket_bind(&d->udp, PORT_DHCP_SERVER); +} + +void dhcp_server_deinit(dhcp_server_t *d) { dhcp_socket_free(&d->udp); } diff --git a/lib/dhcp_server/dhcp_server.h b/lib/dhcp_server/dhcp_server.h new file mode 100644 index 0000000..e3d61f2 --- /dev/null +++ b/lib/dhcp_server/dhcp_server.h @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H +#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H + +#include "lwip/ip_addr.h" + +#define DHCPS_BASE_IP (16) +#define DHCPS_MAX_IP (8) + +typedef struct _dhcp_server_lease_t { + uint8_t mac[6]; + uint16_t expiry; +} dhcp_server_lease_t; + +typedef struct _dhcp_server_t { + ip_addr_t ip; + ip_addr_t nm; + dhcp_server_lease_t lease[DHCPS_MAX_IP]; + struct udp_pcb *udp; +} dhcp_server_t; + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); +void dhcp_server_deinit(dhcp_server_t *d); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H + +#ifdef __cplusplus +} +#endif diff --git a/mein_projekt/main.cpp b/mein_projekt/main.cpp deleted file mode 100644 index 5900cfd..0000000 --- a/mein_projekt/main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "bme280.h" -#include "hardware/i2c.h" -#include "pico/cyw43_arch.h" -#include "pico/stdlib.h" -#include - -constexpr int i2cAddress{0x76}; - -int main() { - stdio_init_all(); - sleep_ms(3000); - cyw43_arch_init(); - - bme280_handle_t handle; - int result{bme280_init(&handle, i2cAddress, INTERVAL_1000MS)}; - printf("I2C scan...\n"); - for (uint8_t addr = 0x08; addr < 0x78; addr++) { - uint8_t dummy; - int ret = i2c_read_blocking(i2c1, addr, &dummy, 1, false); - if (ret >= 0) { - printf("Gefunden: 0x%02X\n", addr); - } - } - - while (true) { - int result{bme280_init(&handle, i2cAddress, INTERVAL_1000MS)}; - printf("Init result: %d\n", result); - bme280_read_data(handle); - printf("%s\n", bme280_get_json(handle)); - sleep_ms(3000); - } -} diff --git a/mein_projekt/CMakeLists.txt b/sensor-pico/CMakeLists.txt similarity index 54% rename from mein_projekt/CMakeLists.txt rename to sensor-pico/CMakeLists.txt index e9f3d51..f3c291e 100644 --- a/mein_projekt/CMakeLists.txt +++ b/sensor-pico/CMakeLists.txt @@ -8,28 +8,37 @@ set(PICO_SDK_PATH ${CMAKE_SOURCE_DIR}/../pico-sdk) include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) -project(mein_projekt C CXX ASM) +project(sensor-pico C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) pico_sdk_init() add_subdirectory(../lib/bme280 bme280_build) +add_subdirectory(../lib/dhcp_server dhcp_server_build) -add_executable(mein_projekt +add_executable(sensor-pico main.cpp + webserver.c ) # Standard Ein-/Ausgabe über USB (du siehst printf im Terminal) -pico_enable_stdio_usb(mein_projekt 1) -pico_enable_stdio_uart(mein_projekt 0) +pico_enable_stdio_usb(sensor-pico 1) +pico_enable_stdio_uart(sensor-pico 0) + +target_include_directories(sensor-pico PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) # Benötigte Bibliotheken -target_link_libraries(mein_projekt +target_link_libraries(sensor-pico pico_stdlib - pico_cyw43_arch_none + pico_cyw43_arch_lwip_threadsafe_background bme280 + dhcp_server + pico_lwip + pico_lwip_http ) # Erzeugt .uf2 Datei zum Flashen -pico_add_extra_outputs(mein_projekt) +pico_add_extra_outputs(sensor-pico) diff --git a/sensor-pico/lwipopts.h b/sensor-pico/lwipopts.h new file mode 100644 index 0000000..86cd48a --- /dev/null +++ b/sensor-pico/lwipopts.h @@ -0,0 +1,34 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +#define NO_SYS 1 +#define LWIP_SOCKET 0 +#define LWIP_NETCONN 0 + +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DHCP 1 +#define LWIP_DNS 1 +#define LWIP_RAW 1 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 + +#define LWIP_HTTPD 1 +#define LWIP_HTTPD_CGI 1 +#define LWIP_HTTPD_SSI 1 + +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_STATUS_CALLBACK 1 + +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define PBUF_POOL_SIZE 24 + +#define TCP_MSS 1460 +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) + +#endif diff --git a/sensor-pico/main.cpp b/sensor-pico/main.cpp new file mode 100644 index 0000000..3d19a44 --- /dev/null +++ b/sensor-pico/main.cpp @@ -0,0 +1,29 @@ +#include "bme280.h" +#include "dhcp_server.h" +#include "hardware/i2c.h" +#include "lwip/apps/httpd.h" +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" +#include + +int main() { + stdio_init_all(); + sleep_ms(3000); + cyw43_arch_init(); + cyw43_arch_enable_ap_mode("SensorAP", "passwort123", CYW43_AUTH_WPA2_AES_PSK); + + ip_addr_t gw{}; + ip_addr_t mask{}; + IP4_ADDR(&gw, 192, 168, 4, 1); + IP4_ADDR(&mask, 255, 255, 255, 0); + + dhcp_server_t dhcp_server; + dhcp_server_init(&dhcp_server, &gw, &mask); + + httpd_init(); + + while (true) { + cyw43_arch_poll(); + sleep_ms(100); + } +} diff --git a/sensor-pico/webserver.c b/sensor-pico/webserver.c new file mode 100644 index 0000000..d71b938 --- /dev/null +++ b/sensor-pico/webserver.c @@ -0,0 +1 @@ +// to be continued