Using lwip sntp to set the RTC.

This commit is contained in:
Martin Brodbeck 2022-12-31 15:42:43 +01:00
parent 8b5f6f99e3
commit d7d6151efc
4 changed files with 56 additions and 190 deletions

View file

@ -1,7 +1,6 @@
#set(CMAKE_INCLUDE_CURRENT_DIR ON) #set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(SOURCES set(SOURCES
ntp_time.cpp
abfall.cpp abfall.cpp
) )
@ -16,6 +15,21 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
) )
#add_definitions(
# -DSNTP_SERVER_DNS=1
# -DSNTP_SERVER_ADDRESS="pool.ntp.org"
# -DSNTP_STARTUP_DELAY=0
# -DSNTP_SET_SYSTEM_TIME=set_system_time
#)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
SNTP_SERVER_DNS=1
SNTP_SERVER_ADDRESS="pool.ntp.org"
SNTP_STARTUP_DELAY=0
SNTP_SET_SYSTEM_TIME=set_system_time
SNTP_UPDATE_DELAY=86400
)
set_target_properties(${CMAKE_PROJECT_NAME} set_target_properties(${CMAKE_PROJECT_NAME}
PROPERTIES PROPERTIES
CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
@ -30,6 +44,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME}
pico_stdlib pico_stdlib
pico_cyw43_arch_lwip_threadsafe_background pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_http pico_lwip_http
pico_lwip_sntp
hardware_rtc hardware_rtc
) )

View file

