/* main.c
 * Pico W wireguard IPv6: 
 * skenování sítí a nalezení sítí, ke ktrým se můžeme připojit
 * SNTP klient a seřízení času
 * Měření teploty interním čidlem a posílání hodnot Wireguardem na server.
 * 
 * (c) Jirka Chráska 2026; <jirka@lixis.cz>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 * 3. Neither the name of "Lixis s.r.o." nor the names of
 *  its contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Jirka Chráska <jirka@lixis.cz>
 */

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "wireguardif.h"
#include "pico/cyw43_arch.h"
#include "flash_utils.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "lwip/pbuf.h"
#include "lwip/ip6_addr.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include "hardware/rtc.h"
#include "time.h"
#include "hardware/adc.h"
#include "setupWifi.h"

#define HOSTNAME            "cidlo2-ipv6"
#define MERENI_PORT         4848
#define KAM_POSLAT          "fd68:abcd::1"
#define MOJE_WG_IP          "fd68:abcd::2"
#define PEER_PUBKEY         "XSqRUMJxLgFu9hejbzKzDpjCkibzNaek3Ql6sMRQBXk="
#define MUJ_PRIVKEY         "wKXLsUiN8B196cWy5e+i90JIYyI8a/UG1+O++Bb4ImI="
#define WG_ADDRESS          "fd68:abcd::2"
#define WG_GATEWAY_IP       "fd68:abcd::1"
#define IPV6_ANY            "::"
#define WG_SUBNET_MASK_IP   "ffff:ffff:ffff:ffff:0:0:0:0"
#define PEER_HOSTNAME       "m1.jr.lixis.cz"
#define PEER_IPV6           "2a0e:5340:4:1::801"
#define WG_ENDPOINT_PORT    48484
#define WG_KEEPALIVE        15
#define WG_ALLOWED_IP1      "fd68:abcd::1"
#define WG_ALLOWED_MASK1    "ffff:ffff:ffff:ffff:0:0:0:0"
// SNTP
#define NTP_SERVER          "tik.cesnet.cz"
#define TIMEZONE            3600

// převod chyb z LWIP na řetezec
char * error_to_string( err_t e )
{
static char * err_ok         = "OK";
static char * err_mem        = "ERR_MEM";
static char * err_buf        = "ERR_BUF";
static char * err_timeout    = "ERR_TIMEOUT";
static char * err_rte        = "ERR_RTE";
static char * err_inprogress = "ERR_INPROGRESS";
static char * err_val        = "ERR_VAL"; 
static char * err_wouldblock = "ERR_WOULDBLOCK";
static char * err_use        = "ERR_USE";
static char * err_already    = "ERR_ALREADY"; 
static char * err_isconn     = "ERR_ISCONN";
static char * err_conn       = "ERR_CONN";
static char * err_if         = "ERR_IF";
static char * err_abrt       = "ERR_ABRT";
static char * err_rst        = "ERR_RST";
static char * err_clsd       = "ERR_CLSD";
static char * err_arg        = "ERR_ARG";
char * err_str = err_ok;
    switch(e) {
        case   0: err_str = err_ok;         break; /* No error, everything OK.       */
        case  -1: err_str = err_mem;        break; /* Out of memory error.           */
        case  -2: err_str = err_buf;        break; /* Buffer error.                  */
        case  -3: err_str = err_timeout;    break; /* Timeout error.                 */
        case  -4: err_str = err_rte;        break; /* Routing problem                */
        case  -5: err_str = err_inprogress; break; /* Operation in progress          */
        case  -6: err_str = err_val;        break; /* Illegal value                  */
        case  -7: err_str = err_wouldblock; break; /* Operation would block          */
        case  -8: err_str = err_use;        break; /* Address in use                 */
        case  -9: err_str = err_already;    break; /* Already connecting             */
        case -10: err_str = err_isconn;     break; /* Connection already established */
        case -11: err_str = err_conn;       break; /* Not connected                  */
        case -12: err_str = err_if;         break; /* Low-level netif error          */
        case -13: err_str = err_abrt;       break; /* Connection aborted             */
        case -14: err_str = err_rst;        break; /* Connection reset.              */
        case -15: err_str = err_clsd;       break; /* Connection closed.             */
        case -16: err_str = err_arg;        break; /* Illegal argument               */
    }    
return err_str;
}
// --------------------------------------------------------------------------------------------
// připojení wireguardu

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


