2026年7月7日火曜日

回転軸をスムーズに動かす工夫


回転軸をスムーズに動かしたいことがあります。右の図は把手です。つまみを持って回転させる機構です。

 この時に必要なのはつまみがスムーズに動くことです。


3D設計

 この機構を次のように設計しました。


 左図のように、ハンドルにはφ6.0mmの穴が空いています。押さえの差し込まれる部分はφ5.5mmで、少しだけ小さくなっています。また右図のように、厚みではハンドルの穴が1mm、押さえが1.2mmと押さえの厚みが少しだけ熱くなっています。

 これにより押さえをつまみにネジ留めすると、つまみと押さえがしっかりと固定され、わずかな隙間があるため、ハンドルとはスムーズに動かすことができます。


 この機構は、ネジ留めで固定するときに、スムーズに動かす必要がある場合に活用することができます。




2026年6月12日金曜日

マイコンゲームの乱数初期化方法(2)

  マイコンによっては、EEPROMが使えない場合があります。その時の一例です。

スイッチ回路

 右はスイッチ回路を接続した図です。
左のボードはESP32マイコンをGroveコネクタで接続できるようにした拡張ボードです。

 ブレッドボードにスイッチを配置して、GroveケーブルでIO13ーGND間に接続しています。

スイッチから乱数列を初期化する

 ゲームをするときに、ほぼスタートスイッチで始める場合が多いかと思います。このスイッチを利用します。スイッチを押してから離すまでの時間を使って乱数の初期化をします。

 プログラム例です。

#define SW  13
long cnt=0;
void setup() {
  pinMode(SW,INPUT_PULLUP);
  Serial.begin(115200);
  delay(100);
  Serial.printf("Plese press the SW.\n");
  // 押されるまで待つ
  while(digitalRead(SW)!=0);
  Serial.printf("Plese release the SW.\n");
  cnt=micros();
  // 離すまで待つ
  while(digitalRead(SW)==0);
  cnt=micros()-cnt;  
  Serial.printf("cnt: %5ld\n", cnt);
  // 乱数の初期化
  srand(cnt);
  for(int i=0;i<10;i++){
    int r=random();
    Serial.printf("%5d\n",r);
  }
}
 
void loop() {
} 
 Arduinoでは内部時間を持ってします。millis()関数やmicros()関数を使うと、リセットされてからの時間を取得できます。
 これを利用して、スイッチを押したときの内部時間と話したときの内部時間の差を取ると、スイッチを押していた時間が取得できます。この時間は個人差があり、毎回異なります。この時間をmicros()関数で計算すると、マイクロ秒で時間を取得できます。マイクロ秒単位であれば、ほとんど同じになることはありません。
 実行結果を示します。

 先頭のcntがスイッチを押していた時間[μS]です。これを乱数の初期化に使うと、毎回異なる乱数列となります。

 Arduinoマイコンでない場合でも、押しているときに数値をカウントするだけで、同じ働きが作ることができます。

2026年6月11日木曜日

マイコンゲームの乱数初期化方法(1)

  マイコンでゲームをプログラミングする場合、生成される乱数列が固定されてしまいます。つまり、常に同じ乱数でゲームをすることになり、記憶すれば攻略することができてしまいます。


random関数の動作を確認する

 まず、random関数の動作確認をします。次のプログラムを実行します。

void setup() {
  Serial.begin(115200);
  delay(100);
  for(int i=0;i<10;i++){
    int r=random();
    Serial.printf("%5d\n",r);
  }
}
void loop() {
}
 乱数を10個表示させるプログラムです。
 実行させると、右図のように乱数が表示されます。
何度もリセットボタンを押して、再起動しても同じ乱数が表示されます。

EEPROMを使う

 ゲームのプログラミングを行うときに、パソコンのように内部時計が動作していると、起動時の時間から乱数列の初期化を行うことができます。
 しかし、マイコンで、WiFi機能があるとインターネットに接続することもできますが、一般的には割高になってしまいます。
 初期化する方法として、EEPROMにプログラムが実行された回数を記憶しておき、その数値で乱数の初期化をすることができます。

