2017年10月06日

FlashAirのserialを使ってシェルっぽいもの

FlashAirにせっかくシリアル通信機能がついたので、適当にババっとシェルっぽいものを作ってみた。
ちょうどコマンドインタプリタが作りたい時期だったので、ぴったりでした。

shelllike.png

シリアルコンソール的な感じで、変換アダプタなりなんなりを噛ませて繋いでください。
一応、FTLE上で走らせる前提になってます。開発途中なので。ブラウザから実行してください。
(設定すれば起動時実行もできるとは思います。)

3.3Vの変換アダプタを使ってくださいね。
Tx: D0
Rx: CMD

lsとcdとcatとexitくらいしか使えません。
LuaFileSystemの機能が思ってた以上に削られててびっくりしたよね。


require "/FTLE/breakpoint"

--[[
LuaFileSystemで使えるもの一覧

dir
rmdir
touch
mkdir
attributes
]]

last_cmd = ""
pwd = "/"

--文字列として送信する
function put(s)
fa.serial("write",tostring(s))
collectgarbage()
end

--1文字送信する
function putc(c)
fa.serial("write",c)
end

--改行付きで文字列を送信する(printと同じく可変長で受け付ける)
function puts(...)
for i = 1, select("#", ...) do
local s = select(i, ...)
if(s == nil)then
s=""
end
put(s)
put(" ")
end
put("\r\n")
end

--受信バッファを空にする
function clean()
while fa.serial("read") ~= nil do
breakpoint()
collectgarbage()
end
end

--1ライン消去
function linedel(s)
for i=0,s:len() do
putc("\x08") --表示上の文字を削る
putc(" ")
putc("\x08")
end
end

--1文字受け取る。受け取るまでロックする
function getc()
while true do
local c = fa.serial("read")
if(c ~= nil)then
return string.format("%c",c)
else
sleep(1)
collectgarbage()
breakpoint()
end
end
end

--文字列を受け取る。受け取るまでロックする
function gets()
local buf = ""
while true do
local c = getc()
if(c == "\x1B")then
if(getc() == "\x5B")then
if(getc() == "\x41")then --↑キーを押した場合は前回の入力を返す
linedel(buf) --行を消し
putc(">")
put(last_cmd) --改行なしで出力
buf = last_cmd
end
end
c="" --何もなかったことにする
end

--改行コード
if(c == "\x0D" or c == "\x0A")then
puts() --改行
clean();
return buf
end

--BS, DEL
if(c == "\x08" or c == "\x7F")then
if(buf:len() > 0)then --1文字以上のとき
putc("\x08") --表示上の文字を削る
putc(" ")
putc("\x08")

buf = buf:sub(1,-2) --1文字削る
end
end

--Break / Ctrl + C
if(c == "\x03")then
error("SIGINT")
end


--puts(string.format("%02X",string.byte(c))) --16進数でエコーバックする
if(c >= "\x20" and c <="\x7e" )then --表示可能文字のとき
putc(c) --エコーバックする
--puts(string.format("%02X",string.byte(c))) --16進数でエコーバックする
buf = buf .. c --バッファに追加
end
end
end

--初期化とウェルカムメッセージ
function init()
fa.serial("init")
puts(fa.control("eva"))
puts("Welcome to FlashTools Minimal Shell v0.1")
end

-----------------------------
--シェルレベルで必要な機能

function getAbsolutePath(path)
if(path == ".")then
return pwd
end
--一つ上に戻る
if(path == "..")then
local x,y,path = pwd:find("([^%c]+)%/")
if(path == nil)then
return "/"
else
return path
end
end
local head = path:sub(1,1)
if(head == "/")then --先頭が/なら絶対パス
return path
else
if(pwd == "/")then
return "/"..path --ルートのときは特別です
else
return pwd.."/"..path --先頭が/じゃないなら相対パス
end
end
end
getAbsolutePath("DCIM")

-----------------------------
--コマンド実装

function cmd_help()
puts("--- help ---")
puts("EXIT : exit shell")
puts("ERROR : test shell error handling")
puts("CAT [path] : view file")
puts("LS [path] : file list")
puts("CD [path] : Change directory")

--[[
EXIT
VIEW
CD
LS
RM

CP
mv
call

]]
end

function cmd_cat(arg)
local path = getAbsolutePath(arg)
local f = io.open(path)
if(f == nil)then
puts("cat: file not found.")
return
end
while true do
local d = f:read("*l")
if(d == nil)then
break
end
d = d:gsub("\x0D","") --改行キラー
d = d:gsub("\x0A","")
puts(d)
end
end

function cmd_ls(arg)
local mode = ""
local path = pwd
if(arg ~= "")then
path = getAbsolutePath(arg)
end

puts(path)
for file in lfs.dir(path) do
local info = lfs.attributes(path.."/"..file)
if(info.mode == "directory")then
mode = " DIR:"
end
if(info.mode == "file")then
mode = "FILE:"
end
puts(mode,file)
end
end

function cmd_cd(arg)
if(arg == "")then
arg = "."
end

local path = getAbsolutePath(arg)
if(pcall(lfs.dir,path) == true)then --存在チェック(無存在でエラー吐くので保護実行)
pwd = path
puts(path)
else
puts("cd: not found")
end
end


-----------------------------

--コマンド処理
function process()
while true do
putc(">")
last_cmd = gets()
local cmd,x,arg = string.match(last_cmd, "(%g+)(%s*)([%g%s]*)") --コマンドと引数に分ける。コマンド間のスペースは複数入れてもOK
if(cmd ~= nil)then
cmd = cmd:upper() --コマンドは大文字にする
else
cmd = ""
end

--puts("CMD:",cmd)
--puts("ARG:",arg) --Argはあえて前処理しない。アプリケーションに任せる

if(cmd == "EXIT")then
puts("Bye!")
return
elseif(cmd == "ERROR")then
error("Test Error")
elseif(cmd == "HELP")then
cmd_help(arg)
elseif(cmd == "CAT")then
cmd_cat(arg)
elseif(cmd == "LS")then
cmd_ls(arg)
elseif(cmd == "CD")then
cmd_cd(arg)
else
puts("command not found")
end
end
end

init()

success = false
while (not success) do
success, errmsg = pcall(process)
if(success == false)then
puts()
puts("--- Shell Error ---")
puts(errmsg)
puts("-------------------")

--Ctrl+Cで止められたら便利かなと
if(errmsg:match("SIGINT") ~= nil)then
break
end
end
end

puts("-- Shell terminated --")
print("end")
posted by gpsnmeajp at 00:15| Comment(0) | TrackBack(0) | プログラム
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/181201702
※ブログオーナーが承認したトラックバックのみ表示されます。
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック