2016年01月23日

HSPでPCにあるドライブの一覧を取得し、さらに詳細情報を得る



#uselib "Kernel32"
#func GetLogicalDriveStrings "GetLogicalDriveStringsA" int,var
#cfunc GetDriveType "GetDriveTypeA" var
#define DRIVE_UNKNOWN 0 //ドライブの種類が判別できません。
#define DRIVE_NO_ROOT_DIR 1 //指定のルートディレクトリが存在しません。
#define DRIVE_REMOVABLE 2 //ドライブからディスクを抜くことができます。
#define DRIVE_FIXED 3 //ドライブからディスクを抜くことができません。
#define DRIVE_REMOTE 4 //リモート (ネットワーク) ドライブです。
#define DRIVE_CDROM 5 //CD-ROM ドライブです。
#define DRIVE_RAMDISK 6 //RAM ディスクです。


sdim buf,1024
//ドライブ一覧を取得
GetLogicalDriveStrings 1024,buf

//NULL文字で区切られているので、分離して文字列に
null_cnt=0;
REPEAT 1024
b = peek(buf,cnt)
if(b == 0) : {
null_cnt++;
poke buf,cnt,','
}else{
null_cnt=0;
}

if(null_cnt>=2):{
poke buf,cnt,0
break
}
LOOP

//末端に付いた,を除去
buf = strtrim(buf,2,',')
//文字列を分離して配列に
split buf,",",drives

//詳細情報取得
dim drives_info,30 //詳細配列初期化
foreach drives
drives_info(cnt) = GetDriveType(drives.cnt)
loop

//表示
DriveListString=""
foreach drives
Show = ""+drives.cnt+" : "
if(drives_info(cnt) == DRIVE_UNKNOWN) : Show+="DRIVE_UNKNOWN"
if(drives_info(cnt) == DRIVE_NO_ROOT_DIR) : Show+="DRIVE_NO_ROOT_DIR"
if(drives_info(cnt) == DRIVE_REMOVABLE) : Show+="DRIVE_REMOVABLE"
if(drives_info(cnt) == DRIVE_FIXED) : Show+="DRIVE_FIXED"
if(drives_info(cnt) == DRIVE_REMOTE) : Show+="DRIVE_REMOTE"
if(drives_info(cnt) == DRIVE_CDROM) : Show+="DRIVE_CDROM"
if(drives_info(cnt) == DRIVE_RAMDISK) : Show+="DRIVE_RAMDISK"
mes Show

DriveListString += Show + "\n"
loop

objsize 500
DriveSelect= -1
listbox DriveSelect,100,DriveListString
button "OK",*OK

stop
*OK
mes drives.DriveSelect
posted by gpsnmeajp at 15:10| Comment(0) | TrackBack(0) | 未分類

2016年01月22日

FlashAir対話型応用設定ツールを考案中

FlashAirでよくやりたくなることってなんだろう。
それがわかるなら、簡単に設定できる(Luaスクリプトを生成する)ツールを作ろうかなと思っている。

現状ありそうだと思うこと

・外部入力があったら外部のURLを叩く
・外部入力があったら日時のログファイルを作ってDropboxにアップロードする

・特定のファイルが更新されたら外部のURLを叩く
・特定のファイルが更新されたらDropboxにアップロードする

・特定のフォルダにファイルが追加されたら外部のURLを叩く
・特定のフォルダにファイルが追加されたらDropboxにアップロードする

・何らかのファイルが更新されたら外部のURLを叩く
・何らかのファイルが更新されたらDropboxにアップロードする

・AirioのLEDをチカチカする

外部のURL
 →IFTTT maker Channel
 →stewgate(twitter)
 →pushover(プッシュ通知)
 →その他の任意のURL

なにか他に、よく使われそうな機能の要望があったら、この記事のコメント欄か、
twitterの方に教えてほしい。



posted by gpsnmeajp at 22:17| Comment(0) | TrackBack(0) | 未分類

2016年01月18日

Boost AsioでSerialのレスポンスが妙に遅い(その上それがばらつく)とき

Boost AsioでSerialのレスポンスが妙に遅い(その上それがばらつく)とき。
例えば、ループバックで1バイト送って帰ってくるのに、16ms〜70msとかかかるとき。
特に、再起動直後重かったりする場合。

大抵、Readに時間がかかっています。
そして、Windows APIで直接COMポートを叩いたり、Boost Asioを使う場合でも
固定長のデータをやり取りする場合は早かったりします。

解決方法
1.読み込み時のバッファ変数を小さくする
2.transfer_at_leastを使わないでReadする。

これだけで、応答が1ms以下になったりしますが、これでも解決しない場合。

USBシリアル変換器を使っていませんか?
その場合は、デバイスマネージャの当該機器の詳細設定の「レイテンシタイマー」を
適切な値(環境によりますが、1ms〜6msの間くらい)で設定してあげると、早くなります。
posted by gpsnmeajp at 21:43| Comment(0) | TrackBack(0) | 未分類

2016年01月12日

Pebble Time用のWatchFaceのサンプルを作った。

Pebble Time用のWatchFaceのサンプルを作った。

・画像の表示
・月/日 時:分の表示
・バッテリー残量の表示
・Bluetooth状態の表示/接続・切断時バイブ

と、基本的に欲しい機能を突っ込みました。
ほとんど公式のチュートリアル通りです。


プロジェクトのファイルを置いておくので、CloudPebbleにインポートすれば
あとは背景画像を差し替えるだけで使えると思います。
Pebble Classicでも使えるとは思いますが、動作確認はしていませんし、
動くようにも作っていません。(各オプションがPebble Time用になっています)

ダウンロード


動画を見ながらどうぞ。(3:40から手順解説になります)


以下ソースコード。

#include <pebble.h>
#include "main.h"

//うまくWatchFaceに登録されない時は、一度スマホからこのWatchFaceを削除したり
//UUIDやバージョン、作者名を変更したりすると良い。

//mainウィンドウポインタ
static Window *s_main_window;
//時間表示用文字列レイヤー
static TextLayer *s_time_layer;
static TextLayer *sw_time_layer;

//バッテリー表示レイヤー
static TextLayer *s_bat_layer;

//ビットマップ表示用レイヤー
static BitmapLayer *s_background_layer;
//ビットマップデータポインタ
static GBitmap *s_background_bitmap;

//Bluetoothステータス
static char bt_status[4] = "BT";
//過去のBluetoothステータス
static int bt_st_old = 1;

//バッテリーハンドラ
static void battery_handler(BatteryChargeState new_state) {
//バッテリー状態取得して表示(ついでにBluetooth接続状態を表示)
static char s_battery_buffer[32];
snprintf(s_battery_buffer, sizeof(s_battery_buffer), "Bat: %d%% %s", new_state.charge_percent,bt_status);
text_layer_set_text
(s_bat_layer, s_battery_buffer);
}

//Bluetoothハンドラ
void bt_handler(bool connected) {
if (connected) {
//接続に変化したらバイブ
if(bt_st_old == 0)
vibes_short_pulse
();
bt_st_old
= 1;
snprintf(bt_status,4,"BT");
} else {
//切断に変化したらバイブ
if(bt_st_old == 1)
vibes_short_pulse
();
bt_st_old
= 0;
snprintf(bt_status,4,"--");
}
//現在のバッテリーを読む(BT表示)
battery_handler
(battery_state_service_peek());
}

//画面更新
static void update_time() {
//時刻構造体を得る
time_t temp = time(NULL);
//ローカル時刻を得る
struct tm *tick_time = localtime(&temp);

//文字列バッファを確保し、そこに文字列形式で時刻を得る。
//24時間表示かどうかで切り替えている。
static char s_buffer[8];
strftime(s_buffer, sizeof(s_buffer), clock_is_24h_style() ?
"%H:%M" : "%I:%M", tick_time);
static char sp_buffer[16];
snprintf(sp_buffer,16,"%02d/%02d %s",
(tick_time->tm_mon+1),
tick_time
->tm_mday,
s_buffer
);

//テキストレイヤーにセットする
text_layer_set_text
(s_time_layer, sp_buffer);
text_layer_set_text
(sw_time_layer, sp_buffer);


}


