V tomto článku popíšu měření a posílání dat pomocí virtuální privátní sítě Wireguard. Wireguard bude na IPv4, na IPv6 se mi ho na Picu nepodařilo rozchodit. Měřící sonda (Raspberry Pi PicoW) se připojí na Wifi a dostane IPv4 adresu, výchozí bránu a DNS z DHCP serveru.

Zdrojové kódy

cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico_w)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(PicoWG4_mereni C CXX ASM)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

pico_sdk_init()
include(pico_wireguard_import.cmake)

add_executable( PicoWG4_mereni
    main.c
    hodiny.c
    disp/ssd1306.c
)


target_include_directories( PicoWG4_mereni PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}
    ${pico_wireguard_SOURCE_DIR}/src
    ${CMAKE_CURRENT_LIST_DIR}/disp
)

include(arguments.cmake)

target_link_libraries( PicoWG4_mereni
    pico_stdlib
    pico_cyw43_arch_lwip_threadsafe_background
    pico_wireguard
    hardware_i2c
    hardware_adc
    hardware_rtc
)

pico_enable_stdio_usb(PicoWG4_mereni 1)
pico_enable_stdio_uart(PicoWG4_mereni 0)
pico_add_extra_outputs(PicoWG4_mereni)
if (DEFINED ENV{PICO_WIREGUARD_PATH} AND (NOT PICO_WIREGUARD_PATH))
    set(PICO_WIREGUARD_PATH $ENV{PICO_WIREGUARD_PATH})
    message("Using PICO_WIREGUARD_PATH from environment ('${PICO_WIREGUARD_PATH}')")
endif ()

if (NOT PICO_WIREGUARD_PATH)
  message(FATAL_ERROR "No pico wireguard found")
endif ()

add_subdirectory(${PICO_WIREGUARD_PATH} build/lib/pico_wireguard)
if (DEFINED ENV{WIFI_SSID} AND (NOT WIFI_SSID))
    set(WIFI_SSID $ENV{WIFI_SSID})
    message("Using WIFI_SSID from environment ('${WIFI_SSID}')")
endif()
if (NOT WIFI_SSID)
    message(FATAL_ERROR "Missing argument WIFI_SSID")
endif()

if (DEFINED ENV{WIFI_PASSWORD} AND (NOT WIFI_PASSWORD))
    set(WIFI_PASSWORD $ENV{WIFI_PASSWORD})
    message("Using WIFI_PASSWORD from environment ('${WIFI_PASSWORD}')")
endif()
if (NOT WIFI_PASSWORD)
    message(FATAL_ERROR "Missing argument WIFI_PASSWORD")
endif()

if (DEFINED ENV{WG_PRIVATE_KEY} AND (NOT WG_PRIVATE_KEY))
    set(WG_PRIVATE_KEY $ENV{WG_PRIVATE_KEY})
    message("Using WG_PRIVATE_KEY from environment ('${WG_PRIVATE_KEY}')")
endif()
if (NOT WG_PRIVATE_KEY)
    message(FATAL_ERROR "Missing argument WG_PRIVATE_KEY")
endif()

if (DEFINED ENV{WG_ADDRESS} AND (NOT WG_ADDRESS))
    set(WG_ADDRESS $ENV{WG_ADDRESS})
    message("Using WG_ADDRESS from environment ('${WG_ADDRESS}')")
endif()
if (NOT WG_ADDRESS)
    message(FATAL_ERROR "Missing argument WG_ADDRESS")
endif()

if (DEFINED ENV{WG_SUBNET_MASK_IP} AND (NOT WG_SUBNET_MASK_IP))
    set(WG_SUBNET_MASK_IP $ENV{WG_SUBNET_MASK_IP})
    message("Using WG_SUBNET_MASK_IP from environment ('${WG_SUBNET_MASK_IP}')")
endif()
if (NOT WG_SUBNET_MASK_IP)
    message(FATAL_ERROR "Missing argument WG_SUBNET_MASK_IP")
endif()

if (DEFINED ENV{WG_GATEWAY_IP} AND (NOT WG_GATEWAY_IP))
    set(WG_GATEWAY_IP $ENV{WG_GATEWAY_IP})
    message("Using WG_GATEWAY_IP from environment ('${WG_GATEWAY_IP}')")
