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
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
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(<e_verbunden); // Gib ein Semaphor an. Setzte die Zählung von lte_verbunden auf Null
break; // <e_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(<e_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;
}
}
}
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(<e_verbunden); // Gib ein Semaphor an. Setzte die Zählung von lte_verbunden auf Null
break; // <e_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(<e_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;
}
}
}