SyntaxHighlighter

2016年9月15日木曜日

ボクのかんがえたさいきょうのFlashAir拡張ボード

ボクのかんがえたさいきょうのFlashAir拡張ボード。
検討だけ。

・ボード上にはMCUが乗っており、共有メモリを使ってFlashAirと通信する

・共有メモリ上には固定の領域を設けておく。
 デジタル入力(8本?)
 デジタル出力(8本?)
 A/D入力(10bit?5本?)
 PWM出力(サーボ用)(5本?)
 LCD(I2Cでもなんでもいいので適当に)
 UART(つける?)

・一定周期で更新。
 出力は、サイクルごとにメモリを読みだし、に書き込まれた情報通りに出力。
 A/Dや入力も、サイクルごとに行う。
 LCDは、共有メモリ上の更新フラグが立った時、LCDに書き込む感じ。

・MCUはAVRかそこらで実装する。
・MCUの書き換えは想定しない。

・空き領域を使って、特殊コマンドを発行していろいろ出来ると楽しそう?

利点
・共有メモリなので、FlashAir上のLuaや、ブラウザのjavascriptの両方から操作可能。
・入出力が少なく、種類もないFlashAirの欠点をカバーできる
・一般的な5VのI/Oが利用できる
・Wi-Fiを活かせる
・FlashAir側からすれば単に共有メモリへの読み書きであり、プログラムがシンプル
・MCU側で好きなようにできるため、機能の拡張がしやすい

欠点
・共有メモリを扱う都合上、遅い。8MHzで通信しても、書き込み・読み出し合計3ms掛かる。
 いろいろ考えると、200Hzで更新できればいいほうか。
・汎用マイコンでいい気もしないでもない。
・せっかくのPIOやSPI機能を活かせない
・I2CのIOポートエキスパンダーのほうが安価では?
iSDIOとしての通信になるため、SDカードライセンスの問題が出てくる


ライセンスさえ問題なければ、個人で作って売れそうな気もしないでもない。

SDカード対応と謳わなければよいのか?
ArduinoとSDカードシールドなんかのライセンスはどうなってるんだ?
プログラムが入っていなければ問題ないのか?

Airioなんかは、あれはSDカードとしての動作ではないから引っかからないのか?

2016年9月9日金曜日

FlashAirの共有メモリにアクセスするサンプル

突然、無性にArduinoのSDカードライブラリに頼らず、FlashAirの共有メモリにアクセスしたくなったので。

今後、Luaスクリプトとマイコンの間で通信したくなる機会が増えると思うので、
苦手意識の強かったSDカードコマンドでの通信を試すべきだと思った。

SDカードライブラリに頼らなかったのは、あれがGPLだということと、
PIC等他のC環境で使いたかったため。移植を楽にするため、共有メモリアクセスに特化したかった。

ACMD41での初期化など、色々はまったので、SDカードの初期化の手順的な参考にもなるかもしれない。
プログラム本体は、長いのでサイトの方にあげてある。
https://sites.google.com/site/gpsnmeajp/electricmemo/flashairno-gong-youmemoriniakusesusurusanpuru

DIV4で、512Byteのアクセスに2.5ms程度。
DIV2だと1.8ms程度。

2016年9月6日火曜日

Grove MP3モジュール v2.0 (KT403A)の仕様について(曲選択機能)

Grove MP3モジュール v2.0 (KT403A)の仕様について。

秋月で売られているKT403Aのモジュールは、安価で、
SDカード(およびUSBメモリ)からmp3音楽を再生できる、非常に便利なモジュールであるが、
曲選択機能においてわかりづらい場所があって困ったので、記録しておく。

このモジュール、単に入れた曲を再生したり、切り替えたりするのは問題ない。
しかし、このモジュールを使いたい多くの人は、特定のmp3ファイルを指定して再生したいだろう。
ここで引っかかりが出る。

まず、予め言っておくが、秋月のページに有る英文データシートは明らかに項目数が少ない。
翻訳の際の誤植もある。中国版データシートを参照することをおすすめする。

ちなみに、通信のチェックサムがさもあるかのように書かれているが、実際に通信する際には
チェックサムは不要である。有効にするオプションが有るのかもしれないが。


まず、最初に、特定のファイルを再生しようとして使うのは、
"Play specific track*" だろう。
Groveのサンプルで言うところの
void SpecifyMusicPlay(uint16_t index)
である。

