prettyprint

2025年4月14日 星期一

[Raspberry Pi Pico2 W] SNTP & RTC || RP2350

 本影片介紹了Raspberry Pi Pico 2 W對SNTP和RTC功能的實作。相同的程式碼可以在 Pico W 上運行。

專案的UI使用LVGL函式庫(TFT display & Rotary Encoder)

LwIP SNTP application API在專案中包裝程一組Libarary提供三個 functions:

bool pico_sntp_enable(int8_t tz);
bool pico_sntp_get_system_time_timeout_ms(uint32_t ms);
bool pico_sntp_restart_timeout_ms(int8_t tz, uint32_t ms);

Pico 2與Pico 使用RTC的Hardware 不一樣,新的aon_timer_xxx這組的api適用於RP2040與RP2350,詳細說明可參閱RP2040與RP2350 datasheet。

其他詳細內容與成果展示影片,附於文末。


成果影片:



程式碼:

sntp_lib:

  • CMakeLists.txt

add_library(sntp_lib INTERFACE)
target_sources(sntp_lib INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}/sntp_lib.c
)

target_include_directories(sntp_lib INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}
)

target_link_libraries(sntp_lib INTERFACE
        pico_aon_timer
        pico_lwip_sntp
)

  • sntp_lib.c

#include "stdio.h"
#include "pico/stdlib.h"
#include "sntp_lib.h"
#include "lwip/apps/sntp.h"
#include "pico/aon_timer.h"

//SNTP
static bool ntp_datetime_ok=false;
static int8_t sntp_timezone;
void sntp_set_system_time(u32_t sec)
{
    char buf[32];
    struct tm current_time_val;

    time_t current_time = (sec+sntp_timezone*60*60);
    struct tm* p= gmtime(&current_time);

    aon_timer_set_time_calendar(p);

    ntp_datetime_ok=true;
}

bool pico_sntp_enable(int8_t tz) {
    sntp_timezone = tz;
    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, "pool.ntp.org");      //SNTP_SERVER_DNS (lwipop.h)
    //ip_addr_t ntpserver;
    //ipaddr_aton("118.163.81.63", &ntpserver);
    //sntp_setserver(0, &ntpserver);
    sntp_init();
    if (!sntp_enabled()) {
        printf("sntp not enable\n");
        return false;
    }
    return true;
}

bool pico_sntp_get_system_time_timeout_ms(uint32_t ms) {
    absolute_time_t curTime = get_absolute_time();
    bool ret = false;
    while (absolute_time_diff_us(curTime, get_absolute_time()) < ms*1000) {
        if (ntp_datetime_ok) {
            ret = true;
            break;
        } else {
            sleep_ms(100);
        }
    }
    return ret;
}

bool pico_sntp_restart_timeout_ms(int8_t tz, uint32_t ms) {
    sntp_stop();
    if (pico_sntp_enable(tz)) {
        return pico_sntp_get_system_time_timeout_ms(ms);
    } else {
        return false;
    }
}

  • sntp_lib.h

#ifndef __SNTP_LIB__
#define __SNTP_LIB__

bool pico_sntp_enable(int8_t tz);
bool pico_sntp_get_system_time_timeout_ms(uint32_t ms);
bool pico_sntp_restart_timeout_ms(int8_t tz, uint32_t ms);

#endif

  • CMakeLists.txt(root)

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)

# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
    set(USERHOME $ENV{USERPROFILE})
else()
    set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.1.1)
set(toolchainVersion 13_3_Rel1)
set(picotoolVersion 2.1.1)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico_w CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