endif()
if (NOT WG_GATEWAY_IP)
    message(FATAL_ERROR "Missing argument WG_GATEWAY_IP")
endif()

if (DEFINED ENV{WG_PUBLIC_KEY} AND (NOT WG_PUBLIC_KEY))
    set(WG_PUBLIC_KEY $ENV{WG_PUBLIC_KEY})
    message("Using WG_PUBLIC_KEY from environment ('${WG_PUBLIC_KEY}')")
endif()
if (NOT WG_PUBLIC_KEY)
    message(FATAL_ERROR "Missing argument WG_PUBLIC_KEY")
endif()

if (DEFINED ENV{WG_KEEPALIVE} AND (NOT WG_KEEPALIVE))
    set(WG_KEEPALIVE $ENV{WG_KEEPALIVE})
    message("Using WG_KEEPALIVE from environment ('${WG_KEEPALIVE}')")
endif()
if( NOT WG_KEEPALIVE)
    message(FATAL_ERROR "Missing argument WG_KEEPALIVE")
endif()

if (DEFINED ENV{WG_ALLOWED_IP} AND (NOT WG_ALLOWED_IP))
    set(WG_ALLOWED_IP $ENV{WG_ALLOWED_IP})
    message("Using WG_ALLOWED_IP from environment ('${WG_ALLOWED_IP}')")
endif()
if (NOT WG_ALLOWED_IP)
    message(FATAL_ERROR "Missing argument WG_ALLOWED_IP")
endif()

if (DEFINED ENV{WG_ALLOWED_IP_MASK_IP} AND (NOT WG_ALLOWED_IP_MASK_IP))
    set(WG_ALLOWED_IP_MASK_IP $ENV{WG_ALLOWED_IP_MASK_IP})
    message("Using WG_ALLOWED_IP_MASK_IP from environment ('${WG_ALLOWED_IP_MASK_IP}')")
endif()
if (NOT WG_ALLOWED_IP_MASK_IP)
    message(FATAL_ERROR "Missing argument WG_ALLOWED_IP_MASK_IP")
endif()

if (DEFINED ENV{WG_ENDPOINT_IP} AND (NOT WG_ENDPOINT_IP))
    set(WG_ENDPOINT_IP $ENV{WG_ENDPOINT_IP})
    message("Using WG_ENDPOINT_IP from environment ('${WG_ENDPOINT_IP}')")
endif()
if (NOT WG_ENDPOINT_IP)
    message(FATAL_ERROR "Missing argument WG_ENDPOINT_IP")
endif()

if (DEFINED ENV{WG_ENDPOINT_PORT} AND (NOT WG_ENDPOINT_PORT))
    set(WG_ENDPOINT_PORT $ENV{WG_ENDPOINT_PORT})
    message("Using WG_ENDPOINT_PORT from environment ('${WG_ENDPOINT_PORT}')")
endif()
if (NOT WG_ENDPOINT_PORT)
    message(FATAL_ERROR "Missing argument WG_ENDPOINT_PORT")
endif()

if (DEFINED ENV{NTP_SERVER} AND (NOT NTP_SERVER))
    set(NTP_SERVER $ENV{NTP_SERVER})
    message("Using NTP_SERVER from environment ('${NTP_SERVER}')")
endif()
if (NOT NTP_SERVER)
    message(FATAL_ERROR "Missing argument NTP_SERVER")
endif()

if (DEFINED ENV{MOJE_HOSTNAME} AND (NOT MOJE_HOSTNAME))
    set(MOJE_HOSTNAME $ENV{MOJE_HOSTNAME})
    message("Using MOJE_HOSTNAME from environment ('${MOJE_HOSTNAME}')")
endif()
if (NOT MOJE_HOSTNAME)
    message(FATAL_ERROR "Missing argument MOJE_HOSTNAME")
endif()

if (DEFINED ENV{MERENI_PORT} AND (NOT MERENI_PORT))
    set(MERENI_PORT $ENV{MERENI_PORT})
    message("Using MERENI_PORT from environment ('${MERENI_PORT}')")
endif()
if (NOT MERENI_PORT)
    message(FATAL_ERROR "Missing argument MERENI_PORT")