0x7E 0xFF 0x06 0x03 0x00 FF FF 0xEF
と送れば、65535.mp3が再生される、と思うかもしれない。
実際には、65535番目に記録されたファイルが再生される。

0.mp3
1.mp3
999.mp3

があったとき、
0x7E 0xFF 0x06 0x03 0x00 00 02 0xEF
と送ると、999.mp3が再生される。(02ではなく、03だったかもしれないが)

データシートをよく読むと、「Note that the empty filed need to be filled with specific numbers.」と
書かれているが、それはこの仕様のためである。


"2.3.7 Specify a file to play tracks inside [0x14]"
も同じで、

Play track 0255 (track’s name) in folder 0001 : 7E FF 06 14 00 10 FF FD D8 EF
Play track 1999 (track’s name) in folder 0001 : 7E FF 06 14 00 17 CF FE 01 EF
Play track 0001 (track’s name) in folder 0012 : 7E FF 06 14 00 C0 01 FE 26 EF
Play track 0255 (track’s name) in folder 0012 : 7E FF 06 14 00 C0 FF FD 28 EF
Play track 1999 (track’s name) in folder 0012 : 7E FF 06 14 00 C7 CF FD 51 EF

と書かれていることから、

例えば
7E FF 06 14 00 10 FF FD D8 EF
と送れば、フォルダ0001の、0255.mp3が再生される。
...と思うだろう。実際には、1番目のフォルダの、255番目に記録されたファイルが再生される。
255番目である。ファイル名に関係なく、FATに記録された順番で再生されるらしい。


特定のファイル名を再生したいならば、
"Specify a certain folder and play tracks inside [0x0F]"
Groveのサンプルで言うところの
void SpecifyfolderPlay(uint8_t folder, uint8_t index)
を使う必要がある。

Specify folder name 01 and track name 001xxx.mp3 7E FF 06 0F 00 01 01 EF
Specify folder name 11 and track name 100xxx.mp3 7E FF 06 0F 00 0B 64 EF
Specify folder name 99 and track name 255xxx.mp3 7E FF 06 0F 00 63 FF EF

※255が誤植で001になっているので注意。

これも表記がわかりにくいのだが、

Specify folder name 01 and track name 001xxx.mp3 7E FF 06 0F 00 01 01 EF
というのは、つまり、01という名前のフォルダの、001から始まるファイル名が再生されるということである。
中国版データシートでは、スクリーンショットとともに丁寧に説明されている。

つまり、
01\001Test.mp3 → 7E FF 06 0F 00 01 01 EF
01\002Wow.mp3 → 7E FF 06 0F 00 01 02 EF
02\255Buzz.mp3 → 7E FF 06 0F 00 02 FF EF
ということである。

もちろんこちらは空のファイルで埋める必要はない。きちんとファイル名で再生してくれる。

とりあえず以上。

2016年9月5日月曜日

Pebble Timeのファームウェアが4.0になった。

Pebble Timeのファームウェアが4.0になった。
メニュー画面がシンプルになり、アイコンが表示されてわかりやすくなった。


また、QuickViewが追加され、近いイベントがウォッチフェイスに表示されるようになった。
ところがこれが曲者で、自作のWatchFaceは、ちょうどQuickViewにすっぽり隠れる位置に
情報を集約させているため、時計が見えなくなってしまうのだ。

時計が見えず、次の予定までの時間のみが見える時計、というのも、悪くはないかもしれないが困る。
一応、設定からQuickViewを無効にすれば今までどおりに使えるのだが、
せっかくのスマートウォッチな機能を無効にするのもアレである。

ので、QuickViewが表示されるときは、単にWatchFace全体を上にずらすことにした。単純明快である。

しかし、Pebble公式SDKの解説がわかりづらくて困った。自分の読解力がないだけかもしれない。
ようは簡単な話で、

今まで、
   GRect bounds = layer_get_bounds(window_layer);
で得ていた全画面の描画領域を、
   GRect bounds = layer_get_unobstructed_bounds(window_layer);
に置き換えて、座標指定を相対化してあれば勝手にずれる、という話なのだが、
自作のWatchFaceはそれではうまくいかない。

結局、描画オブジェクトを1枚のレイヤーに集約し、

   GRect QVbounds = layer_get_unobstructed_bounds(window_layer);
   layer_set_bounds(all_layer, GRect(0, QVbounds.size.h +(0-168), QVbounds.size.w, 168));