void connect_wireguard() 
{
  struct wireguardif_init_data wg;
  struct wireguardif_peer peer;
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t allowed_ip;
  ip_addr_t gateway;
  s8_t route_idx;
  err_t e;
  char *err_str = "";
  
  ip6addr_aton(WG_ADDRESS, &ipaddr);
  ip6addr_aton(WG_SUBNET_MASK_IP, &netmask);
  ip6addr_aton(WG_ALLOWED_IP1, &allowed_ip);
  ip6addr_aton(WG_GATEWAY_IP,  &gateway);
  
  wg.private_key = MUJ_PRIVKEY;
  wg.listen_port = 51881;
  wg.bind_netif  = NULL;

  wg_netif = netif_add(&wg_netif_struct, &wg, &wireguardif_init, &ip6_input);
  netif_add_ip6_address(wg_netif, &ipaddr, NETIF_NO_INDEX );
  netif_set_up(wg_netif);
  
  wireguardif_peer_init(&peer);
  peer.public_key    = PEER_PUBKEY;
  peer.preshared_key = NULL;
  peer.keep_alive    = WG_KEEPALIVE;
  peer.allowed_ip    = allowed_ip;
  peer.allowed_mask  = netmask;
  ip6addr_aton(PEER_IPV6, &peer.endpoint_ip);
  peer.endport_port  = WG_ENDPOINT_PORT;

  wireguardif_add_peer(wg_netif, &peer, &wireguard_peer_index);

  if ((wireguard_peer_index != WIREGUARDIF_INVALID_INDEX) &&
      !ip_addr_isany(&peer.endpoint_ip)) {
    err_t e = wireguardif_connect(wg_netif, wireguard_peer_index);
    printf("wireguardif_connect %s\n",error_to_string(e));
  }  
}
// --------------------------------------------------------------------------------------------
// 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 2000000

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);
    gpio_pull_up(SDA_PIN);
    gpio_pull_up(SCL_PIN);
}

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, HOSTNAME);
    ssd1306_show(&disp); // zobrazíme písmenka na displeji
}

// ---------------------------------------------------------------------------
// známé sítě jsou uloženy ve flash paměti

const uint8_t *flash_target_content; 

// struktura údajů o známé síti
typedef struct wifi_zname {
        char* ssid;     // jméno sítě
        char* pass;     // heslo v otevřeném textu
        char* auth;     // způsob autorizace
        char* ipv6;     // umí síť IPv6 (y/n)
        char* ipv4;     // umí síť IPv4 (y/n)
        int   rssi;     // aktuální síla signálu
        struct wifi_zname * next; // ukazatel na další prvek seznamu
} ZNAME_WIFI;

// ukazatel na počátek seznamu známých sítí
ZNAME_WIFI* zwifi = NULL;

// vytvoří seznam známých Wifi, ke kterým se můžeme připojit 
void napln_zname_wifi(void)
{
char *wifidata; // pomocný ukazatel do XIP flash
ZNAME_WIFI *zw;

    flash_target_content = (const uint8_t *) getAddressPersistent();
    for( int i=0; i<8; i++ ){
        wifidata = (char*) flash_target_content+(i*128);
        zw = malloc(sizeof(ZNAME_WIFI));
        if(zw != NULL) {
            zw->ssid = wifidata+5;
            // printf("%p ssid='%s' ", wifidata, zw->ssid);
            int len = strlen(zw->ssid)+6;
            zw->pass = wifidata+len+5;
            // printf("%p pass='%s' ", wifidata, zw->pass);
            len += strlen(zw->pass)+6;
            zw->auth = wifidata+len+5;
            // printf("%p auth='%s' ", wifidata, zw->auth);
            len += strlen(zw->auth)+6;
            zw->ipv6 = wifidata+len+5;
            //printf("%p ipv6='%s' ", wifidata, zw->ipv6);
            len += strlen(zw->ipv6)+6;
            zw->ipv4 = wifidata+len+5;
            // printf("%p ipv4='%s'\n", wifidata, zw->ipv4);
            // počáteční signál -120 dBm 
            zw->rssi = -120;
            if( zwifi != NULL ) { // v seznamu už něco máme
                zw->next = zwifi;
            } else {              // vkládáme první prvek (v seznamu bude poslední) 
                zw->next = NULL;
            }
            zwifi = zw;
        } else {
            printf("Nedostatek paměti.\n");
            break;
        }
    }
}