endif()

target_compile_definitions(PicoWG4_mereni PRIVATE
         WIFI_SSID=${WIFI_SSID}
         WIFI_PASSWORD=${WIFI_PASSWORD}
         WG_PRIVATE_KEY=${WG_PRIVATE_KEY}
         WG_ADDRESS=${WG_ADDRESS}
         WG_SUBNET_MASK_IP=${WG_SUBNET_MASK_IP}
         WG_GATEWAY_IP=${WG_GATEWAY_IP}
         WG_PUBLIC_KEY=${WG_PUBLIC_KEY}
         WG_KEEPALIVE=${WG_KEEPALIVE}
         WG_ALLOWED_IP=${WG_ALLOWED_IP}
         WG_ALLOWED_IP_MASK_IP=${WG_ALLOWED_IP_MASK_IP}
         WG_ENDPOINT_IP=${WG_ENDPOINT_IP}
         WG_ENDPOINT_PORT=${WG_ENDPOINT_PORT}
         NTP_SERVER=${NTP_SERVER}
         MOJE_HOSTNAME=${MOJE_HOSTNAME}
         MERENI_PORT=${MERENI_PORT}
        )
link:lwipopts.h
#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
#define MEM_SIZE 4000
#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__ */
/* Nastavení Wifi karty */

int setup(uint32_t country, const char *ssid, const char *pass,
          uint32_t auth, const char *hostname, ip_addr_t *ip,
          ip_addr_t *mask, ip_addr_t *gw)
{

    if (cyw43_arch_init_with_country(country))
    {
        return 1;
    }

    cyw43_arch_enable_sta_mode();
    if (hostname != NULL)
    {
        netif_set_hostname(netif_default, hostname);
    }
    if (cyw43_arch_wifi_connect_async(ssid, pass, auth))
    {
        return 2;
    }
    int flashrate = 1000;
    int status = CYW43_LINK_UP + 1;
    while (status >= 0 && status != CYW43_LINK_UP)
    {
        int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
        if (new_status != status)
        {
            status = new_status;
            flashrate = flashrate / (status + 1);
            printf("stav připojení k Wifi: %d %d\n", status, flashrate);
        }
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
        sleep_ms(flashrate);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
        sleep_ms(flashrate);
    }
    if (status < 0)
    {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
    }
    else
    {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
        if (ip != NULL)
        {
            netif_set_ipaddr(netif_default, ip);
        }
        if (mask != NULL)
        {
            netif_set_netmask(netif_default, mask);
        }
        if (gw != NULL)
        {
            netif_set_gw(netif_default, gw);
        }

        printf("IP: %s\n",        ip4addr_ntoa(netif_ip_addr4(netif_default)));
        printf("Maska: %s\n",      ip4addr_ntoa(netif_ip_netmask4(netif_default)));
        printf("Brána: %s\n",   ip4addr_ntoa(netif_ip_gw4(netif_default)));
        printf("Hostname: %s\n", netif_get_hostname(netif_default));
    }
    return status;
}
/* hodiny.h
*
*/

struct timeStatus
{
    bool ready;
    struct udp_pcb *pcb;
};

// získání času z hodin Pica
bool getDateNow(struct tm *t);

bool pollSNTP(struct timeStatus *tstatus);

struct timeStatus *getSNTP();
void cancelSNTP(struct timeStatus *tstatus);
void dns_found(const char *name, const ip_addr_t *ip, void *arg);
/* hodiny.c
 * Nastavení hodin pomocí SNTP protokolu
 * (c) Jirka Chráska 2025, <jirka@lixis.cz>
 */

#include <stdio.h>

#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "hardware/rtc.h"
#include "time.h"
#include "lwip/dns.h"

#define STRINGIFY(x) #x
#define TO_STRING(x) STRINGIFY(x)


// nastavení časové zóny Europe/Prague
// středoevropský čas (v létě to nebude fungovat)
#define TIMEZONE 3600
//#define NTP_SERVER "tik.cesnet.cz"