で移動。

しかしこれでは、表示中にQuickViewが現れた場合に対応しないので、
//unobstructedハンドラ
static void prv_unobstructed_will_change(GRect final_unobstructed_screen_area,
void *context) {
  GRect bounds = final_unobstructed_screen_area;
  layer_set_bounds(all_layer, GRect(0, bounds.size.h +(0-168), bounds.size.w, 168));
}

//unobstructedハンドラ
static void prv_unobstructed_did_change() {
   Layer *window_layer = window_get_root_layer(s_main_window);
  GRect bounds = layer_get_unobstructed_bounds(window_layer);

  layer_set_bounds(all_layer, GRect(0, bounds.size.h +(0-168), bounds.size.w, 168));
}
を追加。

static void main_window_load(Window *window) 
  //画面領域サービス(Quickview)に登録する
  UnobstructedAreaHandlers handlers = {
    .will_change = prv_unobstructed_will_change,
    .did_change = prv_unobstructed_did_change
  };
  unobstructed_area_service_subscribe(handlers, NULL);
を追加。

でなんとかなった。
詳細は後でサンプルを上げようと思う。

2016年9月1日木曜日

ポテンシャル法のC言語での実装例

ロボットの障害物回避などに使われる、ポテンシャル法のC言語での実装例
(あっているかは不明なので、参考程度に)


なんで調べても数式ばっかりで実装例が出てこないんだろう?


*ポテンシャル場の確認
#include <stdio.h>
#include <math.h>

double get_pot(double x, double y)
{
const double Enemy1_x = 5.;
const double Enemy1_y = 10.;

const double Enemy2_x = -10.;
const double Enemy2_y = -20.;

const double Goal_x = 17.;
const double Goal_y = 0.;

const double Weight_Enemy1 = 1.0;
const double Weight_Enemy2 = 1.0;
const double Weight_Goal = 5.0;

//スカラー場なのでx,y成分共に同じ
double Enemy1_pot = 1.0 / sqrt((x - Enemy1_x)*(x - Enemy1_x) + (y - Enemy1_y)*(y - Enemy1_y));
double Enemy2_pot = 1.0 / sqrt((x - Enemy2_x)*(x - Enemy2_x) + (y - Enemy2_y)*(y - Enemy2_y));
double Goal_pot = -1.0 / sqrt((x - Goal_x)*(x - Goal_x) + (y - Goal_y)*(y - Goal_y));

double pot = Enemy1_pot * Weight_Enemy1 + Enemy2_pot * Weight_Enemy2 + Goal_pot * Weight_Goal;
return pot;
}

int main(void)
{
FILE *fp = fopen("output.txt", "w");
if (fp == NULL)
{
printf("Err\n");
return -1;
}

printf("wait...\n");

const double delta = 0.1;
const double max = 1.0; //上限(発散部分除外)
// const double zoom = 10.;
for (double x = -30; x < 30; x += delta)
{
for (double y = -30; y < 30; y += delta)
{
double pot = get_pot(x, y);
if (pot > max)
pot = max;
if (pot < -max)
pot = -max;


fprintf(fp, "%lf,%lf,%lf\n", x, y, pot);
}
fprintf(fp, "\n");//pm3dは空行がないと補完しない
}
fclose(fp);
printf("Done\n");

return 0;
}


*GNUPLOTスクリプト
set pm3d corners2color max
set pm3d depthorder
set datafile separator ","
set xrange[-30:30]
set yrange[-30:30]
#set view map
splot "output.txt" with pm3d


*結果


*勾配ベクトル場の確認
#include <stdio.h>
#include <math.h>

double get_pot(double x,double y)
{
const double Enemy1_x = 5.;
const double Enemy1_y = 10.;

const double Enemy2_x = -10.;
const double Enemy2_y = -20.;

const double Goal_x = 17.;
const double Goal_y = 0.;

const double Weight_Enemy1 = 1.0;
const double Weight_Enemy2 = 1.0;
const double Weight_Goal = 5.0;

//スカラー場なのでx,y成分共に同じ
double Enemy1_pot = 1.0 / sqrt((x - Enemy1_x)*(x - Enemy1_x) + (y - Enemy1_y)*(y - Enemy1_y));
double Enemy2_pot = 1.0 / sqrt((x - Enemy2_x)*(x - Enemy2_x) + (y - Enemy2_y)*(y - Enemy2_y));
double Goal_pot = -1.0 / sqrt((x - Goal_x)*(x - Goal_x) + (y - Goal_y)*(y - Goal_y));

double pot = Enemy1_pot * Weight_Enemy1 + Enemy2_pot * Weight_Enemy2 + Goal_pot * Weight_Goal;
return pot;
}

