esp-open-rtosを使ってみる

NTP Client(UDP Client)

example/sntpにsntpによる時刻同期のサンプルが含まれています。
これを実行すると、以下の様に一応時刻同期が行われますが、
「Function called without core lock」のメッセージが表示されます。
ソースを見てみましたが、raw APIを使って作られていました。
SDK version:0.9.9
mode : sta(18:fe:34:d4:2f:77)
add if0
scandone
add 0
aid 7
cnt

connected with aterm-e625c0-g, channel 11
dhcp client start...
ip:192.168.10.154,mask:255.255.255.0,gw:192.168.10.1
Starting SNTP... Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
Function called without core lock
DONE!
TIME: Sat Jan 26 01:52:13 2019
TIME: Sat Jan 26 01:52:18 2019
TIME: Sat Jan 26 01:52:23 2019
TIME: Sat Jan 26 01:52:28 2019

そこで、Socket APIを使って作り直してみました。

NTPパケットの構造体はこ ちらから拝借しました。
NTPパケットの定数は「extras/sntp/sntp.c」から拝借しました。
基本的なコードはこ ちらから拝借しました。

RTCの更新はsettimeofday()を使っています。
この関数を使うためにはMakefileに以下を追加する必要が有ります。
EXTRA_COMPONENTS = extras/timekeeping


以下のコードでは、3つのタスクを起動しています。

task1(NTPタスク)
NTPクライアントタスクです。

task2(Clockタスク)
200Tick毎にRTCの現在時間を表示します。

task3(Dummyタスク)
task1の処理中、task1以外のタスクがどのようにブロックされるのかを確認するために動かしています。
Semaphoreを使って、task1のAPへの接続完了を待って動き始めます。
task1の終了で、このタスクも終了します。

/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include <string.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "esp8266.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

#define SNTP_SERVER "ntp.nict.jp"
//#define SNTP_SERVER "pool.ntp.org"
#define SNTP_PORT 123

#define SNTP_OFFSET_LI_VN_MODE      0
#define SNTP_LI_MASK                0xC0
#define SNTP_LI_NO_WARNING          0x00
#define SNTP_LI_LAST_MINUTE_61_SEC  0x01
#define SNTP_LI_LAST_MINUTE_59_SEC  0x02
#define SNTP_LI_ALARM_CONDITION     0x03 /* (clock not synchronized) */

#define SNTP_VERSION_MASK           0x38
#define SNTP_VERSION                (4/* NTP Version 4*/<<3)

#define SNTP_MODE_MASK              0x07
#define SNTP_MODE_CLIENT            0x03
#define SNTP_MODE_SERVER            0x04
#define SNTP_MODE_BROADCAST         0x05

/* number of seconds between 1900 and 1970 */
#define DIFF_SEC_1900_1970         (2208988800UL)

int TaskIsAlive = 1;
SemaphoreHandle_t xSemaphore;

void sntp_update_rtc(time_t t, uint32_t us);

void showNetworkInfo() {
  uint8_t hwaddr[6];
  if (sdk_wifi_get_macaddr(STATION_IF, hwaddr)) {
    printf("[%s] MAC address="MACSTR"\n",pcTaskGetName(0),MAC2STR(hwaddr));
  }
  struct ip_info info;
  if (sdk_wifi_get_ip_info(STATION_IF, &info)) {
    printf("[%s] IP address="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.ip));
    printf("[%s] Netmask="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.netmask));
    printf("[%s] Gateway="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.gw));
  }
}