// získání času z hodin Pica
bool getDateNow(struct tm *t)
{
    datetime_t rtc;
    bool state = rtc_get_datetime(&rtc);
    if (state)
    {

        t->tm_sec = rtc.sec;
        t->tm_min = rtc.min;
        t->tm_hour = rtc.hour;
        t->tm_mday = rtc.day;
        t->tm_mon = rtc.month - 1;
        t->tm_year = rtc.year - 1900;
        t->tm_wday = rtc.dotw;
        t->tm_yday = 0;
        t->tm_isdst = -1;
    }
    return state;
}

// nastavení hodin reálného času na Picu
void setRTC(struct tm *datetime)
{
    datetime_t t;
    t.year = datetime->tm_year + 1900;
    t.month = datetime->tm_mon + 1;
    t.day = datetime->tm_mday;
    t.dotw = datetime->tm_wday;
    t.hour = datetime->tm_hour;
    t.min = datetime->tm_min;
    t.sec = datetime->tm_sec;
    rtc_init();
    rtc_set_datetime(&t);
}

struct timeStatus
{
    bool ready;
    struct udp_pcb *pcb;
};

void recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    struct timeStatus *tstatus = (struct timeStatus *)arg;
    printf("SNTP odpovídá\n");
    if (p != NULL)
    {
        uint8_t seconds_buf[4];
        pbuf_copy_partial(p, seconds_buf, sizeof(seconds_buf), 40);
        uint32_t seconds_since_1900 = seconds_buf[0] << 24 | seconds_buf[1] << 16 | seconds_buf[2] << 8 | seconds_buf[3];
        time_t seconds_since_1970 = seconds_since_1900 - 2208988800 + TIMEZONE;
        struct tm *datetime = gmtime(&seconds_since_1970);
        setRTC(datetime);
        pbuf_free(p);
        udp_remove(pcb);
        tstatus->pcb=NULL;
        tstatus->ready = true;
    }
}

bool pollSNTP(struct timeStatus *tstatus)
{
    if (tstatus == NULL)
        return true;
    if (tstatus->ready)
    {
        free(tstatus);
        tstatus = NULL;
        return true;
    }
    return false;
}

void dns_found(const char *name, const ip_addr_t *ip, void *arg)
{
    struct timeStatus *tstatus = (struct timeStatus *)arg;
    printf("IP NTP serveru z DNS %s\n", ipaddr_ntoa(ip));
    struct udp_pcb *pcb = udp_new();
    tstatus->pcb = pcb;
    udp_recv(pcb, recv, arg);
    udp_bind(pcb, IP_ADDR_ANY, 123);
    udp_connect(pcb, ip, 123);
    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 48, PBUF_RAM);
    uint8_t *payload = (uint8_t *)p->payload;
    memset(payload, 0, 48);
    *payload = 0x1b;
    err_t er = udp_send(pcb, p);
    pbuf_free(p);
}

struct timeStatus *getSNTP()
{
    struct timeStatus *tstatus = malloc(sizeof(struct timeStatus));
    tstatus->ready = false;
    tstatus->pcb=NULL;
    ip_addr_t ip;
    cyw43_arch_lwip_begin();
    err_t err = dns_gethostbyname(TO_STRING(NTP_SERVER), &ip, dns_found, tstatus);
    cyw43_arch_lwip_end();
    if (err == ERR_OK)
    {
        printf("DNS cache %s\n", ipaddr_ntoa(&ip));
        dns_found("", &ip, tstatus);
    }
    return tstatus;
}

void cancelSNTP(struct timeStatus *tstatus)
{
    if (tstatus != NULL)
    {
        udp_remove(tstatus->pcb);
        free(tstatus);
        tstatus = NULL;
        printf("čekám na DNS\n");
    }
}
/* Posílání hodnot teploty pomocí wireguardu.
 * (c) Jirka Chráska 2026, <jirka@lixis.cz
 * Verze 0.9
 * Nevyřešený problém:
 * Někdy se wireguard připojuje hodně dlouho.
 * Stačí na serveru udělat: wg setconf wg0 /etc/wireguard/wg0.conf 
 * a připojí se to téměř okamžitě.
 * 
 */
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/adc.h"
#include "pico/cyw43_arch.h"

#include <lwip/ip.h>
#include <lwip/ip_addr.h>
#include <lwip/netif.h>
#include <lwip/pbuf.h>
#include <lwip/udp.h>
#include "wireguardif.h"
#include "setupWifi.h"
#include "time.h"
#include "hodiny.h"

