iPhone+iVCam+FaceRigでバ美肉した話。

 もうそろそろ大学ではZoomでの映像授業が始まのですが、自分の顔が何十人にも配信されるのは恥ずかしいのでバ美肉しようと思います。

 もちろん、うちにはインカメラ付きのノパソはあってもウェブカメラはないので顔のキャプチャをスマホ(iPhone)でやるしかありません。iPhoneをウェブカメラとして認識させるには何通りか方法があるのですが、今回は一番簡単そうなiVCamというソフトを使いました。

 これは、iPhoneとパソコンを有線かWifiでつなぐことで簡単にウェブカメラとして使えるものです。ここ(https://www.e2esoft.com/ivcam/)からダウンロードすることができます。パソコンとiPhoneをつなぎ、双方でアプリを起動することでiPhoneのカメラをウェブカメラとして認識します。

 次にこのウェブカメラでLive2Dモデルを動かすためにFaceRigを買います。キャプチャ
 LiveDを動かしたいときはSteamで"facerig"と検索したとき、多分上二つに出てくる"FaceRig"とそのDLCである"FaceRig Live2D Moduleを買います。

 iVCamを起動し、iPhoneのカメラの映像が表示されている状態でFaceRigを起動すると多分FaceRig側がウェブカメラとして認識してくれると思います。

 そして適当に設定をいじくりまわすと こんな感じにできます。

 次回はNintendo SwitchのJOYSOUNDのマイクで両声類を目指していきます。それではまた今度。

Arduino+ESCでブラシレスモーターを回した話

 前回は自分でドライバを作ってブラシレスモーターを回しましたが、Amazonなどでも売っているESCというものを使うことで簡単に回すことができます。

 今回は下のセットを使いました。

 これは12Aまで流せるもので、ブラシレスモーター本体もついてくるので便利でした。
 配線は
・黄→D9ピン
・赤→5Vピン
・黒→GND
につなぎます。また、太い赤と黒の線は10V程度の電源とGNDにつなぎ、太い青い線は適当にBLDCモーターにつなぎます。

 また、ESCの制御はサーボの制御と一緒でパルス信号で制御します。それなので、プログラムは公式ライブラリのServo.hを使います。コードは以下の通りです。
#include <Servo.h>

const int throttle_high = 2000;
const int throttle_low = 1000;

Servo throttle;
int level = throttle_low;

void setup(){
  throttle.attach(9);
  throttle.writeMicroseconds(level);
  delay(1000);
}

void loop() {
  for (level = throttle_low; level <= throttle_high; level += 100) {
    throttle.writeMicroseconds(level);
    delay(1000);
  }
  for (level = throttle_high; level >= throttle_low; level -= 100) {
    throttle.writeMicroseconds(level);
    delay(1000);
  }
}
 level1の値を1000~2000の間で変化させることでモーターの回転速度を制御します。
 動作させると下のような感じになります。

 ESCを使うと簡単にブラシレスモーターを回すことができるので、難しいことを考えないときにはいいかもしれません。それではまた今度。

Arduinoでブラシレスモーターを回してみた話

 暇なのでBLDCモーターを回してみました。
 基本的にはVVVFインバータのときと変わりません。

・6ステップ制御
 ブラシレスモーターはコイルによってできる磁束の方向を回し、その磁力と永久磁石の磁力の作用で回すという構造になっています。それなので、コイルにかかる電流を変化させることでコイルにより発生する磁力を回転させる必要があります。
 ブラシレスモーターの三相それぞれをU,V,Wとすると、
・U →V
・W→V
・W→U
・V →U
・V →W
・U →W
の方向に電気を流す6つのステップを繰り返すことで回すことができます。下の動画はそれを図式的に表してくれているので、ブラシレスモーターの回転原理がよくわかると思います。



 この動画で説明している制御方法がまさに6ステップ制御です。これをするにあたって必要な回路は下の通りです。
VVVFhbd_回路図
 基本はVVVFの時と同じですが、ハーフブリッジドライバのSDも操作するため、3番ピンもArduinoにつないでおきます。SDを使うことで、ハーフブリッジドライバからの出力を切ることができ、動作に必要ないFETに通電させないことができます。こうすることで各相の出力を以下のようにすることができます。
Untitled Diagram

 これを実現するためのプログラムは以下のようになります。
const int IN_U = 3;
const int SD_U = 2;
const int IN_V = 5;
const int SD_V = 4;
const int IN_W = 9;
const int SD_W = 8;

int delayTime;

void setup() {
  pinMode(IN_U, OUTPUT);
  pinMode(IN_V, OUTPUT);
  pinMode(IN_W, OUTPUT);
  pinMode(SD_U, OUTPUT);
  pinMode(SD_V, OUTPUT);
  pinMode(SD_W, OUTPUT);

  analogWrite(IN_U, 0);
  analogWrite(IN_V, 0);
  analogWrite(IN_W, 0);
  digitalWrite(SD_U, HIGH);
  digitalWrite(SD_V, HIGH);
  digitalWrite(SD_W, HIGH);
}
void loop() {
  if (millis() < 8000) {
    delayTime = 5 - millis() / 2000;
  }

  analogWrite(IN_U, 250);
  analogWrite(IN_V, 0);
  analogWrite(IN_W, 0);
  digitalWrite(SD_U, HIGH);
  digitalWrite(SD_V, LOW);
  digitalWrite(SD_W, HIGH);
  delay(delayTime);

  analogWrite(IN_U, 250);
  analogWrite(IN_V, 0);
  analogWrite(IN_W, 0);
  digitalWrite(SD_U, HIGH);
  digitalWrite(SD_V, HIGH);
  digitalWrite(SD_W, LOW);
  delay(delayTime);

  analogWrite(IN_U, 0);
  analogWrite(IN_V, 0);
  analogWrite(IN_W, 250);
  digitalWrite(SD_U, LOW);
  digitalWrite(SD_V, HIGH);
  digitalWrite(SD_W, HIGH);
  delay(delayTime);

  analogWrite(IN_U, 0);
  analogWrite(IN_V, 0);
  analogWrite(IN_W, 250);
  digitalWrite(SD_U, HIGH);
  digitalWrite(SD_V, LOW);
  digitalWrite(SD_W, HIGH);
  delay(delayTime);

  analogWrite(IN_U, 0);
  analogWrite(IN_V, 250);
  analogWrite(IN_W, 0);
  digitalWrite(SD_U, HIGH);
  digitalWrite(SD_V, HIGH);
  digitalWrite(SD_W, LOW);
  delay(delayTime);

  analogWrite(IN_U, 0);
  analogWrite(IN_V, 250);
  analogWrite(IN_W, 0);
  digitalWrite(SD_U, LOW);
  digitalWrite(SD_V, HIGH);
  digitalWrite(SD_W, HIGH);
  delay(delayTime);
}

 このプログラムは簡易的な加速機能がついており、8秒間かけて3ステップの加速をします。また、delayTimeはモーターによって回転数が違うので調節してください。
 これを使ってブラシレスモーターを回すとこうなりました。


・疑似正弦波制御
 6ステップ制御を発展させたものとして正弦波制御というものがあります。これは、6ステップ制御とは違い、コイルに(疑似)正弦波を入力することでモーターを滑らかに回すというものです。
 これによってコイルによって発生する磁束の方向が滑らかに変化するのでモーターを滑らかに回すことができます。ただし、ArduinoのanalogWriteはPWMなので正確に正弦波を出せるわけではない上、出力がLOWの時は回路上その部分の出力はGNDに短絡されてしまいます。
 そのような不具合はありますが、一応回路図とプログラムを挙げておきます。VVVFhbd_回路図
 6ステップ制御との違いは正弦波制御ではSDを使わないためVCCに短絡させてしまっている点だけです。プログラムは以下のようになります。
const int IN_U = 11;
const int IN_V = 10;
const int IN_W = 9;

int delayTime = 1;
double pwm[360];

void setup() {
  // put your setup code here, to run once:
  pinMode(IN_U, OUTPUT);
  pinMode(IN_V, OUTPUT);
  pinMode(IN_W, OUTPUT);
  analogWrite(IN_U, 0);//UVWのPWM 0〜255
  analogWrite(IN_V, 0);
  analogWrite(IN_W, 0);
  delayMicroseconds(1);

  for (int i = 0 ; i < 360 ; i++) {
    pwm[i] = 125 * sin(i * PI / 180) + 125;
  }
}

void loop() {
  //1 U->W
  for (int i = 0; i < 60; i++) {
    analogWrite(IN_U, pwm[i]);
    analogWrite(IN_V, pwm[i - 240]);
    analogWrite(IN_W, pwm[i - 120]);
    delayMicroseconds(delayTime);
  }

  //2 U->V
  for (int i = 60; i < 120; i++) {
    analogWrite(IN_U, pwm[i]);
    analogWrite(IN_V, pwm[i - 240]);
    analogWrite(IN_W, pwm[i - 120]);
    delayMicroseconds(delayTime);
  }

  //3 W->V
  for (int i = 120; i < 180; i++) {
    analogWrite(IN_U, pwm[i]);
    analogWrite(IN_V, pwm[i - 240]);
    analogWrite(IN_W, pwm[i - 120]);
    delayMicroseconds(delayTime);
  }

  //4 W->U
  for (int i = 180; i < 240; i++) {
    analogWrite(IN_U, pwm[i]);
    analogWrite(IN_V, pwm[i - 240]);
    analogWrite(IN_W, pwm[i - 120]);
    delayMicroseconds(delayTime);
  }

  //5 V->U
  for (int i = 240; i < 300; i++) {
    analogWrite(IN_U, pwm[i]);
    analogWrite(IN_V, pwm[i - 240]);
    analogWrite(IN_W, pwm[i - 120]);
    delayMicroseconds(delayTime);
  }

  //6 V->W
  for (int i = 300; i < 360; i++) {
    analogWrite(IN_U, pwm[i]);
    analogWrite(IN_V, pwm[i - 240]);
    analogWrite(IN_W, pwm[i - 120]);
    delayMicroseconds(delayTime);
  }
}
 この肝はsetup()の段階で360°分の出力値を計算しておき、プログラム実行中の動作を軽くしている点です。ただしこれをやると変数の容量を食うので、今回はArduino UNOのグローバル変数を70%も使います。
 実際に動かすと下のようになります。

 そんなこんなで一応回すことはできたので、これから発展させていきたいと思います。あと今は、フォトカプラが欲しいです。それではまた今度。

ProcessingとArduinoでModulinもどきを作った話

 お久しぶりです。春休みに入って暇になり、留学がなくなり暇になり、春休みが伸びて暇をしています。電動バイクは減速機が作れずにn回目の計画破棄をしたので進捗はないです。

 そんなこんなでなんとなくModulinもどきを作ってみました。
 Modulinはこんなやつです。


 これは確かどこかのシンセサイザー会社が限定で作ったモジュラーシンセサイザーで、センサーだけでものすごく高かったので、自分は楽器が弾けませんが作ってみました。

 作るにあたって、このセンサーと同じようなものや同じような働きのセンサーがありました。それで長いこと探していた時に、この前の記事の時に使っていたものを見つけました。このsoft potというセンサーはは秋月では売ってなく、千石では短いものしかなく、マルツにも在庫がなく、スイッチサイエンスは取扱しておらず、国内で買うことがたぶんできない状態でした。それなので今回はMouserから買いました。なんか関税とかかかって多少割高になりますがこれで買うことができました。

 システムは下のようになります。今回、処理はほぼすべてProcessingで行ったのでArduinoはセンサーの値をまとめてシリアルで送っているだけです。
Untitled Diagram

 センサーには今回、つまみ用の可変抵抗10個、スライド抵抗1個、モーメンタリトグルスイッチ1個、SoftPot 500mm 1個、圧力センサー1個、を使いました。これらの値をArduino MEGAで読み取りProcessingに送っています。

 Processingに送られたデータはMinimライブラリのパラメータにします。MinimはProcessingのライブラリで、これを使うことで様々な波形を出したりそれにエフェクトをかけたりすることができます。今回はこちらのブログを参考にVCO,VCF,VCA,EG,LFOを実装しました。

 これらをいい感じに実装すると下のようになりました。
ah5uf-ml17t
a2mbv-4c1fh

 実際にならしてみるとこんな感じです。
 音楽ができないので引くことはできないのですが自分的には結構いい感じにできたので満足しています。

 今回はここまでです。自分の中ではいい感じにできたので良かったです。それではまた今度。

プログラム(Arduino)
int recv_data_a;
int recv_data_b;
int data=10;
void setup() {
  Serial.begin(250000);
}

void loop() {
  delay(1);
  int value0 = analogRead(0);
  int value1 = analogRead(1);
  int value2 = analogRead(2);
  int value3 = analogRead(3);
  int value4 = analogRead(4);
  int value5 = analogRead(5);
  int value6 = analogRead(6);
  int value7 = analogRead(7);
  int value8 = analogRead(8);
  int value9 = analogRead(9);
  int value10 = analogRead(10);
  int value11 = analogRead(11);
  int value12 = analogRead(12);
  int value13 = analogRead(13);
  recv_data_a = analogRead(14);
  int value15 = analogRead(15);
  if(450 < recv_data_b && recv_data_b < 600){
    if(recv_data_a > 600){
      data++;
    }
    if(recv_data_a < 450){
      data--;
    }
  }
  recv_data_b = recv_data_a;
  Serial.write('H');
  Serial.write(highByte(value0));
  Serial.write(lowByte(value0));
  Serial.write(highByte(value1));
  Serial.write(lowByte(value1));
  Serial.write(highByte(value2));
  Serial.write(lowByte(value2));
  Serial.write(highByte(value3));
  Serial.write(lowByte(value3));
  Serial.write(highByte(value4));
  Serial.write(lowByte(value4));
  Serial.write(highByte(value5));
  Serial.write(lowByte(value5));
  Serial.write(highByte(value6));
  Serial.write(lowByte(value6));
  Serial.write(highByte(value7));
  Serial.write(lowByte(value7));
  Serial.write(highByte(value8));
  Serial.write(lowByte(value8));
  Serial.write(highByte(value9));
  Serial.write(lowByte(value9));
  Serial.write(highByte(value10));
  Serial.write(lowByte(value10));
  Serial.write(highByte(value11));
  Serial.write(lowByte(value11));
  Serial.write(highByte(value12));
  Serial.write(lowByte(value12));
  Serial.write(highByte(value13));
  Serial.write(lowByte(value13));
  Serial.write(highByte(data));
  Serial.write(lowByte(data));
  Serial.write(highByte(value15));
  Serial.write(lowByte(value15));
  Serial.println(value15);
}


プログラム(Processing)
import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;
import  processing.serial.*;

Serial  serial;
Minim minim;
AudioOutput out;
// variance for VCO
Oscil oscil;
int octave;
// variance for VCF
MoogFilter filter;
// variance for VCA
ADSR adsr;
Multiplier multiplier;
// variance for LFO
Oscil vcoLfo, vcfLfo, vcaLfo;
float vcoLfoIntensity;
// variance for keyboard
boolean isbarLocked;
char lockedKey;
int recv_data_0;
int recv_data_1;
int recv_data_2;
int recv_data_3;
int recv_data_4;
int recv_data_5;
int recv_data_6;
int recv_data_7;
int recv_data_8;
int recv_data_9;
int recv_data_10;
int recv_data_11;
int recv_data_12;
int recv_data_13;
int recv_data_14;
int recv_data_15;
float freq[] = new float [1024];


void setup() {
  freq();
  size(1024, 100);
  textAlign(CENTER);
  fill(255);
  background( 0 );
  textSize(36);
  serial = new Serial( this, Serial.list()[1], 250000 );

  minim = new Minim(this);
  out = minim.getLineOut();
  oscil = new Oscil(440, 1.0, Waves.SQUARE);
  filter = new MoogFilter(10000, 0.0, MoogFilter.Type.LP);
  multiplier = new Multiplier(1.0);
  adsr = new ADSR(1.0, 1.0, 1.0, 0.5, 1.0);
  oscil.patch(filter).patch(multiplier).patch(adsr);
  vcoLfo = new Oscil(10, 0.0, Waves.SINE);
  vcoLfo.patch(oscil.frequency);
  vcfLfo = new Oscil(10, 0.0, Waves.SINE);
  vcfLfo.amplitude.setLastValue(0.0);
  vcfLfo.offset.setLastValue(10000);
  vcfLfo.patch(filter.frequency);
  vcaLfo = new Oscil(10, 0.0, Waves.SINE);
  vcaLfo.amplitude.setLastValue(0.0);
  vcaLfo.offset.setLastValue(1.0);
  vcaLfo.patch(multiplier.amplitude);
}

void setVcoWave() {//2
  switch(int(map(recv_data_2, 0, 1023, 0, 6))) {
    case(0):
    oscil.setWaveform(Waves.SINE);
    break;
    case(1):
    oscil.setWaveform(Waves.TRIANGLE);
    break;
    case(2):
    oscil.setWaveform(Waves.SAW);
    break;
    case(3):
    oscil.setWaveform(Waves.SQUARE);
    break;
  }
}

void setLfoWave() {//3
  switch(int(map(recv_data_3, 0, 1023, 0, 6))) {
    case(0):
    vcoLfo.setWaveform(Waves.SINE);
    vcfLfo.setWaveform(Waves.SINE);
    vcaLfo.setWaveform(Waves.SINE);
    break;
    case(1):
    vcoLfo.setWaveform(Waves.TRIANGLE);
    vcfLfo.setWaveform(Waves.TRIANGLE);
    vcaLfo.setWaveform(Waves.TRIANGLE);
    break;
    case(2):
    vcoLfo.setWaveform(Waves.SAW);
    vcfLfo.setWaveform(Waves.SAW);
    vcaLfo.setWaveform(Waves.SAW);
    break;
    case(3):
    vcoLfo.setWaveform(Waves.SQUARE);
    vcfLfo.setWaveform(Waves.SQUARE);
    vcaLfo.setWaveform(Waves.SQUARE);
    break;
  }
}

void setCutoffFrequency() {//4
  vcfLfo.amplitude.setLastValue(map(recv_data_4, 0, 1023, 0, 10000) / 2.0 * map(recv_data_8, 0, 1023, 0, 1));
  vcfLfo.offset.setLastValue(map(recv_data_4, 0, 1023, 0, 10000));
}

void setResonance() {//5
  filter.resonance.setLastValue(map(recv_data_5, 0, 1023, 0, 1));
}

void setLfoRate() {//6
  float value = map(recv_data_6, 0, 1023, 0, 100);
  vcoLfo.setFrequency(value);
  vcfLfo.setFrequency(value);
  vcaLfo.setFrequency(value);
}

//7

void setVcfIntensity() {//8
  float value = map(recv_data_8, 0, 1023, 0, 1);
  vcfLfo.amplitude.setLastValue(map(recv_data_4, 0, 1023, 0, 10000) / 2.0 * value);
}

void setVcaIntensity() {//9
  float value = map(recv_data_9, 0, 1023, 0, 1);
  vcaLfo.amplitude.setLastValue(map(recv_data_15, 0, 1023, 0, 1) / 2.0 * value);
}

void setParameters() {//10,11,12,13
  adsr.setParameters(1.0, map(recv_data_10, 0, 1023, 0, 1), map(recv_data_11, 0, 1023, 0, 1), map(recv_data_12, 0, 1023, 0, 1), map(recv_data_13, 0, 1023, 0, 1), 0.0, 0.0);
}

void setAmplitude() {//15
  float value = map(recv_data_15, 0, 1023, 0, 1);
  vcaLfo.amplitude.setLastValue(value / 2.0 * map(recv_data_9, 0, 1023, 0, 1));
  vcaLfo.offset.setLastValue(value);
}

void serialEvent(Serial port) {  
  if ( port.available() >= 33 ) {
    if ( port.read() == 'H' ) {
      //0
      int high = port.read();
      int low = port.read();
      recv_data_0 = high * 256 + low;
      //1
      high = port.read();
      low = port.read();
      recv_data_1 = high * 256 + low;
      //2
      high = port.read();
      low = port.read();
      recv_data_2 = high * 256 + low;
      //3
      high = port.read();
      low = port.read();
      recv_data_3 = high * 256 + low;
      //4
      high = port.read();
      low = port.read();
      recv_data_4 = high * 256 + low;
      //5
      high = port.read();
      low = port.read();
      recv_data_5 = high * 256 + low;
      //6
      high = port.read();
      low = port.read();
      recv_data_6 = high * 256 + low;
      //7
      high = port.read();
      low = port.read();
      recv_data_7 = high * 256 + low;
      //8
      high = port.read();
      low = port.read();
      recv_data_8 = high * 256 + low;
      //9
      high = port.read();
      low = port.read();
      recv_data_9 = high * 256 + low;
      //10
      high = port.read();
      low = port.read();
      recv_data_10 = high * 256 + low;
      //11
      high = port.read();
      low = port.read();
      recv_data_11 = high * 256 + low;
      //12
      high = port.read();
      low = port.read();
      recv_data_12 = high * 256 + low;
      //13
      high = port.read();
      low = port.read();
      recv_data_13 = high * 256 + low;
      //14
      high = port.read();
      low = port.read();
      recv_data_14 = high * 256 + low;
      //15
      high = port.read();
      low = port.read();
      recv_data_15 = high * 256 + low;

      setVcoWave();//2
      setLfoWave();//3
      setCutoffFrequency();//4
      setResonance();//5
      setLfoRate();//6
      setVcfIntensity();//8
      setVcaIntensity();//9
      setParameters();//10,11,12,13
      setAmplitude();//15
      
       print(recv_data_0);
       print(",");
       println(440*pow(2,freq[recv_data_0]/12.0));
       
    }
  }
}

void draw() {
  float waveH = 50;
  background(0);
  for(int i = 0; i < out.bufferSize()-1; i++)
  {
    point(i, 50 + out.left.get(i)*waveH);
  }
  stroke(255);
  if (recv_data_0>=18&&recv_data_1<=1000) {
    vcoLfo.amplitude.setLastValue(440*pow(2, (freq[recv_data_0]/12.0)+recv_data_14-10) / 2.0 * map(recv_data_7, 0, 1023, 0, 1));
    vcoLfo.offset.setLastValue(440*pow(2, (freq[recv_data_0]/12.0)+recv_data_14-10));
    adsr.unpatch(out);
    adsr.noteOn();
    adsr.patch(out);
    isbarLocked = true;
  } else {
    if (isbarLocked) {
      adsr.unpatchAfterRelease(out);
      adsr.noteOff();
      isbarLocked = false;
    }
  }
}

ARゴーグルを作りたい話3

 最近なんとなく作っているARグラスの新型ができたので書きます。

ab58m-igftm
a5s3u-qio8s
ajukv-llh3f

 こんな感じです。メガネはダイソーで売ってるサングラスで、それに外付けのモジュールを付けました。細い方の筒の中は空で、ここを縮めることはできるのですが、家にある設備で切れるレンズがシートレンズしかなく、それだと焦点距離が伸びないのでしょうがなく長くなっています。また、LCD-反射板間の距離が長くなってしまったので全体的に像が暗くなってしまいました。


aqw3p-mi7xb

 内容物はこんな感じです。今回から表示したい文字をシリアル通信で送れるようにしたのでArduino nanoのほかにXBeeも積んでいます。今回はやりませんでしたが、いろいろな荒業を使えば電装の機構の体積は半減させることができます。まあ、今回使ったリポバッテリーは↓なのですが、こんなに容量はいらないので、もっと小さいものがあれば小型化できます。


 これらのことから、全力で小型化すれば細い方の筒の容量だけで実現できる気がします。まあ、やる気は起きませんが。次やるときはそもそもARじゃなくて普通に視界の一部分をふさいで表示しようと思います。普通に薄くて見にくいし、そもそも自分が見ている深度とARグラスに表示しているものの深度が違えばどうせ水晶体でピント調節しないといけないしであまり利点がないと思うっからです。

 技術的なことを言えば今回は下図のようにArduino-XBee-XBee-Arduino-LCDという風にデータを送り表示しています。これは、将来を見越してセンサーの値とかを送れるようにするのと、ワイヤレスが条件だったのでこうなりました。
Untitled Diagram
 いつもの通りプログラムは下に書いときます。今回のプログラムは過去作品の寄せ集めですが、SPIとハードウェアシリアル通信をやっているのでそこだけひねってあります。

 これを作っている間にTsukubaMiniMakerFaireにお手伝いに行って色々見てきたのでいろいろ作ろうと思います。それではまた今度。

送信側
int LED = 13;
int count = 0;
String s1 = "   Hello";//ここに表示したい文字
String s2 = "   World";

void setup() {
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
}

void loop() {
  count++;
  Serial.write(count);
  if ((count % 2) == 1) {
    digitalWrite(LED, HIGH);
    Serial.write('$');
    Serial.write(s1.length());
    for (int i = 0; i < s1.length(); i++) {
      Serial.write(s1.charAt(i));
    }
  } else {
    digitalWrite(LED, LOW);
    Serial.write('$');
    Serial.write(s2.length());
    for (int i = 0; i < s2.length(); i++) {
      Serial.write(s2.charAt(i));
    }
  }
  delay(50000);
}
受信側
int LED = 13;
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>             // Arduino SPI library#include <Adafruit_GFX.h>    // Core graphics library

// ST7789 TFT module connections
#define TFT_CS    10  // define chip select pin
#define TFT_DC     9  // define data/command pin
#define TFT_RST    8  // define reset pin, or set to -1 and connect to Arduino RESET pin

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  if (Serial.read() == '$') {
    int i = Serial.read();
    char s[i];
    for (int c; c < i; c++) {
      s[c] = Serial.read();
    }
    tft.init(240, 240, SPI_MODE2);
    tft.setTextColor(ST77XX_WHITE);
    tft.setRotation(0);
    tft.fillScreen(ST77XX_BLACK);
    tft.setTextSize(4);
    tft.setCursor(0, 0);
    for (int c; c < i; c++) {
      tft.print(s[c]);
      Serial.print(s[c]);
    }
    tft.println("");
    SPI.end();
  }
  delay(10);
}

