ソロでたのしむ

キャンプ、電子工作、DIY、乗り物などの趣味を紹介

無段変速フィニッシングモーターを自作しよう! 釣り竿のガイド交換用Arduino工作

こんにちは!

このところ魚釣りのトレーニングに集中していて、しばらく電子工作から遠ざかっていました~

電子工作も大好きなのですが、日常生活で使えるものを作りたい!ので、必要に迫られた時しか、はんだごてに手が伸びません。

そして魚釣りを楽しむ中で、釣り竿の糸を通す部分(ガイド)に錆が浮いてきたので、自分でガイドを新品に交換したいと思うようになりました。

交換作業の終盤で、エポキシ系の仕上げ塗装(フィニッシャー)をしないといけないのですが、これが半硬化するまでの約5時間ほど、竿をくるくると回転し続けないと、塗装が垂れてきて見苦しい仕上がりになってしまします。

・・・そこで・・・

久しぶりのArduino工作で、自動で釣り竿を回し続けてくれる装置を、自作してみます。

スピードコントロール フィニッシングモーターを自作します

 

スピードコントロール付きフィニッシングモーター

フィニッシングモーターって何なの?というご質問もあると思いますので、ご説明しておきます。

機能としては釣り竿を回転させ続けるだけ!というシンプルなもので、厚めの塗装を施す場合に、塗料のタレを発生しにくくするための道具です。

釣り竿を装着して作動させると、こんな感じです。

youtu.be

似たようなものとして、

  • 肉の丸焼き用のロティサリー
  • バームクーヘン製造機
  • ちくわ焼き機

がありますね!...食べ物系しか思いつきませんでしたけど。

要するに、長時間にわたって、ゆっくりと回転し続けてくれる、一部の界隈でのお役立ちアイテムです。

今回私が作りたいのは、硬化時間24時間のエポキシ塗料が釣り竿の表面に均一な被膜を形成するためのものになります。

それでは、回転方向と回転速度が調整できる、スピードコントロール フィニッシングモーターを自作しましょう!

用意するもの

フィニッシングモーターを自作するにあたり準備した主要な部品は、Arduino-Nanoと、ステッピングモーター28BYJ48です。

5Vで駆動するので、たいへん使いやすいステッピングモーターは、これまでも様々な自作品で活躍してくれました。

そのほかに必要なのは、モーター類を収納するプロジェクトボックスと可変抵抗(ボリューム)、そして配線に必要な電線やハンド付けキットなど、電子工作好きなご家庭には常備されていそうなものです。

配線

回路図をFritzingで作成してみました。

自作 フィニッシング モーター 回路図

はんだ付けが大変そうに見えますが、既製品のステップモーター駆動ボードを使えば、自分で配線するのは左半分だけになるので、ずいぶん楽ですよ~

無段変速フィニッシングモーターの回路

上の写真は、回路図とピン番号が少し違いますが、はんだ付け作業中のミステリーということで、ご理解とご容赦ください。

組み立て

電源は、5Vで数アンペア流せられれば大丈夫です。

私は家庭用コンセントを使いたいので、AC/DCコンバーターも収納するために、少し大きめのプロジェクトボックスを用意しました。

自作フィニッシングモーターの中身

Arduinoやコンバーターは、ホットメルトでボックスに固定しました。

ボックスの上面に、モーターと可変抵抗を取り付けて、本体が完成!

そして、釣り竿を固定する部分ですが、悩んだあげくに、

「その都度、マスキングテープで釣り竿を固定できれば良しとしよう~」

と決めました。

そこで、マスキングテープで固定しやすいように、ペットボトルのキャップとその付近をつけておきます。

ロッドのフィニッシングモーターを自作する

もっとスマートな方法が見つかったらバージョンアップするとして、当分はこれでいきます~

実使用

それでは、フニッシングモーターを動かしてみましょう~

実際に使っている途中で、エポキシ塗装をしている風景を撮影してみました。

youtu.be

可変抵抗で無段変速できて、正逆転も選べるようにしておいたので、なにかと便利です。

ちなみに、ボリューム調整は、

  • 中央で停止
  • 左右に動かすとその方向に回転しはじめて、徐々に速く

という仕様になっていきます。

硬化中は、最大速さよりも少し遅くした状態で、半硬化する5時間を目安に回転させ続けます。

使用したステッピングモーター28BYJ-48は、作動音が静かなので、長時間お部屋で動かしていても気になりません。

超有名メーカーの富士工業さんから美しい既製品を入手できますが、自作した無骨な物もいいじゃない!

<ご参考>

最後に、可変抵抗(ボリューム)1個で、回転方向とスピードをコントロールするスケッチ(プログラム)を紹介させていただきます。

なお、記事内の実物写真は、少し回路を変更しています。

このスケッチは、記事内の回路図どおりに配線した場合のスケッチとなります。

int motorPin1 = 4;    // Blue   - 28BYJ48 pin 1
int motorPin2 = 5;    // Pink   - 28BYJ48 pin 2
int motorPin3 = 6;    // Yellow - 28BYJ48 pin 3
int motorPin4 = 7;    // Orange - 28BYJ48 pin 4
int volumePin = 3;
long motorSpeed;
int motorSpeedtimes = 40;

int lookup[8] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001};

void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
  Serial.begin(9600);
}

void loop() {
   if( analogRead(volumePin) < 482 ) {
    motorSpeed  =  (512 - (482 - analogRead(volumePin))) * motorSpeedtimes ; // change into speed
    anticlockwise();
   }
   else if( analogRead(volumePin) > 542 ) {
    motorSpeed  =  (512 - (analogRead(volumePin) - 542)) * motorSpeedtimes ; // change into speed
    clockwise();
   }
   else {
    digitalWrite(motorPin1, LOW);
    digitalWrite(motorPin2, LOW);
    digitalWrite(motorPin3, LOW);
    digitalWrite(motorPin4, LOW);
   }
}

void anticlockwise()
{
  for (int i = 0; i < 8; i++)
  {
    setOutput(i);
      delayMicroseconds(motorSpeed) ;
}
}
void clockwise()
{
  for (int i = 7; i >= 0; i--)
  {
    setOutput(i);
      delayMicroseconds(motorSpeed);
}
}
void setOutput(int out)
{
  digitalWrite(motorPin1, bitRead(lookup[out], 0));
  digitalWrite(motorPin2, bitRead(lookup[out], 1));
  digitalWrite(motorPin3, bitRead(lookup[out], 2));
  digitalWrite(motorPin4, bitRead(lookup[out], 3));
}

お部屋で焚き火!なフロアランプver.2

 

お部屋で焚き火!

皆さまキャンプ楽しまれていますか?

キャンプの醍醐味といえば焚火ですよね~

そんなキャンプの雰囲気をお部屋に持ち込んでみましょうシリーズのパート2として、丸いフロアアンプに焚火を閉じ込めてみました。

丸いフロアアンプに焚火を閉じ込めてみました

キャンプ場に瞬間移動

今回お部屋に追加した、自称「お部屋でキャンプファイヤー」は2作目です。

以前に作ったフロアランプの全貌は、こちらです。

ランダムな炎の雰囲気をLEDで表現したもので、暖炉がない部屋ですが、炎に見とれる至福の時間を与えてくれます。

スイッチをオン・オフするだけの手軽さも、めんどくさがりの私にピッタリ!

このフロアランプは、お部屋の片隅で夜のキャンプ場の雰囲気を演出してくれているのですが、使い続けているうちに、なんだか寂しさを感じるようになってきました。

ソロキャンプ好きの私といえども、焚火が一か所しかないのは物足りない...

そこで、もう一台の焚火台らしきものをお部屋にお迎えすることにします。

...と決意してしまうと、自作したい熱が盛り上がってきました!

炎の住処

炎のダンスをお部屋で表現することは決めましたが、悩ましいのが「何に炎を閉じ込めるか?」です。

以前に円筒形のものを作った時には、これだ!というフロアランプをIKEAで発見することができました。

そこで今回も、イメージに合致するものとの出会いを求めて、IKEAを散策してみました。

ショールームを抜けて、ランプコーナーに入ったところで、目に飛び込んできたのは...

IKEAのランプフード

フロアランプではなくランプフードですが、何かを訴えかけてきます。

これが床置きでも自立するなら、フロアランプにしてもいいのでは?

特に、右から2つ目の丸いフードは、そのシンプルな形に惹かれるだけでなく、以前に作った焚き火風フロアランプと材質感も同じです。

このランプフードに炎を入れて、フロアランプにしてしまいましょう!

炎を生み出す

IKEAから帰宅したら、さっそく今回のランプの肝となる「炎」を生み出す光源を作ります。

まずは、ゴソゴソと材料を準備しましょう。

Arduinoで、ランプを炎を入れる

紙の筒は、食材用ラップの芯です。

これをもう少し細く丸めなおすことで、ランプの中で直立する柱として再利用します。

テープ状のLEDと黒いボックスが、光源を作り出す部分になります。

ボックスの中には、直流5Vの電源回路と、マイコンが入っています。

Arduino プロジェクトボックス

これらの光源の作り方は、以前に作った円筒形のフロアランプと、LEDの高さを除いて一緒です。

回路図やプログラムについて、もっと詳しく知りたい!と思っていただけた方は、過去記事にて紹介しているので、ご参考にしてください~

www.solocamptouring.com

さてさて、今回のフロアランプ制作を続けます。

黒いボックスの中に、炎を作り出す制御部分を収納したら、次にラップの芯から作った柱に、LEDテープを貼り付けます。

テープをハサミで4分割して、柱に貼り付けました。

この4分割されているLEDテープを、電線を仲介してハンダ付けで繋ぎなおしたら、IKEAのランプフードの中に紐でぶら下げて、フロアランプの完成です。

入魂しましょう

それでは、ランプフードからフロアランプに生まれ変わった焚き火ランプを、点灯してみましょう!

youtu.be

どうですか?

炎っぽく見えません? 

先入観で他が考えられなくなるタイプの私には、焚き火の炎と同じに見えます!

炎パターン以外にも、いろいろな光り方のプログラムを入れておいたので、気分に合わせて変えられます。

予期せぬ発見ですが、丸いランプなので、惑星のような光り方も良い雰囲気になります。

お部屋での気分は、キャンプ場から宇宙へと旅立ちました〜

しばし丸い光を楽しんだのちに、せっかくなので筒状のフロアランプとならべてみました。

火みたいに光るフロアライト

この統一感、思惑通りです。

なんだか親子みたいですね〜

赤色の炎も良いのですが、青色の炎もお気に入りで、お部屋が深海のような雰囲気になります。

お部屋にいながら、キャンプ場から宇宙にも深海にも連れていってくれるランプって、凄い!

...と自画自賛。

ミニチュア キャンプ場

それでは! 完成したフロアランプを配置した、キャンプなお部屋を紹介してみます。

焚き火風でキャンプなお部屋

「アウトドアの雰囲気を、お部屋で再現」をコンセプトにしていますが、自分の中に「やりすぎは良くない」と訴えるものがあり、ほどほどにアウトドア感をだしています。

つまり、部屋でテントを張るようなことは、自主規制!

そのため、キャンプ用のチェアが常設されている他には、森の中にいるような感覚を誘うために、観葉植物とフェイクグリーンのスクリーンを置いているにとどめています。

それでも、炎のフロアランプのおかげで、夜にはジャングルな雰囲気に一変します。

わたしの部屋

光の視覚効果は大きいですね〜

キャンプでの焚き火時間のように、飲み物を片手に、リラックス姿勢で沈思黙考できる空間です。

あえて足りないものをあげるとしたら、大自然の中で感じる空気の流れ、つまり風の揺らぎです。

...と気付いてしまうと、もう風のことが頭から離れない...
焚き火の雰囲気を楽しみながら、部屋で自然風を再現する方法について、まったりと考えてみます。

良い案を思いついたら、次作「お部屋でキャンプの風」編に続けますね。

IKEAのライトを観葉植物用に改造する! WS2812Bで超簡単に電子工作

こんにちは。

観葉植物を育て始めました!

f:id:solocamptouring:20220109152720j:plain

...と、唐突に電子工作とは程遠いご挨拶になってしまいましたが、一人暮らしの部屋を賑やかにしたくて、モンステラを部屋に迎えました。

これまで植物には全く興味がなかったのですが、モンステラに癒される日々から栽培の楽しさに目覚めてしまい、現在はセロームの水耕栽培にも挑戦しています。

元気な植物たちですが、成長スピードは普通なのか?が分からず、最近になって、

「もしかしたら、(部屋の窓の大きさや位置からすると、)明るさが足りていないのでは?」

と不安になってきました。

そこで今回は、Arduinoを使って観葉植物の育成ライトを作っちゃいましょう〜!というお話です。

IKEAのライトを観葉植物用に改造する! WS2812B

観葉植物 育成用ライトの波長

さて、植物を室内で育てる場合、どのような色(=波長)の光を当ててあげるといいのでしょうか?

ネットで検索をしてみると...

  • 光合成には赤色
  • 成長には青色

の波長の光が好影響するようです。

つまり、

  • 養分は赤色の光で作られる
  • 葉や茎は青色光の方向を向く

ということ!

そうであれば、赤色と青色の光を同じ方向(一般的には上側)から照射するのが、最も効果的だと思われます。

市販の植物育成用ライトで白色光のものは、 の3色が混ざっている筈なので、有効だということですね。

LEDの光なら波長は一定

色と色のピンポイントの波長の光」で植物育成ライトを作るとなると、うってつけの物があります。

WS2812Bで植物観察用ライトをつくる

いつも電子工作でお世話になっているRGB-LED、つまり光の三原色である それぞれの光の強さを調節して出すことで、遠目にはほぼ全ての可視光色を表現できるLEDです。

これを使えば、赤色と青色(つまり遠目には紫色)を重点的に発光させられるだけでなく、その割合を変えることも可能です。

そんなに細かく調節するの?と自問自答しつつも、「そろそろ電子工作をしたい!」という欲求も満たされることですし、とにかくRGB-LEDを使って、自分だけの植物育成ライトを作ってみましょう。

お部屋の雰囲気に合うライトスタンドを探す

植物育成ライトを作るにあたり、そのベースになるライトスタンドを探します。

いつもお世話になっているIKEAに来ましたよ〜

私が狙っているのは、関節付きのデスクライト

PIXERアニメーションの本編前に飛び跳ねているような、アレです。

IKEAにあることは、前週の定例パトロールでわかっていたので、売り場に直行して入手しました。

大きいフードは、私の部屋の雰囲気には合わない気がするので、使いません。

それでは、このデスクライトを植物育成ライトに変貌させましょう。

お洒落な植物育成ライトに仕立てます

こちらも、いつもお世話になっているものばかりですが、個別制御できるカラーLEDのWS2812Bと、Arduinoを使って、色を変えられる光源を作ります。

発光色のパターン

今回作るものは、ボリュームのツマミを回すことで、

  • 明るさ

を、自分好みに選択ができるようにします。

さらに色については、

  • 白色
  • フルカラーで好みの色
  • 自動で徐々に変化するフルカラー

から選択できるように、プログラム(スケッチ)を作ってみます。

植物育成ライトの回路図

RGB LEDのWS2812Bは5Vの直流電源で光らせることができます。

Arduinoも5Vで動かせるので、電源を共有する回路にしておきます。

他に使うものは、明るさと色を変えるための2個のボリュームだけ!という、大変シンプルな回路図...というより、もはやただの配線図です。

WS2812BでIKEAのライトをカラフルにする回路図

回路図は、直感的な操作でサクサク書けるFritzingで作成しました。

植物育成ライトの組立て

ArduinoとWS2812Bで観葉植物用のライトを自作 

今回の作業は、IKEAのデスクライトにLEDを移植するだけなので、ハンダ付けが主な作業になります。

作業風景を動画で撮っておいたので、よろしければご覧ください。

youtu.be

途中でハンダ付けを失敗して修正したりと無駄な作業もしていますが、暖かく見守っていただければ幸いです。

完成した植物育成ライトは、育成効果があるか確認できていませんが、植物鑑賞ライトとしては、我ながら上出来です。

お部屋が一段と妖艶な雰囲気になりました。

観葉植物をライトアップしたい!

植物育成ライトのプログラム(スケッチ)