//#include "argument_definitions.h"

#define STRINGIFY(x) #x
#define TO_STRING(x) STRINGIFY(x)

#define WG_LISTEN_PORT		51820
#define KOREKCE_TEPLOTY     -5.5
#define KAM_POSLAT TO_STRING(WG_GATEWAY_IP)

static const char *net_errors[] = { "OK", 
	                                "ERR_MEM", 
									"ERR_BUF", 
									"ERR_TIMEOUT", 
									"ERR_RTE", 
									"ERR_INPROGRESS", 
									"ERR_VAL", 
									"ERR_WOULDBLOCK", 
									"ERR_USE", 
									"ERR_ALREADY", 
									"ERR_ISCONN", 
									"ERR_CONN", 
									"ERR_IF", 
									"ERR_ABRT", 
									"ERR_RST", 
									"ERR_CLSD", 
									"ERR_ARG", 
									"ERR_UNKNOWN" };


// ---------------------------------------------------------------------------------
// chybová hlášení převedená na řetězce
const char * chyba( int err )
{
const char *err_str;
switch(err) {
			case   0: err_str = net_errors[0]; 	break; /** No error, everything OK. */
			case  -1: err_str = net_errors[1]; 	break; /** Out of memory error.     */
			case  -2: err_str = net_errors[2];  break; /** Out .     */
			case  -3: err_str = net_errors[3]; 	break; /** Buffer error.            */
			case  -4: err_str = net_errors[4];	break;
			case  -5: err_str = net_errors[5];	break;
			case  -6: err_str = net_errors[6];	break;
			case  -7: err_str = net_errors[7];	break;
			case  -8: err_str = net_errors[8];	break;
			case  -9: err_str = net_errors[9];	break;
			case -10: err_str = net_errors[10];	break;
			case -11: err_str = net_errors[11];	break;
			case -12: err_str = net_errors[12];	break;
			case -13: err_str = net_errors[13];	break;
			case -14: err_str = net_errors[14];	break;
			case -15: err_str = net_errors[15];	break;
			case -16: err_str = net_errors[16];	break;
			default:  err_str = net_errors[17]; break;
		}	
return err_str;
}
// ---------------------------------------------------------------------------------




static struct netif wg_netif_struct = {0};
static struct netif *wg_netif = NULL;
static uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;

static void wireguard_setup() 
{
	struct wireguardif_init_data wg;
	struct wireguardif_peer peer;

	memset(&wg,0,sizeof(wg));
	
	// nastavení wireguard sítě
	ip_addr_t ipaddr  ;
	ipaddr_aton(TO_STRING(WG_ADDRESS), &ipaddr);
	ip_addr_t netmask ;
	ipaddr_aton(TO_STRING(WG_SUBNET_MASK_IP), &netmask);
	ip_addr_t gateway ;
	ipaddr_aton(TO_STRING(WG_GATEWAY_IP), &gateway);
    

	// Nastavení struktury zařízení WireGuard 
	wg.private_key = TO_STRING(WG_PRIVATE_KEY);
	wg.listen_port = WG_LISTEN_PORT ;
	printf("WG listen port: %d\n", wg.listen_port);
	wg.bind_netif = NULL;

	// Registrace nové WireGuard síťové karty do lwIP
	wg_netif = netif_add(&wg_netif_struct, &ipaddr, &netmask, &gateway, &wg, &wireguardif_init, &ip_input);

	// Mark the interface as administratively up, link up flag is set automatically when peer connects
	netif_set_up(wg_netif);

	// Inicializace struktury prvního konce WireGuard 
	wireguardif_peer_init(&peer);
	peer.public_key    = TO_STRING(WG_PUBLIC_KEY);
	
	printf("WG public_key: %s\n", peer.public_key);
	peer.preshared_key = NULL;
	peer.keep_alive    = WG_KEEPALIVE ;
	printf("WG keep_alive: %d\n", peer.keep_alive);
	
	peer.allowed_ip = gateway;
	peer.allowed_mask = netmask;
    
	// IP adresa a port endpointu
	ipaddr_aton(TO_STRING(WG_ENDPOINT_IP), &peer.endpoint_ip);
	peer.endport_port = WG_ENDPOINT_PORT ;
	printf("WG enpoint: port: %d\n",peer.endport_port); 

	// Register the new WireGuard peer with the netwok interface
	err_t e = wireguardif_add_peer(wg_netif, &peer, &wireguard_peer_index);
	printf("wireguardif_add_peer: %s\n", chyba(e));
	
	if ((wireguard_peer_index != WIREGUARDIF_INVALID_INDEX) && !ip_addr_isany(&peer.endpoint_ip)) {
		// Start outbound connection to peer
		err_t err = wireguardif_connect(wg_netif, wireguard_peer_index);
		printf("wireguardif_connect: %s\n", chyba(err));
	}
}

