ジョイスティックマウスを作りたい

ジョイスティックマウス

Arduinoとジョイスティックで、マウスが作れるらしいので作ってみる。スケッチ例だけだと自分の環境では動かなかったので、一個ずつやってみる。

お買い物

http://akizukidenshi.com/catalog/g/gM-08763/

センタープッシュが付いてるやつにした。

接続

つないだ。

とりあえずanalogRead()で取得

int axisX = 0;
int axisY = 0;

void setup() {
  pinMode(18,INPUT);
  pinMode(19,INPUT);

  Serial.begin(9600);
}

void loop() {
  axisX = analogRead(18);
  axisY = analogRead(19);
  Serial.print("X: ");
  Serial.print(axisX);
  Serial.print("   Y: ");
  Serial.print(axisY);
  Serial.println();
}

analogRead()は基準電圧間を10bit(0-1023)のデジタルデータに変換してくれる。静止状態だと、中央値の511-512になるはずなので、この結果はちょっとずれてる。キャリブレーションが必要だけど、まぁ無視する。

Mouse.hを使ってみる

#include <Mouse.h>

boolean finished = false;

void setup() {
  Mouse.begin();

}

void loop() {
  if (!finished) {
    for (int i = 1 ; i <= 10; i++) {
      Mouse.move(-10, 10, 0);
      delay(1000);
      finished = true;
    }
  }
}

止まらなくなるのが怖いので、一旦何回かやったら止まるようにする。Mouse.move()の意味がよく分かってなかったのだけど、現在の位置から、引数の分だけマウスが飛ぶ感じの動きみたい。引数を大きくすれば飛ぶ量が大きくなる。スイーっと動くんじゃなくて、飛ぶ。

2つを合わせてみる

#include <Mouse.h>

boolean finished = false;
int axisX = 0;
int axisY = 0;
signed char X;
signed char Y;

void setup() {
  pinMode(18, INPUT);
  pinMode(19, INPUT);

  Mouse.begin();
}

void loop() {

  if (!finished) {
    for (int i = 1 ; i <= 10000; i++) {
      axisX = analogRead(18);
      axisY = analogRead(19);
      X = map(axisX, 0, 1023, -3, 3);
      Y = map(axisY, 0, 1023, -3, 3);

      Mouse.move(X, Y, 0);
      delay(2);
      finished = true;
    }
  }
}

取りあえず動くものを書いてみた。これだけでも既にカーソルが動いてくれる。すげーな。ただ静止状態でもカーソルが動いてしまうし、動きはカクカクしていてなめらか~な感じじゃない。XY軸沿いの動きはするんだけど、斜めになるととたんに反応が悪くなる。なんだろなー。

map()

https://www.arduino.cc/reference/en/language/functions/math/map/

https://qiita.com/QUANON/items/188baf469fd493a2f1e6

これ便利ね。

参考

https://www.arduino.cc/reference/en/language/functions/usb/mouse/

カテゴリー: したい | タグ: , , | コメントする

モールス信号を復号したい

モールス信号

モールス信号を復号したい。誰かから送られてきたやつじゃなくて、自分が入力したやつを復号したい。

スイッチを押下・離上間の時間を取得

unsigned long starttime = 0;
unsigned long endtime = 0;
unsigned long diff = 0;