最後に、ご参考でArduinoのプログラム(スケッチ)を紹介させていただきます。

GitHubのFastLEDライブラリを使っています。

#include "FastLED.h"

#define DATA_PIN    5
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 20
#define WIDTH 1
#define NUM_LEDS HEIGHT*WIDTH
#define MAX_BRIGHTNESS 164      // Full is 164
#define MIN_BRIGHTNESS 41       // 25% is better
#define FRAMES_PER_SECOND  10

CRGB leds[NUM_LEDS];
CRGBPalette16 gPal;

int val = 0;
int BRIGHTNESS = 50;
int blightPin = 3;
int selectPin = 4;

void setup() {
  delay(1000); // 3 second delay for recovery
  // tell FastLED about the LED strip configuration
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
}

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = {allWhite, allColor, allRainbow};

uint8_t gCurrentPatternNumber = 0;  // Index number of which pattern is current
uint8_t gHue = 96; // from 0 to 255
uint8_t colorSelect = 0; // from 0 to 255

void loop()
{

// Data collection
  // Choose the blightness
   if (analogRead(blightPin) < 50) {
    BRIGHTNESS = 0 ;
   } else {
    BRIGHTNESS = map(analogRead(blightPin), 50, 1024, MIN_BRIGHTNESS, MAX_BRIGHTNESS) ;
   } 
    FastLED.setBrightness(BRIGHTNESS); 

  // Choose the pattern
   val = analogRead(selectPin); //check the volume to choose the pattern
      if (val < 50) {
    gCurrentPatternNumber = 0;
      } else if (val > 949){
    gCurrentPatternNumber = 2;
       } else{
    gCurrentPatternNumber = 1;
    colorSelect = map(val, 50, 949, 0, 255) ;
 }
    gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array

// End of data collection

  // set master brightness control
  FastLED.setBrightness(BRIGHTNESS);

  // Call the current pattern function once, updating the 'leds' array
  gPatterns[gCurrentPatternNumber]();

  // send the 'leds' array out to the actual LED strip
  FastLED.show();  
  // insert a delay to keep the framerate modest
  FastLED.delay(1000/FRAMES_PER_SECOND); 

  // do some periodic updates
  EVERY_N_MILLISECONDS( 500 ) { gHue++; } // slowly cycle the "base color" through the rainbow
}


void allColor() 
{
  // all led selected color
  fill_solid( leds, NUM_LEDS, CHSV( colorSelect, 255, 192)); 
}


void allWhite() 
{
  // all led white color
  fill_solid( leds, NUM_LEDS, CRGB::White); 
}

void allRainbow() 
{
  // FastLED's built-in rainbow generator
  fill_rainbow( leds, NUM_LEDS, gHue, 1);
}

それでは観葉植物と一緒に、リラックス時間を楽しみましょう!

モンステラをライトアップ

Arduinoな生活を、少し紹介してみます

こんにちは。

急に寒くなりましたが、皆様いかがお過ごしでしょうか?

暖かくしてご自愛くださいね。

...と、前置きと文末が同時に来たかのような挨拶になってしまいましたが、今回は、どうしても皆様にご報告とお礼を申し上げたくて、このブログ内としては少し異質な内容の記事を、いつもより少ない写真とともに紹介せていただきます。

Arduinoで楽しんでいて良かった!

「ソロで楽しむ」ブログ

本題に入る前に、少しだけこのブログについて紹介させてください。

この「ソロで楽しむ」は、私が人生で初めて開設したブログです。

大勢の人前で喋ることが不得意の私が、ブログを始めるにあたっては、自分には、皆様のお役にたてて頂けるような情報の質や量が、充分にあるのだろうか?という不安がありました。

一方で自分には、割と深く没頭している趣味が複数ある!というのも事実で、全部あわせれば、お役立ち情報も少しはあるのでは?とも思われます。

そこで、全ての趣味を楽しむ中で、

  • 調べたこと
  • 気付いたこと
  • 工夫したこと

について、余す事なく記事にしてみましょう!という方針を決めて、キャンプや乗り物、工作と幅広く紹介させていただくことにしました。

...となると、悩ましいのがブログタイトルです。

全ての趣味に共通していて、内容をイメージしていただけそうな言葉...

と考えていたら、

そういえば、お一人様でも楽しめる趣味ばかりですね!

ということに気付き、一人で苦笑いした結果、ひねりを入れることもなく、「ソロで楽しむ」というタイトルになりました。

電子工作も楽しむ

このブログで紹介させていただいている趣味のうちのひとつが、電子工作です。

売ってないものは、作りましょう!をモットーにしているので、電子工作自体も好きですが、どちらかというと、作ったものを日常生活で使っている時に、幸せを感じています。

そのため、ブログ内で紹介している工作物のほとんどが、別の趣味とリンクしています。

たとえば、キャンプで夜のテント周りを、アウトドアの雰囲気に調和して彩る照明なんてものも作りました。

テント周りのお洒落な照明が欲しい

お問い合わせやお声掛けをいただきました

趣味の範疇で、お役にたてる情報を!と思いながら、記事数を増やしてきましたが、電子工作については、いくつかお問合せお声かけをいただいています。

アメリカやフランスの方からも、

「Arduinoが動かない。助けて!」

というようなお問合せをいただき、メールのやりとりだけで、必死にトラブルシュートしたことがありました。

GPSモジュールを使った作品に関してのお問い合わせでは、私が使用したものとは違うメーカーのセンサーを使用するだけで、うまく動かないことがある!ということが、長い時間と多くのメール連絡の末に判明しました。

私自身も勉強になり、最終的に解決できた時には、お互いに

「やったね!ありがとう〜」

と盛り上がりました~

フランス語でのメールやりとりは大変でしたけど...

一方で、お声かけいただいた内容では、

「弊社のプリント基板を使ってみませんか?」

など、こちらも嬉しいものばかりでしたが、中には、

「うちのお店で使うものを作ってほしい!」

と仰っていただいたことがありました。

...しかし私が作っているのは、ブログタイトルどおり、あくまでソロで楽しむレベルのものです...

心惹かれるお声かけでしたが、お店で使える耐久性などの品質を保証する作品を、ご迷惑をおかけしない期間内で作れる自信はなく、泣く泣く辞退させていただきました。

皆様に御礼申し上げます

ご紹介してきた他にも、これまで色々とお問合せいただきました。

まだまだ情報量が少なかったり、わかりづらい記事内容ゆえにいただいているお問合せかもしれませんが、お問合せいただけるほどお役にたてているに違いない!と、「超」がつくほど前向きに捉えています。

特に情報が無くて困っている方のお役にたちたいのは、ブログを始める際の目標でもあったので、とても嬉しいことです。

同じ理由から、お問合せしていただいた方だけでなく、読んでいただいた全員の方に感謝しております。

そして、あらためて感謝とともにご報告したいことがあります。

工学社様の書籍に、掲載していただきました

はい! サブタイトルの通りです。

工学社様の書籍、

「Arduinoライブラリ」で作る電子工作

の一部に、ご掲載していただきました。

気軽にArduinoを始めませんか?

立派な本ですね! 私(の記事)のような分際だと、身分不相応になっていないか心配...

とにかく、このような形で、より多くの皆様にご参考にしていただけるチャンスをいただける日が来るとは、夢にも思っていませんでした。

これも全て、私のブログにご来訪いただき盛り上げてくださった皆様のおかげです。

ありがとうございます。

多趣味の雑記ブログなので、Arduinoを日常生活で使いたいプロジェクトを思いつき、また完成したものが皆様に報告してもよいレベルにあると自信が持てた時にしか記事数が増えませんが、今後ともよろしくお願いいたします。

面白い! 作ってみたい! 欲しい!などと感じていただける作品を紹介できるように努めつつ、まだまだ続けさせていただきますね!

Thank you! Arduino

それでは皆さま、

Have a Happy Arduino Life!

テント内で焚き火!っぽいランタンの炎を楽しむ

キャンプで、そろそろ寝ようかな〜という時に、大自然に囲まれていた雰囲気とテント内の雰囲気のギャップに、少しガッカリしたことがあります。

火器が使えないテント内では、LEDの灯りなどを使うことになりますが、目に刺さるような白色に、日常を感じてしまい、焚き火の炎に照らされて醸成されていたリラックスモードが、一気に無くなってしまったように気分に...

一方で、このところキャンプに行っていないどころか、買い物だけしか外出していませんが、このような時だからこそ!ということで、「外でもお家でもキャンプ」気分を盛り上げる小道具を増やそうと思います。

そこで今回は、テント内でも使える「焚き火の炎」を作って、どこでもキャンプファイヤーの光を、手軽に楽しめるようにしてみます!

テント内で焚き火!っぽいランタンを楽しむ

 

ランタンを見つめ続けた結果!

キャンプ用のLEDランタンは、停電などの非常時にも使えますよね!

私は、屋内ではいつも目につく場所に置いて、来たるべき次回のキャンプに備えて時々充電したり、来てほしくない非常時に備えたりしています。

キャンプや夜釣りで活躍していたLEDランタンですが、唯一の欠点が、明るすぎることです。

キャンプ用LEDランタンを改造します

焚火などの穏やかな明かるさに慣れた状態で、この光量を直視してしまうと、目が焼けるかのように感じます。

もっとキャンプで使いたくなるランタンにしたいと思いつつも、キャンプに行かない室内生活で、視野に入れるように置き続けていて、ふと思いつきました。

のように光らせたら、室内とキャンプのどちらでも、良い雰囲気作りができる!

少し前になりますが、フロアランプを焚き火風に光らせることができたので、それより少ないLEDを使えば良いランタンの改造は、難しくはない筈です。

早速、炎をランタンに封入してみましょう。

LEDランタンを改造

炎の光パターンを作るために、5Vで駆動するマイコンを使います。

2年ほど使い続けているランタンは、もともと白色LEDが使われており、またUSB(5V)で充電できるものなので、電源系はそのまま使えそうです。

炎のランタン作成に使うもの

まずは、WS2812Bが一定間隔で配置されているカラーLEDテープを準備しました。

WS2812Bで炎を作ります

写真には2種類のものが写っています。

今回は、少ないスペースにより多くのLEDを埋め込みたいので、外側でぐるぐるっとしているLEDの間隔が少ないものを使います。

材料として他に入手したのは、炎のパターンを作るために働いていただく、Arduinoだけです。

配線類などの副資材は、既にあるものや廃材を利用します。

LEDランタンの中身

自己責任!となるので、少しドキドキしながら、LEDランタンを開封します。

赤色の配線がプラス、青色の配線がマイナス...かと思いきや、一箇所だけ赤色のマイナス線がありますね。

(自己責任で)LEDランタンを分解
(自己責任で)LEDランタンを改造
  • 茶色の基盤;電源管理
  • 青色の円柱;リチウム電池
  • 緑色の基盤;スイッチ管理とLEDの定電流化

ということのようです。

回路の電圧を測ってみると、4V以上でている様子で、ギリギリですが思惑どおりArduinoも動かせそう!

本来であれば電圧変換すべきですが、とりあえず、オリジナルの回路をそのまま使って、様子をみましょう〜

ただし、もともとのLEDには定電流ダイオードを通して電源が供給されていましたが、ArduinoとWS2812Bには、定電流ダイオードを通さずに電源供給して、たくさん働いていただきます。

大まかな回路構成がわかったので、もともとのLEDWS2812Bに交換して、光り方を制御するArduinoを追加する作業に進みます。

ArduinoとWS2812Bを配線しましょう

LEDランタンには白色LEDが3本使われていたので、同じ長さになるように切断したWS2812Bテープ3本を、直列で配線します。

今回使うのは7個x3列の、合計21個ですね。

WS2812Bを3列に並べて使います
Arduinoをランタンに埋め込む!

ArduinoからWS2812Bへの信号線は、黄色の配線1本のみです。

他には、それぞれに電源線2本を接続するだけ!

作業の途中で、Arduinoのデジタルピンではなく、アナログピンに信号線をハンダ付けしてしまうというトラブルがあったものの、サクサクっとハンダ付けを終わらせて、準備しておいたプログラム(スケッチ)を書き込みます。

WS2812Bの炎パターン
ランタンのLEDをWS2812Bに改造

さて、電源を入れて試運転してみましょう。

...していたとおりに光っています!

いつも思うのですが、この瞬間の安堵感と嬉しさは、格別です〜

しっかり動くことがわかったので、後はランタンを組み立て直すだけですが、より炎っぽさを表現するために、シェード様のものを追加しておきます。

LEDの点光源感がバレてしまうと炎としては不自然なので、ボカシを入れるイメージです。

ランタンにキャンプファイヤーを封入

シェードには、よくある梱包材を廃材利用しました。

右の写真はシェード1枚での発光の様子ですが、まだ点光源感が残っていたので、左の写真のように二重のシェードを入れることにしました。

焚火風ランタンを自作

完成状態での点灯チェック!

本物の炎のようで、しばらく見入ってしまいました。

光り方は、動画でもご確認いただけます。

回路

今回つくる回路は、一か月ほど前に作った光ファイバーアートの時と、WS2812Bの個数を除いて全く同じです。

www.solocamptouring.com

回路図は、こちらで紹介しています。

WS2812Bは、配線数が少なくていいですね!

作業風景は、早送りですが動画でも紹介しています。

youtu.be

スケッチ

ご参考で、Arduinoのスケッチ(プログラム)です。

WS2812Bを使うため、GitHubのFastLEDライブラリを使用させていただきます。

炎の発光は、Fire2012WithPaletteのスケッチを応用しました。

#include "FastLED.h"
#define DATA_PIN    5
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 7
#define WIDTH 3
#define NUM_LEDS HEIGHT*WIDTH
#define lighting_PER_SECOND 30 // Mainly for fire frame pattern

CRGB leds[NUM_LEDS];
CRGBPalette16 gPal;

int BRIGHTNESS = 20;
int scaleVol = 230; // Scale the heat value from 0-255
int SPARKING = 220; //50-200 out of 255
int COOLING = 50; // 20-100: Less = taller flames.  More = shorter flames. 

bool gReverseDirection = false;

void setup() {
  delay(1000); //delay for recovery
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
}