// OLED displej
#include "disp/ssd1306.h"
#include "disp/font_spleen_8x5.h"
#include "disp/font_spleen_16x8a.h"

#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 32
#define I2C_ADDRESS 0x3C
#define I2C_FREQ 400000

uint SDA_PIN=4;
uint SCL_PIN=5;

ssd1306_t disp;

// nastavení I2C sběrnice
void setup_i2c(void)
{
    i2c_init(i2c0, I2C_FREQ);
    gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(SCL_PIN, GPIO_FUNC_I2C);
    // Pull-up rezistory jsou v displeji, netřeba nastavovat
}

void zapnuti_displeje( void )
{
    ssd1306_init(&disp, DISPLAY_WIDTH, DISPLAY_HEIGHT, I2C_ADDRESS, i2c0); // inicializace
    ssd1306_poweron(&disp); // zapnutí displeje
    ssd1306_clear(&disp);   // vymazání displeje  
    ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_16x8a, TO_STRING( MOJE_HOSTNAME ));
    ssd1306_show(&disp); // zobrazíme písmenka na displeji
}
// --------------------------------------------------------------------------

int connect()
{
    
    char ssid[]      = TO_STRING( WIFI_SSID );
    char pass[]      = TO_STRING( WIFI_PASSWORD );

	uint32_t country = CYW43_COUNTRY_CZECH_REPUBLIC;
    uint32_t auth    = CYW43_AUTH_WPA2_MIXED_PSK;
    return  setup(country, ssid, pass, auth, TO_STRING( MOJE_HOSTNAME ), NULL, NULL, NULL);
}

ip_addr_t   moje_ip;
ip_addr_t   ip_kam;
static struct udp_pcb *pcb;

void setup_posilani()
{
	pcb = udp_new();
}

err_t send_temperature(ip_addr_t *ip, uint16_t port, float temp, struct tm *t)
{
char buf1[128];

	struct pbuf *packet = pbuf_alloc(PBUF_TRANSPORT,256,PBUF_RAM);
	char *payload = (char *) packet->payload;
	memset(payload,0,256);
	strftime(buf1, sizeof(buf1), "%d %b %Y %k:%M:%S %Z", t);
	sprintf(payload,"%s: Teplota %5.2f C, Cas %s", TO_STRING( MOJE_HOSTNAME ), temp, buf1);
	err_t error = udp_sendto(pcb, packet, ip, port);
	pbuf_free(packet);
	
return error;
}