//タイマーハンドラ
static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
update_time
();//1分に1回画面更新する
}

//Windowが生成された時
static void main_window_load(Window *window) {
//ウィンドウの情報を取得
Layer
*window_layer = window_get_root_layer(window);
GRect bounds
= layer_get_bounds(window_layer);

//ビットマップデータの読み込み
//RESOURCESではRESOURCE_ID_を含めない名前で指定する。
//この場合。IDENTIFIER=BACKGROUND
s_background_bitmap
= gbitmap_create_with_resource(RESOURCE_ID_BACKGROUND);
//ビットマップレイヤーの作成
s_background_layer
= bitmap_layer_create(bounds);
//ビットマップレイヤーにビットマップを割り
bitmap_layer_set_bitmap(s_background_layer, s_background_bitmap);
//windowレイヤーに子レイヤーとしてビットマップレイヤーを追加
layer_add_child
(window_layer, bitmap_layer_get_layer(s_background_layer));

//-------------------------------------

//境界付きでテキストレイヤーを作成
sw_time_layer = text_layer_create(
//x, y, w , h
GRect
(1, 111, bounds.size.w, 50)

//PBL_IF_ROUND_ELSE(if_true, if_false)なので、円形ならx=58,角形ならx=52
//GRect(0, PBL_IF_ROUND_ELSE(58, 52), bounds.size.w, 50)
);

//テキストレイヤーを設定
text_layer_set_background_color
(sw_time_layer, GColorClear);
text_layer_set_text_color
(sw_time_layer, GColorFromHEX(0x000000));
//text_layer_set_text(s_time_layer, "00:00");
text_layer_set_font
(sw_time_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28));
text_layer_set_text_alignment
(sw_time_layer, GTextAlignmentCenter);

//windowレイヤーに子レイヤーとしてテキストレイヤーを追加
layer_add_child
(window_layer, text_layer_get_layer(sw_time_layer));

//-------------------------------------

//境界付きでテキストレイヤーを作成
s_time_layer = text_layer_create(
//x, y, w , h
GRect
(0, 110, bounds.size.w, 50)

//PBL_IF_ROUND_ELSE(if_true, if_false)なので、円形ならx=58,角形ならx=52
//GRect(0, PBL_IF_ROUND_ELSE(58, 52), bounds.size.w, 50)
);

//テキストレイヤーを設定
text_layer_set_background_color
(s_time_layer, GColorClear);
text_layer_set_text_color
(s_time_layer, GColorFromHEX(0xFFFFFF));
//text_layer_set_text(s_time_layer, "00:00");

text_layer_set_font
(s_time_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28));
text_layer_set_text_alignment
(s_time_layer, GTextAlignmentCenter);

//windowレイヤーに子レイヤーとしてテキストレイヤーを追加
layer_add_child
(window_layer, text_layer_get_layer(s_time_layer));

//-------------------------------------

//境界付きでテキストレイヤーを作成
s_bat_layer
= text_layer_create(
//x, y, w , h
GRect
(0, 140, bounds.size.w, 50)

//PBL_IF_ROUND_ELSE(if_true, if_false)なので、円形ならx=58,角形ならx=52
//GRect(0, PBL_IF_ROUND_ELSE(58, 52), bounds.size.w, 50)
);

//テキストレイヤーを設定
text_layer_set_background_color
(s_bat_layer, GColorClear);
text_layer_set_text_color
(s_bat_layer, GColorFromHEX(0xFFFFFF));
//text_layer_set_text(s_time_layer, "00:00");

text_layer_set_font
(s_bat_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14));
text_layer_set_text_alignment
(s_bat_layer, GTextAlignmentCenter);

//windowレイヤーに子レイヤーとしてテキストレイヤーを追加
layer_add_child
(window_layer, text_layer_get_layer(s_bat_layer));

//---------------------

//タイマーサービスに分単位での更新を登録する。
tick_timer_service_subscribe
(MINUTE_UNIT, tick_handler);

//バッテリー状態サービスに登録する
battery_state_service_subscribe
(battery_handler);
//現在のバッテリーを読む
battery_handler
(battery_state_service_peek());

//Bluetooth接続状態サービスに登録する
connection_service_subscribe
((ConnectionHandlers){
.pebble_app_connection_handler = bt_handler
}
);
}

//Windowが破棄された時
static void main_window_unload(Window *window) {
//ビットマップデータを破棄
gbitmap_destroy
(s_background_bitmap);
//ビットマップレイヤーを破棄
bitmap_layer_destroy
(s_background_layer);
//テキストレイヤーを破棄
text_layer_destroy
(s_time_layer);
}

//Appの読み込み時
static void init() {
//Windowエレメントを作成し、ポインタに割り付け
s_main_window
= window_create();

//背景色を黒色に設定
window_set_background_color
(s_main_window, GColorCyan);

//ウィンドウハンドラを割り付け
window_set_window_handlers
(s_main_window, (WindowHandlers){
.load = main_window_load,
.unload = main_window_unload
}
);

//スタックにプッシュし表示。アニメーション有効=true
window_stack_push
(s_main_window, true);
//この時点でmain_window_loadが呼ばれる?

//時刻を初期設定する。
update_time
();
}

static void deinit() {
//ウィンドウを破棄
window_destroy
(s_main_window);
}

int main(void) {
init
();
app_event_loop
();
deinit
();
}
posted by gpsnmeajp at 00:38| Comment(0) | TrackBack(0) | 未分類

2016年01月11日

Pebble Time用日本語言語パック

twitterのリプライに表示不可文字が目立ったので、

Pebble language pack - 日本語言語パックさんにて、寄付をしていたのもあり、
Pebble Laboratory: 言語パック製作所を使ってみました。


設定は以下のとおり。


 で、これでも常用漢字の一部が抜けていたので、常用漢字とキャピキャピ系顔文字、2ch系顔文字、数学記号、罫線、事務系機種依存文字、ギリシャ記号を追加。


データサイズは1226KB。(Pebble Timeの推奨上限は1400KB)
すべてが適用できているわけではないようだが、概ね入ったようだ。
もしかすると、生成元のフォントにない文字が多いのかもしれない。


公開してもよいのかわからないが、上げておく。
サイズが大きいため、Pebble Timeが不安定になる可能性がある。よって利用を推奨はしない。



使用後、まだ不足している文字があることに気付いたので、
いっその事と思い、ShiftJISに含まれるほぼすべての文字を含めた言語パックも作成した。
1388KBと、かなりギリギリのサイズなので注意。

PebbleTimeJapanesShiftJISv2(Seg_faul).pbl

(ところで、これにtwitter頻出5000字を加えると100KBほど増えるのだが、一体どんな漢字が使われているのか...)


これを利用される方は、ぜひPebble language pack - 日本語言語パックさんにて、
寄付をして欲しい。

Ω・響・ω・aなどが表示できていることがわかる。

posted by gpsnmeajp at 00:57| Comment(0) | TrackBack(0) | 未分類

2016年01月10日

FlashAirのmicroSD版

はありませんので。

こういうのとか
変換名人 SDHC/SD → microSDHC/microSDに逆変換するアダプタ SDB-TFA
変換名人 SDHC/SD → microSDHC/microSDに逆変換するアダプタ SDB-TFA

こういうのを使う形になります。
サンコー SD-microSD変換エクステンションケーブル SDCVET2K
サンコー SD-microSD変換エクステンションケーブル SDCVET2K

ほら。

posted by gpsnmeajp at 14:04| Comment(0) | TrackBack(0) | 未分類

2016年01月09日

Pebble使って1日目

Pebble使って1日目、使ってみてわかったこと。

・アニメーションかわいい。とにかくかわいい。

・設定は意外と細かくできる。

・iPhone(iOS)上でのPebble Timeアプリではフィルタリングができないが、
 これはApple Notification Center Serviceで送信しているため。
 なので、iOSの設定アプリから「通知センターに表示する」を無効にすると送信されず、
 有効にしていれば送信される、という挙動をするので、代用ができそうだ。