void loop()
{
  gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(6);
      int y ;
      if(x > 3){
        y = 2*HEIGHT-4+x;
      } else if(x > 1){
        y = 2*HEIGHT+1-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else{
          pixelnumber = (3*HEIGHT-1) - j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }

// Let's light them now
  random16_add_entropy( random());
  FastLED.show();  // send the 'leds' array out to the actual LED strip
  FastLED.delay(1000 / lighting_PER_SECOND);
// End of lighting
  
}

以前に作ったフロアランプは、WS2812Bを4列配置しましたが、今回のランタンは3列です。

また、今回は発光パターンを赤色の炎だけに限定したので、ランタンのスケッチは、フロアランプに比べて、たいへん短くなっています。

最後に、ランタンとフロアランプの炎を並べて、記念撮影

どちらも、なかなか良い雰囲気で、他の明かりを消して、室内で楽しんでいます!

夜に外から見たら、怪しいと思われているかもしれませんね。

WS2812Bで、炎パターンで光るライトを自作

光ファイバーの夜景アートが欲しい!

前回に引き続き、お部屋で楽しむノスタルジーシリーズです。

きらきらと、小さな光点が点滅したり色を変える夜景のアート、数十年前に流行りましたよね!

思い返せば、その頃から自作好きだったようで、夜のブルックリン橋のポスターを買ってきて、背景のビルの窓に、光ファイバーを一本づつ埋め込んだな〜と思い出します。

最近になって、この夜景アートが欲しくなり、ネットなどで調べているのですが、なかなか見つかりません。

薄型ディスプレイが出回っている昨今では、流行らないのでしょうか?

入手しづらいとわかると、あの幻想的にも感じた光が、どうしても欲しい!との思いが、なおさら増してきます。

またまた、無いものは作るしかない!ということで、お部屋で過ごす時間を使って、自作してみます。

Fiber optic picture DIY

夜景のポスター

今回の「光る夜景アート」のDIYにあたり、最初にベースとなる夜景のポスターを探してみました。

...しかし、私が探しているA3サイズの大きさで、感性を揺さぶるようなポスターは、なかなか見つかりません。

空が真っ赤に染まっているような夕焼けの景色はあるのですが、どちらかというと日が沈み切った後の景色が欲しい!

まあ、慌てずに探しましょう~と、1か月ほど構想を温めていた頃、普段から行くことがあるホームセンターの奥の方で、一枚の絵に目が止まりました。

タイで見つけたオーロラのアートフレーム

何、このオーロラの発色? 綺麗すぎます!

夕焼けに染まる街のアートフレームもありましたが、このグラデーションがかかったグリーンの空から、目が離せません。

10分ほど絵を眺めてつつ妄想にふけっていて、いい事を思いついてしまいました。

このオーロラをで光らせたら、おもしろそう!

お手頃価格で、DIYにちょうどいい大きさのこのアートフレーム、即購入しました。

ちなみに、このアートフレームは時計です。

オーロラのアートフレーム

オーロラが発生している夜空に、くっきりと時間が表示されていて、少し違和感がありますね。 

お家に帰ってきてアートフレームの中身を確認した時に、時計機能は撤去することにしました。

アートフレームの中のデジタル時計

時計を埋め込むのにちょうどいい入れ物が見つかったら、時計ユニットは再生いたします。

街の夜景とオーロラを同時に楽しむ

一目惚れしたアートフレームには、山の上空のオーロラに照らされて、海沿いの人里が描かれています。

漁師町といった雰囲気でしょうか? 私の勝手なイメージでは、カニ漁の漁船が係留されている筈です!

今回は、LEDから光ファイバーケーブルで導光して漁師町をライティングすると共に、オーロラはLEDの光を裏から当てます。

もうすっかり使い慣れたカラーLED、WS2812Bが25個直列で繋がったテープを使って、最初の3個は漁師町、残りの22個はオーロラ用とします。

ARDUINOで、ファイバー ライト ピクチャーを自作

山の影を作りましょう

アートフレーム全体は、キャンバス生地の裏側から光らせます。

夜空に、山の稜線が漆黒の姿を映すようにしたいので、山の輪郭に沿って、板を入れておきます。

この板は、光ファイバーケーブルの光源を取り付けるスペースとしても、活躍してもらいます。

街の夜景

街といっても、漁師町なので、派手すぎず、かといって寂しくは感じない程度の光りをつくります。

光源となるLED3個は、ランダムなタイミングで、それぞれの色を変えていきます

数えてはいませんが、たくさんの光ファイバーケーブルの端末を、町の光りとして埋め込んで、反対側の端末は、LED3個の光源近くに束ねました。

光ファイバーケーブルで光るアートを作りましょう

夜空に揺れるオーロラ

アートフレームの上方に22個のLEDを並べて、徐々に色を変えつつ、光量のゆらぎを作ります

個々のLEDごとに色と光量を制御できるWS2812Bのメリットをいかした表現で、実際のオーロラをイメージして、光量のゆらぎの大きさ発生頻度などをトライ&エラーで決めます。

瞬く星も、この光源を使って、数個つくっておきました。

WS2812B LEDでアートを光らせる

回路図

回路と言っても、ものすごく簡単なものです。

手動で光量や色を変更することも無いので、可変抵抗(ボリューム)も使いません。

回路図はFritzingで作成しました。

LED lighting circuit for Fiber light picture

回路の作成シーンや、アートフレームの改造風景は、動画でも紹介しています。

youtu.be

参考;夜景アートを光らせるArduinoこスケッチ(プログラム)

簡単な命令文で、綺麗な光を演出できるGitHubのFastLEDライブラリを使わせていただきます。

おかげさまで、随分と短いスケッチになりました。

#include "FastLED.h"
#define DATA_PIN    5
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 25              //  aurora 22 + village 3
#define WIDTH 1
#define NUM_LEDS HEIGHT*WIDTH
#define lighting_PER_SECOND 10 // aurora pattern changr per sec.

CRGB currentleds[NUM_LEDS];
CRGB leds[NUM_LEDS];
int BRIGHTNESS = 160; //out of 255
int auroraSparkMax = 90; // aurora spark volume 0-100 out of 255 -BRIGHTNESS
int auroraSparkMin = 60; // aurora spark volume 0-100 out of 255 -BRIGHTNESS
static uint8_t heat[NUM_LEDS]; // Array of temperature readings
uint8_t gHue = 1; // starting color out of the patterns 0to255  


void setup() 
{
  delay(1000); //delay for recovery 
  FastLED.addLeds<LED_TYPE, DATA_PIN, GRB>(leds, NUM_LEDS);
}

void loop()
{
 unsigned long startMillis= millis();  // Start of sample window
 FastLED.setBrightness(BRIGHTNESS); 
 fadeToBlackBy( leds, NUM_LEDS, 200);
 
// make aurora and village light
  // aurora each cell heat slides
  EVERY_N_MILLISECONDS( 200 ) {
    for( int i = NUM_LEDS-1; i > 3; i--) {
     heat[i] = heat[i-1]  ; 
     }
     heat[3] = random8(auroraSparkMin, auroraSparkMax) ;
  }
     for( int j = 3; j < NUM_LEDS; j++) {
     leds[j] += CHSV( gHue + j, 255, heat[j]);
    }


  // village color

  EVERY_N_MILLISECONDS( 100 ) {
    for( int j = 0; j < 3; j++) {
    heat[j] = BRIGHTNESS  ;   
     int ColorChange = random8(250);
     if (ColorChange < 1){
     uint8_t gHueVillage = gHue + 127; 
     leds[j] += CHSV( gHueVillage, 255, heat[j]);
     currentleds[j] = leds[j] ;
     }else{
     leds[j] = currentleds[j] ;
     }
     uint8_t colorindex = scale8( heat[j], 255);
     }
  }
  
// Let's light them now
  FastLED.show();  // send the 'leds' array out to the LED strip
  FastLED.delay(1000 / lighting_PER_SECOND);
  
  EVERY_N_MILLISECONDS( 500 ) { 
    gHue++ ;
  }
  
// End of lighting

}

オーロラと街の光は、どちらも徐々に色を変えていきますが、意図的に違う色になるようにしています。

どちらかが緑色(に近い色)の時は、もう一方は紫色(に近い色)といったイメージです。

ファイバーライトピクチャーの自作

夜景アートというと、大都会の景色を連想しますが、偶然出会ったオーロラの景色に一目惚れしたことで、今回は一風変わった夜景アートを作ることになりました。

私としては、一本のLEDテープを光源に使い、オーロラと街の夜景を上手く表現できたと感じています。

このところ続いているお家で楽しむシリーズですが、今回は、懐かしさが漂うファイバー ライト ピクチャーを自作してみました。

スペースレールのモーター音を静かにしたい!

Light up the SPACERAIL

こんにちは。

皆様は、お家時間をいかがお過ごしでしょうか?

私は最近、お家に籠っている時間を使って、

スペースレールという知育おもちゃを、少しだけ改造して組み立てました!

「そういえば昔に持っていた!」と、懐かしく思い出される方も多いと思いますが、おそらくそれは、1983年くらいから5年ほど販売されていたスペースワープですね!

私も相当昔にスペースワープを組み立てたことがありますが、大人になった今回は、アップデートされて蘇ったスペースレールに、少し改造を織り込んで作成してみます。

お部屋のオブジェとして動かし続けるには、どうすれば良い?という観点で、小技も繰り出します!

SPACERAILのモーターの音を静かにしましょう!

スペースレール(SPACERAIL) 

スペースレールは、上の写真のようにレールを組み立てて、直径1/2インチ(1cmくらい)のボールを、上手く転がそう!という、パズル的な要素がある玩具です。

組み立てキットは14種類あり、難易度レベルが9段階で付与されています。

組み立て時間は、最も簡単なもので1時間、最高難易度のものだと60時間くらいかかるらしいとのこと!

スペースワープの時代には、レベル3に相当するものを組み立てたと記憶していますが、今回はレベル4.1に挑戦します。

大人の楽しみ方

さて、お部屋のオブジェにしましょう!というコンセプトで作るスペースレールは、スペースワープ時代の記憶を辿ると、2点ほど改良したくなります。

  1. エレベーターからボールが落ちることがある
  2. エレベーターを動かしているドライブユニットの音が大きい

f:id:solocamptouring:20210725161634j:plain

1 は、エレベーターの途中にあるボールが、他のボールがエレベーターに突入してくる時の衝撃で落ちることが多かったと思いますので、なんらかの落下防止ガードを付けたいと思います。

については、手の込んだ改善が必要だと思われます。

DCモーターと、その動力をエレベーターに伝えるギアの双方が、ウィ~ンという大きめな音を発生する原因となっている様子...

Inside the drive unit of Spacerail

静かな部屋で動かし続けるためには、少し耐えがたいドライブ音は消してしまって、ボールが転がる音と、エレベーターにボールが吸い込まれる瞬間の音だけにしたい!

静かに動かすのであれば、ステッピングモーター化がお手軽でしょう!ということで、モーターの種類を変えて、静音化してみます。

ステップモーター :  28BYJ-48 

ステッピングモーターは、速度(回転数)や位置を制御しやすいので、3Dプリンターにも使われていますね。

今回使うステッピングモーターは、ドライバーとセットで売られていることが多い28BYJ-48です。

ステップモーター 28BYJ-48をSpacerailに仕込む

上の写真で、右に写っているのが、そのステッピングモーターです。

スペースレールのドライブユニットのハウジングに収まりそうな、ちょうどいい大きさですね。

ドライバーとセットで安価に入手できる28BYJ-48は、マイコンボードで簡単に動かせます。

私がよく使うマイコンッボードは、Arduino Nanoという、小さいタイプのものです。

少しArduinoに興味がでてきた!という方は、使い方などを纏めている記事があるので、よろしければ参考にしてみてください。

www.solocamptouring.com

早速、必要な部品を入手して、ステッピングモーター化に取り組みます。

モーター交換のためのハンダ付け作業や、ハウジングの加工を終えて、静か~に動くドライブユニットが完成しました。

スペースレールを、ステップモータで動かす
Spacerailの、静かに動くドライブユニットが完成

作業の様子は、早送りですが、動画内で紹介しています。

ステッピングモータを動かす回路図

Fritzingで作成した回路図を紹介いたします。 

作るのが大変そうに見えるかもしれませんが、回路図の右半分は、ステッピングモータと、そのドライバなので、既製品です。

この回路では、USB接続の5V電源を使います。

ハンダ付け作業も少なく、配線をつなぐと、下の写真のような状態になります。

Arduinoとスッテプモーターで、スペースレールを動かす

スペースレールを組み立てましょう

ドライブユニットの試運転して、満足できるレベルの静音になっていることを確認したら、いよいよスペースレールを組み立てます。

説明書を見ながら、まずは支柱を立てていきます

スペースレールを組み立てます
Spacerail installation

1時間以上かけて、支柱の作業を終えました!

次に、レールを切ってから、取り付けていきます

説明書で指示されているレールの長さは少し余裕を持たせているようで、実際に装着した後で余った部分をさらに切断します。

ちなみに、同経路のレール左右でも、余る長さが違ってきます。

スペースレールのレール取付け
Install rails on the Spacerail

2時間以上かけて、レールも完成しました。

途中で、ボールを試走させながらレールを微調整しているので、予想以上に時間がかかります。

これらの作業と、静かに動くドライブユニットの様子は、動画でもご確認いただけます。

youtu.be

ほとんどの作業を早送り再生しているので、動画内では超高速で完成していますね。

エレベータータワーから落ちてしまうボールの対処方法

エレベータの中間地点を登っているボールが、他のボールがエレベータに突入してきた時の衝撃で落ちてしまうことがあります。

もう少ししっかりとボールを固定してくれる設計ならいいのに~」と言っていても仕方がないので、衝撃が加わってもボールが落ちないようにする手立てを考えます。

エレベータータワーから落ちてしまうボールの対処方法

これ、何かわかりますか?

はい! 食品を包んだりする時に使う、台所用ラップの芯です。

らせん状に切れ目を入れて、エレベータータワーの長さ(高さ)に合わせて切断したら、すぽっと入れるだけでボールの落下を防止できます。

ちょうどいい直径のアクリルパイプがあれば、もっと完成度が増すと思われますが、ラップの芯でも、それほど違和感はありません。

お家に在りそうな物での対策としては充分でしょう!

ボールの落下がなくなり、モーターもほぼ無音になったので、お部屋の置き物として、長時間動かし続けられるものになりました。

けっこう時間がかかりましたが、組み立て作業も楽しかった!

今日も、スペースレールの前に椅子を置いて、ボールの動きを目で追い続けています。

さて、次は何を作ってみようかな?

おまけ その1; ライトアップしてみました

テーマパークのジェットコースターを思い起こさせるスペースレールですが、さらに無事故(落下しない)かつ静音化して、オブシェとして動かし続けることに成功しました。

お部屋で眺めていて、もう少し幻想的にしたいと思い、ライトアップしてみました。

その様子を、約15分間にわたり録画してみます。

youtu.be

録音した音は、全く手を加えていないので、ボールの音だけが聞こえる様子をご確認いただけると思います。

おまけ その2;ステッピングモータを動かすスケッチ(プログラム)

ご参考で、スケッチを紹介いたします。

ステッピングモーターのドライバーに対して、コイルに順番に電気を流しなさい~と命令しているだけの、簡単なスケッチです。

int motorPin1 = 4;    // Blue   - 28BYJ48 pin 1
int motorPin2 = 5;    // Pink   - 28BYJ48 pin 2
int motorPin3 = 6;    // Yellow - 28BYJ48 pin 3
int motorPin4 = 7;    // Orange - 28BYJ48 pin 4
int volumePin = 5;
int motorSpeed;
int motorSpeedtimes = 2;

int lookup[8] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001};

void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  motorSpeed  = 1024 * (motorSpeedtimes + 1) - (analogRead(volumePin) * motorSpeedtimes) ; //0to1023 change into speed
  clockwise();
}

void anticlockwise()
{
  for (int i = 0; i < 8; i++)
  {
    setOutput(i);
      delayMicroseconds(motorSpeed) ;
}
}
void clockwise()
{
  for (int i = 7; i >= 0; i--)
  {
    setOutput(i);
      delayMicroseconds(motorSpeed);
}
}
void setOutput(int out)
{
  digitalWrite(motorPin1, bitRead(lookup[out], 0));
  digitalWrite(motorPin2, bitRead(lookup[out], 1));
  digitalWrite(motorPin3, bitRead(lookup[out], 2));
  digitalWrite(motorPin4, bitRead(lookup[out], 3));
}

それでは、皆さま良いお家時間をお過ごしください!

お家でエレクトリカルパレード

いつでも正確な時計を自作。GPSからの情報を秒単位で表示します

いつでも正確な時計を自作。GPSからの情報を秒単位で表示しますこんにちは!

皆様お元気でおすごしですか?

私は、ここのところの天候不順でアウトドアで過ごす予定がたてられない日が続いています。

しかし、そんな時こそキャンプでも使えるガジェットを自作すべき!ということで、在宅時でもアウトドアでも使える、いつでも正確な時間を教えてくれる時計を作ってみます。

いつでも時間が正確な時計を作ってみる

 

マイコン「Arduino」は難しくない!

最初に、「デジタル時計なんて自作できるのか?」問題について、少しだけ私の所感を紹介させていただきます。

マイコンを使う工作なんて無理〜と思っていた私が、Arduinoに出会ったのは、数年前です。

その頃すでに、日本語でArduinoの使い方を紹介されているサイトなどがありましたが、専門用語が多くて少し難解だったり、応用しづらかったりして、やはり基礎知識がないと無理なのかな?と最初は思いました。

海外のサイトからも情報を得たりしながら、いろいろ作ることに成功した現在、結論を言うと、「基本的なエクセルの関数計算を使うことができるなら、難しくない!」です。

Arduinoの場合は、プログラムのことを「スケッチ」と呼びますが、スケッチを描くのと同じくらい簡単ですよ!ということのようです。

表計算ソフトのエクセルで「もし〇〇ならば、△△を計算(実行)」の関数を使ったことがあれば、Arduinoスケッチの8割くらいは理解できると思います。

実際に私は、if~else~式を乱用して、スケッチを作っています。残りは数字の種類(整数、桁数)などを少し勉強すれば、だいたい理解できました。

