More on NTP.
This commit is contained in:
parent
10bb31b123
commit
8b5f6f99e3
4 changed files with 191 additions and 2 deletions
|
@ -1,6 +1,7 @@
|
|||
#set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(SOURCES
|
||||
ntp_time.cpp
|
||||
abfall.cpp
|
||||
)
|
||||
|
||||
|
@ -29,6 +30,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME}
|
|||
pico_stdlib
|
||||
pico_cyw43_arch_lwip_threadsafe_background
|
||||
pico_lwip_http
|
||||
hardware_rtc
|
||||
)
|
||||
|
||||
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
#include <stdio.h>
|
||||
// #include <ctime>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lwip/apps/http_client.h"
|
||||
#include "hardware/rtc.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/util/datetime.h"
|
||||
|
||||
#include "lwip/apps/http_client.h"
|
||||
|
||||
#include "ntp_time.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
|
@ -116,8 +122,18 @@ int main() {
|
|||
&settings, body_callback, nullptr, nullptr);
|
||||
printf("Status %d\n", err);
|
||||
|
||||
set_rtc();
|
||||
|
||||
while (true) {
|
||||
sleep_ms(1);
|
||||
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;
|
||||
}
|
||||
|
||||
cyw43_arch_deinit();
|
||||
|
|
170
src/ntp_time.cpp
Normal file
170
src/ntp_time.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
#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;
|
||||
}
|
1
src/ntp_time.h
Normal file
1
src/ntp_time.h
Normal file
|
@ -0,0 +1 @@
|
|||
void set_rtc();
|
Loading…
Reference in a new issue