From d7d6151efca6e388aace8506474868072094b4eb Mon Sep 17 00:00:00 2001 From: Martin Brodbeck Date: Sat, 31 Dec 2022 15:42:43 +0100 Subject: [PATCH] Using lwip sntp to set the RTC. --- src/CMakeLists.txt | 17 ++++- src/abfall.cpp | 58 +++++++++++----- src/ntp_time.cpp | 170 --------------------------------------------- src/ntp_time.h | 1 - 4 files changed, 56 insertions(+), 190 deletions(-) delete mode 100644 src/ntp_time.cpp delete mode 100644 src/ntp_time.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aeae271..35d3968 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,6 @@ #set(CMAKE_INCLUDE_CURRENT_DIR ON) set(SOURCES - ntp_time.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 ) +#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} PROPERTIES CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" @@ -30,6 +44,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME} pico_stdlib pico_cyw43_arch_lwip_threadsafe_background pico_lwip_http + pico_lwip_sntp hardware_rtc ) diff --git a/src/abfall.cpp b/src/abfall.cpp index e78fde2..0d5fa7f 100644 --- a/src/abfall.cpp +++ b/src/abfall.cpp @@ -1,5 +1,5 @@ -#include -// #include +#include +#include #include @@ -9,8 +9,7 @@ #include "pico/util/datetime.h" #include "lwip/apps/http_client.h" - -#include "ntp_time.h" +#include "lwip/apps/sntp.h" using std::string; @@ -41,7 +40,7 @@ int wifi_setup(uint32_t country, const string &ssid, const string &pw, bool firs continue; } 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); 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) { - printf("Body\n"); + //printf("Body\n"); pbuf_copy_partial(p, myBuffer, p->tot_len, 0); - printf("%s", myBuffer); + // printf("%s", myBuffer); // TODO: Parse CSV 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() { const string ssid{"Apis cerana"}; const string pw{"2JkJEh2vptVT"}; const uint32_t country{CYW43_COUNTRY_GERMANY}; stdio_init_all(); + rtc_init(); bool firstTry = true; int res = -1; @@ -120,20 +137,25 @@ int main() { err_t err = httpc_get_file_dns("beenas.brodbeck-online.de", port, "/abfall/abfall.csv", &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) { - sleep_ms(1000); - datetime_t *dt = new datetime_t(); - if (rtc_get_datetime(dt) == false) { - printf("RTC NOT RUNNING !!\n"); - } - char dt_str[256]; - datetime_to_str(dt_str, 128, dt); - printf("DateTime: %s\n", dt_str); - delete dt; + rtc_get_datetime(&dt); + datetime_to_str(datetime_str, sizeof(datetime_buf), &dt); + printf("DateTime: %s\n", datetime_str); + sleep_ms(3000); } cyw43_arch_deinit(); diff --git a/src/ntp_time.cpp b/src/ntp_time.cpp deleted file mode 100644 index c83bc8a..0000000 --- a/src/ntp_time.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include - -#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; -} \ No newline at end of file diff --git a/src/ntp_time.h b/src/ntp_time.h deleted file mode 100644 index 2458cad..0000000 --- a/src/ntp_time.h +++ /dev/null @@ -1 +0,0 @@ -void set_rtc(); \ No newline at end of file