ソロでたのしむ

キャンプ、電子工作、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) ;
      }
    }
  }
}