How to add DTLS to nRF coap client

Hello,

i ran  nRF Coap Client and it worked pretty well.

I am working with nRF9160 DK.

Now, I want to add DTLS to this example to secure the data.

Can someone please help me or is there any tutorials on how to do that?

Best regards,

Cedric

Parents
  • Hello Cedric,

    ced27 said:
    1. How did the GET-request reach the californium Server? did you do any configurations on the server side, before you could get a response from server?

    Californium is a publicly available server which the device is connecting to via an IP address. The modem is performing a DNS lookup against the domain's authoritative name server, which ensures the connection to it. CoAP is a protocol above the internet and transport layer and as long as the server supports it, the basic configuration should be in place.

    ced27 said:
    2. If i want to use another CONFIG_COAP_RESOURCE other than "obs", what are the other resources? Are these resources already given or should i defined one myself? 

    That depends on the server. If you want to use Californium, I recommend you to check their tutorial and/or get in touch with them.

    ced27 said:
    3. If i can define it myself, what are the requirements to make sure i get a given response from the server?

    The resource has to exist on the server side and if it doesn’t, the server should allow the device to create it via a POST/PUT request.

    ced27 said:
    4. How can i make sure that for example a message from my modem reaches the host i run Californium?

    The server should be publicly available with an IP address/port forwarding. The IP address can either be static or registered as a hostname via a DNS server. Californium fulfills these criteria. Additionally, Confirmable messages can be used which will be acknowledged by the server.

    I hope this answers your questions!

    Regards,

    Markus

  • Thank you very much for your reply.

    I trace the iteraction once again. That's what i got:

    trace-2021-10-28T10-52-42.203Z.zip

    Here is my code again:

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr.h>
    #include <stdio.h>
    #include <modem/lte_lc.h>
    #include <net/socket.h>
    #include <random/rand32.h>
    #include <net/coap.h>
    
    #include <modem/nrf_modem_lib.h>
    #include <logging/log.h>
    #include <sys/printk.h>
    #include <modem/modem_key_mgmt.h>
    #include <net/tls_credentials.h>
    #include <nrfx.h>
    #include <nrf_socket.h>
    
    #include "ui.h"
    #include "config.h"
    
    #define PAYLOAD_BYTES 100
    
    static int sock; // BSD socket API
    static struct sockaddr_storage server; // structure für CoAP-Server- Addressinformation.
    #define APP_COAP_VERSION 1  // CoAP Version
    
    static uint16_t next_token; // request ID
    static struct pollfd fds; /* File descriptor */
    
    static uint8_t data[CONFIG_DATA_UPLOAD_SIZE_BYTES]; // Maximale Nachrichtgröße
    
    // Benutzeroberflächenmodul.
    static void ui_evt_handler(struct ui_evt *evt) // Event-Handler für LEDS
    {
    	if (!evt) {
    		return;
    	}
    }
    
    
    int coap_backend_ping(void)
    {
    	int err;
    	struct coap_packet ping;
    
    	next_token = 0;
    
    	err = coap_packet_init(&ping, data, sizeof(data),
    			       APP_COAP_VERSION, COAP_TYPE_CON,
    			       0, NULL, 0, coap_next_id());
    	if (err < 0) {
    		printk("Failed to create CoAP request, %d\n", err);
    		return err;
    	}
    
    	err = send(sock, ping.data, ping.offset, 0);
    	if (err < 0) {
    		printk("Failed to send CoAP PING, %d\n", errno);
    		return -errno;
    	}
    
    	printk("CoAP PING sent: token 0x%04x\n", next_token);
    
    	return 0;
    }
    
    /*static int dtls_init(void){
    
    int err;
    
    #if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
    	err = modem_key_mgmt_write(PSK_TAG,
    				MODEM_KEY_MGMT_CRED_TYPE_PSK,
    				client_psk,
    				strlen(client_psk));
    	if (err < 0) {
    		printk("Bereitstellung des PSK fehlgeschlagen: %d", err);
                    return err;
    	}
    
           err = modem_key_mgmt_write(PSK_TAG,
    				MODEM_KEY_MGMT_CRED_TYPE_IDENTITY,
    				psk_id,
    				strlen(psk_id) - 1);
    	if (err < 0) {
    		printk("Bereitstellung der PSK-ID fehlgeschlagen: %d", err);
                    return err;
    	}
    #endif
    
    
    #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
    
            // Provision Client PSK.
    	err = tls_credential_add(PSK_TAG,
    				TLS_CREDENTIAL_PSK,
    				client_psk,
    				sizeof(client_psk));
    	if (err < 0) {
    		printk("Fehler beim Registrieren von PSK: %d", err);
                    return err;
    	}
    
            // Provision Client Identity.
    	err = tls_credential_add(PSK_TAG,
    				TLS_CREDENTIAL_PSK_ID,
    				psk_id,
    				sizeof(psk_id) - 1);
    	if (err < 0) {
    		printk("Fehler beim Registrieren von PSK ID: %d", err);
                    return err;
    	}
    
    #endif
            return 0;
    }*/
    
    static int server_initialisieren(void)
    {
    
    	int err;
    	struct addrinfo *result;
    	struct addrinfo hints = {
    		.ai_family = AF_INET,
    		.ai_socktype = SOCK_DGRAM
    	};
    	
            char ipv4_addr[NET_IPV4_ADDR_LEN];
    
    	err = getaddrinfo(CONFIG_SERVER_ADDRESS, NULL, &hints, &result);
    	if (err != 0) {
    		printk("ERROR: getaddrinfo failed %d\n", err);
    		return -EIO;
    	}
    
    	if (result == NULL) {
    		printk("ERROR: Address not found\n");
    		return -ENOENT;
    	}
    
    	/* IPv4 Address. */
    	struct sockaddr_in *server4 = ((struct sockaddr_in *)&server);  // Socket-Adressstruktur für IPv4
    
    	server4->sin_addr.s_addr =
    		((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
    	server4->sin_family = AF_INET;  // AF_INET  = IP Protokoll Version 4 (IPv4)
    	server4->sin_port = htons(CONFIG_SERVER_PORT);  //Schreibe Port in Network-Byte-Order in das Feld sin_port
    
    	inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr,
    		  sizeof(ipv4_addr));  // Schreibe IP-Adresse des Servers in die sockaddr_in-Struktur
    
    
    	printk("IPv4 Address found %s\n", ipv4_addr);
    
    	/* Free the address. */
            freeaddrinfo(result);
    
    	return 0;
    
    }
    
    static int server_verbinden(void)
    {
    	int err;
    
    
    #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) && defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
    
    	sec_tag_t tls_tag_list[] = {
    		CONFIG_COAP_SEC_TAG,
    	};
    
    	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
    	if (sock < 0) {
    		printk("Fehler beim Erstellen des CoAP/DTLS-Sockets: error %d\n", errno);
                    err = -errno;
    		goto error;
    	}
    
    	err = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
    			 tls_tag_list, sizeof(tls_tag_list));
    	if (err < 0) {
    		printk("Failed to set TLS_SEC_TAG_LIST option: %d", errno);
                    err = -errno;
    		goto error;
    	}
    
    #else
    	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    	if (sock < 0) {
    		printk("Fehler beim Erstellen des CoAP-Sockets: error %d\n", errno);
                    err = -errno;
    		goto error;
    	}
    #endif
    
    	err = connect(sock, (struct sockaddr *)&server,
    		      sizeof(struct sockaddr_in)); // Verbindung zum Server herstellen
    
            // err=0 => success, err<0 => fehler
    	if (err < 0) {
    		printk("Verbindung fehlgeschlagen: error %d\n", errno);
                    err = -errno;
    		goto error;
    	}
    
            /* Initialize FDS, for poll. */
    	fds.fd = sock;
    	fds.events = POLLIN;
    
    	/* Randomize token. */
    	next_token = sys_rand32_get();
    
    	return 0;
    
    error:
    	(void)close(sock); // beende die Verbindung zum Socket 
    
    	return err;
    }
    
    /**@brief Handles responses from the remote CoAP server. */
    static int client_handle_get_response(uint8_t *buf, int received)
    {
    	int err;
    	struct coap_packet reply;
    	const uint8_t *payload;
    	uint16_t payload_len;
    	uint8_t token[8];
    	uint16_t token_len;
    	uint8_t temp_buf[16];
    
    	err = coap_packet_parse(&reply, buf, received, NULL, 0);
    	if (err < 0) {
    		printk("Malformed response received: %d\n", err);
    		return err;
    	}
    
    	payload = coap_packet_get_payload(&reply, &payload_len);
    	token_len = coap_header_get_token(&reply, token);
    
    	if ((token_len != sizeof(next_token)) &&
    	    (memcmp(&next_token, token, sizeof(next_token)) != 0)) {
    		printk("Invalid token received: 0x%02x%02x\n",
    		       token[1], token[0]);
    		return 0;
    	}
    
    	snprintf(temp_buf, MAX(payload_len, sizeof(temp_buf)), "%s", payload);
    
    	printk("CoAP response: code: 0x%x, token 0x%02x%02x, payload: %s\n",
    	       coap_header_get_code(&reply), token[1], token[0], temp_buf);
    
    	return 0;
    }
    
    static int uebertragung_zu_server_fn(void)
    {
    	int err;
    
            struct coap_packet request; // Anfragepaket
            
            const char payload[PAYLOAD_BYTES]="AAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // Payload, der übertragen wird
    
            next_token++;
    
            //char uri_path[] = "obs"; //Coap Ressource: Californium test Ressouce (URI)
    
            // Initialisiere CoAP Anfrage
            err = coap_packet_init(&request, data, sizeof(data),
    			       APP_COAP_VERSION, COAP_TYPE_NON_CON,
    			       sizeof(next_token),(uint8_t *)&next_token,
                                   COAP_METHOD_PUT, coap_next_id());
    
    	if (err < 0) {
    		printk("CoAP-Anfrage konnte nicht erstellt werden, error %d\n", err);
    		return err;
    	}
            // Fügt eine Option an das Paket an
    	coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
                                      (uint8_t *)CONFIG_COAP_RESOURCE, 
                                      strlen(CONFIG_COAP_RESOURCE));
    
    
            // Payload-Marker an CoAP-Paket anhängen
            coap_packet_append_payload_marker(&request);
    
            // Payload an CoAP-Paket anhängen
            err=coap_packet_append_payload(&request, (uint8_t *)payload, sizeof(payload) - 1);
            if (err < 0) {
    		printk("Payload konnte nicht angehängt werden, %d\n", err);
    		return err;
    	}
    
    	err = send(sock, request.data, request.offset, 0); // Daten werden an den Port 5683 gesendet
    	if (err < 0) {
    		printk("Paket konnte nicht übertragen werden,error %d\n", errno);
    		return -errno;
    	}
    
            printk("CoAP Anfrage gesendet: token 0x%04x\n", next_token);
    
            /*printk("Payload von %d bytes wird an die ", sizeof(payload));
             // CONFIG_SERVER_ADDRESS = californium.eclipseprojects.io
             // CONFIG_SERVER_PORT = 5683
            printk("IP Adresse %s, Port Nummer %d übertragen\n",
    	       CONFIG_SERVER_ADDRESS,
    	       CONFIG_SERVER_PORT);*/
    
            return 0;
    
    }
    
    // Ein Semaphore ist ein Kernel-Objekt, das ein traditionelles Zählsemaphor implementiert.
    // Ein Semaphor wird verwendet, um den Zugriff auf eine Reihe von Ressourcen durch mehrere
    // Threads zu steuern und um die Verarbeitung zwischen einem produzierenden und einem 
    // verbrauchenden Thread oder Interrupt Service Routine (ISRs) zu synchronisieren.
    
    static K_SEM_DEFINE(lte_verbunden, 0, 1); // Semaphore statisch definiert und initialisiert. 
                                              // lte_verbunden = Name des Semaphores. 0 = anfängliche Semaphorezählung 
                                              // 1 = maximal zulässige Semaphoranzahl.
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
    	switch (evt->type) {
    	case LTE_LC_EVT_NW_REG_STATUS: // emfangenes Ereignis mit Informationen zum Netwerkregistrierungsstatus
                                           // des Modems
    		if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) &&
    		     (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) {
    			break;
    		}
                      
    		printk("Netwerkregistrierungsstatus: %s",
    			evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ?
    			"verbunden mit Heimnetwerk\n" : "verbunden mit Roaming\n");
    
    		k_sem_give(&lte_verbunden); // Gib ein Semaphor an. Setzte die Zählung von lte_verbunden auf Null 
    		break;                      // &lte_verbunden= Adresse des Semapores
    
    	case LTE_LC_EVT_PSM_UPDATE: // empfangenes Ereignis mit Informationen zu PSM-Updates. Die Updates enthalten 
                                        //  PSM-Parameter, die vom Netwerk vorgeben wurden.
    
                    // Das zugehörige Payload ist das Mitglied psm_cfg vom Typ lte_lc_psm_cfg in dem Library lte.lc.h
                    // tau = Periodisches Aktualisierungsintervall des Tracking-Bereichs.
                    // active_time = Aktiv-Zeit (Zeit von RRC Leerlauf bis PSM)
    		printk("PSM-Parameter update: TAU: %d, Active time: %d\n",
    			evt->psm_cfg.tau, evt->psm_cfg.active_time);
    		break;
    
    	case LTE_LC_EVT_EDRX_UPDATE:{// empfangenes Ereignis mit Informationen zu eDRX-Updates. Die Updates enthalten 
                                        //  eDRX-Parameter, die vom Netwerk vorgeben wurden.
    
                    // Das zugehörige Payload ist das Mitglied edrx_cfg vom Typ lte_lc_edrx_cfg in dem Library lte.lc.h
                    // edrx = eDRX-Intervallwert [s]
                    // ptw = Paging-Zeitfenster [s]
    		char log_buf[60];
    		ssize_t len;
    
    		len = snprintf(log_buf, sizeof(log_buf),
    			       "eDRX parameter update: eDRX: %f, PTW: %f\n",
    			       evt->edrx_cfg.edrx, evt->edrx_cfg.ptw);
    		if (len > 0) {
    			printk("%s\n", log_buf);
    		}
    		break;
             }
    
    	case LTE_LC_EVT_RRC_UPDATE: // empfangenes Ereignis mit Informationen über den 
                                        // Radio Ressource Control(RRC)-Status des Modems
                    
                    // Das zugehörige Payload ist das Mitglied rrc_mode vom Typ lte_lc_rrc_mode in dem Library lte.lc.h
                    // RRC Modus ist entweder verbunden oder idle.
    		printk("RRC Modus: %s",
    			evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ?
    			"verbunden\n" : "Idle\n");
    		break;
    
    	case LTE_LC_EVT_CELL_UPDATE: // empfangenes Ereignis mit Informationen über die aktuell verbundene Zelle
    
                    // Das zugehörige Payload ist das Mitglied Cell vom Typ lte_lc_cell in dem Library lte.lc.h
                    // id = Mobilfunkzellenidentifikation
                    // tac = Tracking area code
    		printk("LTE veränderte Zelle: Zelle ID: %d, Tracking area: %d\n",
    			evt->cell.id, evt->cell.tac);
    		break;
    
    	case LTE_LC_EVT_LTE_MODE_UPDATE: // Wenn ein Systemmodus konfiguiert ist,der entweder LTE-M oder NB-IoT
                                             // aktiviert, kann das Modem den aktuell aktiven LTE-Modus basierend auf
                                             // der Netwerkverfügbarkeit ändern. Dieses Ereignis zeigt dann an, welcher
                                             // LTE-Modus derzeit vom Modem verwendet wird.
    
    		printk("Active LTE Modus: %s",
    			evt->lte_mode == LTE_LC_LTE_MODE_NONE ? "None\n" :
    			evt->lte_mode == LTE_LC_LTE_MODE_LTEM ? "LTE-M\n" :
    			evt->lte_mode == LTE_LC_LTE_MODE_NBIOT ? "NB-IoT\n" :
    			"Unknown\n");
    		break;
    	default:
    		break;
    	}
    }
    
    static void modem_konfigurieren(void)
    {
    #if defined(CONFIG_NRF_MODEM_LIB) // CONFIG_NRF_MODEM_LIB = verwende Nordic Modem Library
    	if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { 
                      // CONFIG_LTE_AUTO_INIT_AND_CONNECT = automatische Registrierung bei LTE-M oder NB-IoT
    		  // passiert nichts, falls der Modem bereits konfiguriert und LTE verbunden ist.
    
    	} else {
    
    		int err; // integer für errors
    
    #if defined(CONFIG_PSM_ENABLE) // CONFIG_PSM_ENABLE fordert PSM vom Modbilfunknetz an.
    
    		/* Das Anfordern von PSM vor der Verbindung ermöglicht es dem Modem, das
    		 * Netwerk bereits während des Verbindungsvorgangs über unseren Wunsch
    		 * nach einer bestimmten PSM-Konfiguration zu informieren, anstatt in 
    		 * einer separaten Anfrage nach der Verbindungsherstellung, die in einigen
    		 * Netzwerken abgelehnt werden kann */
    		
    		err = lte_lc_psm_req(true); // Anfordern des Modems, das Power saving mode zu aktivieren
    		if (err < 0) {
                             // Falls err= negativ =>  Fehler aufgetreten
    
    			printk("Power Saving Mode wurde nicht aktiviert, error: %d\n",
    				err);
    		} else{
                          // falls err=0  => das Anfordern war erfolgreich
    
                          printk("PSM-Modus aktiviert\n\n");
                    }
    #endif
    
    #if defined(CONFIG_EDRX_ENABLE) // CONFIG_EDRX_ENABLE fordert eDRX vom Modbilfunknetz an.
    
    	/** Extended Discontinuous Reception (eDRX) ermöglicht es das Sleep mode
                in RRC idle mode auf Minuten oder Stunden zu verlängern, was noch deutlichere
                Einsparungen beim Energieverbrauch bietet*/
    
    	err = lte_lc_edrx_req(true); // eDRX wird aktiviert
    	if (err<0) {
    		printk("eDRX wurde nicht aktiviert, error: %d\n", err);
    	}else {
    		printk("eDRX aktiviert\n");
    	}
    #endif
    
    
                    ui_led_set_pattern(UI_LTE_CONNECTING); // LED3 blinkt, um der Verbindungsherstellungsvorgang 
                                                          // zu signalisieren.
    		printk("Verbindung zum LTE-Netz wird hergestellt\n");
    		printk("Das kann ein paar Minuten dauern..\n");
    		
                    // lte_lc_init_and_connect_async initialisiert das LTE-Modul, 
                    // konfiguriert das Modem und verbindet sich mit dem LTE-Netzwerk.
                    // lte_lc_init_and_connect_async überprüft zusätzlich die LTE-Ereignisse
                    // vom Typ LTE_LC_EVT_NW_REG_STATUS in lte_handler(), um festzustellen, wann
                    // die LTE-Verbindung besteht.
    
    		err = lte_lc_init_and_connect_async(lte_handler);
    		if (err) {
                    // err= negativ => Fehler aufgetreten
    
    			printk("Modem konnte nicht konfiguriert werden, error: %d\n",
    				err);
    			return;
    		}
                    // err=0 => besteht LTE-Verbindung 
    
                    printk("Mit LTE-Netz verbunden\n");
    		ui_led_set_pattern(UI_LTE_CONNECTED); // LED3 leuchtet, um das Bestehen der Verbindung vorzugeben.
    	}
    #endif
    }
    
    
    /* Gibt 0 zurück, wenn Daten verfügbar sind.
     * Gibt -EAGAIN zurück, wenn eine Zeitüberschreitung aufgetreten ist und keine Daten geschickt sind.
     * Gibt im Falle eines Poll-Fehlers einen anderen, negativen Fehlercode zurück.
     */
    static int wait(int timeout)
    {
    	int ret = poll(&fds, 1, timeout);
    
    	if (ret < 0) {
    		printk("poll error: %d\n", errno);
    		return -errno;
    	}
    
    	if (ret == 0) {
    		/* Timeout. */
    		return -EAGAIN;
    	}
    
    	if ((fds.revents & POLLERR) == POLLERR) {
    		printk("warte: POLLERR\n");
    		return -EIO;
    	}
    
    	if ((fds.revents & POLLNVAL) == POLLNVAL) {
    		printk("warte: POLLNVAL\n");
    		return -EBADF;
    	}
    
    	if ((fds.revents & POLLIN) != POLLIN) {
    		return -EAGAIN;
    	}
    
    	return 0;
    }
    
    void main(void)
    {
    
            int64_t next_msg_time = CONFIG_DATA_UPLOAD_FREQUENCY_MS;
    	int err,received;
    
    	printk("Startet.....\n");
    
            //err = dtls_init();
            //  if (err<0) {
            //  printk("DTLS Init Error: %d\n", err);
            //}
    
            // Initialisiere das Benutzeroberflächenmodul
            ui_init(ui_evt_handler);
    
            // Modem wird konfiguriert
    #if defined(CONFIG_NRF_MODEM_LIB)
    
    	modem_konfigurieren();
    
    	k_sem_take(&lte_verbunden, K_FOREVER);
    #endif
    
          next_msg_time = k_uptime_get();
    
            // Verbindungsaufbau des Clients zu dem Server
    	err = server_initialisieren();
    	if (err) {
    		printk("Serververbindung kann nicht initialisiert werden\n");
    		return;
    	}
            
            // Verbindung zum Server herstellen
    	err = server_verbinden();
    	if (err) {
    		printk("Keine Verbindung zum Server möglich\n");
    		return;
    	}
    
    while (1) {
    		if (k_uptime_get() >= next_msg_time) {
    			if (uebertragung_zu_server_fn() != 0) {
    				printk("Fehler beim Senden der Anfrage, exit...\n");
    				break;
    			}
    
    			next_msg_time += CONFIG_DATA_UPLOAD_FREQUENCY_MS;
    		}
    
    		int64_t remaining = next_msg_time - k_uptime_get();
    
    		if (remaining < 0) {
    			remaining = 0;
    		}
    
    		err = wait(remaining);
    		if (err < 0) {
    			if (err == -EAGAIN) {
    				continue;
    			}
    
    			printk("Poll error, exit...\n");
    			break;
    		}
    
                    
    		received = recv(sock, data, sizeof(data), MSG_DONTWAIT);
    		if (received < 0) {
    			if (errno == EAGAIN || errno == EWOULDBLOCK) {
    				printk("socket EAGAIN\n");
    				continue;
    			} else {
    				printk("Socket error, exit...\n");
    				break;
    			}
    		}
    
    		if (received == 0) {
    			printk("Empty datagram\n");
    			continue;
    		}
    
    		err = client_handle_get_response(data, received);
    		if (err < 0) {
    			printk("Invalid response, exit...\n");
    			break;
    		}
    
    	}
    
    }
    