ARゴーグルを自作したい話2

 前回作ったときにいろいろ思うところがあったので作り直してみました。ただ、今回のにも課題があるのですぐ作り直すと思います。

 前回の課題として、重い、バンドで固定しないといけない、表示が面倒というものがありました。特に三個めが顕著で、一回反射の像を見ることになっていたので、左右が反転してしまっていました。そのため表示したいものは左右反転画像を用意してやってそれを表示していました。これではシステムの冗長性が一切確保できないので、これを解決するために二回反射するものを作りました。

 そんなこんなで、今回はArduino LCDぐらいで簡単にできるものを目指しました。 Untitled Diagram
 いつも出てくる適当模式図です。二回反射させることで反転しません。またLCDはこの間使ったやつを使っています。このLCDはドライバが面倒だけど図形とか文字とか表示できるのでとっても便利です。
 あと今回鏡はPカッターじゃ切れないことが分かったのでガラスカッターを買いました。めっちゃきれいにガラスが切れるので最初から使えばよかったと思いました。


 まず、3Dモデルで適当に作って印刷して組み立ててのトライ&エラーを繰り返します。
 それでできた3Dモデルはこちらです。1
2
 パソコン上で見るとなんかスタイリッシュに見えなくもないので騙されてました。
 実際に組み立てるとこうなります。aott3-qy9zq
