Arduboy应用之abshell

Arduboy应用之abshell

最近在看 Arduboy 中一些应用的源码,发现了一个叫 abshell 的应用,用来实现通过串口与arduboy进行命令行交互的,这个应用可以很方便的能够通过串口命令行在arduboy的屏幕上面进行图形绘制,看了下源码,发现里面包含了一个叫ntshell 的开源库。今天就来看下 abshell 是如何实现的,以及 ntshell 这个开源库该如何应用。

abshell 使用

github源码链接
下载源码,在 arduino IDE 中打开工程文件,编译烧写到 arduboy 中,就可以实现下面的交互了。
arduboy-shell

abshell 帮助

串口波特率设置为 115200 ,直接在串口命令行中敲入 help,就可以打印出所有支持的命令。

Arduboy>help
help	:show help
info	:show info
print	:print string at the pen position
clear	:clear display
circle	:draw a circle
fcircle	:draw a filled circle
line	:draw a line
rect	:draw a rectangle
frect	:draw a filled rectangle
rrect	:draw a rounded rectangle
frrect	:draw a filled rounded rectangle
tri	:draw a triangle
ftri	:draw a filled triangle
moveto	:set the pen location
tsize	:set text size scale factor
lineto	:draw a line from the pen position
color	:set pen color
keystat	:read the status of buttons
pixels	:set pixels on horizontal line
tone	:play a beep tone
bitmap	:set bitmap destination area
.x	:send raw data in hex string
Arduboy>

abshell 实现

#include "globals.h"
#include "ntshell.h"
extern "C" {
#include "ntshell_arduino.h"
}
#include "usrcmd_arduino.h"

#define PROMPTSTR "Arduboy>"

// 定义一个 ntshell 对象
static ntshell_t ntshell;

// 读写函数实现 这里直接调用串口的读写函数
static int func_read(char *buf, int cnt, void *extobj) {
  if (Serial.available())
    return Serial.readBytes(buf, cnt);
  else
    return 0;
}



static int func_write(const char *buf, int cnt, void *extobj) {
  return Serial.write(buf, cnt);
}


// 回调函数 用于 ntshell 对象回调调用
static int func_callback(const char *text, void *extobj) {
#if 0
  Serial.print("User input text:'");
  Serial.print(text);
  Serial.print("'\r\n");
#else
  //直接调用 用户命令执行函数 usrcmd_execute
  return usrcmd_execute(text);
#endif
}

void setup() {
  arduboy.begin();
  arduboy.setFrameRate(SCRN_FPS);
  G_TXT_CURSOR(0, 48);
  G_PRINT(F("open serial monitor !"));
  arduboy.display();

  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  arduboy.clear();

  // 初始化 ntshell
  ntshell_init(
    &ntshell,
    func_read,
    func_write,
    func_callback,
    (void *)(&ntshell));

  ntshell_set_prompt(&ntshell, PROMPTSTR);
  Serial.println(F("Welcome to Arduboy.\r\n type 'help' for help."));
  Serial.print(PROMPTSTR);
  Serial.flush();
}

void loop() {
  // 串口正常使用后,执行体  ntshell_execute_arduino 
  while(Serial.available())
    ntshell_execute_arduino(&ntshell);

  if (!(arduboy.nextFrame()))
    return;

  arduboy.display();
}

这里面主要学习到了 ntshell 的几个 API 函数。

  • ntshell_init

  • ntshell_set_prompt

  • ntshell_execute_arduino

    除了以上的 API 之外,还需要注意了解一下 命令解析执行函数 usrcmd_execute 的具体实现,这个函数在 usrcmd_arduino.cpp 实现。

usrcmd_arduino.cpp

usrcmd_arduino.cpp 是 arduboy 的shell 命令解析的具体实现,通常来讲,一个 shell 命令的解析器大都是按照这样的逻辑来实现的,下面看一下具体的源码。


#include "ntopt.h"
#include "ntlibc.h"
#include <Arduino.h>
#include <avr/pgmspace.h>
#include "version.h"
#include "usrcmd_arduboy.h"

#define uart_puts Serial.print

void pgm_print(const char *p) {
  char c;
  while ((c = pgm_read_byte(p++)) != 0)
    Serial.write(c);
}

// 定义一个函数指针
typedef int (*USRCMDFUNC)(int argc, char **argv);

static int usrcmd_ntopt_callback(int argc, char **argv, void *extobj);
static int usrcmd_help(int argc, char **argv);
static int usrcmd_info(int argc, char **argv);

const char cmd_0[] PROGMEM = "help";
const char desc_0[] PROGMEM = "show help";
const char cmd_1[] PROGMEM = "info";
const char desc_1[] PROGMEM = "show info";

// shell 命令结构体
typedef struct {
  PROGMEM const char *cmd;  // shell 命令名称
  PROGMEM const char *desc; // shell 命令帮助信息
  USRCMDFUNC func;  // 函数调用
} cmd_table_t;