ひとつ作品を成功させると、可能性が無限に広がったような気持ちになって、何かを表示するだけでなく、動かしたり光らせたりする作品など、いろいろ作ってきました。

もし興味がおありでしたら、見てみてください。

www.solocamptouring.com

 

人工衛星の情報で、正確な時間を入手

さて、前置きが長くなりましたが、置き時計を作ります。

最近は、腕時計でも採用されているものがありますが、地球のまわりを飛んでいる人工衛星から、正確な時間情報をいただきます

使う部品はこれだけです。

GPS時計を自作します

あとは、試作用の汎用基盤などの小物ですね。

GPSモジュールとLCDモジュールは、電力消費がそれほど多くないので、この後で紹介する回路図とは少し違って、USB接続したArduinoからの5Vアウトプットで駆動しています。

GPS時計の回路図

いつもお世話になっているFritzingで回路図を作成しました。

LCDモジュールへの配線数が多いですが、GPSモジュールへの配線はわずか3本です!

これだけで正確な時間情報がいただけるなんて、嬉しいですね。

GPS clock 1604 LCD_回路図

組み立てましょう

はんだ付けって大変!なのですが、便利な道具があると、ずいぶん楽になります。

若かりし頃は、横着なやり方もしていましたが、分別ができる年頃になってから、便利な道具に助けてもらうことを覚えました。

電線どうしのはんだ付けなど、もう一本手が欲しい~と思いながら苦戦していましたが、クリップ付きのはんだこて台があると、本当に楽です。

ハンダ付けを含めた作成の様子は、タイムラプスの動画にしてみました。

youtu.be

完成したことろで、お部屋のテレビ台に置いてみると、こんな感じです。

USBで駆動するGPS置時計

きちんと、秒単位で正確に働いています!

青色の背景に白い文字が浮かび上がっていて、雰囲気も良いですね。

ちなみに、このGPS時計は、電源を入れた後、表示するまで少しだけ時間がかかりますが、一度電波を受信した後は、今のところ表示が止まったりすることはありません。

アウトドアなら問題なく、また置時計として使うにも、電波が受信できる場所であれば、大丈夫でしょう。

GPS時計のスケッチ(プログラム) 

これまで紹介させていただいた作品の中では、おそらく最もシンプルなスケッチです。

バンコク時間に合わせたスケッチになっているので、日本時間を表示する場合は、6行目の

#define time_offset 25200 // define a clock offset of 3600*7 seconds (7 hour) ==> TH = UTC + 7

の部分を、

#define time_offset 32400 // define a clock offset of 3600*9 seconds (9 hour) ==> JP = UTC + 9

にします。 

スケッチは、TinyGPS++と、Arduino Time Librariesのライブラリを、インストールしてから使います。

#include <TinyGPS++.h>    
#include <TimeLib.h>          
#include <SoftwareSerial.h>   
#include <LiquidCrystal.h>    
TinyGPSPlus gps;
#define time_offset   25200  // define a clock offset of 3600*7 seconds (7 hour) ==> TH = UTC + 7
#define S_RX    9
#define S_TX    8
SoftwareSerial SoftSerial(S_RX, S_TX);   
LiquidCrystal lcd(2, 3, 4, 5, 6, 7); // LCD connections (RS, E, D4, D5, D6, D7)
 
// variable definitions
char Time[]  = "00:00:00";
char Date[]  = "2000/00/00";
byte last_second, Second, Minute, Hour, Day, Month;
int Year;
 
void setup(void)
{
  SoftSerial.begin(9600); 
 
  lcd.begin(16, 2); 

// welcome display
  lcd.setCursor(0, 0); 
  lcd.print("   GPS  CLOCK   ");
  lcd.setCursor(0, 1); 
  lcd.print("ARDUINO  POWERED");

  delay(5000);
  lcd.clear()  ;
}
 
void loop()
{
  while (SoftSerial.available() > 0)
  {
    if (gps.encode(SoftSerial.read()))
    {
// get time from GPS
      if (gps.time.isValid())
      {
        Minute = gps.time.minute();
        Second = gps.time.second();
        Hour   = gps.time.hour();
      }
 
// get date from GPS
      if (gps.date.isValid())
      {
        Day   = gps.date.day();
        Month = gps.date.month();
        Year  = gps.date.year();
      }
 
      if(last_second != gps.time.second())  
      {
        last_second = gps.time.second();
        setTime(Hour, Minute, Second, Day, Month, Year);
        adjustTime(time_offset);
 
// update time
        Time[6] = second() / 10 + '0';
        Time[7] = second() % 10 + '0';
        Time[3]  = minute() / 10 + '0';
        Time[4] = minute() % 10 + '0';
        Time[0]  = hour()   / 10 + '0';
        Time[1]  = hour()   % 10 + '0';
 
// update date array
        Date[2] = (year()  / 10) % 10 + '0';
        Date[3] =  year()  % 10 + '0';
        Date[5]  =  month() / 10 + '0';
        Date[6] =  month() % 10 + '0';
        Date[8]  =  day()   / 10 + '0';
        Date[9]  =  day()   % 10 + '0';
 
// show time & date
        print_wday(weekday()); 
        lcd.setCursor(4, 0);    
        lcd.print(Time);         
        lcd.setCursor(0, 1);    
        lcd.print(Date);         
      }
    }
  }
}
 
// for displaying day of the week
void print_wday(byte wday)
{
  lcd.setCursor(11, 1);  // move cursor to column 5, row 1
  switch(wday)
  {
    case 1:  lcd.print("(SUN)");   break;
    case 2:  lcd.print("(MON)");   break;
    case 3:  lcd.print("(TUE)");   break;
    case 4:  lcd.print("(WED)");   break;
    case 5:  lcd.print("(THU)");   break;
    case 6:  lcd.print("(FRI)");   break;
    default: lcd.print("(SAT)");
  }
}

本当に短いスケッチですね~

今回は、簡単に自作できるGPS時計の紹介でした。

早くキャンプに持っていきたい!

電波が届かないキャンプでも正確な時間な時計

それでは最後に、皆様がお家時間もアウトドア時間も楽しまれるよう、祈念しております。

炎も躍るサウンド レベル メーター。IKEAのフロアランプをカラフルに改造

炎のフロアランプを自作しよう

今回は、お家でキャンプ気分シリーズの、スピンオフ編です。

焚き火を眺めてすごす、キャンプでの優雅な時間を、屋内でも楽しみたい!ということで、背が高いフロアランプを、カラーLEDを使って改造して、焚き火台風にしてみます。

 

炎が躍るサウンド レベル メーター。WS2812Bでファイヤーします

焚き火とサウンドレベルメーター

アウトドアを楽しめない週末が続くと、いろいろな思いがこみ上げてきます。

  • 殺風景な部屋に、フロアランプが欲しい!
  • キャンプに行けないのであれば、部屋で焚き火気分を楽しめないかな?
  • 外出できないのであれば、しばらく封印していたDIYでもやってみようか...

こんな妄想たちを、一気に実現するには、焚き火風のフロアランプを自作するしかない!と確信しました。

テーマパークの仕掛けで見かける、本物の炎のような、フェイクな焚き火です。

ファンの風によって布をチラつかせて、さらに光も当てることで、本物の焚き火と錯覚してしまう、あれですね。

購入した方が安くすみそうな気がしますが、自作もできそうな気がします。

 

しかし、構造を考えていて、音について悩みが生じます。

くつろぎの時間を過ごしたいところで、ファンの音に耐えられるだろうか?と。

テーマパークのような、他の音が存在する場所なら気にならないと思いますが、プライベートな部屋ではファンの音は雑音でしかありません。

ファンを使わずに炎のような光を実現するには、小さな光源がたくさん必要になりそう...と考えていて、それならLEDで表現してみましょう!と、コンセプトがみえてきました。

LEDの直接光では、炎のように見せるのは難しいと思われるので、日本の伝統的なフロアライト、行燈の中にLEDたちを閉じ込めてしまいます。

行燈の中に焚き火があっても、いいじゃない!

行燈をサウンド レベル メーターに!

不規則に揺らぐ焚き火の光は、見ていて飽きませんよね〜

あの揺らぎを人為的に表現するには、乱数発生プログラムを使ってLEDを不規則に光らせることが近道だと思いました。

...でも、それだけだと不規則すぎて面白くない気もします。

相変わらずの捻くれ者ぶりを発揮してしまったので、一週間ほど悩んでみました。

時間をかければ閃くこともあるようで、ふと音楽に合わせて光らせてみたら面白いのでは?と思いつきます。

行燈の中で、炎と、音楽に合わせて光が踊っているフロアーランプ!

これは欲しい!と納得できるアイデアが浮かんだので、さっそく制作に取り掛かります。

IKEAで行燈選び

アウトドアには行けない日が続いているものの、家具屋さんのIKEAが家の近くにあり、毎週末のように通って、気分転換をしています。

そんなIKEAには、今回の行燈サウンド レベル メーターの改造ベースにピッタリのフロアランプがあることを知っています。

IKEAのフロアランプに、カラフルなLEDを仕込もう!

フードの材質が紙なので、光の指向性があるLEDとの相性もバッチリです。

いつもIKEAでは散歩スピードでショールームを楽しんでいますが、今回は売り場に直行して、フロアランプを入手してきました。

Arduinoと接続するモジュール

フロアランプを、行燈風サウンド レベル メーターとして光らせるために、個別に制御できるRGB LEDのWS2812Bをを使います。

「〇〇番目のLEDさんは、△△色に光ってください〜」

という情報さえ流せば、その通りに光るので、多くのLEDを個別に光り方を変えるのに便利なものです。

1mあたりにWS2812Bが60m個並べられているカラーLEDテープを3mと、サウンドセンサーモジュール、そして可変抵抗(ボリューム)3個を入手しました。

以前に、可変抵抗1個を使ってナイトライダー風サウンド レベル メーターを作ったことがありますが、今回はフロアランプでもあるので、

  • 光量
  • 光らせ方

の3項目を可変抵抗で選択できるように進化させます。

表示イメージとパターン

もともとは、「焚火 in 行燈」コンセプトで始めたプロジェクトですが、純粋にサウンド レベル メーター風のパターンも含めて、合計7種類の光らせ方を準備します。

 行燈なので、まずは白色で光らせます。

写真は紫色のように写っていますが、実際には白色です。

WS2812Bで行燈を改造。(白色)

 サウンドレベルのバーが立ちます。色は炎のように、オレンジのグラデーションです。

WS2812Bで行燈を改造。(サウンド レベル メーター 赤色)

 サウンドレベルのバーが立ちます。色は可変抵抗で選択した単色です。

WS2812Bで行燈を改造。(サウンド レベル メーター 好きな色)

 サウンドレベルのバーが立ちます。色は徐々に変化します。

WS2812Bで行燈を改造。(サウンド レベル メーター 色は徐々に変化)

 焚き火風に光らせます。色は、「オレンジ白色」の3色です。

WS2812Bで行燈を改造。(ファイヤー 赤色)

 焚き火風に光らせます。色は、「水色白色」の3色です。

WS2812Bで行燈を改造。(ファイヤー 青色)

 焚き火風に光らせます。色は、「可変抵抗で選択した色白色」の2色です。

WS2812Bで行燈を改造。(ファイヤー 好きな色)

 焚き火風に光らせます。色は、「徐々に変化する白色」の2色です。

WS2812Bで行燈を改造。(ファイヤー 徐々に変化する色)

回路図

Arduinoで自作するサウンド レベル メーターの回路図

Fritzingで回路図を作成しました。

マイクモジュールは、MAX4466を搭載したものを使っています。

3個の可変抵抗(ボリューム)は、

  • R1;光量
  • R2;表示7パターンの切り替え
  • R3;色

の調節(選択)をするために使います。

Arduinoを使って、LEDを炎のパターンで光らせる

Arduinoのアナログ入力には、それぞれの可変抵抗から電圧で情報が送られます。

  • 電圧; 0〜5V
  • Arduinoの読み取り値; 0〜1023(整数)

0〜1023をArduinoのスケッチ(プログラム)で選択肢に置き換えれば、LEDの発光を調節できます。

今回は180個が繋がっているものを使うWS2812Bは、先程も簡単に紹介しましたが、それぞれのLEDが個別に信号処理して、色や光量を認識して発光するので、信号線1本と電源の、合計3本の電線を繋ぐだけで、自在に光を制御できる優れものです。

WS2812Bで、炎のランプを!

スケッチは、記事の最後に掲載しておきますね。

炎が踊るフロアーランプ

LEDの配置

LEDを、ランプ中心部の柱にどう配置するか?について、少し試行錯誤しました。

  • 螺旋状に巻く?
  • 縦の列状に配置する?

結論から言うと、縦に配置した方が、LEDの貼り付け作業も、炎やサウンドレベルメーターのスケッチ(プログラム)の作成も楽です。

私は4列で配置することにしました。

WS2812B ファイヤー

180個のWS2812Bテープなので、

「45行x4列 」

になる筈ですが、試行錯誤の段階でちょっとした事故があり、数セルが使えなくなってしまったので、やむなく43x 4の配置になりました。

ウエルカム点灯

電源投入時の動作は、3種類を仕込んでおきます。

  1. ランダム点滅(白色)

    f:id:solocamptouring:20210626123315j:plain

  2. 白色光を上下に一回走らせる

    f:id:solocamptouring:20210626123321j:plain

  3. レインボー色の光を上下に一回走らせる

    f:id:solocamptouring:20210626123435j:plain

静止画だと、どれだけ綺麗に光っているか?が伝わらないですが、光り方はメイキング動画でも、ご確認いただけます。

 

youtu.be

スケッチ(プログラム)

スケッチは、色々なパターンの光らせ方が準備されている、GitHubのFastLEDライブラリをベースにさせていただきます。

炎(fire)パターンは、派生バージョンも含めていくつか紹介されており、Fire2012WithPaletteの、炎を作成する部分のスケッチを使用させていただき、4列の炎専用に少し手を加えました。

もっと短いスケッチにできると思いますが、メモリー容量に余裕があるので良い!ということにして、同じ命令文が何度もでてきます。

#include "FastLED.h"
#define DATA_PIN    5
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 43
#define WIDTH 4
#define NUM_LEDS HEIGHT*WIDTH
#define MAX_BRIGHTNESS 90      // Full is 164
#define MIN_BRIGHTNESS 5       // 25% is better
#define lighting_PER_SECOND 30 // Mainly for fire frame pattern

CRGB leds[NUM_LEDS];
CRGBPalette16 gPal;

const int sampleWindow = 50; // Sound sample window width in mS
unsigned int sample;
int SoundCenterAdjust = 0; // max 1024/2
int SoundLevelAdjust = 6; // defo and min 1 In case bar is small with the usual sound
int BRIGHTNESS = 50;
int scaleVol = 230; // Scale the heat value from 0-255
int SPARKING = 130; //50-200 out of 255
int COOLING = 70; // 20-100: Less = taller flames.  More = shorter flames. 
int colorSelect;
int micPin = 2;
int blightPin = 3;
int selectPin = 4;
int colorPin = 5;
int val = 0;
int numLedsToLight = 0;
long numLedsToLightCal = 0;
bool welcomeLight = true;
bool gReverseDirection = false;


void setup() {
  delay(3000); //delay for recovery
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
}

// List of patterns to cycle through
typedef void (*SimplePatternList[])();

SimplePatternList gPatterns = { allwhite, soundBarFire, soundBarSelect, soundBarRotate, fireRed, fireBlue, fireSelect, fireRotate};

uint8_t gCurrentPatternNumber = 0;  // Index number of which pattern is current
uint8_t gHue = 96;                  // rotating "base color" used by many of the patterns 0to255
// End of the list of patterns to cycle through