awtyp-08b5d
 なんか違う感じがするのがわかるでしょうか?アクリル板の正面にちょうど目が来るので、本体が頬の横に来ます。そうすると正面からのシルエットも相まって面白眼鏡にしか見えません。まあ、写りは下のように悪くないです。
ao640-skvnt

 こんなんで映す方法はいいんですけどデザインがいまいちなので、ver3を作ります。モーターが届いたので自転車も作りたいです。それではまた今度。

AliExpressaから荷物が届いた話。

AliExpressに頼んでいた荷物が届いていたので、その時にやったやり方を書いていこうと思います。

①会員登録
 トップページを開き、右上のsigh inと書かれたら辺にカーソルを合わせるとウィンドウが出てくるので、sign inを押します。するとページが変わり、Quick accessにツイッターのアイコンがあるのでそこを押します。(自分はツイッターでアカウントを作りましたが、それ以外でも問題なくできると思います。)そうすると、アプリ認証のページに飛ぶのでそこで認証すれば登録完了です。

②設定1
 デフォルトだと、値段がUS$になっていて分かりにくいのでこれを変更していきます。まあ特に難しくなく、右上のShip to/JPYというところをクリックして、出てきたCurrencyの中から選ぶだけです。

③買い物
 お買い物の方法はアマゾンと一緒で、適当に検索したりカテゴリーから検索したりします。いい感じに日本語に機械翻訳されているので、特に不自由はないと思います。ほしい商品があったら、色とか、素材とか、どこの倉庫から送るかとか、数量とかを入力し、カートに追加していきます。送り主の倉庫がいくつかある場合、その場所によって値段が違ったりするので気を付けてください。

 買いたいものがすべて決まったらカートに移動します。カートの中の欲しいものを選択していくとそれを買うものという扱いになり、右にその分の合計金額が表示されていきます。なお、ここで選択しなかったものは買えないので注意してください。そして、購入というボタンを押すと購入できます。この時に配送先と支払方法を決めないといけないのですが、そこそこめんどくさいので別の項で説明します。

④送り先
 海外のサイトなので送り先の丹生力が日本と違い分かりにくいです。キャプチャ
 この画面を埋めていく必要があるのでそれぞれの行ごとに説明していきます。全体を通して、Japan以降の住所は日本郵政がわかれば問題ないので、多少おかしくても多分届きます。読むのがめんどくさい人は下の完成例を参考に入力してください。
  • 1行目...名前と携帯番号です。名前は姓名どういう順番でも日本郵政が理解できる形なら大丈夫です。次に携帯番号ですが、080,090,070の一番先頭の0を消し、80,90,70という風に書き始め、あとは普通に入力します。
  • 2行目...streetには、何丁目何番何号、町名を入力していきます。皇居で例えると千代田1-1の部分です。これは、数字→町名の順番で入力します。また、右のApartment,Unit,etcにはその名の通りマンション名とか部屋番号とかを入れていきます。部屋番号は#○○○みたいな感じで入力しとくと言いそうです。
  • 3行目...ここは書いてあることそのまま国,都道府県,市区町村を入力していきます。皇居で言う、東京都千代田区の部分です。
  • 4行目郵便番号です。
 これを入力するとこのようになります。保存しておきたいはチェックマークにチェックしておくと保存できます。

キャプチャ2


⑤支払方法
 だいたいの人はクレジットカード払いをすると思います。その人は普通にカード番号とカード名義と有効期限とセキュリティーコードを入力するだけです。

 ただ、クレジットカード情報を中国企業に渡したくないという人もいると思います。AliExpressでクレジットカード情報が盗まれたみたいな話は聞いたことがありませんが、まあそれでも不安な人はVプリカを使うといいと思います。これは、VISAがやっているプリペイドカードで、ネットで簡単に課金ができるうえに、これ本体はただのプリペイドカードなので課金した金額以上は絶対に使えないのでもし何かあっても被害があまりないという優れものです。
 何年か前までクレジットカードを持ってない未成年がMinecraftを買うにはこれをコンビニで買うしかなかったので、使ったことがある人もいるかもしれません。
 Vプリカのサイトは普通のサイトなので普通に使えると思います。それでもできなかったときはFAQでも読んでくだしあ。
 Vプリカはクレジットカード名? を自分で決めるのですが、これを入力するとき最初の一文字の後スペースを入れて疑似的に姓名っぽくすると認証される必要があります。


 まあそんなこんなで注文すると業者にもよりますがそこそこの日数で届きます。私は1/29に注文したもののうち一つは届きましたが、もう一つはまだ届いていません。荷物が届かないこともなくはないらしいですが、その時は運営に連絡すると返金してくれるようです。というのも、荷物が客のところに届いたのが確認されるまではお金は運営一時的に預かっているという扱いになるらしく、AliExpressの注文履歴から届いたということを入力しない限り業者にお金が払われないようです。それなので荷物が届いたらなるべく早く注文履歴から荷物が届いたことを報告してあげてください。

 そんなこんなで無事に中国から荷物が送られてきました。
