Code d'exemple WIFI/DHCP/ServeurWeb

This commit is contained in:
Samuel 2026-01-31 19:19:59 +01:00
parent 88ffe4f03a
commit eab6bc52de
11 changed files with 1154 additions and 58 deletions

22
.vscode/tasks.json vendored
View File

@ -2,23 +2,23 @@
"tasks": [
{
"type": "shell",
"command": "cd build; cmake ../; make",
"label": "CMake in build/",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
}
},
{
"type": "shell",
"command": "cd build; cmake ../; make Flash",
"command": "mkdir -p build; cd build; cmake -DPICO_BOARD=pico_w ..; make Flash",
"label": "CMake & Make & Flash",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "shell",
"command": "rm -rf build/*",
"label": "clean",
"problemMatcher": [],
"group": {
"kind":"build",
"isDefault": false
}
}
],
"version": "2.0.0"

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(Modele_RPiPico C CXX ASM)
project(picow_access_point_background C CXX ASM)
set(CMAKE_C_STNDARD 11)
set(CMAKE_CXX_STANDARD 17)
@ -10,22 +10,57 @@ set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})
pico_sdk_init()
add_executable(Modele_RPiPico
main.c
add_executable(picow_access_point_background
picow_access_point.c
dhcpserver/dhcpserver.c
dnsserver/dnsserver.c
)
target_link_libraries(Modele_RPiPico
hardware_uart
target_include_directories(picow_access_point_background PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
${CMAKE_CURRENT_LIST_DIR}/dhcpserver
${CMAKE_CURRENT_LIST_DIR}/dnsserver
)
target_link_libraries(picow_access_point_background
pico_cyw43_arch_lwip_threadsafe_background
pico_stdlib
pico_multicore
)
# You can change the address below to change the address of the access point
pico_configure_ip4_address(picow_access_point_background PRIVATE
CYW43_DEFAULT_IP_AP_ADDRESS 192.168.4.1
)
pico_add_extra_outputs(picow_access_point_background)
pico_enable_stdio_usb(Modele_RPiPico 1)
pico_enable_stdio_uart(Modele_RPiPico 1)
add_executable(picow_access_point_poll
picow_access_point.c
dhcpserver/dhcpserver.c
dnsserver/dnsserver.c
)
target_include_directories(picow_access_point_poll PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
${CMAKE_CURRENT_LIST_DIR}/dhcpserver
${CMAKE_CURRENT_LIST_DIR}/dnsserver
)
target_link_libraries(picow_access_point_poll
pico_cyw43_arch_lwip_poll
pico_stdlib
)
# You can change the address below to change the address of the access point
pico_configure_ip4_address(picow_access_point_poll PRIVATE
CYW43_DEFAULT_IP_AP_ADDRESS 192.168.4.1
)
pico_add_extra_outputs(picow_access_point_poll)
pico_add_extra_outputs(Modele_RPiPico)
pico_enable_stdio_usb(picow_access_point_background 1)
pico_enable_stdio_uart(picow_access_point_background 1)
pico_add_extra_outputs(picow_access_point_background)
add_custom_target(Flash
DEPENDS Modele_RPiPico
COMMAND sudo picotool load -f ${PROJECT_BINARY_DIR}/Modele_RPiPico.uf2
DEPENDS picow_access_point_background
COMMAND sudo picotool load -f ${PROJECT_BINARY_DIR}/picow_access_point_background.uf2
)

21
dhcpserver/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-2022 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.

309
dhcpserver/dhcpserver.c Normal file
View File

@ -0,0 +1,309 @@
/*
* 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "cyw43_config.h"
#include "dhcpserver.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);
}

49
dhcpserver/dhcpserver.h Normal file
View File

@ -0,0 +1,49 @@
/*
* 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.
*/
#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

235
dnsserver/dnsserver.c Normal file
View File

@ -0,0 +1,235 @@
/**
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <stdbool.h>
#include "dnsserver.h"
#include "lwip/udp.h"
#define PORT_DNS_SERVER 53
#define DUMP_DATA 0
#define DEBUG_printf(...)
#define ERROR_printf printf
typedef struct dns_header_t_ {
uint16_t id;
uint16_t flags;
uint16_t question_count;
uint16_t answer_record_count;
uint16_t authority_record_count;
uint16_t additional_record_count;
} dns_header_t;
#define MAX_DNS_MSG_SIZE 300
static int dns_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) {
*udp = udp_new();
if (*udp == NULL) {
return -ENOMEM;
}
udp_recv(*udp, cb_udp_recv, (void *)cb_data);
return ERR_OK;
}
static void dns_socket_free(struct udp_pcb **udp) {
if (*udp != NULL) {
udp_remove(*udp);
*udp = NULL;
}
}
static int dns_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) {
ip_addr_t addr;
IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
err_t err = udp_bind(*udp, &addr, port);
if (err != ERR_OK) {
ERROR_printf("dns failed to bind to port %u: %d", port, err);
assert(false);
}
return err;
}
#if DUMP_DATA
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
unsigned int i = 0;
for (i = 0; i < len;) {
if ((i & 0x0f) == 0) {
printf("\n");
} else if ((i & 0x07) == 0) {
printf(" ");
}
printf("%02x ", bptr[i++]);
}
printf("\n");
}
#endif
static int dns_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, const ip_addr_t *dest, uint16_t port) {
if (len > 0xffff) {
len = 0xffff;
}
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
if (p == NULL) {
ERROR_printf("DNS: Failed to send message out of memory\n");
return -ENOMEM;
}
memcpy(p->payload, buf, len);
err_t err = udp_sendto(*udp, p, dest, port);
pbuf_free(p);
if (err != ERR_OK) {
ERROR_printf("DNS: Failed to send message %d\n", err);
return err;
}
#if DUMP_DATA
dump_bytes(buf, len);
#endif
return len;
}
static void dns_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) {
dns_server_t *d = arg;
DEBUG_printf("dns_server_process %u\n", p->tot_len);
uint8_t dns_msg[MAX_DNS_MSG_SIZE];
dns_header_t *dns_hdr = (dns_header_t*)dns_msg;
size_t msg_len = pbuf_copy_partial(p, dns_msg, sizeof(dns_msg), 0);
if (msg_len < sizeof(dns_header_t)) {
goto ignore_request;
}
#if DUMP_DATA
dump_bytes(dns_msg, msg_len);
#endif
uint16_t flags = lwip_ntohs(dns_hdr->flags);
uint16_t question_count = lwip_ntohs(dns_hdr->question_count);
DEBUG_printf("len %d\n", msg_len);
DEBUG_printf("dns flags 0x%x\n", flags);
DEBUG_printf("dns question count 0x%x\n", question_count);
// flags from rfc1035
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// Check QR indicates a query
if (((flags >> 15) & 0x1) != 0) {
DEBUG_printf("Ignoring non-query\n");
goto ignore_request;
}
// Check for standard query
if (((flags >> 11) & 0xf) != 0) {
DEBUG_printf("Ignoring non-standard query\n");
goto ignore_request;
}
// Check question count
if (question_count < 1) {
DEBUG_printf("Invalid question count\n");
goto ignore_request;
}
// Print the question
DEBUG_printf("question: ");
const uint8_t *question_ptr_start = dns_msg + sizeof(dns_header_t);
const uint8_t *question_ptr_end = dns_msg + msg_len;
const uint8_t *question_ptr = question_ptr_start;
while(question_ptr < question_ptr_end) {
if (*question_ptr == 0) {
question_ptr++;
break;
} else {
if (question_ptr > question_ptr_start) {
DEBUG_printf(".");
}
int label_len = *question_ptr++;
if (label_len > 63) {
DEBUG_printf("Invalid label\n");
goto ignore_request;
}
DEBUG_printf("%.*s", label_len, question_ptr);
question_ptr += label_len;
}
}
DEBUG_printf("\n");
// Check question length
if (question_ptr - question_ptr_start > 255) {
DEBUG_printf("Invalid question length\n");
goto ignore_request;
}
// Skip QNAME and QTYPE
question_ptr += 4;
// Generate answer
uint8_t *answer_ptr = dns_msg + (question_ptr - dns_msg);
*answer_ptr++ = 0xc0; // pointer
*answer_ptr++ = question_ptr_start - dns_msg; // pointer to question
*answer_ptr++ = 0;
*answer_ptr++ = 1; // host address
*answer_ptr++ = 0;
*answer_ptr++ = 1; // Internet class
*answer_ptr++ = 0;
*answer_ptr++ = 0;
*answer_ptr++ = 0;
*answer_ptr++ = 60; // ttl 60s
*answer_ptr++ = 0;
*answer_ptr++ = 4; // length
memcpy(answer_ptr, &d->ip.addr, 4); // use our address
answer_ptr += 4;
dns_hdr->flags = lwip_htons(
0x1 << 15 | // QR = response
0x1 << 10 | // AA = authoritative
0x1 << 7); // RA = authenticated
dns_hdr->question_count = lwip_htons(1);
dns_hdr->answer_record_count = lwip_htons(1);
dns_hdr->authority_record_count = 0;
dns_hdr->additional_record_count = 0;
// Send the reply
DEBUG_printf("Sending %d byte reply to %s:%d\n", answer_ptr - dns_msg, ipaddr_ntoa(src_addr), src_port);
dns_socket_sendto(&d->udp, &dns_msg, answer_ptr - dns_msg, src_addr, src_port);
ignore_request:
pbuf_free(p);
}
void dns_server_init(dns_server_t *d, ip_addr_t *ip) {
if (dns_socket_new_dgram(&d->udp, d, dns_server_process) != ERR_OK) {
DEBUG_printf("dns server failed to start\n");
return;
}
if (dns_socket_bind(&d->udp, 0, PORT_DNS_SERVER) != ERR_OK) {
DEBUG_printf("dns server failed to bind\n");
return;
}
ip_addr_copy(d->ip, *ip);
DEBUG_printf("dns server listening on port %d\n", PORT_DNS_SERVER);
}
void dns_server_deinit(dns_server_t *d) {
dns_socket_free(&d->udp);
}

20
dnsserver/dnsserver.h Normal file
View File

@ -0,0 +1,20 @@
/**
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _DNSSERVER_H_
#define _DNSSERVER_H_
#include "lwip/ip_addr.h"
typedef struct dns_server_t_ {
struct udp_pcb *udp;
ip_addr_t ip;
} dns_server_t;
void dns_server_init(dns_server_t *d, ip_addr_t *ip);
void dns_server_deinit(dns_server_t *d);
#endif

10
lwipopts.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _LWIPOPTS_H
#define _LWIPOPTS_H
// Generally you would define your own explicit list of lwIP options
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html)
//
// This example uses a common include to avoid repetition
#include "lwipopts_examples_common.h"
#endif

View File

@ -0,0 +1,92 @@
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
#define _LWIPOPTS_EXAMPLE_COMMONH_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 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)
#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__ */

33
main.c
View File

@ -1,33 +0,0 @@
/*****
* Copyright (c) 2023 - Poivron Robotique
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/stdlib.h"
#include <stdio.h>
bool bouton_Presser = false;
void main(void)
{
stdio_init_all();
gpio_init(8);
gpio_init(9);
gpio_set_function(8,GPIO_IN);
gpio_set_function(9,GPIO_OUT);
gpio_set_function(25,GPIO_OUT);
gpio_pull_down(8);
gpio_put(9,true);
gpio_put(25,true);
sleep_ms(5000);
printf("kartoffen\n");
while(1){
bouton_Presser = gpio_get(8);
printf(">a:%d\n", bouton_Presser );
gpio_put(25,bouton_Presser);
if(bouton_Presser){
printf("bouton a ete presse\n");
}
}
}

358
picow_access_point.c Normal file
View File

@ -0,0 +1,358 @@
/**
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"
#include "lwip/pbuf.h"
#include "lwip/tcp.h"
#include "dhcpserver.h"
#include "dnsserver.h"
#define TCP_PORT 80
#define DEBUG_printf printf
#define POLL_TIME_S 5
#define HTTP_GET "GET"
#define HTTP_RESPONSE_HEADERS "HTTP/1.1 %d OK\nContent-Length: %d\nContent-Type: text/html; charset=utf-8\nConnection: close\n\n"
#define LED_TEST_BODY "<html><body><h1>Hello from Pico.</h1><p>Led is %s</p><p><a href=\"?led=%d\">Turn led %s</a></body></html>"
#define LED_PARAM "led=%d"
#define LED_TEST "/ledtest"
#define LED_GPIO 0
#define HTTP_RESPONSE_REDIRECT "HTTP/1.1 302 Redirect\nLocation: http://%s" LED_TEST "\n\n"
typedef struct TCP_SERVER_T_ {
struct tcp_pcb *server_pcb;
bool complete;
ip_addr_t gw;
} TCP_SERVER_T;
typedef struct TCP_CONNECT_STATE_T_ {
struct tcp_pcb *pcb;
int sent_len;
char headers[128];
char result[256];
int header_len;
int result_len;
ip_addr_t *gw;
} TCP_CONNECT_STATE_T;
static err_t tcp_close_client_connection(TCP_CONNECT_STATE_T *con_state, struct tcp_pcb *client_pcb, err_t close_err) {
if (client_pcb) {
assert(con_state && con_state->pcb == client_pcb);
tcp_arg(client_pcb, NULL);
tcp_poll(client_pcb, NULL, 0);
tcp_sent(client_pcb, NULL);
tcp_recv(client_pcb, NULL);
tcp_err(client_pcb, NULL);
err_t err = tcp_close(client_pcb);
if (err != ERR_OK) {
DEBUG_printf("close failed %d, calling abort\n", err);
tcp_abort(client_pcb);
close_err = ERR_ABRT;
}
if (con_state) {
free(con_state);
}
}
return close_err;
}
static void tcp_server_close(TCP_SERVER_T *state) {
if (state->server_pcb) {
tcp_arg(state->server_pcb, NULL);
tcp_close(state->server_pcb);
state->server_pcb = NULL;
}
}
static err_t tcp_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) {
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
DEBUG_printf("tcp_server_sent %u\n", len);
con_state->sent_len += len;
if (con_state->sent_len >= con_state->header_len + con_state->result_len) {
DEBUG_printf("all done\n");
return tcp_close_client_connection(con_state, pcb, ERR_OK);
}
return ERR_OK;
}
static int test_server_content(const char *request, const char *params, char *result, size_t max_result_len) {
int len = 0;
if (strncmp(request, LED_TEST, sizeof(LED_TEST) - 1) == 0) {
// Get the state of the led
bool value;
cyw43_gpio_get(&cyw43_state, LED_GPIO, &value);
int led_state = value;
// See if the user changed it
if (params) {
int led_param = sscanf(params, LED_PARAM, &led_state);
if (led_param == 1) {
if (led_state) {
// Turn led on
cyw43_gpio_set(&cyw43_state, LED_GPIO, true);
} else {
// Turn led off
cyw43_gpio_set(&cyw43_state, LED_GPIO, false);
}
}
}
// Generate result
if (led_state) {
len = snprintf(result, max_result_len, LED_TEST_BODY, "ON", 0, "OFF");
} else {
len = snprintf(result, max_result_len, LED_TEST_BODY, "OFF", 1, "ON");
}
}
return len;
}
err_t tcp_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
if (!p) {
DEBUG_printf("connection closed\n");
return tcp_close_client_connection(con_state, pcb, ERR_OK);
}
assert(con_state && con_state->pcb == pcb);
if (p->tot_len > 0) {
DEBUG_printf("tcp_server_recv %d err %d\n", p->tot_len, err);
#if 0
for (struct pbuf *q = p; q != NULL; q = q->next) {
DEBUG_printf("in: %.*s\n", q->len, q->payload);
}
#endif
// Copy the request into the buffer
pbuf_copy_partial(p, con_state->headers, p->tot_len > sizeof(con_state->headers) - 1 ? sizeof(con_state->headers) - 1 : p->tot_len, 0);
// Handle GET request
if (strncmp(HTTP_GET, con_state->headers, sizeof(HTTP_GET) - 1) == 0) {
char *request = con_state->headers + sizeof(HTTP_GET); // + space
char *params = strchr(request, '?');
if (params) {
if (*params) {
char *space = strchr(request, ' ');
*params++ = 0;
if (space) {
*space = 0;
}
} else {
params = NULL;
}
}
// Generate content
con_state->result_len = test_server_content(request, params, con_state->result, sizeof(con_state->result));
DEBUG_printf("Request: %s?%s\n", request, params);
DEBUG_printf("Result: %d\n", con_state->result_len);
// Check we had enough buffer space
if (con_state->result_len > sizeof(con_state->result) - 1) {
DEBUG_printf("Too much result data %d\n", con_state->result_len);
return tcp_close_client_connection(con_state, pcb, ERR_CLSD);
}
// Generate web page
if (con_state->result_len > 0) {
con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_HEADERS,
200, con_state->result_len);
if (con_state->header_len > sizeof(con_state->headers) - 1) {
DEBUG_printf("Too much header data %d\n", con_state->header_len);
return tcp_close_client_connection(con_state, pcb, ERR_CLSD);
}
} else {
// Send redirect
con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_REDIRECT,
ipaddr_ntoa(con_state->gw));
DEBUG_printf("Sending redirect %s", con_state->headers);
}
// Send the headers to the client
con_state->sent_len = 0;
err_t err = tcp_write(pcb, con_state->headers, con_state->header_len, 0);
if (err != ERR_OK) {
DEBUG_printf("failed to write header data %d\n", err);
return tcp_close_client_connection(con_state, pcb, err);
}
// Send the body to the client
if (con_state->result_len) {
err = tcp_write(pcb, con_state->result, con_state->result_len, 0);
if (err != ERR_OK) {
DEBUG_printf("failed to write result data %d\n", err);
return tcp_close_client_connection(con_state, pcb, err);
}
}
}
tcp_recved(pcb, p->tot_len);
}
pbuf_free(p);
return ERR_OK;
}
static err_t tcp_server_poll(void *arg, struct tcp_pcb *pcb) {
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
DEBUG_printf("tcp_server_poll_fn\n");
return tcp_close_client_connection(con_state, pcb, ERR_OK); // Just disconnect clent?
}
static void tcp_server_err(void *arg, err_t err) {
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
if (err != ERR_ABRT) {
DEBUG_printf("tcp_client_err_fn %d\n", err);
tcp_close_client_connection(con_state, con_state->pcb, err);
}
}
static err_t tcp_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) {
TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
if (err != ERR_OK || client_pcb == NULL) {
DEBUG_printf("failure in accept\n");
return ERR_VAL;
}
DEBUG_printf("client connected\n");
// Create the state for the connection
TCP_CONNECT_STATE_T *con_state = calloc(1, sizeof(TCP_CONNECT_STATE_T));
if (!con_state) {
DEBUG_printf("failed to allocate connect state\n");
return ERR_MEM;
}
con_state->pcb = client_pcb; // for checking
con_state->gw = &state->gw;
// setup connection to client
tcp_arg(client_pcb, con_state);
tcp_sent(client_pcb, tcp_server_sent);
tcp_recv(client_pcb, tcp_server_recv);
tcp_poll(client_pcb, tcp_server_poll, POLL_TIME_S * 2);
tcp_err(client_pcb, tcp_server_err);
return ERR_OK;
}
static bool tcp_server_open(void *arg, const char *ap_name) {
TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
DEBUG_printf("starting server on port %d\n", TCP_PORT);
struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
if (!pcb) {
DEBUG_printf("failed to create pcb\n");
return false;
}
err_t err = tcp_bind(pcb, IP_ANY_TYPE, TCP_PORT);
if (err) {
DEBUG_printf("failed to bind to port %d\n",TCP_PORT);
return false;
}
state->server_pcb = tcp_listen_with_backlog(pcb, 1);
if (!state->server_pcb) {
DEBUG_printf("failed to listen\n");
if (pcb) {
tcp_close(pcb);
}
return false;
}
tcp_arg(state->server_pcb, state);
tcp_accept(state->server_pcb, tcp_server_accept);
printf("Try connecting to '%s' (press 'd' to disable access point)\n", ap_name);
return true;
}
void key_pressed_func(void *param) {
assert(param);
TCP_SERVER_T *state = (TCP_SERVER_T*)param;
int key = getchar_timeout_us(0); // get any pending key press but don't wait
if (key == 'd' || key == 'D') {
cyw43_arch_lwip_begin();
cyw43_arch_disable_ap_mode();
cyw43_arch_lwip_end();
state->complete = true;
}
}
int main() {
stdio_init_all();
TCP_SERVER_T *state = calloc(1, sizeof(TCP_SERVER_T));
if (!state) {
DEBUG_printf("failed to allocate state\n");
return 1;
}
if (cyw43_arch_init()) {
DEBUG_printf("failed to initialise\n");
return 1;
}
// Get notified if the user presses a key
stdio_set_chars_available_callback(key_pressed_func, state);
const char *ap_name = "picow_test";
#if 1
const char *password = "password";
#else
const char *password = NULL;
#endif
cyw43_arch_enable_ap_mode(ap_name, password, CYW43_AUTH_WPA2_AES_PSK);
#if LWIP_IPV6
#define IP(x) ((x).u_addr.ip4)
#else
#define IP(x) (x)
#endif
ip4_addr_t mask;
IP(state->gw).addr = PP_HTONL(CYW43_DEFAULT_IP_AP_ADDRESS);
IP(mask).addr = PP_HTONL(CYW43_DEFAULT_IP_MASK);
#undef IP
// Start the dhcp server
dhcp_server_t dhcp_server;
dhcp_server_init(&dhcp_server, &state->gw, &mask);
// Start the dns server
dns_server_t dns_server;
dns_server_init(&dns_server, &state->gw);
if (!tcp_server_open(state, ap_name)) {
DEBUG_printf("failed to open server\n");
return 1;
}
state->complete = false;
while(!state->complete) {
// the following #ifdef is only here so this same example can be used in multiple modes;
// you do not need it in your code
#if PICO_CYW43_ARCH_POLL
// if you are using pico_cyw43_arch_poll, then you must poll periodically from your
// main loop (not from a timer interrupt) to check for Wi-Fi driver or lwIP work that needs to be done.
cyw43_arch_poll();
// you can poll as often as you like, however if you have nothing else to do you can
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
#else
// if you are not using pico_cyw43_arch_poll, then Wi-FI driver and lwIP work
// is done via interrupt in the background. This sleep is just an example of some (blocking)
// work you might be doing.
sleep_ms(1000);
#endif
}
tcp_server_close(state);
dns_server_deinit(&dns_server);
dhcp_server_deinit(&dhcp_server);
cyw43_arch_deinit();
printf("Test complete\n");
return 0;
}