・画面は常に表示されてるので、光にかざせばはっきり見える。
 反射型液晶のため、角度に結構依存する。
 (腕時計になれた人なら無意識に見やすい角度を探すと思うので問題ないと思う)

・明るい室内なら特に問題ない。暗い室内なら、バックライト必須。
 (あまり良い比較ではないだろうが、劣化したG-SHOCKよりはよっぽど見やすい)

・バックライトは、腕を振るうと付いてくれるので、そんなに不便ではない。
 また、3段階および完全消灯が選べる。

・バッテリーは、1日中頻繁にいじってて10%減。
 ただし10%単位でしか表示されないので、15%くらいは減っているかもしれない。
 慣れてきて触る機会が減ればもっと持つようになるだろう。

・バイブレーションがかなり強力。
 通知やタイマーとかでバイブが動くが、気づかないことがありえないくらいには強い。
 これも3段階および無効が選べる。

・通知が腕で見れるのは結構幸せ。
 楽。かなり楽。スマートフォンを取り出さずに見れるこの感覚はぜひ味わってもらいたい。

・ショートカットキーが便利。
 時計画面にて、上ボタン長押しおよび、下ボタン長押しで、任意のアプリを起動するように
 設定できる。自分は、上ボタンに通知一覧、下ボタンにタイマーを設定している。
 メニューを開かずに操作ができるので便利。

・ボタンの押し心地が良い。
 押しづらかったり、堅かったりしないので、使いやすい。

・G-SHOCKの時、ボタンの押しにくさと、操作のわかりづらさ、耳が悪く音に気づかない等で
 アラームやタイマーを使わず、もっぱらiPhoneのタイマーを使っていたのだが、
 Pebbleにしてからはバイブで知らせてくれるし、操作はグラフィカルでわかりやすいしで
 Pebbleのタイマーを使っている。
 なにげにタイマーのためにiPhoneを取り出して操作するのはストレスだったのだと再認識。

・夜間モードがある。
 平日・休日に通知バイブを制限する・禁止する時間帯を設定できる。
 スマートフォン上の設定とは関係なしに動いてしまうために付いているのだろう。

・戻るボタン長押しでも夜間モードに入る。マナーモードと同等な感じ。

・スタンバイモードもある。
 30分間、腕の動きがないと睡眠中と判断して、Bluetoothを切断してバッテリーを温存する機能が
 あるようだ。ただ、自分は使っていないが。

・Pebbleのアプリは全て無料...ではない。
 →Pebbleのストア自体に課金機能はないが、複雑な機能を実現しているものの場合、
  スマートフォンに入れる方のコンパニオンアプリが有料なことが多い。(120\〜500\くらい)
  極稀に、設定画面を管理するサーバーのほうで課金がかかる場合がある。

・サードパーティのアプリの中には、中途半端な奴も居る。
 Pebble Classicのみ対応のアプリもある。
 Pebble Timeでカラー表示できるように宣伝しておきながら、
 実は白黒しか出せない(しかも有料)というのもある。

・サードパーティのアプリで、設定画面がちゃんと出てこないことがある。
 数分待てば出てきたり、Wi-Fi切れば出てきたり。
 これは多分、設定画面を外部サーバーにおいている弊害だろう。

・IFTTTのレシピを検索してると、Pebbleに使うために作ったらしきレシピがよく出てくる。
 大抵、出力先がPushoverになっている。
 デバイスの通知を経由しないと送れないため。
 なら、IFTTTのアプリIFの通知機能を使えばいいと思うかもしれないが、
 あれはなぜかよく通知に失敗する。

 Pushoverは、500円買いきりの有料アプリで、HTTPSでRequest投げたり、
 メール送ったり、IFTTTで使ったりして、通知を飛ばせる優秀なアプリ。
 FlashAirでも使っているし、自宅サーバーでも使っているのだが、
 なにが優秀かというと、色々カスタマイズできる上に、しっかり通知してくれること。
 マルチデバイス対応で、マルチプラットフォーム対応なので、安心して使える。
  

 



スクリーンショット集

時計画面は 91 Dub v3.0 を使用。

色が予め用意されたテーマの他、自分で各色好きな色を好きな箇所に使える。

使ってみた結果、変に色が付いているより白黒な方が見やすいのと、
愛用のG-SHOCKに合わせてこういう風に設定。

余談だが、設定はiPhoneのPebble Timeアプリから行うのだが、
HTMLとjavascriptで設定画面が構築できるようだ。


公式ミュージックアプリ
安心してください。日本語対応ですよ。(フォント導入時)



Twebble
twitterを腕の上で。レスポンスは良くないが、とりあえず読める。
コンパニオンアプリと提携しているアプリは、日本語で落ちる事が多いのだが、
これはPebble上のみで動いているので問題なく動く。
Postもできるが、英語のみ。かなり特殊な入力スタイルで打ち込む。



MyNotes
非常にシンプルなメモ帳。
腕の上で読まなければならない短い文章を入れておける。
5ページまで入り、ボタンで切り替えられる。



Image Viewer
外部サーバーから画像を読み込んで表示する。
iPhoneをWi-Fiに接続していればLAN上のサーバーにもアクセス可能。
jpg/png/gifをデコード・減色して表示してくれる。画像サイズが多ければ縮小も。


HTTP REQUEST
名前の通り、リクエストを飛ばしてくれる。上下ボタンのワンタッチと、
中央ボタンから入るメニューに6個登録できる。
ただし、短いURLしか対応していないので注意。どのくらい短いかというと、
IFTTTのMaker chが使えないくらい。
作者スクショを見る限り、LAN内使用を目的としているようなので致し方無いか。



Smartwatch+
Pebbleをよりスマートウォッチ化してくれる有料アプリ。
iPhone上のアプリと提携して、天気情報、カメラ操作&表示(白黒)、
GPS現在位置の表示などができる。

こちらのHTTP Request機能は、長いURLでも対応している。
ただし、カレンダーやリマインダー機能は、日本語が含まれた項目があると落ちる。



Compass
無駄に本格的なコンパスアプリ。
Pebble内臓の電子コンパスを使うため、スマートフォンと接続が切れていてもOK。



Battery+
バッテリーモニタ。
かなり詳細な情報を出してくれる他、グラフなどもとってくれる。
グラフはスマートフォン上でも見れる。



Authenticator
いわゆるGoogle認証アプリと同等の働きをする、ワンタイムパスワード生成器。
便利そうなのだが、まだ使っていない。


設定画面の例
必要最小限のものから、Pebble本体と通信していろいろするものも。
本体と通信するものは、読み込みに時間が掛かる。
どれにせよ、設定画面は外部サーバーに置く仕様のようだ。
アプリ作者がサイトを閉鎖すれば、使えなくなるのだろう。(エミュ鯖でも建てないかぎり)
また、設定が悪いのか、キャリアネットワークでないと設定画面が出てこないものもある。


posted by gpsnmeajp at 22:31| Comment(0) | TrackBack(0) | 未分類

2016年01月08日

スマートウォッチのPebbleを買いました



大体の情報はネットにあるので、要点だけ書きます。

Android・iPhoneの両対応のスマートウォッチ。
日本で発売していないが、公式サイトは日本語対応で、技適も通っている。

メモリ液晶を使っており、充電無しで3日〜7日は動くらしい。
Pebble Timeは64色のパレットカラーが使える。

日本語の表示がデフォではできないのだが、
韓国人の方が作成した日本語化パックを使うとシステムメニューも含め日本語化可能。

昔はAndroid端末からしか日本語化できなかったようだが、現在はiPhoneからでも日本語化可能。
(実際やりました)

スマートウォッチ上で動くアプリは、C言語とjavascriptの両方で可能。
→作ったMyWatchFace

スマートフォン上で動く自作のコンパニオンアプリと提携させられる他、
コンパニオンアプリもjavascriptで書いて実行することも可能。

開発環境は、PC上と、Webサイト上の両方があるが、Webサイト上のほうが便利
(アカウント作るだけでOKな上、実機で走らせることもできる。)