void loop()
{
// Data collection
  // Choose the blightness
   if (analogRead(blightPin) < 50) {
    BRIGHTNESS = 0 ;
   } else {
    BRIGHTNESS = map(analogRead(blightPin), 50, 1024, MIN_BRIGHTNESS, MAX_BRIGHTNESS) ;
   } 
    FastLED.setBrightness(BRIGHTNESS); 

  // Choose the color
   if (analogRead(colorPin) < 50) {
    colorSelect = 0 ;
   } else if (analogRead(colorPin) < 950) {
    colorSelect = map(analogRead(colorPin), 50, 949, 0, 255) ;
   } else {
    colorSelect = 255;
   }

  // Choose the pattern
   val = analogRead(selectPin); //check the volume to choose the pattern
      if (val < 130) {
    gCurrentPatternNumber = 0;
       } else if (val < 260){
    gCurrentPatternNumber = 1;
       } else if (val < 390){
    gCurrentPatternNumber = 2;
       } else if (val < 520){
    gCurrentPatternNumber = 3;
       } else if (val < 650){
    gCurrentPatternNumber = 4;
       } else if (val < 780){
    gCurrentPatternNumber = 5;
       } else if (val < 910){
    gCurrentPatternNumber = 6;
       } else {
    gCurrentPatternNumber = 7;      
       }
    gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array

// End of data collection


// Welcome lighting
  if (welcomeLight == true) {
  // random speckles that blink in and fade smoothly
    for(int led = 0; led < 150; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    int pos = random16(NUM_LEDS);
    leds[pos] += CRGB::White;
    FastLED.show();  
    delay (100-led/2);
    }  
    
  // white up and down
    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[led] += CRGB::White ; 
    leds[HEIGHT*2-1-led] += CRGB::White ; 
    leds[HEIGHT*2+led] += CRGB::White ; 
    leds[HEIGHT*4-1-led] += CRGB::White ; 
    FastLED.show();    
    delay (80);
    }

    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[HEIGHT-1-led] += CRGB::White ; 
    leds[HEIGHT+led] += CRGB::White ; 
    leds[HEIGHT*3-1-led] += CRGB::White ; 
    leds[HEIGHT*3+led] += CRGB::White ; 
    FastLED.show();    
    delay (80);
    }
  
  // rainbow up and down
    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*2-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*2+led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*4-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    FastLED.show();    
    delay (80);
     }
 
    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[HEIGHT-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT+led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*3-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*3+led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    FastLED.show();    
    delay (80);
    }    
    welcomeLight = false;  //End welcome lighting
  }else{

// Let's light them now
  random16_add_entropy( random());
  gPatterns[gCurrentPatternNumber]();
  FastLED.show();  // send the 'leds' array out to the actual LED strip
  FastLED.delay(1000 / lighting_PER_SECOND);
  EVERY_N_MILLISECONDS( 300 ) { gHue++; } // slowly cycle the color through the rainbow
// End of lighting
    }  
}