int main()
{
int         connection_status = 0;
uint16_t    result;
float       teplota;
float       adc;
const float conversion_factor = 3.319f / ( 1 << 12 );
char        buf1[128];
int         len;
char        message[256];
ip_addr_t   wg_endpoint;
struct tm t;

	memset(message,'\0',256);
    memset(buf1,'\0',128);
    stdio_init_all();
	sleep_ms(1000); // pro připojení minicomu
	
	setup_i2c();
    zapnuti_displeje();
	
	printf("\nMěření teploty a posílání na server Wireguardem\n");
	printf("(c) Jirka Chráska 2026, <jirka@lixis.cz>\n");
	printf("Verze 0.9\n");
    
    // nastavení a výběr interního čidla
    adc_init();
    adc_set_temp_sensor_enabled(true);
    adc_select_input(4);

    // nastavení měniče na malý šum
    gpio_set_function(23, GPIO_FUNC_SIO);
    gpio_set_dir( 23, true );
    gpio_put( 23, 1 );
	
    connect();
	while (true)
    {
        ssd1306_clear(&disp);
		ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, "Hodiny...");
		ssd1306_show(&disp); // zobrazíme písmenka na displeji
		
		struct timeStatus *tstatus = getSNTP();
		
        sleep_ms(500);
        if (pollSNTP(tstatus))
            break;
        cancelSNTP(tstatus);
    }
    wireguard_setup();
	setup_posilani();
	
	ip4addr_aton(KAM_POSLAT,&ip_kam);
	ip4addr_aton(TO_STRING( WG_ENDPOINT_IP ),&wg_endpoint);
    err_t er;
	char *err_str;
	u16_t wgport = WG_ENDPOINT_PORT;	
	do {
		getDateNow(&t);
		er = wireguardif_peer_is_up(wg_netif, wireguard_peer_index, &wg_endpoint, &wgport);
		err_str = (char *) chyba(er);
		strftime(buf1, sizeof(buf1), "%a, %d %b %Y %k:%M:%S", &t);
		printf("Peer %s:%d %s: čas: %s\r",TO_STRING( WG_ENDPOINT_IP ), wgport, err_str, buf1);
        ssd1306_clear(&disp);
		sprintf(buf1,"WG peer %s", err_str);
        ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf1);
		sprintf(buf1,"%s:%d",TO_STRING( WG_ENDPOINT_IP ), wgport);
		ssd1306_draw_string_with_font(&disp, 0, 8, 1, font_spleen_8x5, buf1);
		strftime(buf1, sizeof(buf1), "%a, %d %b %Y", &t);
		ssd1306_draw_string_with_font(&disp, 0, 16, 1, font_spleen_8x5, buf1);
		strftime(buf1, sizeof(buf1), "%k:%M:%S %Z", &t);
		ssd1306_draw_string_with_font(&disp, 0, 24, 1, font_spleen_8x5, buf1);
        ssd1306_show(&disp); // zobrazíme písmenka na displeji
		sleep_ms(500);
	} while (er != 0 );
	
    while( true ){
        result = adc_read();
        adc = result * conversion_factor;
        // teplota bez korekce
        teplota = 27.0f - (adc - 0.706f) / 0.001721f;
		teplota += KOREKCE_TEPLOTY;
        sprintf(buf1,"%4.1f%cC", teplota, 0xb0);
        ssd1306_clear(&disp);
        ssd1306_draw_string_with_font(&disp, 0, 0, 2, font_spleen_16x8a, buf1);
		getDateNow(&t);
		strftime(buf1, sizeof(buf1), "%d %b %Y %k:%M:%S %Z", &t);
		ssd1306_draw_string_with_font(&disp, 0, 25, 1, font_spleen_8x5, buf1);
        ssd1306_show(&disp); // zobrazíme písmenka na displeji
        er = send_temperature(&ip_kam,MERENI_PORT,teplota, &t);
		err_str = (char *) chyba(er);
		printf("Poslal jsem: 'Teplota: %5.1f ˚C', Vzdálená IP: %s %s\r", teplota, KAM_POSLAT, err_str);
        sleep_ms(2000);  
    }
    return 0;
}
UDP server v pythonu udp_server.py
import asyncio

class ClientDatagramProtocol(asyncio.DatagramProtocol):
        def datagram_received(self, data, addr):
                message = data.decode("utf8")
                print("Received", message, "from", addr)

async def main():
        loop = asyncio.get_running_loop()
        transport, protocol = await loop.create_datagram_endpoint(
            lambda: ClientDatagramProtocol(),
            local_addr=('10.205.40.1', 4848))
        await asyncio.sleep(100000)
        transport.close()

