「このモーターを使ってコーヒー豆の焙煎機つくれないか?」
友達のオフィスに遊びに行くと、おもむろに引き出しからモーターを取り出してそんなことを言ってきた。
「要件定義は?」
「火力の調整はカセットコンロで手動で行いたい。ただモーターを回してくれればいい」
「ふむふむ。だが市販品があるだろ?」
「加熱の調整は手動でやりたいのだが、豆に均一に熱を与えるところだけ自動にしたい」
「なるほど。全自動はコーヒーオタクの心を満たせないと。たとえばモーターの回転速度の調整と焙煎機内の温度の表示なんか出せたら手動で行う火力調整もやりやすくならないか?」
なんて会話があったかどうかもう半年以上前なので定かではないけども、コーヒー豆焙煎機を作ることになった。
要件
- ドラムに豆を入れてモーターで回し下からカセットコンロで炙れる
- モーターの回転速度を調整できる
- モーターの回転速度とドラム内の温度を計測してモニターに表示する
こんなんでいいのか?
そうだ、データをインターネッツに上げよう。
要件+
- 回転速度と温度をネットにアップしてスマホで見る事ができる
WiFi使えるマイコンならESP8266とESP32だなっと。
開発風景
どのように作るかの説明は後にして、まずは何をつくったかを見ていただこう。
ESP32ってなんぞ?
Raspberry Pi や Jetson なんかは中で Linux が動くのでまるでPCみたいな使い方ができるのだけど、Arduino はもっとシンプルで単純なプログラムだけが動く。ESP8266 と ESP32 はArduino IDE を使ってほぼほぼ Arduino と同じようにプログラミングできるマイコンで、違いはこんな感じ。
- Arduino: 5V!
- ESP8266: 3.3! WiFiが使える。ちっこい。500円くらい。ピンの数が少ない
- ESP32: 3.3! WiFiとBluetoothが使える。ちびっとでかい。1000円くらい
今回の要件では ESP8266 でもいけそうな気がしたけども、なにせ入出力がモーターとディスプレイと温度と3つあったのでピンが多い ESP32 を使うことにした。
安いと700円くらいで買える。
解説本はこのようなものがある。
物理的なものを用意する
必要なのは加熱できるドラムとそれを支えるフレーム。そしてモーターの回転をドラムに伝えるギアなど。3Dプリンターで印刷したり、Amazonでドラムや棒などを購入した。
これらのパーツを組み上げる。
電子パーツとプログラム
では実装の部分の説明に入る。全体のコードはこのgistにまとめた。
実際に動かすとこのようになる。
つまみを回すとモーターの回転速度が変化し、速度と温度計から取得したデータをディスプレイに表示する。
まずはコードの頭にある include から。
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include "Ambient.h"
#include "max6675.h"
上からディスプレイ制御、wifi、データアップロードサービス(Ambient)、温度計のライブラリになる。
各パーツごとに分けてコードを見ていく。
ディスプレイ
初期化
void setupDisplay() {
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
Serial.println("Initialized Display");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.printf("Hello world!");
display.display();
}
モーターの速度と温度を与えると表示する関数を用意してある。
void showDisplay(float motorSpeed, float temp) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.printf("Speed:%d%%", (int)(motorSpeed * 100));
display.setCursor(0, 16);
display.printf("Temp:%dC", (int)temp);
display.display();
}
loop()の中で表示関数を呼び出してやる。
temperatureVal = thermocouple.readCelsius();
showDisplay(motorSpeedVal, temperatureVal);
WiFi
初期化と接続。wifiはこれだけ。
void setupWifi() {
Serial.print("Wifi Connecting");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Wifi Connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
温度計
温度計のMAX6675はとてもシンプル。まず初期化して、
int thermoDO = 19;
int thermoCS = 23;
int thermoCLK = 5;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
loop()の中で取得するだけ
temperatureVal = thermocouple.readCelsius();
showDisplay(motorSpeedVal, temperatureVal);
温度計の先端はドラムの中心に刺してあるパイプの中を通して、ドラムの中心の温度を取得している。
Ambient
モーターの回転数と温度はAmbientというデータを保存できるサービスにアップする。
初期化
void setupAmb() {
ambient.begin(AMB_CHANNEL_ID, AMB_WRITE_KEY, &wifiClient);
}
データをアップする関数を用意し、
void ambPost(float motorSpeed, float temperature) {
float motorSpeedPercent = motorSpeed * 100;
ambient.set(AMB_FIELD_SPEED, motorSpeedPercent);
ambient.set(AMB_FIELD_TEMPERATURE, temperature);
ambient.send();
Serial.printf("Ambient Posted. SPEED:%f TEMP:%f\n", motorSpeedPercent, temperature);
}
loop()の中で一定間隔で呼び出す。
if(liveCounter > 0 && (liveTime >= nextPostTime || liveTime < prevPostTime)) {
prevPostTime = liveTime;
nextPostTime = liveTime + AMB_INTERVAL;
ambPost(motorSpeedVal, temperatureVal);
Serial.printf("Posted %d:%ddays %02d:%02d:%02d\n", liveCounter, dateTime.day, dateTime.hour, dateTime.minute, dateTime.sec);
}
アップしたデータはグラフで確認する事ができる。
モーターの制御
モーターの速度を制御する関数を用意した。別のアプリのコードを再利用したので逆回転モードも入っているが今回は使われない。
void motorSpeed(float speed) {
#define MIN 64
#define MAX 255
if(speed > 0.0) {
digitalWrite(ENABLE_PIN, HIGH);
int sp = MIN + ((MAX- MIN) * speed);
ledcWrite(FORWORD_CH, sp);
ledcWrite(BACK_CH, 0);
Serial.printf("forword speed:%d\n", sp);
}else if(speed < 0.0){
digitalWrite(ENABLE_PIN, HIGH);
int sp = MIN + (MAX - MIN) * (-speed);
ledcWrite(FORWORD_CH, sp);
ledcWrite(BACK_CH, 0);
Serial.printf("back speed:-%d\n", sp);
}else{
digitalWrite(ENABLE_PIN, LOW);
Serial.println("speed:0");
}
}
loop()の中で速度調整のつまみのデータを取得して上記の関数を呼び出す。
int motorVol = analogRead(MOTOR_VOL_PIN);
int fanVol = analogRead(FAN_VOL_PIN);
Serial.printf("MOTOR:%d: FAN:%d\n", motorVol, fanVol);
motorSpeedVal = (float)motorVol / 4095.0;
motorSpeed(motorSpeedVal);
コードとしてはこれで全てだが、モーター制御の電子パーツで一つ変わった対策をした。モータードライバとして使用したL293Dも、使用したモーターも最大電流が0.6Aなので少々心配だった。そこでL293Dをちょっと変わった対応をした。
2階建て!これをすることでL293Dは1.2Aまで耐えれる。なんともかっこいいではないか。
乾杯!
何回か試行錯誤した結果200度で15分くらい焙煎するといい感じの豆ができあがる事が分かった。しかしこれも豆の種類によって変わるだろう。浅煎りが美味しい豆もあるし深入りが美味しい豆もある。
マキタで豆を挽いて、あっ
かんぱーい。
苦労と自分補正も入ってかなり美味い!
おしまい。