30m防水なので雨の中でも水中でも安心。(泳ぐのは非推奨ですが)

後ろの充電ポートはシリアルポートになっており、Arduino等のマイコンとの接続可能。

といったところでしょうか。
公式サイトで注文し、DHLで輸送。1/3日に注文し、1/8に到着しました。
年末年始だから遅いけれども、本来はもっと早い模様。
なんだかんだで合計3万円くらい。

公式サイトからの購入にこだわらなければ、Amazonの並行輸入版が2万円で買える模様。

腕時計上で、自分の好きな時計が使える感覚は、想像以上です。
メールを腕の上で確認できるのが素晴らしい。

まだ使い始めて数十分なので、使い慣れたらまた報告します。


posted by gpsnmeajp at 22:05| Comment(0) | TrackBack(0) | 未分類

2016年01月07日

FlashAiで、書き込まれた最新の1ファイルを自動でDropBoxにアップロードするスクリプト

※本情報は、参考のために残しており、古い情報を含みます。
 現在主に更新しているのはwikiですので、最新の情報はwikiを参照してください。

---



デジカメをDropbox対応にしたり、ゲームフリークをDropbox対応にしたり、
ポメラをDropbox対応にしたり、いろいろ汎用的に使えます。
但し、フォルダ指定の変更をお忘れなく。

アップロードには30秒〜60秒かかります。
もちろん、アップロード中に書き込まれたファイルはアップロードされません。
管理ファイルを用意すれば別ですが。

Dropbox API登録が必要です。手順はこちら(少し古いですが)

フォルダ・ファイル名に日本語や空白が含まれていると失敗する。
(別途対処が必要)

7/17追記
現在、"Error argument:c003fe09" が発生して動作しないのが確認されています。
殆どのHTTPSサイト接続時に起きるようで、原因は不明です。

-----------------dropbox_upload.lua---------------------

--150MBまでの最新のファイルを自動で探索しDropboxにアップロードするサンプル

--ファイルの書き込みをしないので安全。
--但し、書き込まれた時の特定フォルダ内最新1ファイルしかアップロードしない。

--捜索対象のフォルダ(空欄=root)
--末端に/を付けないこと。ファイル探索には成功するが、Dropbox側で怒られる
fpath = "/DCIM/101JVCSO";

--ここにアクセストークン。自分用のAPI登録をしたなら、Generated access tokenで生成できる。
--公式のサンプルのように、appKeyやappSecretを使っていろいろする必要はない。
token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

--アップロード先フォルダ
--App folder– Access to a single folder created specifically for your app.にしたなら、
--アプリフォルダ内にファイルができる。
--Full Dropbox– Access to all files and folders in a user's Dropbox.にしたなら
--自由に。ファイル破壊も発生するので注意。

--uploadDir="/someFolder"
uploadDir="";

--注意:CONFIGでIPアドレスを固定していると、接続してもやっぱり固定されているので色々失敗する。
--DHCP_Enabled=YESにすること。

--注意:STAモードもしくはBridgeモードにすること。
--        APPAUTOTIME=0とすること。(通信が切られかねないため)
--    LUA_SD_EVENT=/dropbox_upload.luaのようにすること。(このファイルをdrobpx_upload.luaにした場合)


--作業用変数
filename = "";

--プロセス開始

   sleep(6000); --ファイル書き込み待機(カメラや使う機器が書き込み終了するまでにかかる時間に合わせて変更する。)
 --ここを下手に短くすると、カメラがエラーを吐いたり、写真が破損したり、1つまえに撮影した画像がアップロードされたりする

--ファイル走査
--http://dotnsf.blog.jp/archives/2015-09-16.htmlより、最新のファイルを探す
last_filepath = ""
max_mod = 0
for filename in lfs.dir(fpath) do
filepath = fpath .. "/" .. filename
mod = lfs.attributes( filepath, "modification" )
if mod > max_mod then
max_mod = mod
last_filepath = filepath
end
end

--ファイルがなければ終了
if(last_filepath == "")then print("No file."); goto EXIT; end;

filename = last_filepath;

--アップロード処理開始

--ファイル本体をデータに
mes = "";
--ファイルサイズを取得
len_s = lfs.attributes(filename,"size");

--アップロード先パス設定
uploadPath = uploadDir..filename;
--Dropbox引数(上書き・通知の有無)
dropboxArg = '{"path": "'.. uploadPath ..'","mode": "overwrite"}';

--ヘッダー情報
hed = {
["Content-Length"] = len_s,
["Authorization"] = "Bearer "..token,
["Dropbox-API-Arg"] = dropboxArg,
["Content-Type"] = "application/octet-stream"
};

  --
--リクエスト
b,c,h = fa.request{
url = "https://content.dropboxapi.com/2/files/upload",
method = "POST",
headers = hed,
body = mes,
file = filename,
bufsize=1460*10
};

--エラーが帰ってきたらファイルに保存する
if(c>200)then
f=io.open("res","w")
f:write(h)
f:write(b)
f:close();
  end

--ブラウザでのデバッグ用
print(dropboxArg);

print(c)
print(h)
print(b)

::EXIT::
--終了処理
posted by gpsnmeajp at 22:56| Comment(0) | TrackBack(0) | 未分類

2016年01月06日

FlashAirからfa.requestを投げる時の注意

※本情報は、参考のために残しており、古い情報を含みます。
 現在主に更新しているのはwikiですので、最新の情報はwikiを参照してください。

---
FlashAirからfa.requestを投げる時の注意。

一言だけ。
「ブラウザからLua実行する場合は、Wi-Fiを弄るな」。

例えば以下の様なスクリプトがある場合、
これをブラウザ(およびFTLE)から実行すると、大抵はc = -1(fa.requestの実行失敗)になる。
たとえ、現在つないでいるWi-Fi APに接続しなおしたとしても、だ。

回避するには、
・ブラウザ(もしくはFTLE)から実行する場合は、Wi-Fi関係の操作をしない
・SDカードイベントもしくは、起動時実行から実行する

--Wi-FiアクセスポイントのSSID
ssid="FlashAir";
--Wi-Fiアクセスポイントのパスワード
networkkey="12345678";
--ここから
fa.Disconnect();
sleep(3000);
fa.Connect(ssid, networkkey);
sleep(15000);
--ここまでをコメントアウトすると動く。
b,c,h = fa.request("http://example.com/")
f = io.open("file.txt","w")
f:write(c);
f:close();

7/17追記
現在、かつて動いていたスクリプトが、"Error argument:c003fe09" が
発生して動作しないのが確認されています。
殆どのHTTPSサイト接続時に起きるようで、原因は不明です。
posted by gpsnmeajp at 21:52| Comment(0) | TrackBack(0) | 未分類

2016年01月05日

FlashAirからDropboxにアップロード"だけ"するスクリプト(公式サンプルより簡単)

--150MBまでのファイルをDropboxにアップロードするサンプル

--追記:送信バッファの設定をしない場合、大きなファイルはアップロードに失敗する
--(成功するがファイルが破損する)ため、公式サンプル同様の送信バッファ設定に修正しました。

--ここにアクセストークン。自分用のAPI登録をしたなら、Generated access tokenで生成できる。
--公式のサンプルのように、appKeyやappSecretを使っていろいろする必要はない。
token = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

--アップロードしたいファイル名
filename = "/FlashTools.lua";

--アップロード先フォルダ
--App folder– Access to a single folder created specifically for your app.にしたなら、
--アプリフォルダ内にファイルができる。
--Full Dropbox– Access to all files and folders in a user's Dropbox.にしたなら
--自由に。ファイル破壊も発生するので注意。

--uploadDir="/someFolder"
uploadDir="";