asyncio.run(main())
Sestavení programu
git clone https://github.com/Mr-Pine/pi-pico-wireguard-lwip
PICO_WIREGUARD_PATH=/home/jirka/vyuka_sspvc/pmp/wifi/pi-pico-wireguard-lwip/
export WIFI_SSID="ssid"
export WIFI_PASSWORD="heslokwifi"
export WG_PRIVATE_KEY="muj privatni klic"
export WG_ADDRESS="10.205.40.2"
export WG_SUBNET_MASK_IP="255.255.255.0"
export WG_GATEWAY_IP="10.205.40.1"
export WG_PUBLIC_KEY="IFmzhnnuVCbTqRAPAu0PD8GS3JauSptI/5rKfJTuozM="
export WG_KEEPALIVE=10
export WG_ALLOWED_IP="10.205.40.1"
export WG_ALLOWED_IP_MASK_IP="255.255.255.255"
export WG_ENDPOINT_IP="46.253.99.44"
export WG_ENDPOINT_PORT=48484
export NTP_SERVER="tik.cesnet.cz"
export MOJE_HOSTNAME="wg4-cidlo2"
export MERENI_PORT=4848
mkdir build
cd build
cmake ..
make -j8
cp PicoWG4_mereni.uf2 /media/jirka/RPI-RP2/
Konfigurace wireguardu na serveru OpenBSD /etc/wireguard/wg0.conf
# wireguard server mereni sit 10.205.40.0/24
# IP wg server 10.205.40.1/24
[Interface]
PrivateKey = privatni klic serveru
ListenPort = 48484

# cidlo1        IP 10.205.40.2/24
[Peer]
PublicKey  = DyhXCHh+L9dwRsiCFM+W7KOQEkgQneUz+aJleUrTXV0=
AllowedIPs = 10.205.40.2
Konfigurace wireguard rozhraní na serveru OpenBSD /etc/hostname.wg0
inet 10.205.40.1 255.255.255.0 NONE
up
!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf
Výstup na klientu (minicom -b 115200 -D /dev/ttyACM0)
Měření teploty a posílání na server Wireguardem
(c) Jirka Chráska 2026, <jirka@lixis.cz>
Verze 0.9
stav připojení k Wifi: 2 166
stav připojení k Wifi: 3 41
IP: 192.168.120.183
Maska: 255.255.255.0
Brána: 192.168.120.2
Hostname: wg4-cidlo2
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
čekám na DNS
IP NTP serveru z DNS 195.113.144.201
IP NTP serveru z DNS 195.113.144.201
IP NTP serveru z DNS 195.113.144.201
IP NTP serveru z DNS 195.113.144.201
SNTP odpovídá
SNTP odpovídá
WG listen port: 51820
Device init took 35ms
WG public_key: IFmzhnnuVCbTqRAPAu0PD8GS3JauSptI/5rKfJTuozM=
WG keep_alive: 10
WG enpoint: port: 48484
Adding peer took 34ms
wireguardif_add_peer: OK
wireguardif_connect: OK
Poslal jsem: 'Teplota: 20.7 ˚C', Vzdálená IP: 10.205.40.1 OK
wg0 na serveru
mereni# wg
interface: wg0
  public key: IFmzhnnuVCbTqRAPAu0PD8GS3JauSptI/5rKfJTuozM=
  private key: (hidden)
  listening port: 48484

peer: DyhXCHh+L9dwRsiCFM+W7KOQEkgQneUz+aJleUrTXV0=
  endpoint: 109.81.118.47:11497
  allowed ips: 10.205.40.2/32
  latest handshake: 59 seconds ago
  transfer: 482.70 KiB received, 14.99 KiB sent
mereni#
Výstup z udp_server.py na serveru
mereni# python3 udp_server.py
Received wg4-cidlo2: Teplota 20.69 C, Cas 02 Jan 2026 15:53:02  from ('10.205.40.2', 51507)
Received wg4-cidlo2: Teplota 20.22 C, Cas 02 Jan 2026 15:53:04  from ('10.205.40.2', 51507)
Received wg4-cidlo2: Teplota 20.69 C, Cas 02 Jan 2026 15:53:06  from ('10.205.40.2', 51507)
Received wg4-cidlo2: Teplota 20.22 C, Cas 02 Jan 2026 15:53:08  from ('10.205.40.2', 51507)
Received wg4-cidlo2: Teplota 20.69 C, Cas 02 Jan 2026 15:53:10  from ('10.205.40.2', 51507)
Received wg4-cidlo2: Teplota 20.22 C, Cas 02 Jan 2026 15:53:12  from ('10.205.40.2', 51507)
Received wg4-cidlo2: Teplota 20.22 C, Cas 02 Jan 2026 15:53:14  from ('10.205.40.2', 51507)

Zdroje a odkazy