Reply
  • Thank you very much for your reply.

    I trace the iteraction once again. That's what i got:

    trace-2021-10-28T10-52-42.203Z.zip

    Here is my code again:

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr.h>
    #include <stdio.h>
    #include <modem/lte_lc.h>
    #include <net/socket.h>
    #include <random/rand32.h>
    #include <net/coap.h>
    
    #include <modem/nrf_modem_lib.h>
    #include <logging/log.h>
    #include <sys/printk.h>
    #include <modem/modem_key_mgmt.h>
    #include <net/tls_credentials.h>
    #include <nrfx.h>
    #include <nrf_socket.h>
    
    #include "ui.h"
    #include "config.h"
    
    #define PAYLOAD_BYTES 100
    
    static int sock; // BSD socket API
    static struct sockaddr_storage server; // structure für CoAP-Server- Addressinformation.
    #define APP_COAP_VERSION 1  // CoAP Version
    
    static uint16_t next_token; // request ID
    static struct pollfd fds; /* File descriptor */
    
    static uint8_t data[CONFIG_DATA_UPLOAD_SIZE_BYTES]; // Maximale Nachrichtgröße
    
    // Benutzeroberflächenmodul.
    static void ui_evt_handler(struct ui_evt *evt) // Event-Handler für LEDS
    {
    	if (!evt) {
    		return;
    	}
    }
    
    
    int coap_backend_ping(void)
    {
    	int err;
    	struct coap_packet ping;
    
    	next_token = 0;
    
    	err = coap_packet_init(&ping, data, sizeof(data),
    			       APP_COAP_VERSION, COAP_TYPE_CON,
    			       0, NULL, 0, coap_next_id());
    	if (err < 0) {
    		printk("Failed to create CoAP request, %d\n", err);
    		return err;
    	}
    
    	err = send(sock, ping.data, ping.offset, 0);
    	if (err < 0) {
    		printk("Failed to send CoAP PING, %d\n", errno);
    		return -errno;
    	}
    
    	printk("CoAP PING sent: token 0x%04x\n", next_token);
    
    	return 0;
    }
    
    /*static int dtls_init(void){
    
    int err;
    
    #if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
    	err = modem_key_mgmt_write(PSK_TAG,
    				MODEM_KEY_MGMT_CRED_TYPE_PSK,
    				client_psk,
    				strlen(client_psk));
    	if (err < 0) {
    		printk("Bereitstellung des PSK fehlgeschlagen: %d", err);
                    return err;
    	}
    
           err = modem_key_mgmt_write(PSK_TAG,
    				MODEM_KEY_MGMT_CRED_TYPE_IDENTITY,
    				psk_id,
    				strlen(psk_id) - 1);
    	if (err < 0) {
    		printk("Bereitstellung der PSK-ID fehlgeschlagen: %d", err);
                    return err;
    	}
    #endif
    
    
    #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
    
            // Provision Client PSK.
    	err = tls_credential_add(PSK_TAG,
    				TLS_CREDENTIAL_PSK,
    				client_psk,
    				sizeof(client_psk));
    	if (err < 0) {
    		printk("Fehler beim Registrieren von PSK: %d", err);
                    return err;
    	}
    
            // Provision Client Identity.
    	err = tls_credential_add(PSK_TAG,
    				TLS_CREDENTIAL_PSK_ID,
    				psk_id,
    				sizeof(psk_id) - 1);
    	if (err < 0) {
    		printk("Fehler beim Registrieren von PSK ID: %d", err);
                    return err;
    	}
    
    #endif
            return 0;
    }*/
    
    static int server_initialisieren(void)
    {
    
    	int err;
    	struct addrinfo *result;
    	struct addrinfo hints = {
    		.ai_family = AF_INET,
    		.ai_socktype = SOCK_DGRAM
    	};
    	
            char ipv4_addr[NET_IPV4_ADDR_LEN];
    
    	err = getaddrinfo(CONFIG_SERVER_ADDRESS, NULL, &hints, &result);
    	if (err != 0) {
    		printk("ERROR: getaddrinfo failed %d\n", err);
    		return -EIO;
    	}
    
    	if (result == NULL) {
    		printk("ERROR: Address not found\n");
    		return -ENOENT;
    	}
    
    	/* IPv4 Address. */
    	struct sockaddr_in *server4 = ((struct sockaddr_in *)&server);  // Socket-Adressstruktur für IPv4
    
    	server4->sin_addr.s_addr =
    		((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
    	server4->sin_family = AF_INET;  // AF_INET  = IP Protokoll Version 4 (IPv4)
    	server4->sin_port = htons(CONFIG_SERVER_PORT);  //Schreibe Port in Network-Byte-Order in das Feld sin_port
    
    	inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr,
    		  sizeof(ipv4_addr));  // Schreibe IP-Adresse des Servers in die sockaddr_in-Struktur
    
    
    	printk("IPv4 Address found %s\n", ipv4_addr);
    
    	/* Free the address. */
            freeaddrinfo(result);
    
    	return 0;
    
    }
    
    static int server_verbinden(void)
    {
    	int err;
    
    
    #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) && defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
    
    	sec_tag_t tls_tag_list[] = {
    		CONFIG_COAP_SEC_TAG,
    	};
    
    	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
    	if (sock < 0) {
    		printk("Fehler beim Erstellen des CoAP/DTLS-Sockets: error %d\n", errno);
                    err = -errno;
    		goto error;
    	}
    
    	err = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
    			 tls_tag_list, sizeof(tls_tag_list));
    	if (err < 0) {
    		printk("Failed to set TLS_SEC_TAG_LIST option: %d", errno);
                    err = -errno;
    		goto error;
    	}
    
    #else
    	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    	if (sock < 0) {
    		printk("Fehler beim Erstellen des CoAP-Sockets: error %d\n", errno);
                    err = -errno;
    		goto error;
    	}
    #endif
    
    	err = connect(sock, (struct sockaddr *)&server,
    		      sizeof(struct sockaddr_in)); // Verbindung zum Server herstellen
    
            // err=0 => success, err<0 => fehler
    	if (err < 0) {
    		printk("Verbindung fehlgeschlagen: error %d\n", errno);
                    err = -errno;
    		goto error;
    	}
    
            /* Initialize FDS, for poll. */
    	fds.fd = sock;
    	fds.events = POLLIN;
    
    	/* Randomize token. */
    	next_token = sys_rand32_get();
    
    	return 0;
    
    error:
    	(void)close(sock); // beende die Verbindung zum Socket 
    
    	return err;
    }
    
    /**@brief Handles responses from the remote CoAP server. */
    static int client_handle_get_response(uint8_t *buf, int received)
    {
    	int err;
    	struct coap_packet reply;
    	const uint8_t *payload;
    	uint16_t payload_len;
    	uint8_t token[8];
    	uint16_t token_len;
    	uint8_t temp_buf[16];
    
    	err = coap_packet_parse(&reply, buf, received, NULL, 0);
    	if (err < 0) {
    		printk("Malformed response received: %d\n", err);
    		return err;
    	}
    
    	payload = coap_packet_get_payload(&reply, &payload_len);
    	token_len = coap_header_get_token(&reply, token);
    
    	if ((token_len != sizeof(next_token)) &&
    	    (memcmp(&next_token, token, sizeof(next_token)) != 0)) {
    		printk("Invalid token received: 0x%02x%02x\n",
    		       token[1], token[0]);
    		return 0;
    	}
    
    	snprintf(temp_buf, MAX(payload_len, sizeof(temp_buf)), "%s", payload);
    
    	printk("CoAP response: code: 0x%x, token 0x%02x%02x, payload: %s\n",
    	       coap_header_get_code(&reply), token[1], token[0], temp_buf);
    
    	return 0;
    }
    
    static int uebertragung_zu_server_fn(void)
    {
    	int err;
    
            struct coap_packet request; // Anfragepaket
            
            const char payload[PAYLOAD_BYTES]="AAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // Payload, der übertragen wird
    
            next_token++;
    
            //char uri_path[] = "obs"; //Coap Ressource: Californium test Ressouce (URI)
    
            // Initialisiere CoAP Anfrage
            err = coap_packet_init(&request, data, sizeof(data),
    			       APP_COAP_VERSION, COAP_TYPE_NON_CON,
    			       sizeof(next_token),(uint8_t *)&next_token,
                                   COAP_METHOD_PUT, coap_next_id());
    
    	if (err < 0) {
    		printk("CoAP-Anfrage konnte nicht erstellt werden, error %d\n", err);
    		return err;
    	}
            // Fügt eine Option an das Paket an
    	coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
                                      (uint8_t *)CONFIG_COAP_RESOURCE, 
                                      strlen(CONFIG_COAP_RESOURCE));
    
    
            // Payload-Marker an CoAP-Paket anhängen
            coap_packet_append_payload_marker(&request);
    
            // Payload an CoAP-Paket anhängen
            err=coap_packet_append_payload(&request, (uint8_t *)payload, sizeof(payload) - 1);
            if (err < 0) {
    		printk("Payload konnte nicht angehängt werden, %d\n", err);
    		return err;
    	}
    
    	err = send(sock, request.data, request.offset, 0); // Daten werden an den Port 5683 gesendet
    	if (err < 0) {
    		printk("Paket konnte nicht übertragen werden,error %d\n", errno);
    		return -errno;
    	}
    
            printk("CoAP Anfrage gesendet: token 0x%04x\n", next_token);
    
            /*printk("Payload von %d bytes wird an die ", sizeof(payload));
             // CONFIG_SERVER_ADDRESS = californium.eclipseprojects.io
             // CONFIG_SERVER_PORT = 5683
            printk("IP Adresse %s, Port Nummer %d übertragen\n",
    	       CONFIG_SERVER_ADDRESS,
    	       CONFIG_SERVER_PORT);*/
    
            return 0;
    
    }
    
    // Ein Semaphore ist ein Kernel-Objekt, das ein traditionelles Zählsemaphor implementiert.
    // Ein Semaphor wird verwendet, um den Zugriff auf eine Reihe von Ressourcen durch mehrere
    // Threads zu steuern und um die Verarbeitung zwischen einem produzierenden und einem 
    // verbrauchenden Thread oder Interrupt Service Routine (ISRs) zu synchronisieren.
    
    static K_SEM_DEFINE(lte_verbunden, 0, 1); // Semaphore statisch definiert und initialisiert. 
                                              // lte_verbunden = Name des Semaphores. 0 = anfängliche Semaphorezählung 
                                              // 1 = maximal zulässige Semaphoranzahl.
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
    	switch (evt->type) {
    	case LTE_LC_EVT_NW_REG_STATUS: // emfangenes Ereignis mit Informationen zum Netwerkregistrierungsstatus
                                           // des Modems
    		if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) &&
    		     (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) {
    			break;
    		}
                      
    		printk("Netwerkregistrierungsstatus: %s",
    			evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ?
    			"verbunden mit Heimnetwerk\n" : "verbunden mit Roaming\n");
    
    		k_sem_give(&lte_verbunden); // Gib ein Semaphor an. Setzte die Zählung von lte_verbunden auf Null 
    		break;                      // &lte_verbunden= Adresse des Semapores
    
    	case LTE_LC_EVT_PSM_UPDATE: // empfangenes Ereignis mit Informationen zu PSM-Updates. Die Updates enthalten 
                                        //  PSM-Parameter, die vom Netwerk vorgeben wurden.
    
                    // Das zugehörige Payload ist das Mitglied psm_cfg vom Typ lte_lc_psm_cfg in dem Library lte.lc.h
                    // tau = Periodisches Aktualisierungsintervall des Tracking-Bereichs.
                    // active_time = Aktiv-Zeit (Zeit von RRC Leerlauf bis PSM)
    		printk("PSM-Parameter update: TAU: %d, Active time: %d\n",
    			evt->psm_cfg.tau, evt->psm_cfg.active_time);
    		break;
    
    	case LTE_LC_EVT_EDRX_UPDATE:{// empfangenes Ereignis mit Informationen zu eDRX-Updates. Die Updates enthalten 
                                        //  eDRX-Parameter, die vom Netwerk vorgeben wurden.
    
                    // Das zugehörige Payload ist das Mitglied edrx_cfg vom Typ lte_lc_edrx_cfg in dem Library lte.lc.h
                    // edrx = eDRX-Intervallwert [s]
                    // ptw = Paging-Zeitfenster [s]
    		char log_buf[60];
    		ssize_t len;
    
    		len = snprintf(log_buf, sizeof(log_buf),
    			       "eDRX parameter update: eDRX: %f, PTW: %f\n",
    			       evt->edrx_cfg.edrx, evt->edrx_cfg.ptw);
    		if (len > 0) {
    			printk("%s\n", log_buf);
    		}
    		break;
             }
    
    	case LTE_LC_EVT_RRC_UPDATE: // empfangenes Ereignis mit Informationen über den 
                                        // Radio Ressource Control(RRC)-Status des Modems
                    
                    // Das zugehörige Payload ist das Mitglied rrc_mode vom Typ lte_lc_rrc_mode in dem Library lte.lc.h
                    // RRC Modus ist entweder verbunden oder idle.
    		printk("RRC Modus: %s",
    			evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ?
    			"verbunden\n" : "Idle\n");
    		break;
    
    	case LTE_LC_EVT_CELL_UPDATE: // empfangenes Ereignis mit Informationen über die aktuell verbundene Zelle
    
                    // Das zugehörige Payload ist das Mitglied Cell vom Typ lte_lc_cell in dem Library lte.lc.h
                    // id = Mobilfunkzellenidentifikation
                    // tac = Tracking area code
    		printk("LTE veränderte Zelle: Zelle ID: %d, Tracking area: %d\n",
    			evt->cell.id, evt->cell.tac);
    		break;
    
    	case LTE_LC_EVT_LTE_MODE_UPDATE: // Wenn ein Systemmodus konfiguiert ist,der entweder LTE-M oder NB-IoT
                                             // aktiviert, kann das Modem den aktuell aktiven LTE-Modus basierend auf
                                             // der Netwerkverfügbarkeit ändern. Dieses Ereignis zeigt dann an, welcher
                                             // LTE-Modus derzeit vom Modem verwendet wird.
    
    		printk("Active LTE Modus: %s",
    			evt->lte_mode == LTE_LC_LTE_MODE_NONE ? "None\n" :
    			evt->lte_mode == LTE_LC_LTE_MODE_LTEM ? "LTE-M\n" :
    			evt->lte_mode == LTE_LC_LTE_MODE_NBIOT ? "NB-IoT\n" :
    			"Unknown\n");
    		break;
    	default:
    		break;
    	}
    }
    
    static void modem_konfigurieren(void)
    {
    #if defined(CONFIG_NRF_MODEM_LIB) // CONFIG_NRF_MODEM_LIB = verwende Nordic Modem Library
    	if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { 
                      // CONFIG_LTE_AUTO_INIT_AND_CONNECT = automatische Registrierung bei LTE-M oder NB-IoT
    		  // passiert nichts, falls der Modem bereits konfiguriert und LTE verbunden ist.
    
    	} else {
    
    		int err; // integer für errors
    
    #if defined(CONFIG_PSM_ENABLE) // CONFIG_PSM_ENABLE fordert PSM vom Modbilfunknetz an.
    
    		/* Das Anfordern von PSM vor der Verbindung ermöglicht es dem Modem, das
    		 * Netwerk bereits während des Verbindungsvorgangs über unseren Wunsch
    		 * nach einer bestimmten PSM-Konfiguration zu informieren, anstatt in 
    		 * einer separaten Anfrage nach der Verbindungsherstellung, die in einigen
    		 * Netzwerken abgelehnt werden kann */
    		
    		err = lte_lc_psm_req(true); // Anfordern des Modems, das Power saving mode zu aktivieren
    		if (err < 0) {
                             // Falls err= negativ =>  Fehler aufgetreten
    
    			printk("Power Saving Mode wurde nicht aktiviert, error: %d\n",
    				err);
    		} else{
                          // falls err=0  => das Anfordern war erfolgreich
    
                          printk("PSM-Modus aktiviert\n\n");
                    }
    #endif
    
    #if defined(CONFIG_EDRX_ENABLE) // CONFIG_EDRX_ENABLE fordert eDRX vom Modbilfunknetz an.
    
    	/** Extended Discontinuous Reception (eDRX) ermöglicht es das Sleep mode
                in RRC idle mode auf Minuten oder Stunden zu verlängern, was noch deutlichere
                Einsparungen beim Energieverbrauch bietet*/
    
    	err = lte_lc_edrx_req(true); // eDRX wird aktiviert
    	if (err<0) {
    		printk("eDRX wurde nicht aktiviert, error: %d\n", err);
    	}else {
    		printk("eDRX aktiviert\n");
    	}
    #endif
    
    
                    ui_led_set_pattern(UI_LTE_CONNECTING); // LED3 blinkt, um der Verbindungsherstellungsvorgang 
                                                          // zu signalisieren.
    		printk("Verbindung zum LTE-Netz wird hergestellt\n");
    		printk("Das kann ein paar Minuten dauern..\n");
    		
                    // lte_lc_init_and_connect_async initialisiert das LTE-Modul, 
                    // konfiguriert das Modem und verbindet sich mit dem LTE-Netzwerk.
                    // lte_lc_init_and_connect_async überprüft zusätzlich die LTE-Ereignisse
                    // vom Typ LTE_LC_EVT_NW_REG_STATUS in lte_handler(), um festzustellen, wann
                    // die LTE-Verbindung besteht.
    
    		err = lte_lc_init_and_connect_async(lte_handler);
    		if (err) {
                    // err= negativ => Fehler aufgetreten
    
    			printk("Modem konnte nicht konfiguriert werden, error: %d\n",
    				err);
    			return;
    		}
                    // err=0 => besteht LTE-Verbindung 
    
                    printk("Mit LTE-Netz verbunden\n");
    		ui_led_set_pattern(UI_LTE_CONNECTED); // LED3 leuchtet, um das Bestehen der Verbindung vorzugeben.
    	}
    #endif
    }
    
    
    /* Gibt 0 zurück, wenn Daten verfügbar sind.
     * Gibt -EAGAIN zurück, wenn eine Zeitüberschreitung aufgetreten ist und keine Daten geschickt sind.
     * Gibt im Falle eines Poll-Fehlers einen anderen, negativen Fehlercode zurück.
     */
    static int wait(int timeout)
    {
    	int ret = poll(&fds, 1, timeout);
    
    	if (ret < 0) {
    		printk("poll error: %d\n", errno);
    		return -errno;
    	}
    
    	if (ret == 0) {
    		/* Timeout. */
    		return -EAGAIN;
    	}
    
    	if ((fds.revents & POLLERR) == POLLERR) {
    		printk("warte: POLLERR\n");
    		return -EIO;
    	}
    
    	if ((fds.revents & POLLNVAL) == POLLNVAL) {
    		printk("warte: POLLNVAL\n");
    		return -EBADF;
    	}
    
    	if ((fds.revents & POLLIN) != POLLIN) {
    		return -EAGAIN;
    	}
    
    	return 0;
    }
    
    void main(void)
    {
    
            int64_t next_msg_time = CONFIG_DATA_UPLOAD_FREQUENCY_MS;
    	int err,received;
    
    	printk("Startet.....\n");
    
            //err = dtls_init();
            //  if (err<0) {
            //  printk("DTLS Init Error: %d\n", err);
            //}
    
            // Initialisiere das Benutzeroberflächenmodul
            ui_init(ui_evt_handler);
    
            // Modem wird konfiguriert
    #if defined(CONFIG_NRF_MODEM_LIB)
    
    	modem_konfigurieren();
    
    	k_sem_take(&lte_verbunden, K_FOREVER);
    #endif
    
          next_msg_time = k_uptime_get();
    
            // Verbindungsaufbau des Clients zu dem Server
    	err = server_initialisieren();
    	if (err) {
    		printk("Serververbindung kann nicht initialisiert werden\n");
    		return;
    	}
            
            // Verbindung zum Server herstellen
    	err = server_verbinden();
    	if (err) {
    		printk("Keine Verbindung zum Server möglich\n");
    		return;
    	}
    
    while (1) {
    		if (k_uptime_get() >= next_msg_time) {
    			if (uebertragung_zu_server_fn() != 0) {
    				printk("Fehler beim Senden der Anfrage, exit...\n");
    				break;
    			}
    
    			next_msg_time += CONFIG_DATA_UPLOAD_FREQUENCY_MS;
    		}
    
    		int64_t remaining = next_msg_time - k_uptime_get();
    
    		if (remaining < 0) {
    			remaining = 0;
    		}
    
    		err = wait(remaining);
    		if (err < 0) {
    			if (err == -EAGAIN) {
    				continue;
    			}
    
    			printk("Poll error, exit...\n");
    			break;
    		}
    
                    
    		received = recv(sock, data, sizeof(data), MSG_DONTWAIT);
    		if (received < 0) {
    			if (errno == EAGAIN || errno == EWOULDBLOCK) {
    				printk("socket EAGAIN\n");
    				continue;
    			} else {
    				printk("Socket error, exit...\n");
    				break;
    			}
    		}
    
    		if (received == 0) {
    			printk("Empty datagram\n");
    			continue;
    		}
    
    		err = client_handle_get_response(data, received);
    		if (err < 0) {
    			printk("Invalid response, exit...\n");
    			break;
    		}
    
    	}
    
    }
    

Children
No Data
Related