#include "EEPROM.h"
int addr = 0;
#define EEPROM_SIZE 64
void setup() {
  Serial.begin(115200);
  EEPROM.begin(EEPROM_SIZE);
  delay(100);
  byte cnt=EEPROM.read(addr);
  Serial.printf("cnt: %2d\n", cnt);
  // 乱数の初期化
  srand(cnt);
  // EEPROMのカウントアップ
  cnt++;
  EEPROM.write(addr, cnt);
  EEPROM.commit();

  for(int i=0;i<10;i++){
    int r=random();
    Serial.printf("%5d\n",r);
  }
}

void loop() {
}
 プログラムでは、EEPROMから読み出し、プラス1してEEPROMに書き込み直します。読みだした数値で乱数の初期化を行い、その後、10個の乱数を発生、表示しています。

 実行結果が右図です。
 先頭でEEPROMの数値cntを表示後、10個の乱数を表示させた結果です。
 cntの値が変わると、乱数列が変わっていることが確認できます。



EEPROMを使った乱数の初期化(アレンジ)

 プログラムの起動回数cntを使った乱数の初期化ですが、起動回数が同じであれば、同じ乱数になってしまいます。
 これを回避する一例として、ハイスコアを別のEEPROMのアドレスに記憶しておき、起動回数cntと組み合わせて、乱数の初期化を行うことや、スイッチの押された回数を記憶して、起動回数cntと組み合わせるなどのアレンジをすると、同じ起動回数でも乱数列を変えることができます。





2026年6月4日木曜日

ロードセルを使った重心計算(3)

 重心の計算のための準備

 重心を求めるためには座標の概念が必要です。そこで、右図のように座標を設定します。


 ボードの中央が原点Oです。
 正規化して-1~+1の割り当てでも良いのですが、計算するときに、浮動小数点演算となるので、座標をー100~+100としました。





重心の計算アルゴリズム

 重心計算は物理学で出てきます。

 右図のように、点A~Cの座標x、yとその点の荷重mが与えられたときに、重心Gの座標XG、YGは次の式で計算することができます。

 

 4点以上でも同様な計算で求めることができます。


 それぞれの点は

 点A(-100,+100,m1)

 点B(+100,+100,m2)

 点C(-100,-100,m3)

 点D(+100,-100,m4)

となるので、重心の式に当てはめると、次の式となります。

 よって、ロードセルで重量を測定し、右の式で計算することで、重心位置(XG,YG)を計算することができます。



 計算結果を表示させたものが、右図です。
図の一番右側に、重心の座標が(XG,YG)が表示されています。
(重心位置が中央に近い場合には非表示にしています)

ロードセルを使った重心計算(2)

 重心を測定するためのハードウェア

 ロードセルを部材に付けるために、右のように3Dプリンタでネジ留しやすいように設計しました。また、ケーブルも細いため、切断しにくいように、スパイラルチューブで保護しています。


 重心を測定するため、1x4材の木組みの角にロードセルを4個設置しました。各ロードセルから、HX711を経由して、中央のESP32マイコンに接続しています。

 これで、4つのロードセルにかかる荷重を測定できるようになります。





プログラム

 4個のロードセルから読み込むためには、インスタンスを4個作ってプログラムします。 

  HX711 scale1(  32, 33 );
  HX711 scale2(  25, 26 );
  HX711 scale3(  19, 23 );
  HX711 scale4(   5, 18 );

  あとはそれぞれに対して、begin()、getGram()などのコマンドにより、4つのロードセルにかかる荷重を測定します。

 実行結果(シリアルモニタ)を示します。

 W1~W4が、それぞれのロードセルから読み込んだ重量、Wが合計になります。
(合計は総重量となります)



2026年6月3日水曜日

ロードセルを使った重心計算(1)

 ロードセルとADモジュール

 ロードセルは、電子はかりなどに使われているセンサです。いろいろな形状があるのですが、右図は今回使用したロードセルです。50Kgまで測定できます。

 ロードセルにはひずみゲージが付けられており、その抵抗の変化で、重さを測定するものです。

 

 ロードセルから重さのデータを取得するには、HX711と呼ばれるロードセル用のADモジュールが使われます。

 このモジュールでは、標準で1秒間に10回(10Hz)のデータ取得ですが、右上の抵抗(0Ω)を80Hz側にする(短絡)ことで、1秒間に最大80回までのデータ取得が可能になります。

 ロードセルとの接続は、左側のE+、E-、A-、A+の4端子を使って接続します。Arduinoとの接続は、右側のGND、DT、SCK、VCCの4端子を接続します。