IMG_2095IMG_2094

 Amazonで買った時も思いましたが、中国から贈られてくるものは基本的に梱包が雑です。以前モーターを買ったときは、プチプチシートで包んだ生身のモーターが送られてきたのですが、これを運んでくれる日本郵政ってすごいんだなぁと思います。

 今回はこれで終わりです。ARゴーグルもどきのver2ができているのでそのことも近く記事にすると思います。それではまた今度。

ソレノイドで星形5気筒エンジンを作った話

 なんとなくソレノイドを使ってエンジンを作ることにしました。
 実用性は求めていないので、見た目的に美しく構造も簡単な星形エンジンにすることにしました。

 まず、星形エンジンを作るにあたって、Fusionでモデルを作ってシミュレーションしてみました。
 それがこちらです。なんか適当に作っても回る気がしてきました。

 これを実際に印刷して動くようにするのはめんどくさいので、あくまで参考にしつつ3Dプリントしてきちんと動くようにしていきます。この作業はまあまあめんどくさく、例えばM2のビスを通すときの穴の大きさは2.6mmだったり、520ベアリングがはまる穴の大きさは5.4mmだったりするのを経験則であてはめながらモデルを作っていきます。

 キャプチャ

 いい感じにビス頭とかの空間を開けつついい簡易に干渉しないように作っていきます。めんどくさいのでベアリングやビスは作りませんし、設計段階でソレノイドの正確な位置を決めるのもぶっちゃけめン独裁のでいい感じにレールだけ作っておきます。

 これを3Dプリンタで印刷して、組み立てて、適当に回路を組み、プログラムも適当に描くと動きました。
 なんかかくかく言っているのでプログラムの回転数を小さくしてみるといい感じになりました。


 配線の具合を整えたらこんな感じになります。apxl8-zesbz

 見た目もきれいですし、見ていて楽しいので昨日から意味もなく机の上で回したりしています。

 最近ガラスカッターを買って、100均の鏡が簡単に切れるようになったので、ARデバイスver2の開発をしています。春休みは、台湾に短期留学するのとTMMFに呼ばれている以外絶妙に暇なので、来週中にも完成させてブログに書けるぐらいまで進めたいと思います。それではまた今度。