project(pico2w_clock_ht C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(pico2w_clock_ht pico2w_clock_ht.c display.c)

pico_set_program_name(pico2w_clock_ht "pico2w_clock_ht")
pico_set_program_version(pico2w_clock_ht "0.1")

# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(pico2w_clock_ht 1)
pico_enable_stdio_usb(pico2w_clock_ht 0)

# Add the standard library to the build
target_link_libraries(pico2w_clock_ht
        pico_stdlib
        pico_aon_timer
        hardware_pio
        hardware_timer
        )

# Add the standard include files to the build
target_include_directories(pico2w_clock_ht PRIVATE
        ${CMAKE_CURRENT_LIST_DIR}
)

# Add any user requested libraries
target_link_libraries(pico2w_clock_ht 
        pico_cyw43_arch_lwip_threadsafe_background
        )



target_compile_definitions(pico2w_clock_ht PRIVATE
        WIFI_SSID="$ENV{WIFI_SSID}"
        WIFI_PASSWD="$ENV{WIFI_PASSWD}"
)


add_subdirectory(pico_lvgl)
add_subdirectory(sntp_lib)
add_subdirectory(aht10_lib)
target_link_libraries(pico2w_clock_ht
        pico_lvgl
        sntp_lib
        aht10_lib
        )

pico_add_extra_outputs(pico2w_clock_ht)

  • pico2w_clock_ht.c

#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "hardware/pio.h"
#include "pico/aon_timer.h"

#include "pico_lvgl.h"
#include "sntp_lib.h"
#include "display.h"
#include "aht10.h"

#define TFT_PIO             pio0
#define TFT_SM              2
#define TFT_SDI_GPIO        9
#define TFT_CSX_DCX_SCK_GPIO 6 // CSX=8, DCX(A0)=7, SCK=6, SIDE_SET

#define TIME_ZONE           8

#define AHT10_I2C_PORT      i2c0
#define AHT10_SDA           16
#define AHT10_SCL           17


bool timer_callback(repeating_timer_t* rt) { 
    display_clock_meter();
    return true;
}
bool aht10_callback(repeating_timer_t* rt) {
    display_ht();
    return true;
}

int main()
{
    stdio_init_all();

    gpio_init(ALARM_BUZZER_PIN);
    gpio_set_dir(ALARM_BUZZER_PIN, true);
    gpio_put(ALARM_BUZZER_PIN, false);

    pico_lvgl_tft_init(TFT_PIO, TFT_SM, TFT_SDI_GPIO, TFT_CSX_DCX_SCK_GPIO);
    tft_set_orientation(TFT_ORIENTATION_LANDSCAPE_MIRROR);
    pico_lvgl_display_init(5);
    pico_lvgl_encoder_init(true);
    
    struct tm dt;
    dt.tm_year = 2025-1900;
    dt.tm_mon = 3;
    dt.tm_mday = 1;
    dt.tm_hour = 0;
    dt.tm_min = 0;
    dt.tm_sec = 0;
    if (!aon_timer_start_calendar(&dt)) {
        show_msgbox("Error", "Start RTC error");
        return 1;
    }
    char msgbuff[100];
    // Initialise the Wi-Fi chip
    if (cyw43_arch_init()) {
        printf("Wi-Fi init failed\n");
        show_msgbox( "Error", "Wi-Fi init failed");
        return -1;
    }

    // Enable wifi station
    cyw43_arch_enable_sta_mode();

    printf("Connecting to Wi-Fi...\n");
    show_msgbox("Message", "Connecting to Wi-Fi...");
    if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
        printf("failed to connect.\n");
        close_msgbox();
        show_msgbox("Error", "failed to connect.");
        return 1;
    } else {
        printf("Connected.\n");       
        // Read the ip address in a human readable way
        uint8_t *ip_address = (uint8_t*)&(cyw43_state.netif[0].ip_addr.addr);
        
        sprintf(msgbuff, "IP address %d.%d.%d.%d", ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
        printf("%s\n",msgbuff);
        close_msgbox();
        show_msgbox( "Message", msgbuff);
        sleep_ms(2000);
        close_msgbox();        
    }
    // enable SNTP
    if (!pico_sntp_enable(TIME_ZONE)) {
        show_msgbox("Error", "Enable SNTP Error");
        return -1;
    }
    show_msgbox("Message", "Getting NTP Time...");
    if (pico_sntp_get_system_time_timeout_ms(20000)) {
        struct tm dt;
        aon_timer_get_time_calendar(&dt);
        sprintf(msgbuff, "Date:%04d-%02d-%02d\nTime:%02d:%02d:%02d\n", dt.tm_year+1900, dt.tm_mon+1, dt.tm_mday,
                    dt.tm_hour, dt.tm_min, dt.tm_sec);
        close_msgbox();
        show_msgbox("Info", msgbuff);
    } else {
        printf("sntp time out\n");
        close_msgbox();
        show_msgbox("Info", "Restarting SNTP");
        if (!pico_sntp_restart_timeout_ms(TIME_ZONE, 20000)) {
            close_msgbox();
            show_msgbox("Info", "Get NTP Time timeout");
            return -1;
        } else {
            close_msgbox();
            struct tm dt;
            aon_timer_get_time_calendar(&dt);
            sprintf(msgbuff, "Date:%04d-%02d-%02d\nTime:%02d:%02d:%02d\n", dt.tm_year+1900, dt.tm_mon+1, dt.tm_mday,
                        dt.tm_hour, dt.tm_min, dt.tm_sec);
            show_msgbox("Info", msgbuff);
        } 
    }
    close_msgbox();

    draw_screens();  // draw analog clock screen and Alarm setting screen
    lv_disp_load_scr(clock_scr); // load clock screen as default
    repeating_timer_t rt;
    add_repeating_timer_ms(-1000, timer_callback, NULL, &rt);

    //init AHT10 sensor and read Temperature/Humidity every 5 seconds
    aht10_init(AHT10_I2C_PORT, AHT10_SDA,AHT10_SCL);
    repeating_timer_t rt_ht;
    add_repeating_timer_ms(-5000, aht10_callback, NULL, &rt_ht);

    while (true) {
        lv_timer_handler();
        sleep_ms(10);
    }
}


沒有留言:

張貼留言