--アップロード処理開始

   --ファイル本体をデータに
   mes = "";
   --ファイルサイズを取得
   len_s = lfs.attributes(filename,"size");

   --アップロード先パス設定
   uploadPath = uploadDir..filename;
   --Dropbox引数(上書き・通知の有無)
   dropboxArg = '{"path": "'.. uploadPath ..'","mode": "overwrite"}';
   print(dropboxArg);


   --ヘッダー情報
   hed = {
      ["Content-Length"] = len_s,
      ["Authorization"] = "Bearer "..token,
      ["Dropbox-API-Arg"] = dropboxArg,
      ["Content-Type"] = "application/octet-stream"
   };

   --リクエスト
   b,c,h = fa.request{
      url = "https://content.dropboxapi.com/2/files/upload",
      method = "POST",
      headers = hed,
      body = mes,
      file = filename,
      bufsize=1460*10
   };

   --結果
   --c=400なら失敗。200なら成功。
   print(h);
   print(b);
posted by gpsnmeajp at 22:11| Comment(0) | TrackBack(0) | 未分類

2016年01月03日

FlashAirの裏領域

このツイートを見て、ピンと来た。

以前、FlashAirを東芝ツールで初期化する際、どういう処理が行われているのか
中間生成されたファイルを比較して調べていたことがあった。

その際、
COMMAND=fat cp /SD_WLAN/BCONFIG 1:/BCONFIG
というコマンド列が現れた。

これは、多分、裏領域にパスワードを書き込んで、フォーマット後も変更されないようにする
機能を初期化してもとに戻すための処置なのだが、確かに1:でアクセスしている。

これを元に、Luaから 1:/Hello.txtを生成してみたところ、書き込みに成功し、読み込めた。
つまり、裏領域にLuaからアクセスできた。

この領域、ブラウザからはアクセスできず、SDカードリーダーからもアクセス出来ないが
Luaから自由に読み書きできるらしい。
そのため、FlashTools Lua EditorのLoad処理(Luaを使用)でも読みだすことができた。