1.3インチ SPI LCDディスプレイ(CSなし)を使ってみた話。

 アマゾンで買ったIPSパネルのLCD(SPI通信なのにCSがないやつ)を動かしたのでその方法です。ちなみに同じディスプレイがアマゾン以外でも売っているらしいです。
 ちなみに買ったディスプレイは↓です。


 そもそも、この商品ページがクッソわかりずらい上に、通常のライブラリはCSがあるものとしてできているので、配線をしても普通のライブラリのサンプルそのままでは動きません。また、結構マイナーなものらしく、日本語で使い方を解説しているところが見つかりませんでした。
 なのでその使い方です。

①配線
ディスプレイ:Arduino(UNO)
GND  : GND
VCC : 3.3V(5V)
SCL : 13pin
SDA : 11pin
RES : 8pin
DC : 9pin
です。プログラムをいじれば13,11ピンはArduinoに元からついているSCL,SDA(A5,A4)に変えることもできます。むしろそうした方が速度は速いです。

②ライブラリ
 IDEを開いて、"スケッチ"->"ライブラリをインクルード"->"ライブラリを管理"
と進んで検索ウィンドウを出します。
そこで"Adafruit GFX graphics"と検索して一番上に出てくるものと"Adafruit ST7789"と検索して出てくるものをインストールします。