// 实例化 shell 命令数组 这里面大部分的 shell 命令的实现都是在 usrcmd_arduboy.cpp 中完成的
// 如果需要添加命令,可以在这个命令列表中进行添加,并针对相对应的命令编写回调函数
static const cmd_table_t cmdlist[] PROGMEM = {
  { cmd_0, desc_0, usrcmd_help },
  { cmd_1, desc_1, usrcmd_info },
  { cmd_10, desc_10, usrcmd_print },
  { cmd_11, desc_11, usrcmd_clear },
  { cmd_12, desc_12, usrcmd_circle },
  { cmd_13, desc_13, usrcmd_fcircle },
  { cmd_14, desc_14, usrcmd_line },
  { cmd_15, desc_15, usrcmd_rect },
  { cmd_16, desc_16, usrcmd_frect },
  { cmd_17, desc_17, usrcmd_rrect },
  { cmd_18, desc_18, usrcmd_frrect },
  { cmd_19, desc_19, usrcmd_tri },
  { cmd_20, desc_20, usrcmd_ftri },
  { cmd_21, desc_21, usrcmd_moveto },
  { cmd_22, desc_22, usrcmd_tsize },
  { cmd_23, desc_23, usrcmd_lineto },
  { cmd_24, desc_24, usrcmd_color },
  { cmd_25, desc_25, usrcmd_keystat },
  { cmd_26, desc_26, usrcmd_pixels },
  { cmd_30, desc_30, usrcmd_tone },
  { cmd_40, desc_40, usrcmd_bitmap },
  { cmd_41, desc_41, usrcmd_x },
};

int usrcmd_execute(const char *text)
{
  // 回调函数 调用 usrcmd_ntopt_callback
  return ntopt_parse(text, usrcmd_ntopt_callback, 0);
}

// shell 命令解析执行 调用 shell 命令相关的回调
static int usrcmd_ntopt_callback(int argc, char **argv, void *extobj)
{
  if (argc == 0) {
    return 0;
  }
  cmd_table_t *p;
  char cmd[MAX_CMD_LEN];
  p = (cmd_table_t *) &cmdlist[0];
  for (int i = 0; i < sizeof(cmdlist) / sizeof(cmdlist[0]); i++) {
    char *pcmd = pgm_read_word(&((*p).cmd));
    USRCMDFUNC pfunc = pgm_read_word(&((*p).func));
    strcpy_P(cmd, pcmd);
    if (ntlibc_strcmp((const char *)argv[0], cmd) == 0) {
      return (*pfunc)(argc, argv);  //执行 shell 命令相关回调函数
    }
    p++;
  }
  uart_puts(F("Unknown command\r\n"));
  Serial.flush();
  return 0;
}

static int usrcmd_help(int argc, char **argv)
{
  const cmd_table_t *p = &cmdlist[0];
  for (int i = 0; i < sizeof(cmdlist) / sizeof(cmdlist[0]); i++) {
    char *pcmd = pgm_read_word(&((*p).cmd));
    char *pdesc = pgm_read_word(&((*p).desc));
    pgm_print(pcmd);
    uart_puts("\t:");
    pgm_print(pdesc);
    uart_puts("\r\n");
    p++;
  }
  Serial.flush();
  return 0;
}

static int usrcmd_info(int argc, char **argv)
{
  if (argc != 2) {
    uart_puts(F("info sys\r\n"));
    uart_puts(F("info ver\r\n"));
    return 0;
  }
  if (ntlibc_strcmp(argv[1], "sys") == 0) {
    uart_puts(SYSINFOSTR);
    return 0;
  }
  if (ntlibc_strcmp(argv[1], "ver") == 0) {
    uart_puts(VERSIONSTR);
    return 0;
  }
  uart_puts(F("Unknown sub command found\r\n"));
  return -1;
}

ntshell 介绍

ntshell 实现了一个终端命令行交互的组件框架,除此之外还实现了一些特殊按键输入的处理,ntshell 的实现也比较简单,代码全部使用C语言编写,完全兼容 C89 标准。
如果要使用 ntshell ,我们只需要关系输入、输出以及命令处理部分的三个回调函数即可,这部分如何使用,可以直接参考 arduboy 上面的 abshell 的调用。
在使用 ntshell 过程中,没有对第三方库的依赖,也非常方便的移植。
目前,在 ntshell 官网上面可以下载到的最新的代码版本是 v0.3.1

官网介绍

ntshell 官网

ntshell 架构

ntshell-architecture.png

ntshell 核心库代码说明

结合着 ntshell 的框架图,查看 ntshell-v0.3.1 的源码,源码结构如下。
libcore

util

如何在其他设备上使用 ntshell 开源组件

移植 ntshell 也非常方便,直接将 ntshell-v0.3.1 文件夹中的 lib库移植到自己的工程文件中即可,至于 ntshell 相关API 的调用范例,可以直接参考 arduboy 中的abshell 的具体实现,也可以参考 ntshell-v0.3.1 中提供的sample,这里面还提供了两款单片机中的移植实现,以及相关API的调用。

linux 下运行示例

这里提供一种在linux 测试运行 ntshell 的示例。

linux下运行示例

stm32中运行nt-shell实例

潘多拉开发板运行nt-shell实例

参考链接

STM32,嵌入式系统中简单shell的实现
STM32 Shell之NT-Shell

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飘雪冰峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值