esp-open-rtosを使ってみる

Telnet Client

あまり使い道はないかもしれませんが、Telnet Clientのプログラムが動きました。
Telnetプロトコルでは、ログインの前に、クライアントとサーバ間でネゴシエーション(Telnet Negotiation)を行います。
Telnet Negotiationに関する日本語の資料はこちらに 有りますが、あまり詳しく書かれていません。
ネゴシエーションが終わらないと、サーバのプロンプトが表示されないので、意外に面倒です。
以下のコードではDebianとArmbianのサーバへログインできることを確認しています。
ログインしてpsコマンドを発行するだけのものですが、ESP側では手に負えない様な複雑な処理は、
サーバー側に任せることができます。
サーバのIPアドレス、ユーザ名、パスワードは適当に変えてください。
/* 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 "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

#define SERVER_IP "192.168.10.42"
#define SERVER_PORT 23
#define byte uint8_t

const char user[] = "nopnop2002\n";
const char password[] = "hogehoge\n";
const char command[] = "ps -ef\n";

SemaphoreHandle_t xSemaphore;

//Telnet Commands
const byte cmdSE   = 0xF0;
const byte cmdNOP  = 0xF1;
const byte cmdDM   = 0xF2;
const byte cmdBRK  = 0xF3;
const byte cmdIP   = 0xF4;
const byte cmdAO   = 0xF5;
const byte cmdAYT  = 0xF6;
const byte cmdEC   = 0xF7;
const byte cmdEL   = 0xF8;
const byte cmdGA   = 0xF9;
const byte cmdSB   = 0xFA;
const byte cmdWILL = 0xFB;
const byte cmdWONT = 0xFC;
const byte cmdDO   = 0xFD;
const byte cmdDONT = 0xFE;
const byte cmdIAC  = 0xFF;

//Telnet Options
const byte op_echo                   = 0x01;
const byte op_suppress_go_ahead      = 0x03;
const byte op_status                 = 0x05;
const byte op_timing_mark            = 0x06;
const byte op_terminal_type          = 0x18;
const byte op_window_size            = 0x1F;
const byte op_terminal_speed         = 0x20;
const byte op_remote_flow_control    = 0x21;
const byte op_linemode               = 0x22;
const byte op_X_display_location     = 0x23;
const byte op_environment_variables  = 0x24;
const byte op_new_environment        = 0x27;

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));
  }
}

// WiFi Task
void task1(void *pvParameters)
{
  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);
  vTaskDelete( NULL );
}

int readSocket(int fd, char * buf, int blen, bool flag) {
  TickType_t nowTick;
  int ret;
  /* read something */
  memset(buf,0,blen);
  ret = lwip_read(fd, buf, blen);
  LWIP_ASSERT("ret > 0", ret > 0);
  if (!flag) return ret;

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

  if (ret > 0) {
    for(int i=0;i<ret;i++) {
      printf("[%02x]",buf[i]);
    }
    printf("\n");
  }
  return ret;
}


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

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

  /* set up address to connect to */
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(SERVER_PORT);
  addr.sin_addr.s_addr = inet_addr(SERVER_IP);

  /* create the socket */
  int fd;
  int ret;
  fd = lwip_socket(AF_INET, SOCK_STREAM, 0);
  LWIP_ASSERT("fd >= 0", fd >= 0);

  /* connect */
  ret = lwip_connect(fd, (struct sockaddr*)&addr, sizeof(addr));
  /* should succeed */
  LWIP_ASSERT("ret == 0", ret == 0);

  /* write Telnet Negotiation */
  char buf[256];
  buf[0] = cmdIAC;
  buf[1] = cmdWILL;
  buf[2] = op_terminal_type;
  buf[3] = cmdIAC;
  buf[4] = cmdDO;
  buf[5] = op_suppress_go_ahead;
  buf[6] = cmdIAC;
  buf[7] = cmdWILL;
  buf[8] = op_suppress_go_ahead;
  buf[9] = cmdIAC;
  buf[10] = cmdDO;
  buf[11] = op_echo;
  buf[12] = cmdIAC;
  buf[13] = cmdWILL;
  buf[14] = op_window_size;
  ret = lwip_write(fd, buf, 15);
  LWIP_ASSERT("ret == 15", ret == 15);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_write ret=%d\n",pcTaskGetName(0),nowTick,ret);

  fd_set fds, readfds;
  struct timeval tv;

  FD_ZERO(&readfds);
  FD_SET(fd, &readfds);
  tv.tv_sec = 10;
  tv.tv_usec = 0;

  memcpy(&fds, &readfds, sizeof(fd_set));
  ret = select(fd+1, &fds, NULL, NULL, &tv);
  if (ret != 0) readSocket(fd, buf, sizeof(buf), true);

  buf[0] = cmdIAC;
  buf[1] = cmdWONT;
  buf[2] = op_terminal_speed;
  buf[3] = cmdIAC;
  buf[4] = cmdWONT;
  buf[5] = op_X_display_location;
  buf[6] = cmdIAC;
  buf[7] = cmdWONT;
  buf[8] = op_new_environment;
  ret = lwip_write(fd, buf, 9);
  LWIP_ASSERT("ret == 9", ret == 9);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_write ret=%d\n",pcTaskGetName(0),nowTick,ret);

  memcpy(&fds, &readfds, sizeof(fd_set));
  ret = select(fd+1, &fds, NULL, NULL, &tv);
  if (ret != 0) readSocket(fd, buf, sizeof(buf), true);

  buf[0] = cmdIAC;
  buf[1] = cmdSB;
  buf[2] = op_window_size;
  buf[3] = 0x00;
  buf[4] = 0x50; //80
  buf[5] = 0x00;
  buf[6] = 0x18; //24
  buf[7] = 0xFF;
  buf[8] = 0xF0;
  ret = lwip_write(fd, buf, 9);
  LWIP_ASSERT("ret == 9", ret == 9);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_write ret=%d\n",pcTaskGetName(0),nowTick,ret);

  memcpy(&fds, &readfds, sizeof(fd_set));
  ret = select(fd+1, &fds, NULL, NULL, &tv);
  if (ret != 0) readSocket(fd, buf, sizeof(buf), true);

  buf[0] = cmdIAC;
  buf[1] = cmdSB;
  buf[2] = op_terminal_type;
  buf[3] = 0x00;
  buf[4] = 0x78;
  buf[5] = 0x74;
  buf[6] = 0x65;
  buf[7] = 0x72;
  buf[8] = 0x6D;
  buf[9] = 0xFF;
  buf[10] = 0xF0;
  ret = lwip_write(fd, buf, 11);
  LWIP_ASSERT("ret == 11", ret == 11);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_write ret=%d\n",pcTaskGetName(0),nowTick,ret);

  memcpy(&fds, &readfds, sizeof(fd_set));
  ret = select(fd+1, &fds, NULL, NULL, &tv);
  if (ret != 0) readSocket(fd, buf, sizeof(buf), true);

  buf[0] = cmdIAC;
  buf[1] = cmdWONT;
  buf[2] = op_echo;
  buf[3] = cmdIAC;
  buf[4] = cmdDONT;
  buf[5] = op_status;
  buf[6] = cmdIAC;
  buf[7] = cmdWONT;
  buf[8] = op_remote_flow_control;
  ret = lwip_write(fd, buf, 9);
  LWIP_ASSERT("ret == 9", ret == 9);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_write ret=%d\n",pcTaskGetName(0),nowTick,ret);

  int login = 0;
  int passwd = 0;
  while(1) {
    memcpy(&fds, &readfds, sizeof(fd_set));
    ret = select(fd+1, &fds, NULL, NULL, &tv);
    if (ret == 0) continue;
    ret = readSocket(fd, buf, sizeof(buf), false);
    printf("%s",buf);
    if (strstr(buf,"login:") != 0 && login == 0) {
      //ret = lwip_write(fd, "orangepi\n", 9);
      ret = lwip_write(fd, user, strlen(user));
      LWIP_ASSERT("ret == strlen(user)", ret == strlen(user));
      login = 1;
      continue;
    }

    if (strstr(buf,"Password:") != 0 && passwd == 0) {
      //ret = lwip_write(fd, "orangepi\n", 9);
      ret = lwip_write(fd, password, strlen(password));
      LWIP_ASSERT("ret == strlen(password)", ret == strlen(password));
      passwd = 1;
      continue;
    }

    if (strstr(buf,"$") != 0) break;
  }
  nowTick = xTaskGetTickCount();
  printf("\n[%s:%d] login complete\n",pcTaskGetName(0),nowTick);

  /* send some linux command */
  ret = lwip_write(fd, command, strlen(command));
  LWIP_ASSERT("ret == strlen(command)", ret == strlen(command));
  while(1) {
    memcpy(&fds, &readfds, sizeof(fd_set));
    ret = select(fd+1, &fds, NULL, NULL, &tv);
    if (ret == 0) continue;
    ret = readSocket(fd, buf, sizeof(buf), false);
    //printf("buf=[%s]\n",buf);
    printf("%s",buf);
    if (strstr(buf,"$") != 0) break;
  }
  printf("\n");

  printf("\n[%s:%d] logout\n",pcTaskGetName(0),nowTick);
  ret = lwip_write(fd, "exit\n", 5);
  LWIP_ASSERT("ret == 5", ret == 5);

  /* close */
  ret = lwip_close(fd);
  LWIP_ASSERT("ret == 0", ret == 0);
  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, "WiFi", 256, NULL, 2, NULL);
  xTaskCreate(task2, "Telnet", 512, NULL, 2, NULL);

}

続く....