@ -1,5 +1,5 @@
#include <stdio.h> #include <cstdio>
// #include <ctime> #include <ctime>
#include <string> #include <string>
@ -9,8 +9,7 @@
#include "pico/util/datetime.h" #include "pico/util/datetime.h"
#include "lwip/apps/http_client.h" #include "lwip/apps/http_client.h"
#include "lwip/apps/sntp.h"
#include "ntp_time.h"
using std::string; using std::string;
@ -41,7 +40,7 @@ int wifi_setup(uint32_t country, const string &ssid, const string &pw, bool firs
continue; continue;
} }
flashrate = flashrate / (status + 1); flashrate = flashrate / (status + 1);
printf("Connect status: %d %d\n", status, flashrate); //printf("Connect status: %d %d\n", status, flashrate);
} }
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(flashrate); sleep_ms(flashrate);
@ -83,21 +82,39 @@ err_t headers_callback(httpc_state_t *connection, void *arg, struct pbuf *hdr, u
} }
err_t body_callback(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err) { err_t body_callback(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err) {
printf("Body\n"); //printf("Body\n");
pbuf_copy_partial(p, myBuffer, p->tot_len, 0); pbuf_copy_partial(p, myBuffer, p->tot_len, 0);
printf("%s", myBuffer); // printf("%s", myBuffer);
// TODO: Parse CSV // TODO: Parse CSV
return ERR_OK; return ERR_OK;
} }
// Called by lwip sntp in order to set the time
extern "C" void set_system_time(u32_t sec) {
time_t epoch = sec;
struct tm *utc = gmtime(&epoch);
datetime_t datetime;
datetime.year = utc->tm_year + 1900;
datetime.month = utc->tm_mon + 1;
datetime.day = utc->tm_mday;
datetime.hour = utc->tm_hour;
datetime.min = utc->tm_min;
datetime.sec = utc->tm_sec;
datetime.dotw = utc->tm_wday;
if (rtc_set_datetime(&datetime) == true) {
printf("RTC successfully set.\n");
}
}
int main() { int main() {
const string ssid{"Apis cerana"}; const string ssid{"Apis cerana"};
const string pw{"2JkJEh2vptVT"}; const string pw{"2JkJEh2vptVT"};
const uint32_t country{CYW43_COUNTRY_GERMANY}; const uint32_t country{CYW43_COUNTRY_GERMANY};
stdio_init_all(); stdio_init_all();
rtc_init();
bool firstTry = true; bool firstTry = true;
int res = -1; int res = -1;
@ -120,20 +137,25 @@ int main() {
err_t err = httpc_get_file_dns("beenas.brodbeck-online.de", port, "/abfall/abfall.csv", err_t err = httpc_get_file_dns("beenas.brodbeck-online.de", port, "/abfall/abfall.csv",
&settings, body_callback, nullptr, nullptr); &settings, body_callback, nullptr, nullptr);
printf("Status %d\n", err); // printf("Status %d\n", err);
set_rtc(); sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_init();
// It takes some time for the system to set the RTC. Wait, until the RTC runs properly.
while (!rtc_running()) {
sleep_ms(100);
}
char datetime_buf[256];
char *datetime_str = &datetime_buf[0];
datetime_t dt;
while (true) { while (true) {
sleep_ms(1000); rtc_get_datetime(&dt);
datetime_t *dt = new datetime_t(); datetime_to_str(datetime_str, sizeof(datetime_buf), &dt);
if (rtc_get_datetime(dt) == false) { printf("DateTime: %s\n", datetime_str);
printf("RTC NOT RUNNING !!\n"); sleep_ms(3000);
}
char dt_str[256];
datetime_to_str(dt_str, 128, dt);
printf("DateTime: %s\n", dt_str);
delete dt;
} }
cyw43_arch_deinit(); cyw43_arch_deinit();

View file

@ -1,170 +0,0 @@
#include <string.h>
#include <time.h>
#include "hardware/rtc.h"
#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"
#include "lwip/dns.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
typedef struct NTP_T_ {
ip_addr_t ntp_server_address;
bool dns_request_sent;
struct udp_pcb *ntp_pcb;
absolute_time_t ntp_test_time;
alarm_id_t ntp_resend_alarm;
} NTP_T;
#define NTP_SERVER "pool.ntp.org"
#define NTP_MSG_LEN 48
#define NTP_PORT 123
#define NTP_DELTA 2208988800 // seconds between 1 Jan 1900 and 1 Jan 1970
#define NTP_TEST_TIME (30 * 1000)
#define NTP_RESEND_TIME (10 * 1000)
// Called with results of operation
static void ntp_result(NTP_T *state, int status, time_t *result) {
if (status == 0 && result) {
struct tm *utc = gmtime(result);
printf("got ntp response: %02d/%02d/%04d %02d:%02d:%02d\n", utc->tm_mday, utc->tm_mon + 1,
utc->tm_year + 1900, utc->tm_hour, utc->tm_min, utc->tm_sec);
// TODO: Set hardware RTC
datetime_t test;
test.year = utc->tm_year + 1900;
test.month = utc->tm_mon + 1;
test.day = utc->tm_mday;
test.hour = utc->tm_hour;
test.min = utc->tm_min;
test.sec = utc->tm_sec;
test.dotw = utc->tm_wday - 1;
if (rtc_set_datetime(&test) == true) {
printf("RTC successfully set.\n");
}
}
if (state->ntp_resend_alarm > 0) {
cancel_alarm(state->ntp_resend_alarm);
state->ntp_resend_alarm = 0;
}
state->ntp_test_time = make_timeout_time_ms(NTP_TEST_TIME);
state->dns_request_sent = false;
}
static int64_t ntp_failed_handler(alarm_id_t id, void *user_data);
// Make an NTP request
static void ntp_request(NTP_T *state) {
// cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking.
// You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll
// these calls are a no-op and can be omitted, but it is a good practice to use them in
// case you switch the cyw43_arch type later.
cyw43_arch_lwip_begin();
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, NTP_MSG_LEN, PBUF_RAM);
uint8_t *req = (uint8_t *)p->payload;
memset(req, 0, NTP_MSG_LEN);
req[0] = 0x1b;
udp_sendto(state->ntp_pcb, p, &state->ntp_server_address, NTP_PORT);
pbuf_free(p);
cyw43_arch_lwip_end();
}
static int64_t ntp_failed_handler(alarm_id_t id, void *user_data) {
NTP_T *state = (NTP_T *)user_data;
printf("ntp request failed\n");
ntp_result(state, -1, NULL);
return 0;
}
// Call back with a DNS result
static void ntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg) {
NTP_T *state = (NTP_T *)arg;
if (ipaddr) {
state->ntp_server_address = *ipaddr;
printf("ntp address %s\n", ip4addr_ntoa(ipaddr));
ntp_request(state);
} else {
printf("ntp dns request failed\n");
ntp_result(state, -1, NULL);
}
}
// NTP data received
static void ntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
u16_t port) {
NTP_T *state = (NTP_T *)arg;
uint8_t mode = pbuf_get_at(p, 0) & 0x7;
uint8_t stratum = pbuf_get_at(p, 1);
// Check the result
if (ip_addr_cmp(addr, &state->ntp_server_address) && port == NTP_PORT &&
p->tot_len == NTP_MSG_LEN && mode == 0x4 && stratum != 0) {
uint8_t seconds_buf[4] = {0};
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];
uint32_t seconds_since_1970 = seconds_since_1900 - NTP_DELTA;
time_t epoch = seconds_since_1970;
ntp_result(state, 0, &epoch);
} else {
printf("invalid ntp response\n");
ntp_result(state, -1, NULL);
}
pbuf_free(p);
}
// Perform initialisation
static NTP_T *ntp_init(void) {
NTP_T *state = new NTP_T();
if (!state) {
printf("failed to allocate state\n");
return NULL;
}
state->ntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
if (!state->ntp_pcb) {
printf("failed to create pcb\n");
free(state);
return NULL;
}
udp_recv(state->ntp_pcb, ntp_recv, state);
return state;
}
// Runs ntp test forever
void set_rtc() {
NTP_T *state = ntp_init();
if (!state)
return;
if (absolute_time_diff_us(get_absolute_time(), state->ntp_test_time) < 0 &&
!state->dns_request_sent) {
// Set alarm in case udp requests are lost
state->ntp_resend_alarm = add_alarm_in_ms(NTP_RESEND_TIME, ntp_failed_handler, state, true);
// cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct
// locking. You can omit them if you are in a callback from lwIP. Note that when using
// pico_cyw_arch_poll these calls are a no-op and can be omitted, but it is a good
// practice to use them in case you switch the cyw43_arch type later.
cyw43_arch_lwip_begin();
int err = dns_gethostbyname(NTP_SERVER, &state->ntp_server_address, ntp_dns_found, state);
cyw43_arch_lwip_end();
state->dns_request_sent = true;
if (err == ERR_OK) {
ntp_request(state); // Cached result
} else if (err != ERR_INPROGRESS) { // ERR_INPROGRESS means expect a callback
printf("dns request failed\n");
ntp_result(state, -1, NULL);
}
}
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
// is done via interrupt in the background. This sleep is just an example of some (blocking)
// work you might be doing.
// sleep_ms(1000);
delete state;
}

View file

@ -1 +0,0 @@
void set_rtc();