void fireRed() 
{
  gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void fireRotate() 
{
  gPal = CRGBPalette16( CRGB::Black, CHSV( gHue, 255, 192), CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void fireBlue() 
{
  gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua,  CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void fireSelect() 
{
  gPal = CRGBPalette16( CRGB::Black, CHSV( colorSelect, 255, 192), CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
       if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void soundBarFire() 
{

  // Sound level to lighting LED amount
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(micPin);
      if (sample < 1024)
      {
         if (sample > signalMax)
         {
            signalMax = sample;  // save the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;  // save the min levels
         }
      }       
   }
   peakToPeak = signalMax - signalMin ;  // max - min = peak-peak amplitude
   numLedsToLightCal = ((peakToPeak-SoundCenterAdjust)*HEIGHT*SoundLevelAdjust)/1024 ;
   if(numLedsToLightCal >= HEIGHT){
    numLedsToLight = HEIGHT;
   }else {
   numLedsToLight = numLedsToLightCal; // adjusting to the output of the mic 
   }
    fadeToBlackBy( leds, NUM_LEDS, 200);

  for(int led = 0; led < numLedsToLight; led++) { 
  leds[led] += CHSV( 2*(numLedsToLight-1-led), 255, 192);
  }
  for(int led = HEIGHT*2-numLedsToLight; led < HEIGHT*2; led++) { 
  leds[led] += CHSV( 2*(led-(HEIGHT*2-numLedsToLight)), 255, 192);
  }
  for(int led = HEIGHT*2; led < HEIGHT*2+numLedsToLight; led++) { 
  leds[led] += CHSV( 2*(HEIGHT*2+numLedsToLight-1-led), 255, 192);
  }
  for(int led = HEIGHT*4-numLedsToLight; led < HEIGHT*4; led++) { 
  leds[led] += CHSV( 2*(led-(HEIGHT*4-numLedsToLight)), 255, 192);
  }
}

void soundBarSelect() 
{

  // Sound level to lighting LED amount
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(micPin);
      if (sample < 1024)
      {
         if (sample > signalMax)
         {
            signalMax = sample;  // save the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;  // save the min levels
         }
      }       
   }
   peakToPeak = signalMax - signalMin ;  // max - min = peak-peak amplitude
   numLedsToLightCal = ((peakToPeak-SoundCenterAdjust)*HEIGHT*SoundLevelAdjust)/1024 ;
   if(numLedsToLightCal >= HEIGHT){
    numLedsToLight = HEIGHT;
   }else {
   numLedsToLight = numLedsToLightCal; // adjusting to the output of the mic 
   }
    fadeToBlackBy( leds, NUM_LEDS, 200);


  for(int led = 0; led < numLedsToLight; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
  for(int led = HEIGHT*2-numLedsToLight; led < HEIGHT*2; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
  for(int led = HEIGHT*2; led < HEIGHT*2+numLedsToLight; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
  for(int led = HEIGHT*4-numLedsToLight; led < HEIGHT*4; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
}

void soundBarRotate() 
{

  // Sound level to lighting LED amount
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(micPin);
      if (sample < 1024)
      {
         if (sample > signalMax)
         {
            signalMax = sample;  // save the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;  // save the min levels
         }
      }       
   }
   peakToPeak = signalMax - signalMin ;  // max - min = peak-peak amplitude
   numLedsToLightCal = ((peakToPeak-SoundCenterAdjust)*HEIGHT*SoundLevelAdjust)/1024 ;
   if(numLedsToLightCal >= HEIGHT){
    numLedsToLight = HEIGHT;
   }else {
   numLedsToLight = numLedsToLightCal; // adjusting to the output of the mic 
   }
    fadeToBlackBy( leds, NUM_LEDS, 200);

   for(int led = 0; led < numLedsToLight; led++) { 
   leds[led] += CHSV( gHue, 255, 192); 
   }
  for(int led = HEIGHT*2-numLedsToLight; led < HEIGHT*2; led++)  { 
   leds[led] += CHSV( gHue, 255, 192); 
  }
  for(int led = HEIGHT*2; led < HEIGHT*2+numLedsToLight; led++) { 
   leds[led] += CHSV( gHue, 255, 192); 
  }
  for(int led = HEIGHT*4-numLedsToLight; led < HEIGHT*4; led++) { 
   leds[led] += CHSV( gHue, 255, 192); 
  } 
}

void allwhite() 
{
  // all led white color
  fill_solid(leds, NUM_LEDS, CRGB::White); 
}

 

今回は、フロアランプに焚火を仕込みましょう!というコンセプトで、お家ですごす時間を楽しくしてみました。

毎週末を使って、製作期間は一か月ほどかかりましたが、初めて見る人には「フロアランプが燃えている!」と思わせそうな完成度で、満足しています。

WS2812Bでファイヤーパターンのフロアライト

室内で焚火はできないアパート暮らしですが、お部屋に焚き火風の光があると、本当にキャンプしているような気分になって、うっとりと見入っています。

ラジコンを超低速で動かす!ラジコン自動走行への第2歩目

もう1年以上前になってしまいましたが、トイラジコンに超音波センサーを5個搭載して自動走行に挑戦していました。

しかし、高回転型のDCモーターを使っていたため、スピードが早すぎて距離の計測が追いつかない!という問題がありました。

対策を考えながらも、しばらく放置していましたが、挑戦再開の一歩を踏み出します。

まずは、スピードを落としつつ正確に移動距離を制御ために、ステップモーターに変更してみます。

Arduinoとステップモーター28BYJ-48で、ラジコンを自動走行させてみます

前回作った自動走行トイラジコン

f:id:solocamptouring:20190105132806j:plain

以前作った自動走行ロボットの動き方を、簡単に紹介しておきます。

  • 前側3方向(0°と±約45°)を測距しながら、障害物との距離に合わせたスピードで、障害物から遠い方向に軽く操舵しながら前進します。
  • 前方が行き詰まったら、後方2方向(±約45°)を測距しながら、スペースが開いている方に少し後退して、その場で回転します。
  • 回転後は、また前方3方向を測距しながら前進します。

動力部は、トイラジコンに付属していたDCモーターを使って、PWMで速度を変えています。

<ご参考;PWMとは?>

細かくON/OFFを繰り返す波形で、電圧を落とす(変える)のと同じような効果を得るものです。

そのON時間の割合が、デューティー比です。

改造していく途中でわかってきたのですが、デューティー比が低くなる低速では、モーターのトルクも細くなります。

そのため、狙っていたような極低速では、トイラジコンが動かなくなってしまいました。

仕方なく、トイラジコンが動くぎりぎりのデューティー比のところを最低速として動かしましたが、それでもスピードが早すぎて測距が追いつかない場合があります。

その結果、障害物に当たってから後退し始めたり、180°転回を繰り返したりと、暴れん坊の自動走行になっていました。

ステップモーターで自動走行に再挑戦します

前回作った自動走行ロボットを改良して、低速域でのスピードと移動距離を、しっかり制御できるようにします。

5個の測距センサーは、前回取り付けたものを、今後もそのまま使う予定です。

ステップモーターを動かすためには、直流モーターの速度制限(PWM)とは違ったタイプのドライバーが必要になるので、制御用のハードも変更が必要となります。

今回は駆動モーターの変更に注力して作業します。

ステップモーター(ステッピングモーター)を準備します

トイラジコンのモジュール化されているギヤボックスへの改造を最小限にしたいので、トイラジコンに付属しているDCモーターとほぼ同じ大きさのステップモーターを探してみます。

トイラジコンのDCモーターの外形は、

  • モーター外径:約24mm
  • モーター長さ:約30mm

です。

f:id:solocamptouring:20190105135855j:plain

NMB PM25Lを試してみる

トイラジコンのDCモーターとまったく同じ大きさのステップモーターは探しきれなかったので、外径の大きさが近いNMB PM25Lを選択しました。

  • モーター外径:25mm
  • モーター長さ:16mm
  • シャフト径:2mm

f:id:solocamptouring:20190105140032j:plain

モーターシャフトに取り付けるギヤは、2mmのシャフト径用のものが必要です。

トイラジコンのDCモーターについていたのは9歯のメタルギヤでしたが、ステップモーター用で8歯の樹脂ギヤを入手しました。

ステップモータードライバーは、4フェーズ5線式ユニポーラ用の既製品ボードを使います。

駆動ユニットにステップモーターNMB PM25Lを組みます

トイラジコンのDCモーターと減速ギヤは、樹脂のハウジングで一体化されています。

入手したステップモーターの外形は少し大きめなので、ハウジングのモーター格納部は、躊躇せずに切断します。

f:id:solocamptouring:20190105140744j:plain

上の写真は右側の部品だけ加工した状態です。

左側の部品もバッサリと切断して、ステップモーターをつけてみました。

f:id:solocamptouring:20190105141323j:plain

NMB PM25Lは6.4Vでラジコンを動かせるのか?

ここまで深く考えずに作業してきましたが、NMB PM25Lはユニポーラ12V駆動です。

トイラジコンの減速ギヤをそのまま使っているので、6.4Vでも動くのではないかと期待していました。

結果はあっけなく撃沈で、トイラジコンを動かせるほどのトルクは出せませんでした。

やはり電圧に合ったモーターを選ばないといけないと反省して、次の一手を考えます。

28BYJ-48を使ってみる

f:id:solocamptouring:20190105150058j:plain

駆動ユニットにステップモーター28BYJ-48を組みます

さて、気を取り直して、いつも使っているステップモーター28BYJ-48に、今回も活躍してもらうことにします。

減速ギヤ一体式のモーターなので、5~6.4Vでラジコンを動かせることは間違いありません。

ただし、トイラジコンのギヤボックスでさらに減速するとなると、超超低速走行になってしまい、動いていても停止して見えそうです。

そこで、トイラジコンの2段減速ギヤのうち最終段だけを使うことにして、一段目のギヤを直接ステップモーターで回します。

ギヤボックスは、またまた躊躇せずに切断と穴開けを敢行します。

ギヤにも28BYJ-48にちょうどいい形と大きさの穴をあけたら、動力部を組みあげてみました。

トイラジコンのモーターを28BYJ-48にのせかえる

ステップモーター28BYJ-48を6.4Vで動かしてみます

トイラジコンのギヤボックスとホイールを組んだ状態で、ステップモーターを動かしてみます。

www.youtube.com

ものすごく低速ですが、これこそ目指していた回転速度です。

しっかりと壁などを検知させたいので、月面探査機のような速度で充分です。

ステップモーター28BYJ-48と動力部を車体に組んでみました。

Arduinoとステップモーターでラジコンを自動走行させてみます

スペースを確認しながらステップモーターの取り付け角を決めたので、トイラジコンにぴったりと収まっていますね!

今後の予定(予告)

今回はステップモーターを2個使っているので、パルスを発生させるものが合計3個必要になります。

  • 超音波センサー
  • 右ステップモーター
  • 左ステップモーター

超音波センサーは、発音と測距の間に不定でバラツキ幅の大きいタイムラグが発生します。

そのため、超音波センサーを動かしているArduinoは、測距と同時にステップモーター用のパルスを発生させることができません。

このため、まず最初に、Arduinoを複数使うか、モータードライバーを追加するかを決める予定です。

Toy RC with Arduino and 28BYJ-48

Arduinoでの速度と方向制御(構想)

前回の自動走行化では、トイラジコン付属のDCモーターをPWM制御で速度コントロールしていましたが、今回はステップモーターに数種類のパルス信号の組み合わせを出力して、目的の速度と動きを作る予定です。

右左折の操舵も、簡単なプログラムで実現したいので、曲がる時の速度は、

  • 外輪:内輪 = 2:1

とします。

走行パターンも、基本的には

  1. 直進もしくは後退
  2. 右もしくは左への操舵
  3. 右もしくは左への180°転回

の3種類だけで、前後進(1)と操舵(2)は組み合わせで使います。

前進時は3個、後退時は2個のセンサーが測距した障害物の方向と距離に応じて、3種類の走行パターンを切り替えていき、自動走行化します。

To be continued!

Arduinoとモータードライバーの構成を決めたら、後は回路を組んで、スケッチ(プログラム)を少し変えるだけですが、その結果はまた次回とさせていただきます!

きっと、極低速力強く動くだけでなく、作動音も静かになって帰ってきます。

高性能なセンサーを使わずに、超音波センサー5個だけで実現するトイロボットの自動走行化、時期は未定ですが次回のお披露目まで、しばらくお待ちください!

色と明るさを変えられる集魚灯。ArduinoとLEDボードで自作したら爆釣!

先日、初めての夜釣りで小さい根魚を釣り上げました。

f:id:solocamptouring:20190427081000j:plain

手のひらに収まる、あまりにも可愛いサイズだったので、海に元気にお帰りになりました。

次回は、なんとしてでも大きな根魚に出会いたいので、魚が集まりやすい集魚灯を自作して、夜釣りに導入したいと思います。

f:id:solocamptouring:20190427134321j:plain

 

魚が集まる集魚灯の色

集魚灯に魚が集まってくる理由を調べてみました。

陸地からのチョイ投げタイプの海釣りで使う集魚灯は、発光性プランクトンや夜光虫を模したものです。

集魚灯の光に、プランクトンや夜光虫を食べる虫や小魚が寄ってきて、さらにそれらを捕食する魚も寄ってくるようです。食物連鎖でなんでも釣れそうですね!

発光性プランクトンといえば青色緑色のイメージです。

しかし、疑似餌(ルアー)が、実際にはありえない配色のものがよく釣れたりするので、集魚灯に最適な色も、実際に試してみないとわかりません。

そこで、自由に色を変えられる集魚灯を作って、その時々の最適な色を探ってみたいと思います。

集魚灯の自作

なるべく簡単に、そして安く集魚灯を作ってみます。

魚を集める機能を追い求めるので、見栄えは二の次です!

集魚灯の部品を準備します

Arduino

マイコンは、コンパクトな大きさで使いやすいArduino Nanoを使います。

写真は、今回使ったDC5Vの外部電源入力用の部品も写っています。

f:id:solocamptouring:20190427091835j:plain
f:id:solocamptouring:20190427104755j:plain

Arduinoのスケッチ(プログラム)は、記事の最後に掲載しています。

WS2812Bボード

集魚灯の発光部は、WS2812B を7個 配置した円形ボードを使います。

直径23mmと小型なので、堤防で根魚を狙う時などの集魚灯には、ちょうどいい性能と大きさです。

自作した集魚灯に使うWS2812B

ミサイルスイッチ

スイッチはなくても集魚灯は動かせるのですが、実際に使う時の雰囲気づくりのために、他の作品でもミサイルスイッチを使っています。

プロジェクトボックスのスペースをとってしまいますが、ミサイルスイッチで電源を入れる儀式をしてから使うと、何かが起きそうで気分が盛り上がります。

今回は、青色のミサイルスイッチで、釣れるスイッチのおまじないをします。

集魚灯の電源はミサイルスイッチで
集魚灯のプロジェクトボックス

可変抵抗(ボリューム)

集魚灯をコントロールするために、可変抵抗を使います

  • 光の色
  • 光の強さ

をそれぞれ選択できるように、2個の10KΩ可変抵抗を準備します。

コネクターとコード

海中に入れる発光部は5mのコードでコントローラに繋ぎます。

持ち運びやすくしたいので、AWG28の細い電線が3本入っているコードを準備しました。

コントロールボックスと発光部をつなぐコードは、コネクターで接続します。

Arduinoの自作作品に似合うコネクター

通販サイトでは、航空コネクタで検索するとみつかる、この少し仰々しいコネクターも、雰囲気づくりのために愛用しています。

集魚灯の回路をつくる

 WS2812Bボードは、電源線2本と信号線1本の合計の3本の電線をつなぐだけで、様々な光らせ方ができる優れものです。回路も単純な構成になるので、自作品には便利に使えます。

回路図は、Fritzingで作成しました。

f:id:solocamptouring:20190427101733j:plain

スペースの都合で、回路図にWS2812Bは5個だけ表現されていますが、実際には7個が直列につながっています。

発光部を除いて、配線をはんだ付けしてみました。

自作集魚灯の回路

今回は配線が少ないので、Arduinoには直接はんだ付けしています。

はんだごての熱でArduino上の部品を壊さないように、注意しながら作業を進めました。

プロジェクトボックスに回路を入れる

コントローラを仕上げるために、プロジェクトボックスに各部品の取付穴をあけます。

コネクターだけは、ボックスに外側から固定する形なので、配線をはんだ付けする前にボックスにねじ止めします。

続いて、スイッチと可変抵抗も、ボックスの上面に内側から取り付けます。

Arduino Nanoは、ボックス内に両面テープで貼り付けておきました。

自作集魚灯のコントローラ
自作集魚灯のコントローラの中

蓋を閉じたら、コントロールボックスの完成です。

試しに光らせてみましょう

5mの3芯コードの両端に、WS2812Bボードとコネクタをつけて、実際に光らせてみました。

自作集魚灯がほぼ完成

集魚灯として本命案の青色や緑色は、一種類だけでなく、可変抵抗のツマミをまわすことで多彩な色をつくりだすことができます。

集魚灯としての効果は無いかもしれませんが、 ツマミをまわして赤色や黄色などの色も作れます。

記事の最後で紹介しているスケッチでは、色を調整する可変抵抗のツマミを右にいっぱいまで回すと、ゆっくりと全ての色に変わっていくようにしています。

発光部の防水化

WS2812Bボードは、水深5mの水圧でも耐えられるように、透明樹脂で覆って防水化します。 

透明樹脂を流し込むケースに何を使おうか考えていたら、卓球の玉に目がとまりました。

大きさが発光部にちょうどよく、乳白色なので周囲に光を拡散してくれそうです。

さっそく玉の上部を切開します。

錘をつける針金を発光部の穴に通して玉の中にいれた後に、クリスタルレジン を流し込みました。

そのまま動かさないようにして樹脂が固まったら完成ですが、待ちきれないなので再度光らせてみます。

玉の下部に無造作に貼られているマスキングテープは、クリスタルレジンが流れ出ないように塞いでいるもので、樹脂が固まったら取り除きます。

自作集魚灯の発光部

狙いどおり、乳白色の卓球の玉全体で、いい感じで光っています。

自作集魚灯のシェイクダウン

後日、ためしに使ってみました。

釣りには不向きな強風の日に、漁港の堤防の風裏になる場所で、昼過ぎから釣り竿をたらしつつ、夜を待ちます。

明るいうちは何も釣れないままで時間が過ぎていき、あたりが暗くなったところで集魚灯の電源を入れて、釣りをしている場所から2mほど離れた海中に投入してみました。

すると、魚が活発に動く時間になったのもあると思いますが、種類は同じで前回よりも(成長したので!?)大きめの魚が、ほぼ入れ食いとなりました!

自作集魚灯に群がる根魚
色を変えて海中で淡く光る自作集魚灯

キャッチ&リリースを繰り返しましたが、あまりにも簡単に釣れるので、何匹釣ったか数えるのは途中でやめてしまいました。これは、もう夜釣りでは手放せません!

効果的な光の色は、少し青色が入った緑色が良さそうですが、これはもう少し継続して試してみて、確信を得たいと思います。

集魚灯のスケッチ(プログラム)

ご参考で、スケッチの紹介です。

GitHubのFastLEDライブラリを使って、簡単な命令文で光らせているので、短いスケッチになっています。

#include "FastLED.h"
#define DATA_PIN    4
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 1
#define WIDTH 7
#define NUM_LEDS HEIGHT*WIDTH

CRGB leds[NUM_LEDS];

const int sampleWindow = 20; // Sample window width in mS
unsigned int sample;
int colorSelect;
int colorPin = 2;
int selectPin = 5;
int val = 0;
int numLedsToLight = 0;
int numLedsSide = 0;
int volumeLight = 0;

void setup() {

  delay(3000); //delay for recovery

  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
}

uint8_t gHue = 96; // rotating "base color" used by many of the patterns

void loop()
{
  colorSelect = analogRead(colorPin); //check the volume to choose the color

  if (analogRead(colorPin) < 50) {
    colorSelect = 0 ;
  } else if (analogRead(colorPin) < 950) {
    colorSelect = map(analogRead(colorPin), 50, 949, 0, 255) ;
  } else {
    colorSelect = gHue;
  }

  val = analogRead(selectPin); //check the volume to choose the volume

  if (val < 205) {
    volumeLight = 3;
  } else if (val < 1000) {
    volumeLight = map(analogRead(selectPin), 205, 1000, 3, 50);
  } else {
    volumeLight = 50;
  }

  FastLED.setBrightness(volumeLight);

  fill_solid(leds, NUM_LEDS, CHSV( colorSelect, 255, 192));

  FastLED.show();  // send the 'leds' array out to the actual LED strip

  EVERY_N_MILLISECONDS( 300 ) {
    gHue++;  // slowly cycle the "base color" through the rainbow
  }
}

LEDイルミネーションでテントまわりを演出。市販品と自作品

キャンプ場で、水場などへの通り道付近にテントを設置する場合、夜間に自分や通行人が、ペグやガイロープに引っかかることがないか気になります。

光る目印でペグやガイロープの存在をアピールして、安全にキャンプを楽しむ方法について、前半では市販品の応用方法、後半は自作した内容を、それぞれまとめてみました。

キャンプでのイルミネーション

月明りをも切り裂くような強力な光を発するライトが多いので、周りのキャンパーさんに迷惑をかけないために、ライト類を点灯せずに洗い場などに行くことがあります。

同じことを考えてみえるキャンパーさんも多く、また自然を楽しまれているお子様の安全のためにも、自分のサイトのペグやガイロープの存在を光でアピールしたいと思います。

また、暗いなかで洗い場などから帰ってくる時に、自分のテントを発見しやすくするためにも、個性的なイルミネーションは効果的です。

しかし、あまりにも激しく主張するような光では、周りの雰囲気に合わない場合もあるので、できればホタルのような淡い光が欲しいところです。

簡単に入手できそうなものから、これらの条件にあう、キャンプに使えるイルミネーションを探してみた結果と、電子工作の経験がある場合の簡単な自作方法を紹介いたします。

手軽に入手できるものを使う

 キャンプ場でよく見かける物から、「これは使えそう!」という物まで、入手してすぐに、ペグやガイロープの目印として使えるものを選んでみました。

蛍光ブレスレット

f:id:solocamptouring:20181122052431j:plain

テーマパークやイベントでもお馴染みの、ポキッと折ると光り始めるライトスティックです。

使い捨てですが、一本が約20円と経済的で、夕暮れから翌朝まで、ちょうどいい明るさで光り続けます。

ブレスレット風に円形にできるのも、ペグやガイロープやポールに設置する際の自由度が増すメリットとなります。

光の強さを試してみるために、林間フリーサイトのキャンプ場で、蛍光グリーン色のものをテント周りの地面に4個配置してみました(上の写真)。

林間サイトでは自分のテントが闇に紛れこんでしまうことが多いですが、約20m離れたところからでも、やんわりとした蛍光色で自分のテントの位置が確認できて、真っ直ぐに帰ってこられます。

かといって、光が強すぎないので周りの雰囲気を損ねることもありません。

ソロキャンパーさんが多いような、月明かりの静寂を楽しむキャンプ場で使いたいマーカーです

アイスライト

本来は、カクテルグラスに氷のように入れて、イルミネーションを楽しむものです。

飲料に入れるものなので、しっかり防水されていてキャンプで使うのにも適しています。

蛍光ブレスレットよりは強めの光ですが、直視できないほど眩しいものではなく、さりげなくペグやガイロープの存在をアピールできます。

ただし、水に浸すと光りはじめるセンサー式になっているので、キャンプで使用する時には、実際に水に浸すか、センサー部にアルミホイルを使うなどで工夫することが必要です。

リモコン式のイルミネーション

アイスライトの機能に加えて、自分好みの光り方にリモコンで操作できる防水イルミネーションです。

リモコンから受信できる距離は最大で10mなので、一般的なファミリーキャンプ用テントでは、操作可能な範囲内に設置できます。

ON/OFF操作もリモコンでできるので、日中に設置しておいて、周りが暗くなったら簡単に点灯することも可能です。

ちょっとしたDIYでストラップなどを接着しておけば、ポールにぶら下げたりして、設置のバリエーションも広がります。

LED10灯が一体になっているものと、1灯づつ防水ケースに入っていて合計10個がセットになっているものがありますが、テントまわりに使う場合は、バラバラで10個になっているものを選びたいです。

太陽光発電のイルミネーション

昼間に太陽光で充電しておく前提ですが、電池交換が不要のLEDイルミネーションが使えます。

主にクリスマスのイルミネーションや庭先などで使われているもので、電線で繋がったものと、チューブ状のものがありますが、キャンプで使う場合は、絡まりにくいチューブ状の方が使い勝手が良さそうです。

光り方のパターンが変えられるものを選べば、フェードや点滅などの光も楽しめます。

テント周りで使うとなると約10m以上が必要になりますが、その長さだとLEDが100個程度使われているものが多いです。

さりげなく使うには光源が多めなので、ソロキャンパーさんが多い所よりも、ファミリーやグループキャンプなどで賑わうキャンプ場に向いています。

キャンプに似合うイルミネーションを自作します

キャンプに似合うイルミネーションを自作します

これまで紹介してきた既製品は、お手頃価格で入手できて光のバリエーションも豊富なので、手軽にテント周りに使いたい方に、おすすめです。

ここからは、なんでも自分で作ってみたくなる自作派の方への紹介です。

私の場合、使い捨て電池はなるべく使いたくありません。

思いたって突然キャンプに行く事が多いので、ソーラー充電方式では、準備不足により充電時間がしっかりと取れなさそうです。

モバイルバッテリーで一晩駆動できて、気分に応じて好みの光り方を選べられるようなイルミネーションを自作してみます。

Arduino とWS2812Bで、イルミネーションを自作する

Arduino とWS2812Bで、イルミネーションを自作する

光り方は、なるべく派手にならずに、しかしペグやガイロープにつまづいてしまう前に注意を引きたいので、マイコン搭載のLEDボードWS2812Bを10個だけ使います。

参考で、WS2812Bが5個の場合の回路図を、Fritzing で作成しました。

テントに使うクリスマス風イルミネーションをArduinoとWS2812Bで自作する

回路図のとおり、WS2812Bを使えば3本の電線だけで、数多くのLEDを自在に光らせることができます。

実際にはWS2812Bを10個を、0.2sq3芯の電線で約1m間隔で直列につないで、合計約10mのイルミネーションコードを作ります。

WS2812Bを10個つないでArduinoで光らせてみた

2個の可変抵抗(ボリューム)は、それぞれ色と光り方をコントロールするために使います。

光り方をコントロールするArduinoはプロジェクトボックスに入れて、モバイルバッテリーを電源にして光らせます。

Arduino とWS2812Bで、好きな光り方のイルミネーションを自作する

最後の仕上げで、電線をつないだWS2812Bのまわりを防水処理します。

当初は、クリスタルレジンと一緒に型に入れて固めようと考えていました。

しかし、狭い角度で発光するLEDの光を適度に拡散させるには、半透明のほうが良さそうです。

最終的に、100円ショップで入手したイルミネーションからシェードボールだけ使うことにしました。

f:id:solocamptouring:20181215174002j:plain
f:id:solocamptouring:20181215173953j:plain

シェードボールにLEDを入れたら、ホットメルトで穴をふさいでおきます。

思いつきに近い仕上げですが、光が拡散されて良い感じで光っています。

youtu.be

ご参考;クリスマス風イルミネーションのサンプルスケッチ

最後に、Arduinoを使って自作したイルミネーションの、スケッチを紹介しておきます。

光り方は、

  1. sinelon;流れ星のような光り方で往復
  2. confetti;ランダムなホタルのような光り方
  3. sinelonWithGlitter;1と2を合わせたもの
  4. allligh;10個全て点灯

の4種類から、5番のアナログピンにつないだ可変抵抗で選択します。

2番のアナログピンにつないだ可変抵抗では、好きな色か、徐々に変わる色かを選択します。

自作LEDイルミネーションで、テントまわりを安全に光らせてみます

スケッチは、GitHubのFastLEDライブラリを使いました。

#include "FastLED.h"
#define DATA_PIN    4
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 1
#define WIDTH 10
#define NUM_LEDS HEIGHT*WIDTH
CRGB leds[NUM_LEDS];
#define BRIGHTNESS  3

const int sampleWindow = 20; // Sample window width in mS
unsigned int sample;
int colorSelect;
int colorPin = 2;
int selectPin = 5;
int val = 0;
int numLedsToLight = 0;
int numLedsSide = 0;

void setup() {
  delay(3000); //delay for recovery
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
}

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { sinelon, confetti, sinelonWithGlitter, alllight };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 96; // rotating "base color" used by many of the patterns

void loop()
{

  colorSelect = analogRead(colorPin); //check the volume to choose the color
  if (analogRead(colorPin) < 50) {
    colorSelect = 0 ;
  } else if (analogRead(colorPin) < 950) {
    colorSelect = map(analogRead(colorPin), 50, 949, 0, 255) ;
  } else {
    colorSelect = gHue;
  }

  val = analogRead(selectPin); //check the volume to choose the pattern
  if (val < 205) {
    gCurrentPatternNumber = 0;
  } else if (val < 410) {
    gCurrentPatternNumber = 1;
  } else if (val < 615) {
    gCurrentPatternNumber = 2;
  } else if (val < 820) {
    gCurrentPatternNumber = 3;
  } else {
    gCurrentPatternNumber = 0;
  }

  gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array
  FastLED.show();  // send the 'leds' array out to the actual LED strip

  EVERY_N_MILLISECONDS( 300 ) {
    gHue++;  // slowly cycle the "base color" through the rainbow
  }
}

void sinelon()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13, 0, 9);
  leds[pos] += CHSV( colorSelect, 255, 192);
}


void confetti()
{
  // random colored speckles that blink in and fade smoothly
  fadeToBlackBy( leds, NUM_LEDS, 2);
  int pos = random16(600);
  leds[pos] += CHSV( colorSelect, 255, 192);
}

void sinelonWithGlitter()
{
  // built-in sinelon, plus some random sparkly glitter
  sinelon();
  if ( random8() < 3) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;
  }
}


void alllight()
{
  // all led with color
  fill_solid(leds, NUM_LEDS, CHSV( colorSelect, 255, 192));
}

どこでも美味しく炊ける炊飯(炊爨)器をつくる。サーボモーターとArduinoで自動化

キャンプといえば!という料理は、その方の個性があって多種多様ですが、それでも多くの方が楽しまれるのが、炊飯(炊爨)ではないでしょうか?

美味しくお米を炊くためには、経過時間に応じた火力調節が大事ですよね。

キャンプの場合、ベストな火力調整は難しくて、感覚に頼るか、なり行き任せてやってみることになりがちだと思います。

私は、沸騰状況やフタの動きを目安に、感覚で火力を調節していますが、自分好みの炊き上がりにできるか毎回不安になります。

もしも、自動で火力調整できれば、成功事例を忠実に再現できて安心できる上に、炊飯中の時間は他の作業にも専念できる!と思い、サーボモーターとArduinoを使って、キャンプ用自動炊飯器を自作してみました。

キャンプで何度でも美味しくお米が炊ける半自動炊飯(炊爨)器の自作

飯盒炊飯の火力管理

飯盒炊飯の火力管理については、好みの炊き上がりに個人差があるため、いろんな意見があります。

お焦げは最少でいい私は、熱伝導率が良すぎて火力調節が難しいアルミの丸型飯盒を使って、

  1. 中火で2分
  2. 弱火で17分
  3. 強火で1分

として、自動化に挑戦してみます。

お米の量がいつもと違う場合は、時間管理は変えずに、火力をお米の量に比例して調整します。

自動炊飯器を自作します

自動炊飯器といっても、家で使うような釜一体の大掛かりなものは使いませんし、作りません!

キャンプ用の飯盒とCB缶バーナーを使いつつ、バーナーのガスコックに接続する自動火力調節器を自作して、トータルシステムとしては自動炊飯できるようにします。

部品の準備

駆動部は、サーボモーターを使います。

充分なトルクがある、下の写真左側のラジコンカー用のもので動かします。

f:id:solocamptouring:20181007050128j:plain

炊飯を開始する前に、可変抵抗(ボリューム)を操作してサーボモーターを動かし、バーナー火力の初期設定をします。

20分の炊飯時間は、炊飯開始ボタンを押した時からカウントダウンを開始します。

さらに、経過時間や火力調節状態が一目でわかるように、16x2文字のLCDモニターにリアルタイムで情報を表示します。

f:id:solocamptouring:20181007054656j:plain

主な使用部品は

  • Arduino
  • サーボモーター
  • スイッチ
  • ボリューム 
  • LCDモニター

です。

Arduinoでサーボモーターを動かす制御仕様

ArduinoとLCDモニターの電力は、モバイルバッテリーから供給します。

 それとは別に、サーボモーター用の電源として6.4Vバッテリーを準備します。

スイッチは、電源のオンオフではなく、初期入力の切り替えと炊飯時間の管理のために使います。

使い方の流れとしては、

  1. 電源コードをモバイルバッテリーにつなぐ。サーボモーターの電源はラジコン用6.4Vバッテリーにつなぐ。

    automatic rice cooker for camping

  2. CB缶残量や気圧などに応じた初期設定のために、ボリュームで火力を調節して強火にする

    f:id:solocamptouring:20181020200103j:plain

  3. 初期入力値切り替えボタンを押す
  4. ボリュームで火力を調節して弱火にする

    f:id:solocamptouring:20181020200128j:plain

  5. 炊飯スタートボタンを押

の順番に作業すると炊飯タイマーがカウントダウンを始めて、後は炊き上がりまで自動で火力調節します。

自動炊飯中に火力を微調整したい場合は、その都度ボリュームで火力調節すれば、その最新の値を初期設定の値に入れ替えて、その後も自動炊飯し続けます

回路図

Fritzingで回路図をつくりました。

f:id:solocamptouring:20181020200958j:plain

回路図左上のサーボモーターは、その下のボリュームに連動して動かします。

初期設定切り替えボタン(S3)は、強火と弱火の初期設定を切り替えるために使用します。

炊飯スタートボタン(S2)を押したら炊飯タイマーがスタートして、自動で中火→弱火→強火の火力調節を始めます。

回路図右のLCDには、タイマー残り時間や火力を表示します。

はんだ付けで回路を仕上げて作動確認しました。

(下の写真のサーボモーターは、確認時の電源電力の事情により、小型のものを使っています)

Arduinoとサーボモーターでキャンプ用自動炊飯器を自作

完成!

木の風合いのボックスで仕上げたかったので、Arduinoと回路やモーターなどは、100円ショップの木の箱を加工して取り付けていきます。

バーナーの火力調節コックとサーボモーターとの連結は、事務用クリップを使います。

f:id:solocamptouring:20181007191940j:plain
f:id:solocamptouring:20181007194710j:plain

LCDモニターとボリュームなども取り付けて、完成しました。

キャンプ用自動炊飯器を試してみます

自動で上手に炊けるか、さっそく試してみました。

炊飯の準備

電力は2系統です。

サーボーモーターはラジコン用の6.4V、その他のArduinoなどにはジャンプスターターにも使えるモバイルバッテリーから供給します。

バーナーは愛用のSOTO Gストーブを使い、丸形飯盒で炊飯します。

いつでもキャンプで美味しく炊ける半自動炊飯(炊爨)器の自作。サーボモーターとArduino

白米ではなく、私がキャンプでよく楽しんでいる炊き込みご飯を炊飯してみます。

ちょうどムカゴを入手できたので、一緒に飯盒へ入れておきます。

f:id:solocamptouring:20181021050545j:plain

自動炊飯してみます

Gストーブに火をつけて、強火と弱火の初期設定をした後に、炊飯スタートボタンを押します。

自動炊飯器にお任せにする火力調節は、思惑どおりにうまくできています。

youtu.be

今までの手動の炊飯では、途中で不安になって細かく火力調節していましたが、今回は意図して...というよりも我慢して、手を出さないようにします。

20分後、タイマーが終了して、サーボモーターがガスコックを閉じました

そのまま、さらに20分ほど我慢して、蒸らし終わったタイミングで、フタを開けてみます。

...私の好みの炊き加減にできています!

アウトドアで使うことを想定しているので、気温や風速などによっては、火力や時間を少し変える必要があるかもしれません。

とはいえ、季節が変わるまでの間は、何回でも同じ炊き上がりを望めそうです。

一年を通して使ってみて、外気温と火力や炊飯時間との関係性を見出せたら、温度センサーを追加して、自動化を進化させてもいいですね。

参考;サーボモーターを実用するスケッチ(プログラム)

サーボモーターを簡単に制御できるGitHubのVarSpeedServoライブラリを使ってスケッチを作りました。

参考で紹介いたします。

#include <VarSpeedServo.h>

#include <LiquidCrystal.h>

VarSpeedServo myservo1;

LiquidCrystal lcd( 12, 11, 5, 4, 3, 2 );

int volumePin = 3;

int switchPin = 9;

int operationswPin = 10;

int totalTime = 20 ;

int firstStep = 2 ; 

int lastStep = 1 ; 

int lowFlame = 180 ;

int midFlame = 90 ;

int highFlame = 0 ;

unsigned long startTime = 0;

unsigned long nowTime = 0;

int m = 0;

int s = 0;

int valAng = 0 ;

int valTemp = 0 ;

int valAngprint = 0 ;

boolean initialSet = true;

boolean highSet = true;



void setup() {

  Serial.begin(9600);

  pinMode(switchPin, INPUT) ;

  lcd.begin(16, 2);

  lcd.setCursor(0, 0);

  lcd.print("Auto rice cooker");

  lcd.setCursor(0, 1);

  lcd.print("  with arduino  ");

  delay(3000);

  lcd.clear();

  myservo1.attach(7);

}



void loop() {

  Serial.print (nowTime) ;

  Serial.print ("  ") ;

  Serial.print (m) ;

  Serial.print ("  ") ;

  Serial.print (s) ;

  Serial.print ("  ") ;

  Serial.print (valAng) ;

  Serial.print ("  ") ;

  Serial.println (valTemp) ;



  if (digitalRead(switchPin) == HIGH) {

    initialSet = !initialSet ;

    delay(50);

    while (digitalRead(switchPin) == HIGH) {}

  } else {

    if (initialSet == true) {

      myservo1.attach(7);

      startTime = millis();

      if (digitalRead(operationswPin) == HIGH) {

        highSet = !highSet ;

        delay(50);

        while (digitalRead(operationswPin) == HIGH) {}

      } else {

        lcd.setCursor(0, 0);

        lcd.print("Adjust the flame");

        valAng = 180 - analogRead(volumePin) * 3 / 17 ; // 0 to 1024 change into angle

        valAngprint = 180 - valAng;

        if (valAngprint < 10) {

          lcd.setCursor(12, 1);

          lcd.print("   ");

          lcd.setCursor(15, 1);

          lcd.print(valAngprint);

        } else if (valAngprint < 100) {

          lcd.setCursor(12, 1);

          lcd.print("  ");

          lcd.setCursor(14, 1);

          lcd.print(valAngprint);

        } else {

          lcd.setCursor(12, 1);

          lcd.print(" ");

          lcd.setCursor(13, 1);

          lcd.print(valAngprint);

        }

        if (highSet == false) {

          lcd.setCursor(0, 1);

          lcd.print("LOW setting ");

          lowFlame = valAng ;

          valTemp = lowFlame ;

          myservo1.write(valTemp, 30, true);

        } else {

          lcd.setCursor(0, 1);

          lcd.print("High setting");

          highFlame = valAng ;

          valTemp = highFlame ;

          myservo1.write(valTemp, 30, true);

        }

      }

    } else {

      nowTime = totalTime * 60000 + startTime - millis();

      m = nowTime / 60000;

      s = (nowTime % 60000) / 1000;

      if (highSet == false) {

        lowFlame = 180 - analogRead(volumePin) * 3 / 17 ;

      } else {

        highFlame = 180 - analogRead(volumePin) * 3 / 17 ;

      }

      midFlame = (highFlame + lowFlame) / 2 ;

      valAngprint = 180 - valTemp;

      lcd.setCursor(15, 1);

      lcd.print(" ");

      if (valAngprint < 10) {

        lcd.setCursor(12, 1);

        lcd.print("   ");

        lcd.setCursor(15, 1);

        lcd.print(valAngprint);

      } else if (valAngprint < 100) {

        lcd.setCursor(12, 1);

        lcd.print("  ");

        lcd.setCursor(14, 1);

        lcd.print(valAngprint);

      } else {

        lcd.setCursor(12, 1);

        lcd.print(" ");

        lcd.setCursor(13, 1);

        lcd.print(valAngprint);

      }

      if (m < 10) {

        lcd.setCursor(0, 0);

        lcd.print(" ");

        lcd.setCursor(1, 0);

        lcd.print(m);

      } else {

        lcd.setCursor(0, 0);

        lcd.print(m);

      }

      lcd.setCursor(2, 0);

      lcd.print(":");

      if (s < 10) {

        lcd.setCursor(3, 0);

        lcd.print("0");

        lcd.setCursor(4, 0);

        lcd.print(s);

      } else {

        lcd.setCursor(3, 0);

        lcd.print(s);

      }

      if (m > 18) {

        lcd.setCursor(5, 0);

        lcd.print("           ");

      } else  if (m > 16) {

        lcd.setCursor(6, 0);

        lcd.write(">");

      } else  if (m > 14) {

        lcd.setCursor(7, 0);

        lcd.write(">");

      } else  if (m > 12) {

        lcd.setCursor(8, 0);

        lcd.write(">");

      } else  if (m > 10) {

        lcd.setCursor(9, 0);

        lcd.write(">");

      } else  if (m > 8) {

        lcd.setCursor(10, 0);

        lcd.write(">");

      } else  if (m > 6) {

        lcd.setCursor(11, 0);

        lcd.write(">");

      } else  if (m > 4) {

        lcd.setCursor(12, 0);

        lcd.write(">");

      } else  if (m > 2) {

        lcd.setCursor(13, 0);

        lcd.write(">");

      } else  if (m > 0) {

        lcd.setCursor(14, 0);

        lcd.write(">");

      }  else {

        lcd.setCursor(15, 0);

        lcd.write(">");

      }

      if ( nowTime > totalTime * 60000) {

        myservo1.write(180, 30, true);

        myservo1.detach();

        lcd.setCursor(0, 0);

        lcd.print(" Cook finished. ");

        lcd.setCursor(0, 1);

        lcd.print("  Valve closed  ");

        delay(500) ;

      } else if (m >= totalTime - firstStep) {

        if (abs(valTemp - midFlame) < 1) {

          myservo1.stop();

        } else {

          valTemp = midFlame ;

          myservo1.write(valTemp, 30, true);

        }

        lcd.setCursor(0, 1);

        lcd.print("1. Mid level");

      } else if (nowTime >= lastStep * 60000) {

        if (abs(valTemp - lowFlame) < 1) {

          myservo1.stop();

        } else {

          valTemp = lowFlame ;

          myservo1.write(valTemp, 30, true);

        }

        lcd.setCursor(0, 1);

        lcd.print("2. Low level");

      } else if (nowTime > 0) {

        if (abs(valTemp - highFlame) < 1) {

          myservo1.stop();

        } else {

          valTemp = highFlame ;

          myservo1.write(valTemp, 30, true);

        }

        lcd.setCursor(0, 1);

        lcd.print("3. Final HI ");

      } else {

        myservo1.write(180, 30, true);

        myservo1.detach();

        lcd.setCursor(0, 0);

        lcd.print(" Cook finished. ");

        lcd.setCursor(0, 1);

        lcd.print("  Valve closed  ");

        delay(500) ;

      }

    }

  }

}

キャンプでもシュラスコ! アウトドアで使える電動ロティサリーを自作します

ソロキャンプでも、ロティサリーチキンのような回転焼きを電動で楽しみたい!と思い、ステップモーターとArduinoで、小型の電動ロティサリー(回転BBQ器)を自作してみました。

ロティサリーBBQをコンパクトに楽しもう!Arduinoと28BYJ-48で小型の電動回転丸焼き器を自作

ソロキャンプで焼くもの

ソロキャンプでは、チキンの丸焼きは食べきれませんが、手羽やモモ肉など小さめの食材であれば、ロティサリーを使って網焼きとは違った食感が楽しめそうです。

屋外でのBBQでは、網を使ってソーセージを焼くことが多いですが、曲がっているソーセージは焼きムラができてしまい、曲がっていなくても網の上で転がっていってしまうことがあります。

ロティサリーを使えば、ソーセージのような形のものでも、転がる心配をせずに全周を均一に焼くことができそうです。

小型のロティサリーマシンを自作します

ソロキャンプ用の電動ロティサリーで、食べることだけでなく、食材がクルクル回りながら食べごろに焼きあがる過程も、見て楽しみたいと思います。

出力軸を低速回転で動かすことができれば、簡単な工作でつくれそうです。

ソロキャンプスタイルにちょうどいい大きさの電動ロティサリーを自作してみます。

部品の準備

動力部は、低速でも定電圧で動かせるステップモーター28BYJー48を、Arduinoで速度制御して動かします。

f:id:solocamptouring:20181008230435j:plain

回転速度は、可変抵抗(ボリューム)で自在に変えられるようにします。

串を脱着する時には、可変抵抗は操作せずに回転を止めたいので、電源用のスイッチを設置したいところです。

しかし、お気に入りのミサイルスイッチの入手が間に合わなかったので、当分はモバイルバッテリー側のスイッチで代用します。

  • Arduino
  • ステップモーター
  • スイッチ
  • 可変抵抗

駆動力の伝達部は、ユニバーサルジョイント2個で繋いで、BBQ台にのせる串と駆動部の出力軸との軸位置ズレを吸収します。

伝達軸は、ステップモーターの出力軸径と同じ大きさのφ5mmのアルミ棒を使います。

  • φ5mmアルミ棒
  • 同軸ジョイント#1(モーターとアルミ棒)
  • ユニバーサルジョイントx2個
  • 同軸ジョイント#2(アルミ棒と串)

f:id:solocamptouring:20181007064334j:plain

その他、モーター格納用のボックスや、同軸ジョイント#2と串を固定するための、手で締め付けられるネジなども使用します。

機構部を作ります

動力伝達部のジョイント類は既製品を使うので、ほとんどネジの締め付け作業だけで組み上げられます。

軸位置のズレ吸収用ユニバーサルジョイントを2個、動力伝達軸の途中に入れておきます。

f:id:solocamptouring:20181007064435j:plain

串との接続部だけは、様々な串への汎用性を持たせるために穴を拡大したり、手でまわすことができる大きさの締め付け用ボルトを取り付けられるように加工しました。

回路図

Fritzingで回路図をつくりました。

Rotisserie_回路図

電源はモバイルバッテリーから供給して、スイッチで全供給電源をオンオフするようにします。

Arduinoとモーターは並列に電源供給して、Arduinoに過大な電流が流れないようにしておきます。

回路図左側の可変抵抗で、中央のステップモーターの回転速度を変えられる回路になっています。

回路図の右半分は、完成品で入手できるモータードライバーボードを使いました。

 

Arduinoでステップモーター28BYJー48を回す制御仕様

今回は、可変抵抗でステップモーターのスピードを変える、基本的な手法を使います。

最初に、可変抵抗で調節した電圧を、Arduinoで読み取ります。

Arduinoでは、可変抵抗の電圧を回転速度に変換して、その回転速度に合わせたパルスをステップモータードライバーに出力します。

Arduinoの入出力ポートは5か所しか使わないので、はんだ付け作業も楽ですね! 

f:id:solocamptouring:20181008230805j:plain

28BYJー48とArduinoをボックスに取り付けます

ステップモーターやArduinoを入れるのにちょうどいい大きさの、プロジェクトボックスを入手します。

重量感がある印象にしたいので、アルミのボックスを選択しました。

f:id:solocamptouring:20181014165431j:plain

ステップモーターとスイッチ類を取り付ける穴を開けて、全ての部品を取り付けたら完成です。

ロティサリーチキンの代わりに、ソーセージとたこ焼きで試してみます

ソロキャンプ用のロティサリーなので、焼き鳥サイズの串を回してBBQをします。

ステップモーター28BYJー48は、ロティサリーにちょうどいい回転速度域では、よほど大きく重いものを串に刺さなければ、充分なトルクがあります。

少し軽めのものですが、私のキャンプ飯の定番!、ソーセージで試運転をしてみます。

ソロキャンプでいつも使っている小型ウッドストーブで炭をおこして準備します。

ウッドストーブの外縁2カ所に事務用クリップをつけて、その持ち手部分に串を通して、ソーセージを回します。

ゆっくりと回っているのを見ているだけで、電動ロティサリーを作った満足感で癒されます。

約3分で、表面をほぼ焦がさず、均一に焼き上げることができました。

youtu.be

ソロキャンプでいつも楽しんでいる、冷凍たこ焼きも、ロテイサリーで「表面カリッ、中はトローッ」と焼き上げてみました。

f:id:solocamptouring:20181014165938j:plain

まだトルクに余裕があるので、もっと重いものを焼く時でも使えそうです。

アタッチメントを工夫すれば、手動で回していたバームクーヘンにも応用できそうです。

...後日談...

数年にわたり、こっそりとキャンプでもシュラスコを楽しんでいましたが、やはりキャンパーさん達の要望は同じですね~

なんと、ロゴスさんからくるくるクッキングリルが発売されました!

私が自作したものとは違い、たいへんコンパクトに作られており、また別売の回転網を使えば魚などの平たいものも回転させられる優れものです。

自作しなくても簡単に入手できるので、キャンプ場で電動ロティサリーのお仲間を多く見かける日が遠くなさそうですね。

28BYJー48を実用するスケッチ(プログラム)

最後に、ステップモーター28BYJー48を回すスケッチを紹介いたします。

速度は変えられますが、とにかく回し続けるだけの短いスケッチです。

回転方向は、時計回りと反時計回りの両方に対応できるようにしていますが、実際には時計回りだけで使っています。

int motorPin1 = 4;    // Blue   - 28BYJ48 pin 1

int motorPin2 = 5;    // Pink   - 28BYJ48 pin 2

int motorPin3 = 6;    // Yellow - 28BYJ48 pin 3

int motorPin4 = 7;    // Orange - 28BYJ48 pin 4

// Red    - 28BYJ48 pin 5 (VCC)

int volumePin = 5;

int motorSpeed;

int motorSpeedtimes = 3;

int speedMax = 1500 ;

int waitSpeed = 0 ;

int lookup[8] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001};



void setup() {

  pinMode(motorPin1, OUTPUT);

  pinMode(motorPin2, OUTPUT);

  pinMode(motorPin3, OUTPUT);

  pinMode(motorPin4, OUTPUT);

  Serial.begin(9600);

}

void loop() {

  motorSpeed  = 1024 * (motorSpeedtimes + 1) - (analogRead(volumePin) * motorSpeedtimes) ; //0to1023 change into speed

  clockwise();

}
void anticlockwise() { for (int i = 0; i < 8; i++) { setOutput(i); if ( motorSpeed > speedMax ) { waitSpeed = motorSpeed - speedMax ; delayMicroseconds(speedMax) ; digitalWrite(motorPin1, 0); digitalWrite(motorPin2, 0); digitalWrite(motorPin3, 0); digitalWrite(motorPin4, 0); delayMicroseconds(waitSpeed) ; } else { delayMicroseconds(motorSpeed); } } } void clockwise() { for (int i = 7; i >= 0; i--) { setOutput(i); if ( motorSpeed > speedMax ) { waitSpeed = motorSpeed - speedMax ; delayMicroseconds(speedMax) ; digitalWrite(motorPin1, 0); digitalWrite(motorPin2, 0); digitalWrite(motorPin3, 0); digitalWrite(motorPin4, 0); delayMicroseconds(waitSpeed) ; } else { delayMicroseconds(motorSpeed); } } } void setOutput(int out) { digitalWrite(motorPin1, bitRead(lookup[out], 0)); digitalWrite(motorPin2, bitRead(lookup[out], 1)); digitalWrite(motorPin3, bitRead(lookup[out], 2)); digitalWrite(motorPin4, bitRead(lookup[out], 3)); }

f:id:solocamptouring:20181014164507j:plain

ロティサリーを導入したことで、

  • 焼いて
  • 見て
  • 食べて

の合計3回楽しめるようになり、キャンプの食事時間が、さらに楽しくなりました!

人の動きに同調する1/fゆらぎのつくりかた。USB扇風機を改造してみます(動画あり)

USB扇風機を癒しの1/fゆらぎ風に改造して、パソコン前スペースを快適化

空調の効いた屋内でも、いつも風を感じていたい!ので、パソコンのUSB端子を電源とする小型扇風機を使ってみる事にしました。

せっかくなので、自然風のような1/fゆらぎの風を感じられるように改造してみます。

1/fゆらぎについて

1/fゆらぎとは、振幅が周波数 f に反比例する波の集合波形です。

ゆらぎは平均値からの変動幅なので、周波数が小さい、ゆっくりとした波の成分ほど大きく変動するという意味になります。

人体も1/fゆらぎをしていて、周りの1/fゆらぎ を感じると、共鳴して精神安定などの効果があると考えられています。

USB電源のミニ扇風機

改造ベースとなる扇風機は、しっかり風を感じられて静かなものを選びます。

飛行機でSBも採用例がある二重反転プロペラ(コントラペラ)で、効率よく空気を押し出すものが良いようです。

たまたま、実際に使っている人のパソコン前を、静かなので扇風機に気付かずに通った時に、予想外の風量に驚かされたこともあり、写真の二重反転プロペラを持つ扇風機を選びました。

f:id:solocamptouring:20180719045658j:plain

リズム時計製のコントラペラ扇風機で、他にも「くまのプーさん」や「ミッキー」をデザインモチーフとしたバージョンもあります。

私は、一番安かったオーソドックスな形の薄紫色をチョイスしました。

この扇風機は、風量をハイ/ローの二段階に切り替えられます。

ローはそのまま使えるようにして、ハイに切り替えると1/fゆらぎの風になるように改造します。

Arduino と赤外線センサーで1/fゆらぎをつくる

1/fゆらぎの風は、扇風機と人との距離に応じて変動するようにします。

基本は、距離が

  • 近いと弱風
  • 遠いと強風
  • さらに遠いと停止(OFF)

にします。

赤外線センサーは、正面の平面体に対しては安定して距離を測れますが、服などの凹凸物に対しては少しばらつきがある測定結果になります。

これを逆手にとって、ばらつきが

  • 大きい時は、ゆっくり風量を変える
  • 小さい時は、早く風量を変える

ことで、1/fゆらぎの風をつくります。

これをモーターの回転数を変化させて実現するために、Arduinoを使って赤外線センサーで計測した扇風機前のもの(人)との距離から、モーターの目標スピードと、目標スピードに変わるまでの時間を決めて、PWM制御のパルスで出力します。

PWM(Pulse width modulation、パルス幅変調)は、電圧一定で、周期も一定のパルス波形です。このパルスを電源に使いつつ、ONパルスの幅を変える(=デューティ比を変える)と電圧の平均値が変わるため、結果として電圧を変えた時と同じ効果があります。

Arduino を使う回路

回路図は、Fritzingで作成しました。

f:id:solocamptouring:20180722133259j:plain

赤外線センサーGP2Y0E02Aの接続

シャープの、赤外線式の測距モジュールGP2Y0E02Aには、4本の端子があります。

  1. VDD(3.3V電源)
  2. GND
  3. Vout(出力)
  4. GPIO1

1番はArduinoの3.3Vに、2番はGNDに接続します。

3番のVoutは、電圧で距離情報を返してくるので、Arduinoのアナログピン(回路図ではA3ピン)に接続します。

4番のGPIO1は、電圧がVDD付近であれば計測、GND付近であればスタンバイ(計測しない)モードとなります。Arduinoのデジタルピン(回路図ではD5ピン)からの5V信号を、抵抗3個とダイオードで約3.3Vに減圧して接続します。

赤外線センサーGP2Y0E02Aの接続(USB扇風機の癒し改造)

モーターの接続

PWMで速度を変えるため、Arduinoのデジタルピン7からパルス信号を出力します。

パルス信号はトランジスタのベースに送ってスイッチングして、モーターに電流供給します。

パルスがLOW(0V)の時に、モーターの逆起電力からトランジスタを守るために、ダイオード(D1)を、モーターと並列に使用します。 

トランジスタとベース抵抗値の計算

トランジスタ2SD882を使う回路設計

使用するUSB扇風機は、商品説明によると定格電流210mAです。

トランジスタは、余裕を持って作動させられる2SD882を使います。

  • Ic=210mA
  • hFE=100倍
  • VBE=0.8V

の値を使って、ベースに接続する抵抗値を求めます。

RB=(5-0.8)/(0.21/100)=2000Ω

2000Ωの抵抗RBを使用して、トランジスタのベースとArduinoのデジタルピン(D7)を接続します。 

作動を安定させるため、抵抗RBと平行にコンデンサを、またトランジスタのBE間に抵抗を入れておきます。

扇風機から、そのまま使う電子部品

f:id:solocamptouring:20180719050203j:plain

使用するUSB扇風機の、ハイ/ロー切り替えの元回路は、

  • ハイ側: 電源は、2個の直列配置モーターに直接供給
  • ロー側: 2個の定電流ダイオード(直列)を通して、2個の直列配置モーターに電源供給

となっていました。

ロー側の定電流ダイオードは、電源のプラス側からマイナス側に移設して、そのまま使います(回路図では、2個をまとめてD4と記載)。

スイッチをハイ側に切り替えると、Arduinoの電源が入り、トランジスタを通してPWMでモーターを駆動するように、回路を作ります。

USB扇風機にArduinoを搭載して、癒しの1/fゆらぎ風に改造

1/fゆらぎ扇風機のスケッチ(プログラム)

1/fゆらぎのつくりかた

赤外線測距センサーで計測した距離は、100回測定した平均値を使います。

得られた距離に応じて、

  • 5〜60cm: PWMのデューティ比を50%〜100%に変化
  • それ以外: 停止

として、目標速度と、そこに到達するまでの時間を設定します。

また、目標速度に到達するまでの間も距離を測定して、その時点におけるデューティー比のゆらぎ(計算値と、距離測定値から置き換えた数値の差を1/3したもの)を変化量として瞬時に加えます。

デューティー比のゆらぎが負(マイナス)の場合は、数値に応じて短い間、電源を遮断して即答させます。

距離が60cmより遠い場合は、人(私)が扇風機前から席を外したと判断して停止し、以後1秒毎に距離測定して、人が戻ってきたら再開します。

USB扇風機をArduinoで改造

スケッチの紹介

int trigPin = 5;
int pwmOutput = 7;
int distResult = 0;
long distA = 0 ;
int i = 0 ;
int j = 0 ;
int frequencyWindChange = 0 ;
int speedOutput = 0 ;
int speedCurrent = 50 ;
int speedNowTarget = 50 ;
int speedOldGoal = 50 ;
int speedGoal = 50 ;
int speedAdjust = 0 ;
int speedAdjustParameter = 3 ;

void setup() {
pinMode(trigPin, OUTPUT); 
pinMode(pwmOutput, OUTPUT); 
delay (100);
digitalWrite(trigPin, HIGH);
}

void loop() {
  
     long voltAns ;
     voltAns = IDMread(3)  ;           // アナログピン3番のセンサーの値を読込む
     long distAns1 = map(voltAns,0,1023,0,500) ; // 電圧値に変換する(0から5V)
     long distAns2 = map(distAns1,55,220,50,4) ;  // 電圧値から距離に変換する(0.55から2.2Vを50から4cm)
     int distResult = distAns2; 

  if(distResult > 60){
  analogWrite(pwmOutput, 0) ;
  speedOldGoal = 50 ;  
  delay(1000) ;
  }else{
  speedGoal = map(distResult,0,65,50,100) ;
  frequencyWindChange = abs(speedGoal - speedOldGoal) ;

    for (j=0 ; j < frequencyWindChange; j++) {
   speedNowTarget = speedOldGoal + (speedGoal - speedOldGoal)/frequencyWindChange*j ;

      
    long voltAns ;
     voltAns = IDMread(3)  ; 
     long distAns1 = map(voltAns,0,1023,0,500) ; 
     long distAns2 = map(distAns1,55,220,50,4) ; 
     int distResult = distAns2; 
     
     speedAdjust = (map(distResult,0,65,50,70) -  speedNowTarget)/speedAdjustParameter ;
     speedOutput = 255*(speedNowTarget + speedAdjust)/100 ;
     if(speedAdjust < 0){
      analogWrite(pwmOutput,0);
      delay(70*abs(speedAdjust));
     }
     analogWrite(pwmOutput,speedOutput);
     delay(200);
     } 
     
   speedOldGoal = speedGoal ;  
  }
}

// 赤外線測距モジュールから読み込む処理
int IDMread(int PinNo) {
     distA = 0 ;
     for (i=0 ; i < 100; i++) {
          distA  = distA + analogRead(PinNo) ;   // アナログピンから読取る
     } 
     return distA/100 ;                        // 100回の平均値を返す
}

癒しの風を感じてみます

扇風機のプロペラ回転数は細かく変動しています。時々、自然に風のような、ゆっくりとした大きな変動もあり、1/fゆらぎらしくなっています。

回転数に応じてプロペラの残像が刻々と変化するので、ぼ~っと見ていても飽きません。

また、席をはずして戻ってきた時には、停止状態から自動で動き始めるので、「おかえり!」と言われたようで、少し幸せな気持ちになります。

youtu.be

ちょっとした改造ですが、Arduinoと測距センサーを使うことで、オフィスなどの密閉された屋内空間でも、自然のような風のそよぎを感じる事ができて、眠気を誘うほど快適になります。