// NTP Client Task
void task1(void *pvParameters)
{
  typedef struct
  {

    uint8_t li_vn_mode;      // Eight bits. li, vn, and mode.
                             // li.   Two bits.   Leap indicator.
                             // vn.   Three bits. Version number of the protocol.
                             // mode. Three bits. Client will pick mode 3 for client.

    uint8_t stratum;         // Eight bits. Stratum level of the local clock.
    uint8_t poll;            // Eight bits. Maximum interval between successive messages.
    uint8_t precision;       // Eight bits. Precision of the local clock.

    uint32_t rootDelay;      // 32 bits. Total round trip delay time.
    uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
    uint32_t refId;          // 32 bits. Reference clock identifier.

    uint32_t refTm_s;        // 32 bits. Reference time-stamp seconds.
    uint32_t refTm_f;        // 32 bits. Reference time-stamp fraction of a second.

    uint32_t origTm_s;       // 32 bits. Originate time-stamp seconds.
    uint32_t origTm_f;       // 32 bits. Originate time-stamp fraction of a second.

    uint32_t rxTm_s;         // 32 bits. Received time-stamp seconds.
    uint32_t rxTm_f;         // 32 bits. Received time-stamp fraction of a second.

    uint32_t txTm_s;         // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
    uint32_t txTm_f;         // 32 bits. Transmit time-stamp fraction of a second.

  } ntp_packet;              // Total: 384 bits or 48 bytes.


  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  /* Wait until we have joined AP and are assigned an IP */
  int wifiStatus;
  while (1) {
    wifiStatus = sdk_wifi_station_get_connect_status();
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] wifiStatus=%d\n",pcTaskGetName(0),nowTick,wifiStatus);
    //if (sdk_wifi_station_get_connect_status() == STATION_GOT_IP) break;
    if (wifiStatus == STATION_GOT_IP) break;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Connected to AP\n",pcTaskGetName(0),nowTick);
  showNetworkInfo();

  /* Now start task2 */
  xSemaphoreGive(xSemaphore);

  /* Convert URL to IP */
  struct hostent *server; // Server data structure.
  server = gethostbyname(SNTP_SERVER);
  LWIP_ASSERT("server != NULL", server != NULL);

  /* set up address to connect to */
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  //addr.sin_len = sizeof(addr);
  addr.sin_family = AF_INET;
  //addr.sin_port = PP_HTONS(SNTP_PORT);
  addr.sin_port = htons(SNTP_PORT);
  //addr.sin_addr.s_addr = inet_addr("129.250.35.250");

  // Copy the server's IP address to the server address structure.
  bcopy( ( char* )server->h_addr, ( char* ) &addr.sin_addr.s_addr, server->h_length );

  /* create the socket */
  int fd;
  int ret;
  fd = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP ); // Create a UDP socket.
  LWIP_ASSERT("fd >= 0", fd >= 0);

  /* set NTP packet */
  ntp_packet packet;
  memset( &packet, 0, sizeof( ntp_packet ) );
  packet.li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;

  /* send NTP packet */
  ret = lwip_sendto(fd, (char*)&packet, sizeof(ntp_packet), 0, (struct sockaddr *)&addr,sizeof(addr));
  LWIP_ASSERT("ret == 48", ret == 48);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_sendto ret=%d\n",pcTaskGetName(0),nowTick,ret);

  /* recv NTP packet */
  ret = lwip_recv(fd, (char*) &packet, sizeof(ntp_packet), 0);
  LWIP_ASSERT("ret > 0", ret > 0);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_recv ret=%d\n",pcTaskGetName(0),nowTick,ret);

  // NTPタイムをUNIXタイムに変換する
  // NTPタイムは1900年1月1日0時0分からはじまる積算秒数
  // UNITタイムは1970年1月1日0時0分からはじまる積算秒数
  // 1900年から1970年の70年を秒で表すと2208988800秒になる
  // NTPタイムから70年分の秒を引くとUNIXタイムが得られる
  nowTick = xTaskGetTickCount();
  uint32_t ntpTime = ntohl( packet.txTm_s ); // NTP Time-stamp seconds.
  printf("[%s:%d] The NTP Time = %u\n",pcTaskGetName(0),nowTick,ntpTime);
  // グリニッジ標準時間
  time_t txTm = ntpTime - DIFF_SEC_1900_1970; // UNIT Time-stamp seconds.
  //printf("[%s:%d] txTm=%ld\n",pcTaskGetName(0),nowTick,(unsigned long)txTm);
  printf("[%s:%d] The UTC Time: %s",pcTaskGetName(0),nowTick,ctime( ( const time_t* ) &txTm ) );
  // 日本標準時にあわせるために+9時間しておく
  txTm = txTm + (9 * 60 * 60);
  printf("[%s:%d] The JST Time: %s",pcTaskGetName(0),nowTick,ctime( ( const time_t* ) &txTm ) );

#if 0
  uint32_t txUs = ntohl( packet.txTm_f ) / 4295;
  //printf("[%s:%d] txUs=%u\n",pcTaskGetName(0),nowTick,txUs);
  sntp_update_rtc(txTm, txUs);
