ソロでたのしむ

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

どこでも美味しく炊ける炊飯(炊爨)器をつくる。サーボモーターと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) ;

      }

    }

  }

}