int main(void)
{
FILE *fp = fopen("output.txt", "w");
if (fp == NULL)
{
printf("Err\n");
return -1;
}

printf("wait...\n");

const double delta = 1.2;
// const double zoom = 10.;
for (double x = -30; x < 30; x += delta)
{
for (double y = -30; y < 30; y += delta)
{
//勾配ベクトル算出 -grad(P)
double vx = -(get_pot(x + delta, y) - get_pot(x, y))/delta;
double vy = -(get_pot(x, y + delta) - get_pot(x, y))/delta;
double v = sqrt(vx*vx + vy*vy);

// vx *= zoom;
// vy *= zoom;
//見やすさのためにベクトルを正規化
vx /= v;
vy /= v;

// printf("%lf,%lf,%lf\n", x, y);
if(!isnan(vx) && !isnan(vy)) //無効な値は除外
fprintf(fp, "%lf,%lf,%lf,%lf,%lf\n", x, y, vx, vy,v);
}
// fprintf(fp, "\n");//pm3dは空行がないと補完しない
}
fclose(fp);
printf("Done\n");

return 0;
}


*GNUPLOTスクリプト
set datafile separator ","
set xrange[-30:30]
set yrange[-30:30]
set size ratio 1
set palette rgb 33,13,10
#plot "output.txt" u 1:2:3:4:(sqrt($3*$3+$4*$4)) w vector lc palette ti ""
plot "output.txt" u 1:2:3:4:5 w vector lc palette ti ""


*結果

*ポテンシャル法による障害物回避
#include <stdio.h>
#include <math.h>

double get_pot(double x, double y)
{
const double Enemy1_x = 5.;
const double Enemy1_y = 10.;

const double Enemy2_x = -10.;
const double Enemy2_y = -20.;

const double Goal_x = 17.;
const double Goal_y = 0.;

const double Weight_Enemy1 = 1.0;
const double Weight_Enemy2 = 1.0;
const double Weight_Goal = 5.0;

//スカラー場なのでx,y成分共に同じ
double Enemy1_pot = 1.0 / sqrt((x - Enemy1_x)*(x - Enemy1_x) + (y - Enemy1_y)*(y - Enemy1_y));
double Enemy2_pot = 1.0 / sqrt((x - Enemy2_x)*(x - Enemy2_x) + (y - Enemy2_y)*(y - Enemy2_y));
double Goal_pot = -1.0 / sqrt((x - Goal_x)*(x - Goal_x) + (y - Goal_y)*(y - Goal_y));

double pot = Enemy1_pot * Weight_Enemy1 + Enemy2_pot * Weight_Enemy2 + Goal_pot * Weight_Goal;
return pot;
}

int main(void)
{
FILE *fp = fopen("output.txt", "w");
if (fp == NULL)
{
printf("Err\n");
return -1;
}

printf("wait...\n");

//出発地点
double x = -10.;
double y = 20.;

const double delta = 0.1;
const double speed = 0.1;
for (int i = 0; i < 1000;i++)
{
//勾配ベクトル算出 -grad(P)
double vx = -(get_pot(x + delta, y) - get_pot(x, y)) / delta;
double vy = -(get_pot(x, y + delta) - get_pot(x, y)) / delta;
double v = sqrt(vx*vx + vy*vy);


//ベクトルを正規化
vx /= v / speed;
vy /= v / speed;

//移動反映
x += vx;
y += vy;

fprintf(fp, "%lf,%lf,%lf,%lf,%lf\n", x, y, vx, vy, v);
}
fclose(fp);
printf("Done\n");

return 0;
}


*GNUPLOTスクリプト
set datafile separator ","
set xrange[-30:30]
set yrange[-30:30]
set size ratio 1
set palette rgb 33,13,10
#plot "output.txt" u 1:2:3:4:(sqrt($3*$3+$4*$4)) w vector lc palette ti ""
plot "output.txt" u 1:2:3:4:5 w vector lc palette ti ""


*結果

追記: コメントにスカラー場なので」と書いてしまっているが、x,yによらず同じ形状なので、が正しいか。