// najde v seznamu známou WiFi podle ssid
ZNAME_WIFI * najdi_wifi(const char *ssid) 
{
    for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
        if(strcmp(zw->ssid, ssid) == 0 ) {
            return zw;
        }
    }
return NULL;
}

// najde v seznamu Wifi s nejlepším signálem
ZNAME_WIFI *najdi_nejvetsi_rssi( void )
{
int rssi = -120;
ZNAME_WIFI *res = NULL;
    for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
        if(zw->rssi > rssi) {
            res = zw;
            rssi = zw->rssi;
        }
    }
return res;
}

// uvolní seznam známých wifi
void zrus_zname_wifi( void )
{
    for( ZNAME_WIFI *ptr=zwifi; ptr!=NULL;  ) {
        ZNAME_WIFI *ptr2 = ptr;
        ptr = ptr->next;
        free(ptr2);
    }
}

// callback, výsledky skenování
static int scan_result(void *env, const cyw43_ev_scan_result_t *result)
{
    if (result)
    {
        ZNAME_WIFI *znam = najdi_wifi( result->ssid );
        if( znam != NULL) {
            if( result->rssi > znam->rssi ) {
                znam->rssi = result->rssi;
            }
            printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
               result->ssid, result->rssi, result->channel,
               result->bssid[0], result->bssid[1], result->bssid[2],
               result->bssid[3], result->bssid[4], result->bssid[5],
               (result->auth_mode == 5) ? "WPA2_MIXED_PSK" : 
               (result->auth_mode == 7) ? "WPA_TKIP_PSK" : 
               (result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
               "yes" );
        } else {
            printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
               result->ssid, result->rssi, result->channel,
               result->bssid[0], result->bssid[1], result->bssid[2],
               result->bssid[3], result->bssid[4], result->bssid[5],
               (result->auth_mode == 5) ? "WPA2_MIXED_PSK" : 
               (result->auth_mode == 7) ? "WPA_TKIP_PSK" : 
               (result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
               "no" );
        }
    }
    return 0;
}
// připojení k AP
int connect( ZNAME_WIFI *zw, char * hostname )
{
    uint32_t country = CYW43_COUNTRY_CZECH_REPUBLIC;
    uint32_t auth;
    if( strcmp(zw->auth,"WPA2_MIXED_PSK") == 0) auth = CYW43_AUTH_WPA2_MIXED_PSK;
    if( strcmp(zw->auth,"WPA_TKIP_PSK") == 0)   auth = CYW43_AUTH_WPA_TKIP_PSK;
    // používá se nastavení pomocí SLAAC+RD 
    return  setup(country, zw->ssid, zw->pass, auth, hostname );
}
// --------------------------------------------------------------------------------------------
// SNTP
// získání času z nastavených 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;
};

// příjem SNTP odpovědi
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;
}

// získání IP adresy NTP serveru z DNS
void dns_found(const char *name, const ip6_addr_t *ip, void *arg)
{
    struct timeStatus *tstatus = (struct timeStatus *)arg;
    printf("IP NTP serveru z DNS %s\n", ip6addr_ntoa(ip));
    struct udp_pcb *pcb = udp_new_ip_type(6);
    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;
    ip6_addr_t ip;
    cyw43_arch_lwip_begin();
    // err_t err = dns_gethostbyname(xstr(NTP_SERVER), &ip, dns_found, tstatus);
    err_t err = dns_gethostbyname_addrtype(NTP_SERVER, &ip, dns_found, tstatus, LWIP_DNS_ADDRTYPE_IPV6);
    cyw43_arch_lwip_end();
    if (err == ERR_OK)
    {
        printf("DNS %s\n", ip6addr_ntoa(&ip));
        dns_found(NTP_SERVER, &ip, tstatus);
    } else {
        printf("DNS %s err=%d\n", ip6addr_ntoa(&ip), err);
        
    }
    return tstatus;
}

void cancelSNTP(struct timeStatus *tstatus)
{
    if (tstatus != NULL)
    {
        udp_remove(tstatus->pcb);
        free(tstatus);
        tstatus = NULL;
        printf("zrušeno\n");
    }
}
// ----------------------------------------------------------------------------------------
// měření teploty interním čidlem