#endif

  /* Make timeval */
  struct timeval now = { .tv_sec = txTm };

  /* Set the time as well as a timezone */
  /*
     EXTRA_COMPONENTS = extras/timekeeping
  */
  settimeofday(&now, NULL);

  /* close */
  ret = lwip_close(fd);
  LWIP_ASSERT("ret == 0", ret == 0);
  TaskIsAlive = 0;
  vTaskDelete( NULL );

}

// Clock Task
void task2(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  while(1) {
    nowTick = xTaskGetTickCount();
    time_t ts = time(NULL);
    printf("[%s:%d] %s",pcTaskGetName(0),nowTick,ctime(&ts));
    vTaskDelay(2000 / portTICK_PERIOD_MS);
  }
  vTaskDelete( NULL );
}

// Dummy Task
void task3(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  xSemaphoreTake(xSemaphore, portMAX_DELAY);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Take Semaphore\n",pcTaskGetName(0),nowTick);

  while(TaskIsAlive) {
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Running...\n",pcTaskGetName(0),nowTick);
    vTaskDelay(1);
  }
  vTaskDelete( NULL );
}


void user_init(void)
{
  uart_set_baud(0, 115200);
  printf("SDK version:%s\n", sdk_system_get_sdk_version());
  printf("pcTaskGetName=%s\n",pcTaskGetName(0));

  /* Create Semaphore */
  xSemaphore = xSemaphoreCreateBinary();
  /* Check everything was created. */
  configASSERT( xSemaphore );

  xTaskCreate(task1, "NTP", 384, NULL, 2, NULL);
  xTaskCreate(task2, "Clock", 256, NULL, 2, NULL);
  xTaskCreate(task3, "Dummy", 256, NULL, 2, NULL);

}

結果は以下のようになります。
1/201/401の各TickでClockタスクが現在時刻を表示していますが、まだ、時刻同期が行われていないので、1970年1月1日を表 示します。
その後、548Tick目で時刻同期が完了し、601/801/1001/1201の各Tickでは、正しい時刻を表示しています。
546Tickから548TickでUDPの応答待ちとなりますが、そのタイミングでもDummyタスクはブロックされていません。
SDK version:0.9.9
pcTaskGetName=uiT
mode : sta(18:fe:34:d4:2f:77)
add if0
[NTP:1] Start
[NTP:1] wifiStatus=1
[Clock:1] Start
[Clock:1] Thu Jan  1 00:00:00 1970
[Dummy:1] Start
scandone
[NTP:138] wifiStatus=1
add 0
aid 7
cnt

connected with aterm-e625c0-g, channel 11
dhcp client start...
[Clock:201] Thu Jan  1 00:00:00 1970
[NTP:238] wifiStatus=1
[NTP:338] wifiStatus=1
[Clock:401] Thu Jan  1 00:00:00 1970
[NTP:438] wifiStatus=1
ip:192.168.10.154,mask:255.255.255.0,gw:192.168.10.1
[NTP:538] wifiStatus=5
[NTP:538] Connected to AP
[NTP] MAC address=18:fe:34:d4:2f:77
[NTP] IP address=192.168.10.154
[NTP] Netmask=255.255.255.0
[NTP] Gateway=192.168.10.1
[Dummy:538] Take Semaphore
[Dummy:538] Running...
[Dummy:539] Running...
[Dummy:540] Running...
[Dummy:541] Running...
[Dummy:542] Running...
[Dummy:543] Running...
[Dummy:544] Running...
[Dummy:545] Running...
[NTP:545] lwip_sendto ret=48
[Dummy:546] Running...
[Dummy:547] Running...
[Dummy:548] Running...
[NTP:548] lwip_recv ret=48
[NTP:548] The NTP Time = 3757462563
[NTP:548] The UTC Time: Sat Jan 26 03:36:03 2019
[NTP:548] The JST Time: Sat Jan 26 12:36:03 2019
[Clock:601] Sat Jan 26 12:36:04 2019
[Clock:801] Sat Jan 26 12:36:06 2019
[Clock:1001] Sat Jan 26 12:36:08 2019
[Clock:1201] Sat Jan 26 12:36:10 2019

続く
....