③コード
 ここ(https://github.com/jumejume1/tft240x240-spi)からダウンロードして、中の.inoを実行します。そうするとIDEが開かれるのでそのまま書き込めばOKです。

 そうやるとこのように表示されるはずです。 このモジュールは3.3V対応のはずですが、5Vで通信できます。毎度のこと気になる方は抵抗を直列つなぎにするといいと思います。

 今回はここまでです。千石にソレノイドを頼んだんですが、在庫がないらしくて時間がかかるようです。それまで気長に待とうと思います。それではまた今度。

marutsuで買ったLCDを付けた話

 マルツで当時よさそうだと思ったLCDを買ってきたのでその話です。買ってきたのはこのLCDです。秋月でも同じものを売ってるらしいっです。この液晶の裏には薄い膜が貼ってあるだけなので、それをはがせば簡単に透過できるのでは? と思ったので買いました。
 ただ、結果から言うと、文字以外の部分も十分光を通さなかったので、お蔵入りとなりました。

 以下、公式のスイッチサイエンスの半コピです。
atanf-y7l9s
 プログラムでA2,A3をVdd,GNDとするので、アナログピンにさすだけで使えます。裏で緑に光っているのはArduinoの電源ランプです。
 また、プログラムですが、対応するライブラリがない? らしいのでまあまあ長くなります。
 
 まあそんなこんなで使えなくもないLCDだと思いますが、自分は今すぐには使わないので電子部品の山の中に入れておきます。
 常に言っているサークルの自転車は、モーター選定がうまくいっていないので一向に進みません。AliExpressでモーターを買ってみたので届いたらその話も含めてブログにすると思います。それではまた今度。

 以下プログラムです。
#include <Wire.h>

#define vddPin 16    // ArduinoA2
#define gndPin 17    // ArduinoA3
#define sdaPin 18    // ArduinoA4
#define sclPin 19    // ArduinoA5
#define I2Cadr 0x3e  // 固定
byte contrast = 35;  // コントラスト(0~63)

void setup() {
  pinMode(gndPin, OUTPUT);
  digitalWrite(gndPin, LOW);
  pinMode(vddPin, OUTPUT);
  digitalWrite(vddPin, HIGH);
  delay(500);
  Wire.begin();
  lcd_cmd(0b00111000); // function set
  lcd_cmd(0b00111001); // function set
  lcd_cmd(0b00000100); // EntryModeSet
  lcd_cmd(0b00010100); // interval osc
  lcd_cmd(0b01110000 | (contrast & 0xF)); // contrast Low
  lcd_cmd(0b01011100 | ((contrast >> 4) & 0x3)); // contast High/icon/power
  lcd_cmd(0b01101100); // follower control
  delay(200);
  lcd_cmd(0b00111000); // function set
  lcd_cmd(0b00001100); // Display On
  lcd_cmd(0b00000001); // Clear Display
  delay(2);
}

void loop() {
  lcd_setCursor(0, 0);
  lcd_printStr("Hello");
  lcd_setCursor(1, 1);
  lcd_printStr("World");
  delay(100);
}

void lcd_cmd(byte x) {
  Wire.beginTransmission(I2Cadr);
  Wire.write(0b00000000); // CO = 0,RS = 0
  Wire.write(x);
  Wire.endTransmission();
}

void lcd_contdata(byte x) {
  Wire.write(0b11000000); // CO = 1, RS = 1
  Wire.write(x);
}

void lcd_lastdata(byte x) {
  Wire.write(0b01000000); // CO = 0, RS = 1
  Wire.write(x);
}

// 文字の表示
void lcd_printStr(const char *s) {
  Wire.beginTransmission(I2Cadr);
  while (*s) {
    if (*(s + 1)) {
      lcd_contdata(*s);
    } else {
      lcd_lastdata(*s);
    }
    s++;
  }
  Wire.endTransmission();
}

// 表示位置の指定
void lcd_setCursor(byte x, byte y) {
  lcd_cmd(0x80 | (y * 0x40 + x));
}
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

読者登録
LINE読者登録QRコード