Arduinoプログラミング

 Arduinoでは、HX711用のライブラリがありますので、これを利用すると簡単に重さを測定することができます。ただし、ロードセルには測定できる重さが様々のため、パラメータ設定が必要です。サンプル例を示します。

#include "hx711.h"

// HX711    ( dat, sck )  ピンの設定 
HX711 scale(  19, 18 );
// ロードセル設定
#define OUT_VOL   0.001f      // 定格出力 [mV/V]
#define LOAD      50000.0f    // 定格容量 [kg]
long offset;

int kg = -1;
long gram;

void setup(void) {
  Serial.begin( 115200 );
  delay(200);
  Serial.printf("\n\n" );
  Serial.printf("=== start System. ===================\n" );

    // HX711 初期化
  scale.begin();
  if( scale.isReady() ){
    scale.setOutVol( OUT_VOL );
    scale.setLoad( LOAD );
    delay(50);
    long sum=0;
    for(int i=0;i<10;i++){
      sum += abs( scale.getGram() );
    }
    offset = sum / 10;
  }
}

int readWeightKg(){
  kg= -1;
  if( scale.isReady() ){
    gram = abs( abs(scale.getGram()) -  offset ) ;
    kg  = (int)(( gram ) / 1000 );
    if( kg < 0 ) kg = 0;
  }else{
    kg = -1;
  }
  return kg;
}

int cntKg= 0;
int cntLED= 0;
void loop(void) {
  delay(1);
  cntKg++;
  if( cntKg > 100 ) {
    cntKg=0;
    int kg = readWeightKg();
    if( kg >= kg_TH ){
      Serial.printf("Weight: %4ld[g]\n", gram );    
    }
  }
}<10 -="" 0="" 1000="" 10="" abs="" cntkg="" cntled="0;" delay="" else="" gram="" i="" if="" int="" kg="" loop="" offset="" readweightkg="" return="" scale.getgram="" scale.isready="" sum="" void="">

 プログラムでは、ロードセルのばらつきがあるため、setup()内で初期化後に、10回読み込み、平均を取ってオフセット値を計算しています。
 実際の値は、読み込んだ値からオフセット値を引いたものが、実際の重量となります。 
 実行結果は右のシリアルモニタです。


 参考にソースコードを見る方は次のリンクからご覧ください。

ソースコード

2026年5月14日木曜日

LEDマトリックス表示装置の設計・製作 9(画像表示)

  LEDマトリックスに画像を表示させるには、いろいろな画像形式を知る必要があります。現在、よく使われるJPG形式やPNG形式は圧縮処理されて保存されています。これに対して、BMP形式やTIFF形式は非圧縮形式で、画像データがそのまま保存されています。このため、ファイルは大きくなります。


LEDマトリックスで使う画像 BMP形式

 LEDマトリックスに表示させるには、最も単純なBMP形式を使います。非圧縮ですが、LEDマトリックスには16x16ドットのサイズなので、ファイルサイズはあまり大きくないためであり、解凍処理が不要なためです。

 バイナリエディタでファイルを開くと、右図のように、16進数の並びが表示されます。

 この先頭(ピンク部分)はヘッダ領域と呼ばれ、ファイルの形式やファイルに関する情報が書かれています。その後に続く部分(緑部分)はデータ領域で、実際のデータが書かれています。

(すべてのファイルは、この形式をしています)

 このヘッダ領域の先頭2バイトが42H、4DH(文字では"BM")であれば、画像のBMP形式のファイルであることを意味します。(それ以外のヘッダ情報は検索すると出てくるので、割愛します)


 表示している画像データは24ビットカラーです。データ領域ではデータが32ビットごとに1ドットのデータとして並んでいます。図の赤枠の場合、「00 00 00 00」となっています。順番にR(赤)、G(緑)、B(青)、D(ダミー)の情報です。この場合はRGBが000000hなので黒色のドットとなります。この様に読みだしたデータを、何も処理することなく、そのまま1ドットのデータとして利用できます。

 このデータを読み込み、LEDマトリックスに画像を表示させます。

 右下の部分が画像を表示している部分です。




 

回転軸をスムーズに動かす工夫

回転軸をスムーズに動かしたいことがあります。右の図は把手です。つまみを持って回転させる機構です。  この時に必要なのは つまみがスムーズに動く ことです。 3D設計  この機構を次のように設計しました。  左図のように、ハンドルにはφ6.0mmの穴が空いています。押さえの差し込ま...