static struct udp_pcb *pcb_mereni;

void setup_udp_pcb()
{
    pcb_mereni = udp_new_ip_type(6);
}

// poslání naměřené teploty wireguardem na server
int posli_mereni(float teplota)
{
char moje_ip_buf[64];
char message[256];
ip6_addr_t moje_ip;
ip6_addr_t ip_kam;
ip_addr_t curwip;
uint16_t  curwport;
int len;

    printf("MojeIP: %s ", ip6addr_ntoa(netif_ip6_addr(wg_netif,1)));
    sprintf(moje_ip_buf,"%s", ip6addr_ntoa(netif_ip6_addr(wg_netif,1)));
    ip6addr_aton(moje_ip_buf, &moje_ip);
    IP_SET_TYPE_VAL(&moje_ip, IPADDR_TYPE_V6);
    ip6addr_aton(KAM_POSLAT, &ip_kam);
    IP_SET_TYPE_VAL(&ip_kam, IPADDR_TYPE_V6);
    printf("IPv6 adresa na posílání: %s ", ip6addr_ntoa(&ip_kam));
    err_t e = 0;
    err_t ew = wireguardif_peer_is_up(wg_netif, wireguard_peer_index, &curwip, &curwport);
    printf("peer %s %d %s: ",ip6addr_ntoa(&curwip), curwport, error_to_string(ew));

    err_t er = udp_connect( pcb_mereni, &ip_kam, MERENI_PORT);
    e += er;
    printf("udp_connect %s: ", error_to_string(er));
    memset(message,'\0',256);     
    sprintf(message, "Teplota: %5.1f ˚C\n", teplota);
    len = strlen(message);
    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len+1, PBUF_RAM);
    snprintf(p->payload, len+1, "%s", message);
    err_t er2 = udp_send(pcb_mereni,p);
    e += er2;
    pbuf_free(p);
    printf("Poslal jsem: 'Teplota: %5.1f ˚C', %s\n", teplota, error_to_string(er2));
    udp_disconnect(pcb_mereni);
    return e;
}
// ----------------------------------------------------------------------------------------
int main()
{
uint16_t result;
float teplota;
float teplota_arr[10] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
float adc;
const float conversion_factor = 3.3306f / ( 1 << 12 );
char buf1[80];      // buffery pro displej
char buf2[80];
char buf3[80];
char buf4[80];

    stdio_init_all();
    sleep_ms(2000);

    setup_i2c();
    zapnuti_displeje();
    
// 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 );

    
    printf("%s: AP scan...\n",HOSTNAME);
  
    napln_zname_wifi();
    
    // inicializace cyw43 chipu
    if (cyw43_arch_init())
    {
        printf("Nemohu nahodit wifi.\n");
        return 1;
    }

    // Picow Wifi nastavíme jako stanici
    cyw43_arch_enable_sta_mode();
    cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, CYW43_COUNTRY_CZECH_REPUBLIC);
    cyw43_wifi_scan_options_t scan_options = {0};
    // začínáme skenovat
    int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);

    while (true)
    {
        if (!cyw43_wifi_scan_active(&cyw43_state))
            break;
        sleep_ms(1000);
        printf("Skenujeme Wifi... \n");
    }
    printf("Scan je hotov.\n");
    cyw43_arch_deinit();

    // najdeme nejlepší signál mezi známými WiFi
    ZNAME_WIFI *zw = najdi_nejvetsi_rssi();
    if( zw != NULL ) { // máme wifi k připojení
        printf("Nejlepší signál %d má ssid %s sem se připojíme.\n", zw->rssi, zw->ssid);
        int connection_status = 0;
        
        do {
            printf("Připojuji se k AP %s.\n",zw->ssid);
            ssd1306_clear(&disp);
            sprintf(buf1,"Pripojuji AP %s",zw->ssid);
            ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf1);
            ssd1306_show(&disp); // zobrazíme písmenka na displeji
            connection_status = connect(zw, HOSTNAME );
        } while ( connection_status != CYW43_LINK_UP);
        
        printf("Připojeno.\n");
        printf("Linková IPv6 adresa:  %s\n", ip6addr_ntoa(netif_ip6_addr(netif_default,0)));
        printf("Globální IPv6 adresa: %s\n", ip6addr_ntoa(netif_ip6_addr(netif_default,1)));
        printf("Host Name: %s\n", netif_get_hostname(netif_default));
        ssd1306_clear(&disp);
        sprintf(buf1,"Pripojeno AP %s",zw->ssid);
        ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf1);
        sprintf(buf2,"%s",ip6addr_ntoa(netif_ip6_addr(netif_default,1)));
        ssd1306_show(&disp); // zobrazíme písmenka na displeji
        ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf2);
                    
        // nechce-li chodit automatické nastavení DNS serveru
        // dá se nastavit ručně
        //    ip6_addr_t dnsserver_ip ;
        //    ip6addr_aton("2a0e:5340:4:1::1", &dnsserver_ip);
        //    IP_SET_TYPE_VAL(&dnsserver_ip, IPADDR_TYPE_V6);
        //    dns_setserver(0,&dnsserver_ip);
        
        const ip6_addr_t *dnsip = dns_getserver(0);
        printf("DNS server:           %s\n", ip6addr_ntoa(dnsip));
        
        while (true)
        {
            struct timeStatus *tstatus = getSNTP();
            sleep_ms(500);
            if (pollSNTP(tstatus))
                break;
            cancelSNTP(tstatus);
        }
        // nahození wireguardu
        connect_wireguard();
        // nahození UDP pro odesílání
        setup_udp_pcb();

        int i   = 0;
        err_t e = 0;
        // vypneme LED
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
        // nastavení svícení displeje
        ssd1306_contrast(&disp, 20);
        
        while (true)
        {
            // smyčka trvá přesně 1s
            absolute_time_t cekam = make_timeout_time_ms(1000);
            result = adc_read();
            adc = result * conversion_factor;
            // teplotu budeme průměrovat 1x za 10s
            teplota_arr[i%10] = 27.0f - (adc - 0.706f) / 0.001721f;
            
            struct tm t;
            getDateNow(&t);
            char Date[100];
            
            strftime(Date, sizeof(Date), "%a, %d %b %Y %k:%M:%S %Z", &t);
            strftime(buf1, sizeof(buf1), "%a, %d %b %Y", &t);
            strftime(buf2, sizeof(buf2), "%k:%M", &t);
            strftime(buf3, sizeof(buf3), ":%S", &t);
            sprintf(buf4,"%4.1fC", teplota);
            ssd1306_clear(&disp);
            ssd1306_draw_string_with_font(&disp, 0,  0,  1, font_spleen_8x5,   buf1);
            ssd1306_draw_string_with_font(&disp, 0,  8,  2, font_spleen_16x8a, buf2);
            ssd1306_draw_string_with_font(&disp, 90, 20, 1, font_spleen_16x8a, buf3);
            ssd1306_draw_string_with_font(&disp, 90, 10, 1, font_spleen_8x5,   buf4);
            
            printf("Čas: %s teplota: %4.1f ˚C\r", Date, teplota);
            if( i!=0 && i%10 == 0 ) {
                teplota = 0.0;
                for(int j=0; j<10; j++) {
                    teplota += teplota_arr[j];
                }
                teplota *= 0.1;
                // při posílání teploty blikneme LEDkou
                cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
                e = posli_mereni(teplota);
                ssd1306_contrast(&disp, 40);
            } else {
                cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
                ssd1306_contrast(&disp, 1);    
            }
            sprintf(buf4, "%s", e==0?"OK":"ERR");
            ssd1306_draw_string_with_font(&disp, 100, 0, 1, font_spleen_8x5, buf4);
            ssd1306_show(&disp); // zobrazíme písmenka na displeji
            i++;
            busy_wait_until(cekam);
        }
        
    } else {
        printf("Nemohu se připojit, žádná známá Wifi není v dosahu.\n");
        ssd1306_clear(&disp);
        sprintf(buf1,"Bez Wifi :-{");
        ssd1306_draw_string_with_font(&disp,0,0,1,font_spleen_16x8a,buf1);
        ssd1306_show(&disp);
        sleep_ms(10000);
    }
    // úklid seznamu (uvolnění paměti)
    zrus_zname_wifi();
    cyw43_arch_deinit();
    return 0;
}
// --------------------------------------------------------------------------------------------