void setup() {
  pinMode(9, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {

  if (digitalRead(9) == LOW) {
    starttime = millis();
    
    while (digitalRead(9) == LOW) {}
    
    endtime = millis();
    diff = endtime - starttime;
    
    Serial.print(diff);
    Serial.println();
    delay(10);
  }
}

とりあえず時間が取れた。

閾値を設定して短点と長点を判断

#define threshold 200

unsigned long starttime = 0;
unsigned long endtime = 0;
unsigned long diff = 0;

void setup() {
  pinMode(9, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {

  if (digitalRead(9) == LOW) {
    starttime = millis();
    while (digitalRead(9) == LOW) {}
    endtime = millis();
    diff = endtime - starttime;

    if (diff < threshold) {
      Serial.print("・");
      Serial.println();
    } else {
      Serial.print("-");
      Serial.println();
    }

    delay(10);
  }
}

判断もできた。こっからだなぁ。

閾値を設定して文字の区切りを取得

#define thresholdsl 200
#define thresholdchr 400

unsigned long starttime = 0;
unsigned long endtime = 0;
unsigned long diff = 0;
boolean duringinput = false;
boolean decrypted = true;

void setup() {
  pinMode(9, INPUT_PULLUP);
  Serial.begin(9600);
}

String decisl(unsigned long t) {
  if (t < thresholdsl) {
    return "・";
  }
  return "-";
}

void loop() {

  if (digitalRead(9) == LOW) {
    starttime = millis();
    
    if (!duringinput) {
      duringinput = true;
      decrypted = false;
    }

    while (digitalRead(9) == LOW) {}
    endtime = millis();
    diff = endtime - starttime;

    Serial.print(decisl(diff));
    Serial.println();
    delay(10);
  }

  if ( (millis() - endtime) > thresholdchr && !decrypted) {
    Serial.print("result");
    Serial.println();
    duringinput = false;
    decrypted = true;
  }
  
}

行き当たりばったりで書いていったらこうなった。もう一回作れと言われると作れなさそう。できるだけ残そ。

短点2つの場合、

millis() 100 200 300 400 500 750 1000
starttime 0 200 200 400 400 400 400
endtime 0 0 300 300 500 500 500
diff 0 0 100 100 100 100 100
decisl() “・” “・”
millis() – endtime 250 500
print(“result”) “result”
duringinput FALSE TRUE TRUE TRUE TRUE TRUE FALSE
decrypted FALSE FALSE FALSE FALSE FALSE FALSE TRUE

うーん、上手く書けない。こんな感じで遷移してるはず。あとはresultを、入力から対応する文字に復号した結果を表示させるようにすればいいはず。

入力値から復号

Arduinoには、key-value形式で扱えるような、DictionaryとかHashMapみたいなものがない、らしい。探したけど見つかんなかった。「・-」→「A」みたいなデータの引っ張り方はできないってことなのでなにか考えないといかん。最悪switchで愚直に全部書く方法があるけど、それはやだなって思ったので、とりあえず「・」→「0」、「ー」→「1」として、それをkeyにしてデータ引っ張れないかを試してみることにする。

文字 符号 「・」→「0」
「-」→「1」
BIN2DEC 桁数 16bit 添字
A ・- 01 1 2 0100000000000010 4
B -・・・ 1000 8 4 1000000000000100 23
C -・-・ 1010 10 4 1010000000000100 25
D -・・ 100 4 3 1000000000000011 11
E 0 0 1 0000000000000001 1
F ・・-・ 0010 2 4 0010000000000100 17
G --・ 110 6 3 1100000000000011 13
H ・・・・ 0000 0 4 0000000000000100 15
I ・・ 00 0 2 0000000000000010 3
J ・--- 0111 7 4 0111000000000100 22
K -・- 101 5 3 1010000000000011 12
L ・-・・ 0100 4 4 0100000000000100 19
M -- 11 3 2 1100000000000010 6
N -・ 10 2 2 1000000000000010 5
O --- 111 7 3 1110000000000011 14
P ・--・ 0110 6 4 0110000000000100 21
Q --・- 1101 13 4 1101000000000100 28
R ・-・ 010 2 3 0100000000000011 9
S ・・・ 000 0 3 0000000000000011 7
T 1 1 1 1000000000000001 2
U ・・- 001 1 3 0010000000000011 8
V ・・・- 0001 1 4 0001000000000100 16
W ・-- 011 3 3 0110000000000011 10
X -・・- 1001 9 4 1001000000000100 24
Y -・-- 1011 11 4 1011000000000100 26
Z --・・ 1100 12 4 1100000000000100 27
0 ----- 11111 31 5 1111100000000101 62
1 ・---- 01111 15 5 0111100000000101 46
2 ・・--- 00111 7 5 0011100000000101 38
3 ・・・-- 00011 3 5 0001100000000101 34
4 ・・・・- 00001 1 5 0000100000000101 32
5 ・・・・・ 00000 0 5 0000000000000101 31
6 -・・・・ 10000 16 5 1000000000000101 47
7 --・・・ 11000 24 5 1100000000000101 55
8 ---・・ 11100 28 5 1110000000000101 59
9 ----・ 11110 30 5 1111000000000101 61
・----・ 011110 30 6 0111100000000110 93
-・・・・- 100001 33 6 1000010000000110 96
・-・・-・ 010010 18 6 0100100000000110 81
( -・--・ 10110 22 5 1011000000000101 53
) -・--・- 101101 45 6 1011010000000110 108
* -・・- 1001 9 4 1001000000000100 24
, --・・-- 110011 51 6 1100110000000110 114
. ・-・-・- 010101 21 6 0101010000000110 84
/ -・・-・ 10010 18 5 1001000000000101 49
: ---・・・ 111000 56 6 1110000000000110 119
? ・・--・・ 001100 12 6 0011000000000110 75
@ ・--・-・ 011010 26 6 0110100000000110 89
^ ・・・・・・ 000000 0 6 0000000000000110 63
+ ・-・-・ 01010 10 5 0101000000000101 41

最初考えたのはバイナリ化した符号を10進にして、それを配列の添字にしてあげればデータを引っ張れるんじゃないかと思った。でも、10進数にすると重複が発生してしまい、よろしくなかった。なんかないかなーとネットの海を彷徨っていたら、16bitのデータ長で上位12bit(実際に使われるのは6bit)を短長点、下位4bit(実際に使われるのは3bit)を符号長として表すやり方をみつけた。これでいけそうな気がする。で、2分木として扱えばよさげだなーと思ったので、添字を計算した。でも、なんか記号のプログラムのソースコード上での扱い方がわかんなかったので、上表の9までにしか対応ないことにした。

morsetable.h

const String mtable[] = {
  "", "E", "T", "I", "A", "N", "M", "S", "U", "R", "W", "D", "K", "G", "O", "H", "V", "F", "", "L", "", "P", "J", "B", "X", "C", "Y", "Z", "Q", "", "", "5", "4", "", "3", "", "", "", "2", "", "", "", "", "", "", "", "1", "6", "", "", "", "", "", "", "", "7", "", "", "", "8", "", "9", "0"
};

morse

#include "morsetable.h"

#define pin 9
#define tlsl 200
#define tlchr 400

unsigned long starttime = 0;
unsigned long endtime = 0;
unsigned long diff = 0;
boolean duringinput = false;
boolean decrypted = true;
byte mrsbin [] = {0b0, 0b0};

void setup() {
  pinMode(pin, INPUT_PULLUP);
  Serial.begin(9600);
}

void decisl(unsigned long t) {
  mrsbin[0] = mrsbin[0] << 1;

  if (t > tlsl) {
    mrsbin[0] = mrsbin[0] + 1;
  } else {
    mrsbin[0] = mrsbin[0] + 0;
  }

  mrsbin[1] = mrsbin[1] + 1;
}

int mrstblidx (byte b[]) {
  byte msb = 0b0;
  int idx = 0;

  for (int i = 1; i <= (int)b[1]; i++) {
    msb = ((b[0] << (8 - (int)b[1]) + i - 1) & 0b10000000) >> (8 - i);

    if (msb == 0) {
      idx = 2 * idx + 1;
    } else {
      idx = 2 * idx + 2;
    }
  }
  return idx;
}

void loop() {

  if (digitalRead(pin) == LOW) {
    starttime = millis();

    if (!duringinput) {
      duringinput = true;
      decrypted = false;
    }

    while (digitalRead(pin) == LOW) {}
    endtime = millis();
    diff = endtime - starttime;
    decisl(diff);

    //    Serial.print(mrsbin[0]);
    //    Serial.print(mrsbin[1]);
    //    Serial.println();
    delay(10);
  }

  if ( (millis() - endtime) > tlchr && !decrypted) {
    Serial.print(mtable[mrstblidx(mrsbin)]);
    Serial.println();
    duringinput = false;
    decrypted = true;
    mrsbin [0] = 0;
    mrsbin [1] = 0;
  }

}

こんな感じになった。MSB取り出すところがちょっと気持ち悪いなー。

いー感じだ!ただまぁ、短長点の手癖には対応できてないので機械に人間が合わせる感じになるかな。

参考

https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%BC%E3%83%AB%E3%82%B9%E7%AC%A6%E5%8F%B7

http://www.kyohritsu.jp/eclib/DIGIT/KIT/morsejamp.pdf

http://machoto2.g2.xrea.com/page/P14K50/P14_B17.htm

https://ja.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%9C%A8

https://cpprefjp.github.io/lang/cpp14/binary_literals.html

http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=665

http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=784

カテゴリー: したい | タグ: , , | コメントする

スイッチを押す

スイッチ

スイッチを押すとカウントが増えるやつ。

とりあえずやる

こんな感じでつなぎ

int count = 0;

void setup() {
  pinMode(9, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {

  if (digitalRead(9) == LOW) {
    count++;
    Serial.print(count);
    Serial.println();
  }
}

こんなコードを書き 、スイッチをポチッと押すと

12:09:51.333 -> 1
12:09:51.333 -> 2
12:09:51.333 -> 3
~略~
12:09:51.437 -> 757
12:09:51.437 -> 758
12:09:51.437 -> 759

値がすっ飛ぶ。スイッチを離すまでに、次のloopが来てしまうからこういうことになるみたい。(cf.チャタリング)。よろしくないので適当に遅延を入れる。

遅延入れる

int count = 0;

void setup() {
  pinMode(9, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {

  if (digitalRead(9) == LOW) {
    count++;
    Serial.print(count);
    Serial.println();
    delay(200);
  }
}

そうするとまぁ

12:41:46.180 -> 1
12:41:47.033 -> 2
12:41:47.924 -> 3
12:41:48.836 -> 4
12:41:49.723 -> 5

ポチッぐらいだと上手くいく。これがポーッチとかポチポチポチとかだと押した回数とカウントが一致しない。

こんな。ポーッチとかポチポチポチとかにも対応したい。

ボタンを離すとカウントアップ

int count = 0;

void setup() {
  pinMode(9, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {

  if (digitalRead(9) == LOW) {
    while (digitalRead(9) == LOW) {}
    
    count++;
    
    Serial.print(count);
    Serial.println();
    delay(10);
  }
}

押したときではなく、離した時にカウントすればいいのではと考えた。コード的に合ってるか微妙なとこだけどこう書いた。何もしないループって作っていいのかな。微妙にチャタるのでdelayも入れてみてる。

いい感じでは。

カテゴリー: したい | タグ: , , | コメントする

Arduinoでブザーを鳴らしたい

ブザー

音が出る。電気流すだけでできるみたい。

お買い物

http://akizukidenshi.com/catalog/g/gP-09800/

こちらを買いまして。定格5Vなので何も考えずにに繋げば良さそう。

http://akizukidenshi.com/catalog/g/gP-03647/

あとスイッチ。押すと繋がって、離すと切れる。

接続して手動で鳴らす

ブザーには極性があった。Arduino5V→スイッチ→ブザー→ArduinoGNDてつないでみた。

押すと鳴る。でもこれはあんまりよろしくないみたい。スイッチオフのときが、浮いてる状態だから?なの?

プルアップ・プルダウン

詳しくは

https://voltechno.com/blog/pullup-pulldown/

https://slash-mochi.net/?p=978

で、リファレンスは

https://www.arduino.cc/reference/en/language/functions/digital-io/pinmode/

とか

https://www.arduino.cc/en/Tutorial/DigitalPins

などを参考にいたしまして、色々試してみたものの、理解が追いつかず、とりあえずこれでいいやと思いました。

プログラムで鳴らす

こんな感じで、D01(9)ピン→ブザー→GNDで接続して

void setup() {
  pinMode(9, OUTPUT);
}

void loop() {

  for (int i = 1; i <= 3; i++) {
    digitalWrite(9, HIGH);
    delay(100);
    digitalWrite(9, LOW);
    delay(100);
  }

  delay(300);

  for (int i = 1; i <= 3; i++) {
    digitalWrite(9, HIGH);
    delay(300);
    digitalWrite(9, LOW);
    delay(100);
  }

  delay(300);

  for (int i = 1; i <= 3; i++) {
    digitalWrite(9, HIGH);
    delay(100);
    digitalWrite(9, LOW);
    delay(100);
  }

  delay(700);

}

こんな感じのコードを書きまして、カウンタ一緒だけどいいのかな。

すると鳴ります。SOS!。

toneを使う

void setup() {
  pinMode(9, OUTPUT);
}

void loop() {

  for (int i = 32; i <= 8000; i++) {
    tone(9, i);
    delay(10);
    noTone(9);
  }
}

toneを使うと音階が作れる。

スケッチ例のtoneMelodyをやると

動画だと微妙だけど、ちゃんと聞こえる。すげー。

カテゴリー: したい | タグ: , , | コメントする

BadUSBをBadUSBっぽく使ってみたい

BadUSB

この時、BadUSBとしては使わないって書いたけど、やっぱりやってみたくなったのでやってみる。

Keyboard.h

#include <Keyboard.h>

void setup() {
  Keyboard.begin();
  delay(1000);

  Keyboard.press(131);
  delay(1000);
  Keyboard.print("r");
  delay(100);
  Keyboard.releaseAll();
  delay(1000);

  Keyboard.print("notepad");
  Keyboard.press(KEY_RETURN);
  delay(10);
  Keyboard.releaseAll();
  delay(1000);

  Keyboard.print("This message was written by BadUSB !");
  delay(5000);

  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press("a");
  delay(10);
  Keyboard.releaseAll();

  Keyboard.press(KEY_DELETE);

  Keyboard.press(KEY_LEFT_ALT);
  Keyboard.press(KEY_F4);
  delay(10);
  Keyboard.releaseAll();
  delay(1000);
  
  Keyboard.press("n");
  delay(10);
  Keyboard.releaseAll();
  
  Keyboard.end();

}

void loop() {
  // put your main code here, to run repeatedly:

}

win+Rが上手く動かなかったので、printに変えてる。メモ帳閉じる時ダイアログが出るのでnで閉じてる。を追加してる。

動かしてみる

モアレ出まくりで見づらいけど、めっちゃ動くー。USBにぶっ挿すだけでこれだけできんだもんな。すごいなぁ。

参考

https://www.youtube.com/watch?v=peyG24r4sjA

https://www.arduino.cc/reference/en/language/functions/usb/keyboard/

カテゴリー: したい | タグ: , , | コメントする

VL53L0Xのつもりで買った測距センサーのVL53LXXをArduinoで使ってみたい

VL53LXX

今気づいたんですが、VL53L0Xを買ったつもりがVL53LXXって印字されてるやつが届いた。違うじゃん。どうしよう、VL53L0Xと同じような感じで使えんのかな。

超音波使うやつはこの前使ってみたんだけどなんかでっかいので、ちっちゃいやつを探したら見つけた。レーザーですって。すごいじゃん。ただ接続がI2Cなので、超音波のやつを引っこ抜いて代わりにぶっ挿すっていう使い方はできない。できないのね。だから動かしてみて遊んだだけです。

お買い物

https://www.ebay.com/itm/1pcs-GY-VL53L0XV2-L53L0X-TOF-Time-Of-Flight-Distance-Sensor-940nm-Laser-Ranging/183687917709

これ買ったんすよ。でも印字違うの。

VL53L0Xのデータシート

https://www.st.com/resource/en/datasheet/vl53l0x.pdf

接続

Arduino ↔ VL53LXX
5V ↔ VIN
SDA ↔ SDA
SCL ↔ SCL
GND ↔ GND

こんなか。VL53LXXにGPIO1,XSHUTていうピンがあるけど使わない。ていうか何に使うのかよく分からん。

スケッチ例を使ってみる

ライブラリをインストール

これかなぁと思いインストール。VL53L1Xっていうのもあるんだね。GitHubは

https://github.com/pololu/vl53l0x-arduino

こちら。

スケッチ例を開いて書き込み

これ。ContinuousとSingleの違いはなんじゃろー。データシートから引用すると

1. Single ranging
Ranging is performed only once after the API function is called. System returns to SW standby automatically.
2. Continuous ranging
Ranging is performed in a continuous way after the API function is called. As soon as the measurement is finished, another one is started without delay. User has to stop the ranging to return to SW standby. The last measurement is completed before stopping.

そもこのVL53L0XにアクセスするためにはAPIを使ってアクセスしなきゃならんそうで。Singleの場合、APIをコールするとVL53L0Xは1回だけ測距する。Continuoousの場合、APIをコールしたらstopしない限り測距し続ける。ていうのは分かったんだけど、そうするとどうなるのかがよくわからない。どっちかが精度高くなるとか応答が早いとかあるんだろうか。まぁどっちでもいいので開いて、書き込んでみて、シリアルモニタを開くと、出た!えらいスピードで計測結果が流れていく、ので、1秒ディレイを入れた。おー動く。

スマホカメラで撮るとレーザーが出てるのが分かるね。

実測値と比較してみる

ライブラリのreadmeを見るとmmで出力されるらしいんだけど、どうもズレてるような気がしてならないので、定規を使って現実の値と比較してみる。

こんな。この精度が高くねーじゃんと言いたくなるでしょうが専門的な装置なんて持ってないのでこれで測る。

結果

5cm 10cm 15cm
1回目 50 100 153
2回目 47 100 160
3回目 50 99 152
4回目 45 101 152
5回目 49 98 150
6回目 48 97 152
7回目 49 96 152
8回目 48 101 152
9回目 48 97 150
10回目 47 97 151

あれ?意外といーじゃん!もっとガッツリ変で、校正入れる感じになるかなーとか思ったけど大丈夫な気がする。

バラツキをなんとかしたい

つっても、数値がバラつく。まぁそんな精度求めてるわけでもないし、取得頻度が高いわけでもないんだけど、なんか気持ち悪いので。こういう時は値を滑らかにするようなフィルタをかければよかったんだった。そういや方位計算する時やったな。移動平均と、ローパスと、中央値を試してみる。

移動平均

#include <Wire.h>
#include <VL53L0X.h>

#define SAMPLE_SIZE 10 //20, 50, 100

int count = 0;
int discardcount = 0;
uint16_t temp[SAMPLE_SIZE];
uint16_t sum = 0;
uint16_t average = 0;

VL53L0X sensor;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  sensor.startContinuous();
}

void loop() {

  temp[count] = sensor.readRangeContinuousMillimeters();
  if (sensor.timeoutOccurred()) {Serial.print(" TIMEOUT");}
  count++;
  
  if (count == SAMPLE_SIZE) {
    count = 0;
  }

  if (discardcount < SAMPLE_SIZE) {
    discardcount++;
    return;
  }

  for (int i = 0; i < SAMPLE_SIZE; i++) {
    sum += temp[i];
  }

  average = sum / SAMPLE_SIZE;
  sum = 0;

  Serial.print(average);
  Serial.println();
}

Continuousのスケッチ例をこんな感じで書き換えた。相変わらずスコープが分からん。実測値を10cmで、標本数を10, 20, 50, 100にして試してみると

10 20 50 100
1回目 22:08:20.964 -> 96 22:08:55.446 -> 101 22:09:28.327 -> 99 22:10:06.548 -> 99
2回目 22:08:20.998 -> 96 22:08:55.481 -> 101 22:09:28.362 -> 99 22:10:06.595 -> 99
3回目 22:08:21.032 -> 97 22:08:55.481 -> 101 22:09:28.396 -> 99 22:10:06.595 -> 100
4回目 22:08:21.066 -> 97 22:08:55.514 -> 101 22:09:28.431 -> 99 22:10:06.642 -> 100
5回目 22:08:21.100 -> 97 22:08:55.548 -> 101 22:09:28.465 -> 99 22:10:06.689 -> 100
6回目 22:08:21.134 -> 97 22:08:55.581 -> 101 22:09:28.499 -> 99 22:10:06.689 -> 99
7回目 22:08:21.168 -> 97 22:08:55.615 -> 102 22:09:28.533 -> 99 22:10:06.736 -> 99
8回目 22:08:21.201 -> 97 22:08:55.649 -> 102 22:09:28.568 -> 99 22:10:06.782 -> 99
9回目 22:08:21.235 -> 97 22:08:55.682 -> 101 22:09:28.604 -> 99 22:10:06.782 -> 99
10回目 22:08:21.270 -> 97 22:08:55.716 -> 101 22:09:28.638 -> 99 22:10:06.829 -> 99

タイムスタンプついてる。ブレがずいぶん小さくなった。

ローパス(指数加重平均)

#include <Wire.h>
#include <VL53L0X.h>

uint16_t newdata = 0;
uint16_t lastdata = 0;
uint16_t filtereddata = 0;

VL53L0X sensor;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  sensor.startContinuous();
}

void loop() {

  float weight = 0.8;

  newdata = sensor.readRangeContinuousMillimeters();
  if (sensor.timeoutOccurred()) {Serial.print(" TIMEOUT");}

  if (lastdata == 0) {
    lastdata = newdata;
    return; 
  }
   
  filtereddata = (1.0 - weight) * lastdata + (newdata * weight);
  lastdata = newdata;

  Serial.print(filtereddata);
  Serial.println();
}

こんな感じで書き換えた。重み付けを0.8, 0.85, 0.9と変えてみる。結果は

0.8 0.85 0.9
1回目 23:50:33.653 -> 94 23:54:09.415 -> 99 23:54:39.888 -> 99
2回目 23:50:33.687 -> 94 23:54:09.449 -> 99 23:54:39.923 -> 101
3回目 23:50:33.722 -> 96 23:54:09.483 -> 101 23:54:39.923 -> 101
4回目 23:50:33.722 -> 95 23:54:09.518 -> 99 23:54:39.957 -> 101
5回目 23:50:33.756 -> 94 23:54:09.551 -> 100 23:54:39.991 -> 98
6回目 23:50:33.791 -> 95 23:54:09.584 -> 101 23:54:40.024 -> 102
7回目 23:50:33.824 -> 93 23:54:09.618 -> 101 23:54:40.057 -> 99
8回目 23:50:33.859 -> 97 23:54:09.652 -> 97 23:54:40.091 -> 100
9回目 23:50:33.892 -> 95 23:54:09.652 -> 100 23:54:40.125 -> 101
10回目 23:50:33.926 -> 97 23:54:09.685 -> 101 23:54:40.159 -> 101

けっこーバラつく。ある程度は滑らかになってるみたいだけどね。

中央値

つーか中央値ってどう求めるんだ。ちょっと調べてみたらソートして真ん中の値を引っ張ればいいみたい。標本数が偶数の場合は真ん中2つの数字の平均を取るらしい。うーむ。偶数の場合はめんどくさそうなので奇数の場合だけ考えよう。そうすれば要素数は最初に決めてるんだし簡単に引っ張ってこれるはず。問題はソートか…。初めてやるなぁ、とりあえずやってみよう。

配列のソート

バブルソート
uint16_t BubbleSort (uint16_t samples[]) {
  uint16_t tmp = 0;

  for (int j = 0; j < SAMPLE_SIZE - 1; j++) { for (int i = SAMPLE_SIZE - 1; i > j; i--) {
      if (samples[i - 1] > samples[i]) {
        tmp = samples[i - 1];
        samples[i - 1] = samples[i];
        samples[i] = tmp;
      }
    }
  }
  debug(samples);
  return samples[(SAMPLE_SIZE - 1) / 2];
}

こんな、バブルソートは遅いって話だったけどぴょんこぴょんこ数字は出てくる。標本数が11, 51, 101の結果

11 51 101
1回目 13:08:03.561 -> 101 13:08:56.342 -> 104 13:09:53.361 -> 101
2回目 13:08:03.595 -> 101 13:08:56.377 -> 104 13:09:53.396 -> 101
3回目 13:08:03.628 -> 101 13:08:56.410 -> 104 13:09:53.431 -> 101
4回目 13:08:03.662 -> 101 13:08:56.444 -> 104 13:09:53.466 -> 101
5回目 13:08:03.695 -> 101 13:08:56.478 -> 104 13:09:53.500 -> 101
6回目 13:08:03.729 -> 101 13:08:56.512 -> 104 13:09:53.534 -> 101
7回目 13:08:03.762 -> 100 13:08:56.546 -> 104 13:09:53.568 -> 101
8回目 13:08:03.797 -> 100 13:08:56.581 -> 103 13:09:53.602 -> 101
9回目 13:08:03.830 -> 101 13:08:56.581 -> 103 13:09:53.636 -> 101
10回目 13:08:03.864 -> 101 13:08:56.615 -> 103 13:09:53.669 -> 101

バラツキも少なめ。…クイックソートも試してみようと思ったけど別にいーや。気が向いたらやろう。あとソート済みの配列がこれでできたから、新しく測距した値は選択ソートにすれば処理が早そう。これも気が向いたらにしよう。

注意点

今は静止状態で測距してるからあんまり感じないけど、どのフィルタも標本数が多くなってくると応答性が悪くなる。測距した値が変化すると、即時反映するのではなく緩やかな変化になってた。精度と応答性はトレードオフなのかなーと思いました。

長い距離が測れない時

スケッチ例のSingleに、設定する行がある。

//#define LONG_RANGE

コメントアウトされてるのを復帰させると長い距離も測れるようになる。試したらできた。

精度と速度を変更したい時

これもスケッチ例のSingleに、設定する行がある。

// Uncomment ONE of these two lines to get
// - higher speed at the cost of lower accuracy OR
// - higher accuracy at the cost of lower speed

//#define HIGH_SPEED
//#define HIGH_ACCURACY

どっちかだけなので、どっちかコメントから復帰する。こっちは試してない。

参考

https://www.denshi.club/cookbook/sensor/distance/2vl53l0x.html

http://msms1003.blogspot.com/2017/12/vl53l0x.html

http://yuqlid.hatenablog.com/entry/2016/09/11/002804

https://www.codereading.com/algo_and_ds/algo/bubble_sort.html

カテゴリー: したい | タグ: , , , | コメントする

Webカメラとして使えるESP32-CAMを使ってみたい

ESP32-CAM

なんかWifi経由でネットワークカメラとして使えるらしく、つい買ってしまった。何に使うかは決めてない。備忘録~。

お買い物

本体

https://www.ebay.com/itm/ESP32-ESP32-CAM-WiFi-Bluetooth-Module-Camera-Module-Development-Board-OV2640-NU/233172198639

これもしかしてコピー商品なんだろうか…。ESP32っていうのはマイコンの一種みたい。

データ送受信用ケーブル

https://www.ebay.com/itm/Serial-Adapter-Cable-Module-PL2303HX-USB-TTL-UART-Arduino-Pro-Bridge-FR/122594405981

Black cable —– GND

Green cable —– TXD

White cable —– RXD

Red cable ——- VCC

UARTという通信方式らしい。PL2303HXっていうのはチップの名前みたい…。TTLっていうのはよくわかりませんでした。たぶんこれなんかな。

ジャンパピン

http://akizukidenshi.com/catalog/g/gP-03890/

最初無かったからアルミホイルで代用した。やればできるもんだ。

準備

ケーブル

を使えるようにする。ちなみにWin10(64bit)。USB刺して、ドライバの更新ってやってみても使えない。。。

PL2303HXA PHASED OUT SINCE 2012. PLEASE CONTACT YOUR SUPPLIER.

これは正常に動作していない。最初騙されて書けない書けないつってイライラしまくった。こちらを参考にドライバを削除してからダウングレード、紹介されてるバージョンをインストールして再起動すると使えるようになる。

できーた。COM*の数字はそれぞれ変わるはず。

ArduinoIDE

詳しいことは知りませんがArduinoIDEでESP32に書き込みできるらしい。で、こちらを参照してボードを追加する。

接続

こんな感じで繋ぐ。VCCが電源ではなく、ケーブルの赤は5Vに繋ぐという罠があるみたい。

プログラム書き込み

基本、こちらの記事のとおりにやっていくんですが、書き込みできん。AruduinoIDEの設定は

こんな感じ。

A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header

ていうエラーが出た。ジャンパピンが刺さってないときに出たり、リセットボタン押さずに書き込もうとすると出るエラーみたい。ジャンパピン刺して、USB抜き差しやら、リセットボタン押すやらしたら書き込めるようになった。

最大3145728バイトのフラッシュメモリのうち、スケッチが2100679バイト(66%)を使っています。
最大327680バイトのRAMのうち、グローバル変数が53552バイト(16%)を使っていて、ローカル変数で274128バイト使うことができます。
esptool.py v2.6
Serial port COM6
Connecting.......
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
MAC: ****
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 8192 bytes to 47...

Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 624.2 kbit/s)...
Hash of data verified.
Compressed 17392 bytes to 11186...

Writing at 0x00001000... (100 %)
Wrote 17392 bytes (11186 compressed) at 0x00001000 in 0.2 seconds (effective 565.6 kbit/s)...
Hash of data verified.
Compressed 2100800 bytes to 1661765...

Writing at 0x00010000... (0 %)
Writing at 0x00014000... (1 %)
…
Writing at 0x001a0000... (99 %)
Writing at 0x001a4000... (100 %)
Wrote 2100800 bytes (1661765 compressed) at 0x00010000 in 32.8 seconds (effective 512.8 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 119...

Writing at 0x00008000... (100 %)
Wrote 3072 bytes (119 compressed) at 0x00008000 in 0.1 seconds (effective 225.5 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

こんな感じでログ出た。成功っぽい。

起動してみる

早速ジャンパピンを抜いて、シリアルモニタを開き、リセットボタンを押して起動してみたら、シリアルモニタが文字化けしてる。

シリアルモニタが文字化け

色々やってみると、どうもボーレートが悪いみたい。

上記画像の赤丸3点を同じ数字にしてあげるとちゃんと表示されるようになった。

いえい。

ブラウザで開く

自分の場合、指定されたアドレスがhttp://192.168.100.107だったので、こちらにアクセスしてみると

すげー、ほんとにサーバー立ってる。ほんで左下の「start stream」を押すと

ビデオが開始される!マジですげーや。画像は最近お気に入りのさつまいもオレです。今のところ何に使うか考えてないけど、これだけでLAN内監視カメラみたいなものが作れるってすごいよ。なんか他にも色々できそうな感じなんだろうな、やってみたいなぁ。

参考

https://qiita.com/Nabeshin/items/b195cad1afe99ce29f1e

http://galaxystar.image.coocan.jp/esp32_cam.htm

https://tomono.tokyo/2019/03/11/7519/

https://www.yokoweb.net/2018/03/03/windows10-pl2303-driver/

カテゴリー: したい | タグ: , , | コメントする

自動走行車にBluetoothの機能を追加してラジコン化したい

Bluetoothラジコン

タイトルが長い。Bluetoothが使えることが分かったので、自動走行車に組み込んでいく。

basicMotion()

void basicMotion(char c) {

  String s = String(c);

  mySerial.println(s);
  
  if (s == "8") {
    forward();
  } else if (s == "2") {
    reverse();
  } else if (s == "7") {
    turnLeft();
  } else if (s == "9") {
    turnRight();
  } else if (s == "4") {
    rotateLeft();
  } else if (s == "6") {
    rotateRight();
  } else if (s == "5") {
    brake();
  }
}

switch文からif文に変えた。switch文ではintとcharしか扱えないみたいで、比較演算子でどうやって比較すりゃいいのかよく分からんかった。で、if文なら文字列も扱えるだしこっちにしよーと思って書き換えてみたものの、charのままだと判定に引っかかってくれない。なんでやねんと思ったらString()使えってことだったので、そうしてみたら上手くいった。あと、前進・後退・右折・左折・右旋回・左旋回をそれぞれf, r, tr, tl, rr, rlとしてそれをスマホのターミナルで打ちbasicMotion内で判断して動きを決めるみたいにした。

こんな感じでマクロを登録した。でも送信文字列が2文字になると、後退しかしてくれなくなった。なんでだろーと一旦printlnで表示してみたら

こんな感じで、2文字一緒に送信してるつもりが1文字ずつになってた。そりゃ動き変わらんわ。分解されたrの文字で判定してるから後退しかしなかったんだなとわかった。ということで前進・後退・右折・左折・右旋回・左旋回を8, 9, 7, 6, 4にするとなんだか上手くいった。

なんで?

あやふやだが、シリアル通信のUARTの場合、データのやり取りは大体

データ : 8bit
パリティ : なし
ストップビット : 1bit

http://tri-s.world.coocan.jp/SfStS_N64/SfStS_Common/UART/Explain_UART.html#Data_Format

という形式でやり取りされる。Bluetoothモジュールとスマホの間のシリアル通信では、文字はASCIIコードの形式でやり取りしているみたい。ASCIIコードは10進や16進の形式に変換される。例えばfという1文字は10進だと102、16進だと66となる。これを2進数にすると1100110で7bit、で、ストップビットを1bitつけて8bitのデータとしてやり取りしてるってことになる。たぶん。なので1文字ずつしか送れないってことになるんかな。で、これを2文字以上として処理するには終了文字かなんかをつけるようにして、終了文字が来るまでデータを待つみたいな感じで動くようにプログラミングしなきゃならんと思われる。それはめんどくさい。なので数字にした。

でもさ、serial.println()とかでやり取りするときはそういうの意識しなくてもちゃんと複数文字が返ってくるんよね。最初、試しにdelay(1000); serial.println(“hello”);とかにしたらちゃんと送られてきたし。この違いは何なんだろう、謎は深まるばかりですね。wikipediaのASCIIも参照

loop()

ということでloop()内もちょっと変えた。

void loop() {
  
  if (mySerial.available() > 0) {
    char c = mySerial.read();
    basicMotion(c);
  }
  if (withinDistance()) {
    avoid();
  }

}

前にcollisionAvoid()を作ってみたはいいけども、そのままだと何や上手く動かない。collisionAvoid()内でdelayを使っていたので、その間は操作を受け付けないようになっていたわけだ。このメソッドは自動走行の時に必要だったわけなので、思い切って消した。で上記のようになった。意図としては、センサーを読みつつ、操作も受けつつ、ってのを擬似的にでも同時にやりたい!ていう感じ。全体図は次ページ。

配線

怒髪天を衝く感じになった。線いっぱいだー。

動画はとれんかった。ターミナルで動かすのと動画撮影を同時にやれない…残念。

カテゴリー: できた | タグ: , , | コメントする

BluetoothモジュールのHC-06を使いたい Arduino+HC-06

HC-06

Bluetoothで自動走行車を操作したい。これでラジコンといえるんじゃなかろうか。その前にHC-06の使い方を確認していく。といっても参考サイトのコードを使っただけだ。

スケッチ

#include <SoftwareSerial.h>
SoftwareSerial mySerial(16, 14);//RX, TX

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  mySerial.begin(9600);
}

void loop() {
  if (mySerial.available() > 0) {
    char c = mySerial.read();
    if (c == 'n') {
      digitalWrite(LED_BUILTIN, HIGH);
    } else if (c == 'f') {
      digitalWrite(LED_BUILTIN, LOW);
    }
  }
}

DMMicroのRX, TXピンを使っても上手くいかなかった。なので適当なピンを指定してSoftwareSerialを使うと上手くいった。ここまで長かった…。Androidアプリ使うと写真が撮れない。。。あとは動きを作ればラジコンができるぞ。

Androidアプリには

https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal

を使用した。

また、論理レベル変換には

http://akizukidenshi.com/catalog/g/gK-13837/

こちらを使用した。

参考

http://robozak.jugem.jp/?eid=1053969

カテゴリー: したい | タグ: , , , , | コメントする

自動走行車に測距センサーを使って衝突回避機能をつけたかった Arduino+HC-SR04

測距センサー(HC-SR04)

測距センサーを使うと距離を測れる。このHC-SR04だと線4本で扱えるのですごく簡単みたい。やってみたけどちょっと中途半端になった。

お買い物

http://akizukidenshi.com/catalog/g/gM-11009/

秋月で買いました。これの他にもToFっていうなんかレーザー使ったやつみたいなのとかあったけど、ちょっとお高めなのでこっちにした。あとあっちは線が多い。機会があったら使ってみよう。

取り付ける

電源とGNDとトリガーとエコーを接続する。トリガーとエコーでスタートと読み取りを制御するようだ。

目ン玉みたいになった。かわいい。背が高くなってバランスがちと悪い。

スケッチを作成

#define PIN_IN_A1 9
#define PIN_IN_A2 10
#define PIN_IN_B1 1
#define PIN_IN_B2 11
#define TRG_PIN 2
#define ECHO_PIN 3
long randomNum = 0;
double Duration = 0;
double Distance = 0;

void setup() {
  pinMode(PIN_IN_A1, OUTPUT);
  pinMode(PIN_IN_A2, OUTPUT);
  pinMode(PIN_IN_B1, OUTPUT);
  pinMode(PIN_IN_B2, OUTPUT);
  pinMode(TRG_PIN, OUTPUT);
  digitalWrite(TRG_PIN, LOW);
  pinMode(ECHO_PIN, INPUT);
  randomSeed(analogRead(0));
}

double measureDistance() {
  digitalWrite(TRG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRG_PIN, LOW);
  Duration = pulseIn(ECHO_PIN, HIGH);
  if (Duration > 0) {
    Duration = Duration / 2; //往復距離を半分にする
    Distance = Duration * 340 * 100 / 1000000; // 音速を340m/sに設定
  }
  return Distance;
}

void forward() {
  digitalWrite(PIN_IN_A1, HIGH);
  digitalWrite(PIN_IN_A2, LOW);
  digitalWrite(PIN_IN_B1, HIGH);
  digitalWrite(PIN_IN_B2, LOW);
}

//略

void loop() {
  double d = 0;

  d = measureDistance();

  if (d < 10) {
    reverse();
    delay(1000);
    coast();
    rotateRight();
    delay(1000);
    coast();
  }

  randomNum = random(0, 7);

  switch (randomNum) {
    case 0:
      forward();
      break;
    case 1:
      reverse();
      break;
    case 2:
      turnLeft();
      break;
    case 3:
      turnRight();
      break;
    case 4:
      rotateLeft();
      break;
    case 5:
      rotateRight();
      break;
    case 6:
      coast();
      break;
    case 7:
      brake();
      break;
  }
  delay(1500);
  brake();


}

距離を取得する部分は参考サイトで公開されているものを使わせていただいた。また、前回は動きを決めてloop内に書いていたが、randomを使って動きを不規則にしてある。で、ですけど。うまくいかない。上記のコードは

  1. 距離を測る。
  2. 距離が閾値以下だったら後退して右旋回する。
  3. ランダムな値を取得する。
  4. 値を元に動きを決める。
  5. 2秒待つ。(2秒間同じ動きのままになる。)
  6. 1に戻る

という感じ。これだと、2秒待ってる間に障害物があった場合、ぶつかる。理想というか考えたのは、測距処理と走行処理を分けて、測距処理がぶつかりそうと判断した場合、走行処理を中断して回避行動を取る、みたいな流れにしたかった。ただそれを実現する方法がわからない。全然わからない。なんとなくマルチスレッドとかマルチタスクとか、割り込みとか、そういうのかなーと思ったけど、どうなんだろ…。ちょっとあんま意味ない感じの、中途半端な機能になっちゃったなぁ。

動画

参考

https://deviceplus.jp/hobby/entry016/

http://www.musashinodenpa.com/arduino/ref/index.php

 

思いついたのでちょっと変える

カテゴリー: したい | タグ: , , , | コメントする