http://flashair/~/auto.png のようにすれば見えると報告あり。
 (よくよく考えれば当たり前だった。ブラウザから参照できないと画像も表示できない。
  思えば、初回起動時にhttp://flashair/~/Set.htmに飛ばされるのだ。)

以下のスクリプトでファイル一覧が見れる。

--0:/ = / 通常領域
--1:/    裏領域(システムファイル)
--2:/      System unable to describe the error(存在しない)

--ファイル・ディレクトリ一覧表示
for file in lfs.dir("1:/") do
  if file ~= "." and file ~= ".." then
    print( file )
  end
end


実行結果は以下。

FA000001.JPG
auto.png
BCONFIG
Set.htm
backup.htm
bg.png
dcim.png
folder.png
general.js
lang.js
manual.png
movie.png
other.png
photo.png
phtshare.htm
song.png
stpicpth.htm
text.png
welcome.htm
1xsize_parts.png
2xsize_parts.png
photoshare_imge.png
00files.txt

W-03では、カードを空っぽにフォーマットしても正常に動く
(初期設定をスマートフォンからできる)のは、このおかげらしい。

作ったファイルは、fa.remove("1:/Hello.txt")のようにすれば消せる。
posted by gpsnmeajp at 15:35| Comment(0) | TrackBack(0) | 未分類

2016年01月02日

fa.spiでINT端子を出力として使いたい

こちらにて、こんな話があった。
また、本当はFlashAirのSPI用のLuaスクリプト関数であるfa.spi関数を使って制御したかったのですが、うまくいかず、とりあえず今回はfa.pio関数を使って制御しています。
そこからこう考えた。

理由は単純で、過去にソフトSPIを実装した時にも、既存のピンの状態を保存するより、
棄てたほうが遥かに早くI/O操作が行えたからだ。
速度を追求する上では極々当たり前だと考えられる。


そこから、FlashAirとArduino繋いで検証してみた結果。
1.fa.spi("init",n)の操作で、入出力レジスタが設定される。
2.fa.spi("write")およびreadで、出力状態レジスタが設定される。
という様子がわかった。

つまり、fa.spi("init",n)の前にいくら入出力操作をしても、そこで初期化される。

fa.spi("write")およびfa.spi("read")の前に入出力操作をした場合、
入出力方向の操作(と、CSの操作)は保持され、INTはゼロにされる。

よって、INTの操作をしたいなら、INTをプルアップで釣り上げた上で、
出力にすればLow、入力にすればHighといった感じで操作可能かもしれない。

かもしれない、というのは、この操作を行った後SPI機能が正常に動く保証はないためだ。
いわば抜け穴をついてる状態に等しい。


一応、AE-FAIOでの動作(CSのみ操作)は確認した。
注意として、接続するSPI機器の使用を確認しないと、相手方及びFlashAirを破損する。

秋月FlashAir DIP IOボードキットでの使用および、
AirioRP(SW=1-2)でのINT操作は厳禁である。(変換ICの出力が接続されているため)


--コード--

function spi_cs_int(cs,int,sck)
 --csは望み通りに、sckはmode=0,1なら0 / 2,3なら1である。
 --余談だが、この関数を使う場合はCS後の空読みは不要。
 local bit = bit32.bor(bit32.lshift(cs,2),bit32.lshift(sck,1));

 --CS,SCKは設定通りに再設定される
 if(int == 1)then
  fa.pio(0x07,bit); --int=5をINPUT=High
 else
  fa.pio(0x17,bit); --int=5をOUTPUT=Low
 end
end

fa.spi("init",1000000)
fa.spi("mode",3)

spi_cs_int(1,1,1);
fa.spi("read");
sleep(1000)
spi_cs_int(1,0,1);
fa.spi("read");
sleep(1000)
spi_cs_int(0,1,1);
fa.spi("read");
sleep(1000)
spi_cs_int(0,0,1);
fa.spi("read");
sleep(1000)
posted by gpsnmeajp at 13:42| Comment(0) | TrackBack(0) | 未分類

2015年12月27日

FlashAirでLuaスクリプトを組む時のコツ

※本情報は、参考のために残しており、古い情報を含みます。
 現在主に更新しているのはwikiですので、最新の情報はwikiを参照してください。

---
FlashAirでLuaスクリプトを組む時のコツ

※FlashAir本体をアップデートすること!
 W3.00.01にしないと、さまざまなバグに悩まされます。

・メモリは少ない。
 スクリプト含めて16KBくらいと思っておくこと。
 なので、配列確保できるのは大抵10KBくらい。
 大きなデータを処理するのなら、マイコンのようにちまちま小分けで処理しないといけない。

・メモリのお掃除は頻繁に。
 Luaは本来メモリが潤沢にある環境で使うものだと思うのだが、
 そうでないので仕方がない。
 メモリ不足になると、メモリ確保に時間を食うので、処理速度が落ちる。
 また、メモリ不足のメッセージが出力に混ざりこむ。
 これを避けるには、collectgarbage()をプログラムの随所に入れておくこと。
 そうすると速度が落ちない。
 ただ、collectgarbage()自体が時間を食うので、頻繁に呼びすぎてもいけない。

・ライブラリは小さく。
 ライブラリのコードサイズがおもいっきりメモリを食い潰す。
 ライブラリ読み込み時にcollectgarbage()する必要が出ることもある。

・コメント少なく。
 コメントもコードの一部。コメントが大きすぎるとそれだけでメモリエラーになる。
 リリース時にはコメントを全削除するなり、別ファイルにするなりする方がいい。

・変数名・関数名は短く。別名を使うのが良い。
 変数名も多分食ってる。関数名も。
 なので、組み込み関数には別名を割り振って使うと多分軽くなる。
 ただ、これは自分がそう思っているだけで、実際には違うかもしれない。

・argの制限
 arg[0]で自身のパスが分かる。
 arg[1]で引数がわかる。
 引数はrun.lua?Helloのように与えることができる。
 file=A&name=Bのように与えても、分解されることはなくそのままでてくる。
 引数がなければnil。
 URLエンコードしたものは解かれる。バイナリを渡すとバグる。
 URLは(たしか全長含めて)128文字しか受け付けない。
 超えると414が帰る(3.00.01)。古いバージョン(3.00.00)だとバグる。
 末端にはLFがくっついているので注意。

・mathが無いので
 数学関数mathがない。ので、浮動小数点を切り捨てることすら出来ない。
 ビット演算は適当に丸めやがるので、切り捨てたい時には使えない。
 解決方法: block = tonumber(string.format ("%d",num));
 アホみたいだが、こういうやり方になる。

・RTCあるって知ってた?
 地味にあるんです。


・ファイルの書き込みという悪魔の所業
 ファイルの書き込みは色々と問題があるので、正直やりたいものではない。
 ホスト機器に差し込まれているときは尚更だ。
 外部からuploadやPUTをした時は、FATにロックが掛かることを忘れるな。
 Luaスクリプトから書き換えるときは、FATにロックがかからないことを忘れるな。
 FATにロックを掛けたければ
 fa.request("http://127.0.0.1/upload.cgi?WRITEPROTECT=ON")
 逆に、ファイルシステム破壊を恐れず解除したければ(再マウントすれば問題ない)
 fa.request("http://127.0.0.1/upload.cgi?WRITEPROTECT=OFF")


・共有メモリというスクリプト間共通変数
 一旦終了したスクリプトから、次に起動するスクリプトに情報を渡すには
 ファイルに書き込む方法を思いつくだろうが、それは上記の理由でおすすめしない。
 W3.00.01から、Luaで共有メモリにアクセスできるようになったので、
 こちらを使うことをおすすめする。512バイトまで記録できる。
 ブラウザからアクセスすることもできる。


・fa.requestの罠
 まず、3KBまでしか受信できない。
 タイムアウトがない。
 エラーが起きると最悪応答がなくなる。
 ポート番号も指定できない。
 ただ、TLS(https)はできる。
 せめてタイムアウト指定とか、なければタイマー割り込みとかほしい...


・隠し関数は積極的に
 ファイルの削除とか、ファイル名変更とか、ping飛ばしたりとか、できるんですよ実は。
 将来的に削除される可能性もありますが。 

・処理速度は20kHz?
 fa.pioで色々測ってみた限り、1処理に500usくらい食ってる感がある。
 また、処理中にWi-Fi関係とかの割り込みが掛かっている感がある。
 

・Wi-Fi起動電流に注意
 かなり食います。電流測定用にテスターかませるだけでダメになることも。
 起動時に200mA、定常120mA程度食うようです。
 Wi-fiをオフにすると数mA〜数十mAで収まります。
 参考。

・pioとホスト
 fa.pioはホストとの通信を妨害します。
 ホストが初期化する前にpioを呼ぼうものなら、信号が衝突しますし、
 初期化後に拒否られた状態でpio呼んでも不安定になるようです。

 対策:スクリプトの冒頭に下のような行を入れる。
 sleep(15000);
 if(fa.pio(0,0) == 0)then return; end;

・結果を手早く見れるのはLEDと液晶
 ブラウザからLuaスクリプトを起動すると、CGIなので
 スクリプトが終了するまで結果が出てきません。
 なので、途中状態を知りたいのなら、LEDなり液晶なりを繋ぐか、
 fa.requestを受け取るサーバーでも用意するか、な必要があります。


・デバッグ面倒くさいならFTLEがおすすめ(ステマ)
 ブラウザからLuaスクリプトの書き換え・デバッグができるエディターあります。
 デバッグが楽になること間違いなし。


・IOポートの動作確認から始めるといいよ(ステマ)
 IOポートでなにかするのなら、まずはIOポートの出力が対象に伝わっているか
 からチェックするのがいいと思います。便利なツールが有ります。

・ライブラリを使いましょう
 悩む時間のほうがもったいないので。
 Airioを使うならこれ。
 AirioRPをつかうならこれ
 秋月FlashAir DIP IOボードを使うならこれ。

・ブリッジモードオススメ
 APモードでデバッグというのはやりづらいです。(ネットから切断されるので)
 なので、ネットが使えるSTAモードやブリッジモードを使うことをおすすめします。

 STAモードだとIPアドレス変わるからデバッグしにくいじゃないか、という方は
 こちらのIPアドレス固定方法を御覧ください。
 APモードやブリッジモードのFlashAir側のIPアドレスも同様に固定できます。

・古いファームウェアでスクリプトが動かない問題は、以下の様なコードでバージョンチェック
 してしまうことをおすすめします。
 特に新しい関数を使っている場合は読み込み時に落ちるので、
 その場合はブートストラップと本体にスクリプトを分けましょう。

if(fa.md5 ~= nil) then
print("FlashAir firmware version is too old!");
error("error() has been called.");
end;

posted by gpsnmeajp at 23:10| Comment(0) | TrackBack(0) | 未分類

FlashAirにA/Dコンバータ(MCP3008)をつなぐ

FlashAirにA/Dコンバータをつなぐのは非常に簡単。
MCP3008を用意し、以下のようにつなぐ。
(可変抵抗は観察用で、実際には必要な電圧入力を設定する)



次のようなスクリプトを書く。

fa.spi("init",1000);
fa.spi("cs",0);

fa.spi("write",0x01);
a = fa.spi("write",0x80);
b = fa.spi("write",0xFF);

fa.spi("cs",1);
fa.spi("read");

s = bit32.bor(bit32.lshift(bit32.band(a,0x03),8),b)
v = 3.3*s/1023;

print("CH0:"..v.."[V]");

一応解説を入れておくと、
0x01はスタートビット
0x80は測定指示(シングルエンド・CH0・ダミー)
0xFFはダミーである。

a,bに合計10bitのデータが入るので、sで結合し、vで電圧にしている。
MCP3008は厳密にはSPIではない(8bit刻みではない)のだが、
SPIでも互換で使えるように設計されており、
データシートにあるSPI機能を用いた使い方そのままである。

cs=1の後、空読みしているのは、前回の記事に載せた仕様(orバグ)ためである。

センサー情報をWi-Fiで飛ばしたい、あるいはスマホから読み取りたい時には便利なので
試してみてほしい。

より多くのセンサーを繋ぎたいのであれば、I2C変換機能を持ったボード
(秋月FlashAir DIP IOボードもしくはAirioRP)を使い、
I2Cで繋がるA/DコンバータICを使うことをおすすめする。

(細工をすれば、あまった1ピンで2つのSPIスレーブを制御することもできるが)



posted by gpsnmeajp at 13:21| Comment(0) | TrackBack(0) | 未分類

2015年12月24日

fa.spiの挙動(秋月 FlashAir DIP IOボードキットでfa.spiが動かない問題)

※FlashAir本体をアップデートすること!
 W3.00.01にしないと、fa.spi関数は正常に動作しません!
 (W3.00.00にも関数が存在はしていますが、動作不良のようです)

秋月DIPボードの挙動が、fa.spiを使った時だけ妙 + mode切り替えをすると動くことがある
という話を聞いたので、mode切り替えだけで出力が切り替わるのかを調査。


初期状態
SCK  = HIGH(Hi-Z)
MOSI = HIGH(Hi-Z)
INT  = HIGH(Hi-Z)
SS   = HIGH(Hi-Z)
MISO = HIGH(Hi-Z)

fa.spi("init")
SCK  = LOW
MOSI = LOW
INT  = HIGH(Hi-Z)
SS   = HIGH
MISO = HIGH(Hi-Z)

→defaultではモード3とリファレンスに書いてあるが、どうみてもmode3ではない。
 ただ、1度空readをするとmode3らしき挙動になる。
 また、モードを切り替えてもinitするとまた戻される。

fa.spi("mode",0)
SCK  = LOW
MOSI = LOW
INT  = HIGH(Hi-Z)
SS   = HIGH
MISO = HIGH(Hi-Z)

fa.spi("mode",1)
SCK  = LOW
MOSI = LOW
INT  = HIGH(Hi-Z)
SS   = HIGH
MISO = HIGH(Hi-Z)

fa.spi("mode",2)
SCK  = HIGH (時折LOWのまま)
MOSI = LOW
INT  = HIGH(Hi-Z)
SS   = HIGH
MISO = HIGH(Hi-Z)

fa.spi("mode",3)
SCK  = HIGH (時折LOWのまま)
MOSI = LOW
INT  = HIGH(Hi-Z)
SS   = HIGH
MISO = HIGH(Hi-Z)

・結論
切り替わった。

ただし、時折切り替わり不良を起こしているらしき状態がある。
(mode3なのにSCKがLなど。cs切り替えでも発生する)

その場合、いくら切り替えても治らない。(極稀に治る。)
ただ、fa.spi("read")とでもして送信処理をさせれば、必ず切り替わる。


ちなみに、このページの情報を元にどんなに頑張っても
AE-FAIO(秋月 FlashAir DIP IOボードキット)は思い通りに動かないと思います。
サンプル通りソフトSPIで使うことを強くおすすめします。

※動きました。
CSの前、もしくは後に空読みを入れると、不安定さが解消するようです。
CSが反映されないことがあるバグ(or仕様)が影響し、
連続したパケットとして認識されていた可能性が高いです。
ソフトSPIで動いていたのは、このバグ(or仕様)がないためだと考えられます

function write_reg(addr,data)
--fa.spi("read"); --こちらでも良い。
fa.spi("cs",0)
fa.spi("write",0x20)
fa.spi("write",addr)
fa.spi("write",data)
fa.spi("cs",1)
fa.spi("read");
end

if(fa.md5 ~= nil) then
print("FlashAirのファームウェアが古すぎます");
error("error() has been called.");
end;

fa.spi("init",300000);
fa.spi("mode",3);
write_reg(0x00,0xaa) -- IOCONFIG

write_reg(0x01,0x00) -- IOSTATE
sleep(100)
write_reg(0x01,0x0F)
sleep(100)
write_reg(0x01,0x00) -- IOSTATE
sleep(100)


この情報を元に、秋月FlashAir DIP IOボード用のライブラリを作りました。

posted by gpsnmeajp at 21:17| Comment(0) | TrackBack(0) | 未分類

2015年12月16日

FlashAirのLuaスクリプトでtarアーカイブを展開する

--展開にはめっちゃ時間かかります(1ファイル最低5秒以上)
--Tiny Tar Extracter.lua
--超簡易版です。ファイル名と、ファイル種別(フォルダorファイル)、サイズ、チェックサムしか見てません。
--(しかもチェックサムおかしくても展開続けます)
--フォルダ作成処理のところを入れ替えればWindows上でも動きます。

MastarPath="/";

f = io.open ("/FlashAir_Tiny_Lua_Editer.tar","rb");
if(f==nil)then
print "Err";
return;
end
print "OPEN";
next_file_head = 0;

while true do
collectgarbage();
now_file_head = next_file_head;
--FileName
f:seek("set",now_file_head);
fname = f:read(100);
--tar末端検出
fname = string.match (fname,"[^\0]+");--null文字削除
if(fname == nil)then break; end;
print("name:"..fname);

--FileSize
f:seek("set",now_file_head+0x7C);
size = f:read(12);--Tar形式は文字列
size = string.match (size,"[^\0]+");--null文字削除
size = tonumber(size,8);--(ただし8進数!)
print("size:"..size);
block = tonumber(string.format ("%d",((size+511)/512))); --ビット演算ダメ
print("blocks:"..block);
next_file_head = now_file_head + ((1+block)*512); -- HEAD+BODY
print("next_file_head:"..next_file_head);

--chksum
f:seek("set",now_file_head+0x94);
chksum = f:read(8);
chksum = string.match (chksum,"[^\0]+");--null文字削除
chksum = tonumber(chksum,8); --これも8進数
print("chksum:"..chksum);

sum=0;
for pos=0,511 do
f:seek("set",now_file_head+pos);
if((pos >= 0x94) and (pos < (0x94+8)))then
sum = sum+0x20;
else
sum = sum+string.byte(f:read(1));
end
end
print("sum:"..sum);

if(sum == chksum)then
print("chksum OK");
else
print("chksum NG");
end

--typeflag
f:seek("set",now_file_head+0x9C);
typeflag = f:read(1);
if(typeflag == "0")then
type = "File";
elseif(typeflag == "5")then
type = "Directory";
else
type = "Unsupported";
end

print("typeflag:"..typeflag.."/"..type);

--出力
if(type == "Directory")then
--windows用
--fname = string.gsub (fname, "/","\\")
--os.execute("mkdir "..fname)
print(string.sub("mkdir:"..MastarPath..fname,1,-2));
    if(lfs.mkdir(string.sub(MastarPath..fname,1,-2)) == true)then --末端/は削る
      print("mkdir OK");
      else
      print("mkdir fail");
      end
end
if(type == "File")then
fo = io.open (MastarPath..fname,"wb");
if(fo==nil)then
print "Output Err";
return;
end

collectgarbage();--メモリのお掃除


    --debug限界:8192+4096+1024+384 = 13696
    --release限界:8192+4096+1024+540 = 13852
Trans_Block =8192; --転送ブロックサイズ。メモリの余裕によって変更。
    pos_save=0;
    if((size-1) > Trans_Block)then --サイズが小さいならループしない
for pos=0,(size-1-Trans_Block),Trans_Block do
collectgarbage();--メモリのお掃除

      --メモリの関係もあるので64バイトづつ書き込む
f:seek("set",now_file_head+512+pos);
fo:write(f:read(Trans_Block));

--if((pos % 10240) == 0)then
io.write("\rputting..."..pos.."/"..size.."    ");
--end
pos_save=pos;
        end
pos_save=pos_save+Trans_Block;--最後の転送分を加算
end
print(size-pos_save)
f:seek("set",now_file_head+512+pos_save);
fo:write(f:read(size-pos_save));

fo:close();
end

print("\n---");
end


f:close()
print "CLOSED";


Copyright (c) 2015, GPS_NMEAAll rights reserved.



Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


posted by gpsnmeajp at 00:07| Comment(0) | TrackBack(0) | 未分類

2015年12月14日

FlashAirの設定ファイルを自動で書き換えたい人向けLuaスクリプト

FlashAirの設定ファイルを自動で書き換えたい人向けLuaスクリプト。
今までの手抜き置換実装ではなく、本格的にCONFIG丸ごと書き換えられるように作ってみた。
配布Luaスクリプトで設定を自動で書き換えたい場合なんかにどうぞ。
動作確認はしていますが、必ず動作する・設定ファイルを破壊しない保証はありません。

三条項BSDライセンス。

--設定データテーブル。無くてもいいっちゃいい。
ini={
    APPAUTOTIME="",
    APPINFO="",
    APPNAME="",
    APPNETWORKKEY="",
    APPSSID="",
    BRGNETWORKKEY="",
    BRGSSID="",
    CID="",
    CIPATH="",
    DELCGI="",
    LUA_RUN_SCRIPT="",
    LUA_SD_EVENT="",
    MASTERCODE="",
    PRODUCT="",
    STA_RETRY_CT="",
    TIMEZONE="",
    UPDIR="",
    VENDOR="",
    VERSION="",
    IP_Address="",
    Subnet_Mask="",
    Default_Gateway="",
    DNS_Server1="",
    DNS_Server2="",
    Proxy_Name="",
    Port_Number="",
    APPMODE="",
    DNSMODE="",
    IFMODE="",
    LOCK="",
    NOISE_CANCEL="",
    UPLOAD="",
    WEBDAV="",
    DHCP_Enabled="",
    Proxy_Enabled="",
 
--[[
      APPCHANNEL="",
    HTTPDMODE="",
    HTTPDUSER="",
    HTTPDPASS="",
    APPMINTIME="",
    APPMAXTIME="",
    REDIRECT="",
    APPEXT="",
    APPTYPE="",
    AGINGTIME="",
    WLANSTAMODE="",
    DOMAINNAME="",
    COMMAND="",
    APMODE="",
    NOGATEWAYMODE="",
    SCRIPT="",
]]--
}

-- CONFIG READER ---
print "HTTP/1.1 200 OK\nPragma: no-cache\nCache-Control: no-cache\n";

--READ START
f = io.open("/SD_WLAN/CONFIG","r");
if(f==nil) then print ("Not Found.");return; end;

--ANALYZE
for l in f:lines() do
    s = string.gsub(l," ","");
    h = string.sub(s,1,1);
    print(s);
    if (h ~= "[") then
        for key, value in string.gmatch(s, "([^=]+)=([^=]+)") do
            ini[key] = string.gsub(value,"\r","");
        end
    end
end

f:close();

--CONFIG...
--ここで書き換えたい設定を記入する。
--ここで変更しなかったデータはそのまま残る。(順序はシャッフルされるが)

ini["DNSMODE"]="0";

--DELETE VOID KEY
for key,value in pairs(ini) do
    if(value == "")then
        ini[key]=nil;  
    end
end

--OUTPUT a-z
fa.rename("/SD_WLAN/CONFIG","/SD_WLAN/CONFIG.bak");

fo = io.open("/SD_WLAN/CONFIG","w");

print("-----")
print("[Vendor]\n");
fo:write("[Vendor]\r\n\r\n");
for i=0,26,1 do
    n = 0x61 + i;
    for key,value in pairs(ini) do
        h = string.byte(string.lower(key));
        if(h == n) then
            print(key.."="..value.."");
            fo:write(key.."="..value.."\r\n");
          end
    end
end
fo:close();

print "Done";

posted by gpsnmeajp at 23:16| Comment(0) | TrackBack(0) | 未分類

2015年12月06日

FlashAirのLuaスクリプトで得たファイル更新時間を文字列に直す

FlashAirデベロッパーフォーラムで「ファイルの更新日時を取得したい」という話があった。
どうせUNIX時間か何かだろう、と思ったが、試してみるとハマったので書いておく。

FlashAirに搭載されたLuaFileSystemライブラリからファイルの更新時間を
取得する(
lfs.attributes→modification)謎の数値が帰ってくる。例えば、1199991528など。

UNIX時間かな?と思い、変換してみると、今さっき作ったファイルなのに
2008/01/11 03:58:48とかになってしまう。

日付がおかしいのならともかく、時間単位でも合わない。

UNIX時間との差をとっても、次のファイルはまたおかしな値になる。

さっき作ったファイルと、今作ったファイルの値を比較してみても、秒単位ではないなにかで

数値が増えていることがわかる。単純に二倍や1\2でもない。

そう、これは秒数ではない。

では正体は何かというと、FATの時間情報そのものだ。
形式は以下。

01000111100001100110101011101000
YYYYYYYMMMMDDDDDHHHHHMMMMMMSSSSS

FATファイル システムのしくみと操作法 - ELM
http://elm-chan.org/docs/fat.html
こちらのサイトで言うところの、

DIR_WrtDateとDIR_WrtTimeの各16bitを結合した32bitのバイナリ情報だ。

ということで、これらを変換して、人間に読める形式に変換するスクリプトを書いた。

Lua5.2にビット演算機能が実装されていて本当に良かったと思った。

ライセンスはWTFPLなので、自由に使って欲しい。


--FAT形式のファイルアクセス時間を分解する
function GetFileModificationTime(Fat_binary_time)
   Year = bit32.band (bit32.rshift(Fat_binary_time, 9+16),0x7F) + 1980
   Month = bit32.band (bit32.rshift(Fat_binary_time, 5+16),0x0F)
   Day = bit32.band (bit32.rshift(Fat_binary_time,0+16),0x1F)

   Hour = bit32.band (bit32.rshift(Fat_binary_time, 11),0x1F)

   min = bit32.band (bit32.rshift(Fat_binary_time, 5),0x3F)
   sec = bit32.band (Fat_binary_time,0x1F)*2; --FAT時間は秒数が2秒刻み

   return Year,Month,Day,Hour,min,sec

end

--yyyymmdd_hhmmss.jpgを生成する

function TimeToFileName(Year,Month,Day,Hour,min,sec)
   return string.format("%04d%02d%02d_%02d%02d%02d.jpg",Year,Month,Day,Hour,min,sec)
end

-------------------サンプル----------------------

--Hello.txtの更新時間を取得する
a = lfs.attributes("Hello.txt")

--文字列で得る

Year,Month,Day,Hour,min,sec = GetFileModificationTime(a.modification)
print(Year.."年"..Month.."月"..Day.."日"..Hour.."時"..min.."分"..sec.."秒")

--文字列で得たものをファイル名形式にする

print(TimeToFileName(GetFileModificationTime(a.modification)))
posted by gpsnmeajp at 15:27| Comment(2) | TrackBack(0) | 未分類

2015年11月24日

FlashAirなどの外部ストレージを、物理的に取り外さずに再接続する(Windows)

FlashAirを使っていて、カードを抜き差しする場面は2つある。

1つは、設定の更新や、起動時Luaスクリプト実行のテストなどで
 FlashAirの電源を入れ直す必要がある場合。

もう1つは、LuaスクリプトなどでFlashAirの中身が書き換わったが
 Windows側のキャッシュが更新されていないため、見えないという状態を解決するために
 読み込み直しの手段が再接続しかなく、しかたなく行う場合。


割合としては、後者のほうが多いだろう。
SDカードスロットやUSBコネクタは、頻繁な抜き差しに耐えうる構造になっているとはいえ、
大規模なプログラムになるに連れ、デバッグにはかなりの回数の抜き差しが発生するというのは
容易に想像できる。

そこで、どこかのサイトで見かけた方法を使用することにした。

SDカードリーダーは大抵の場合、USB経由で接続されている。(外付け・内蔵問わず)
そのため、デバイスマネージャから、一度無効にし、有効にすれば、
USB機器が再接続されたものと認識され、外部記憶装置の再読み込みが行われる。

しかし、それを手動でやるのはダルい。

そこで、Windowsのドライバー作成キット、WDKに内蔵されている
Windows Device Console (devcon.exe)を使う。

devcon.exeは、デバイスマネージャでできることを、コマンドライン上で実行できるようにする
公式のツールで、バッチファイルからデバイスの操作が可能になる。

詳しくは他サイトを参照して欲しいが、以下の様なバッチファイルを作成すれば良い。

devcon disable "USB\VID_0000&PID_0000&REV_0000"
devcon enable "USB\VID_0000&PID_0000&REV_0000"

USB\VID_0000&PID_0000&REV_0000の部分については、USB機器固有の情報であり、
デバイスマネージャで対象機器(ここではSDカードリーダー)のプロパティを開き、
ハードウェアIDを参照すれば、そのままのものが載っている。

この時、バッチファイルは管理者権限で実行する必要がある。
簡単にやるのであれば、バッチファイルを右クリックし、管理者権限で実行すればよいが、

バッチファイルが起動された時、管理者権限でなければ管理者権限で実行するような
バッチを組むと、さらに便利になる。

バッチファイルからUAC昇格ダイアログを表示する by @hymtk7 on @Qiita http://qiita.com/hymtk7/items/b3b77ad3375095dcd7eb

また、この操作では、USB機器をぶち抜き、ぶっ挿していることになる。
他のツール等を組み合わせ、安全な取り外しをしてから
再接続するようにしても良いかもしれない。


自分の使っているバッチファイルを以下に示す。
動作の保証はしない。

昇格部分には、上記のページのものを参考にさせていただいた。

------ remount.bat -------

@set @temp=0/*
@echo off

whoami /groups | find "S-1-16-12288" > nul
if "%errorlevel%"=="0" (
    echo 管理者権限で実行しています。
) else (
    echo 管理者権限が必要です。
    echo Relaunching Elevated: "%~dpnx0" %*

    if '%1'=='ELEV' (
        shift
    ) else (
        cscript.exe //e:jscript //nologo "%~f0" "%~0"
        exit /B
    )
)


devcon disable "USB\VID_0000&PID_0000&REV_0000"
devcon enable "USB\VID_0000&PID_0000&REV_0000"

goto :EOF
*/
var UAC = new ActiveXObject("Shell.Application");
UAC.ShellExecute(WScript.Arguments(0), "ELEV", "", "runas", 1);
posted by gpsnmeajp at 21:27| Comment(0) | TrackBack(0) | 未分類