お店でイーストが買えないので、レーズンを使った天然酵母づくりに挑戦してみました。
参考にしたのは、レーズンを購入した富沢商店のレシピです。レーズンを計量し、3倍の水と15%の砂糖を加え、ラップで蓋をして発酵させます。
温度を一定にするために、M5Stackに温度センサーとリレー、ヒーターを取り付けます。ヒーターは強力なものでなくて良いので、AliExpressで購入したUSBミルクウォーマー($2.13)を使ってみました。
Pt100のセンサーとMAX31865のモジュールを使い、瓶の外側で温度を測定し、設定温度より低ければヒーターをオンにします。衛生面から発酵液にセンサーを入れず、外で測定しました。設定は30度にしました。内部の発酵液は、室温と30度の間のどこかで落ち着くだろうと考えてのことです。別なセンサー(DHT22)で室温も測ります。比較のために、ヒーターなし、室温での発酵も同時に行いました。
温度が高すぎてカビだらけになり廃棄
5月7日に発酵開始です。ヒーターありの方は2日でカビが発生。その後もカビとの闘いが続き、5月16日には廃棄となりました。設定温度が高すぎたのと、蓋をせずラップのみだったことが原因と思います。ここからは、比較用に作っていた室温発酵のものを、蓋で密閉して発酵を続けました。見極めが難しかったのですが、開始から2週間経った5月21日に発酵終了としています。
温度のグラフです。最初の3日間はヒーターの発熱体と温度センサーが近すぎて、瓶外側の温度が大きく変動していますが、ヒーターが弱いので、瓶内部は安定していると思います。外気温が上昇した場合、室温のピークはだいぶ遅れて夜0時頃となっているのは新たな発見でした。
中だねから焼成まで
同じ作者のレシピを参考に、まず液種40gに同量の小麦粉をまぜました。約2倍に膨らんだところです。
冷蔵庫で一晩休ませて、さらに60gの小麦粉と水を加え、30時間かけてさらに倍に発酵させませした。
これをすべて使い、強力粉200g、水、塩小さじ1弱、砂糖小さじ半分を加えて、ミキサーでこねます。水の量は適当。ちょっと多すぎたかもしれません。
一次発酵して分割、ベンチ、成形、二次発酵まで来ました。
190℃に設定して13分で焼きあがりです。
おいしくいただきました。
M5Stackのプログラム
M5Stackのプログラムは、アルコール蒸留のものを手直しして、室温の測定やリレーのオンオフを加えて使っています。ご利用される際には、includeするライブラリの準備、最初の方にある”xxxxx”を書き換えることと(SSID, password, exec_urlの3か所)、Google App Script側での準備が必要です。
#include <M5Stack.h>
#include <Adafruit_MAX31865.h>
#include <WiFiClientSecure.h>
#include "time.h"
#include <SimpleDHT.h>
//****** ネットワーク関連 ******
const char* ssid = "xxxxx"; // your network SSID (name of wifi network)
const char* password = "xxxxx"; // your network password
const char* host = "script.google.com";
String exec_url = "https://script.google.com/macros/s/xxxxx/exec";
WiFiClientSecure client;
//****** 時計用 ******
const char* ntpServer = "ntp.jst.mfeed.ad.jp";
const long gmtOffset_sec = 9 * 3600; // 時刻取得用
const int daylightOffset_sec = 0;
struct tm timeinfo;
unsigned long startMillis, //シート名を作成した時のmillis()
startSec, //シート名を作成した時の0:00:00からの秒数
measureMillis, //測定した時のmillis()
measureSec; //測定した時の0:00:00からの秒数
#define n_of_data 12//一度に送信するデータ数
unsigned long timeData[n_of_data];//測定時刻
float bottleTempData[n_of_data];//ボトルの温度
float ambTempData[n_of_data];//周囲の温度
int heaterData[n_of_data];//ヒーター状態
String filename, sheetname;// ファイル名、シート名
// Use software SPI: CS, DI, DO, CLK
Adafruit_MAX31865 max31865 = Adafruit_MAX31865(17, 16, 21, 22);
// use hardware SPI, just pass in the CS pin
//Adafruit_MAX31865 max = Adafruit_MAX31865(10);
// The value of the Rref resistor. Use 430.0 for PT100 and 4300.0 for PT1000
#define RREF 421 //0℃の水と100℃の熱湯でセンサ・モジュール毎に校正
//#define RREF 430.0
// The 'nominal' 0-degrees-C resistance of the sensor
// 100.0 for PT100, 1000.0 for PT1000
#define RNOMINAL 100.0
float targetTemp = 25;
boolean heaterOn = true;
int counter = 0; //まとめてデータを送るため測定回数をカウント
// DHT22関連
// for DHT22,
// VCC: 5V or 3V
// GND: GND
// DATA: 2
int pinDHT22 = 2;
SimpleDHT22 dht22(pinDHT22);
float DHTtemp, DHThumid;
String postValues(String values_to_post) {
M5.Lcd.println("postValues");delay(1000);
if (client.connect(host, 443)){
LcdInit();
M5.Lcd.println("Posting data...");
client.println("POST " + exec_url + " HTTP/1.1");
client.println("HOST: " + (String)host);
client.println("Connection: close");
client.println("Content-Type: text/plain");
client.print("Content-Length: ");
client.println(values_to_post.length());
client.println();
client.println(values_to_post);
delay(100);
while (client.available()) {
char c = client.read();
M5.Lcd.print(c);
}
client.stop();
delay(1000);
return "post end";
}
else {
return "ERROR";
}
}
void connectingWiFi(){
boolean WiFiOn; // WiFi接続したらtrue
int n_trial, max_trial = 10; //WiFi接続試行回数とその上限
WiFi.mode(WIFI_STA);
WiFi.disconnect();
WiFi.begin(ssid, password);
LcdInit();
M5.Lcd.print("Connecting to WiFi");
// attempt to connect to Wifi network:
n_trial = 0;
while (WiFi.status() != WL_CONNECTED && n_trial < max_trial) {
M5.Lcd.print(".");
// wait 1 second for re-trying
n_trial++;
delay(1000);
}
LcdInit();
if (WiFi.status() == WL_CONNECTED)
{WiFiOn = true;
M5.Lcd.print("Connected to ");
M5.Lcd.println(ssid);
M5.Lcd.print("IP: ");
M5.Lcd.println(WiFi.localIP()); }
else
{WiFiOn = false;
M5.Lcd.print("WiFi connection failed"); }
delay(1000);
}
void makeFilename(){
LcdInit();
connectingWiFi();
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
M5.Lcd.println("configTime OK.");
getLocalTime(&timeinfo); //時刻取得
startMillis = millis(); //ミリ秒取得
startSec
= (timeinfo.tm_hour)*3600+timeinfo.tm_min*60+timeinfo.tm_sec;///シート名にする時刻の0:00:00(午前零時)からの秒数
M5.Lcd.print("Start Sec =");M5.Lcd.println(startSec);
M5.Lcd.print("Start Millis =");M5.Lcd.println(startMillis);
filename = "TempData_"; // ファイル名初期化 最初に"TempData", 以下、年月日からシート名作成
filename += String(timeinfo.tm_year-100,DEC);//1900年から数えるので調整
if(timeinfo.tm_mon<9){filename += "0";}//0からはじまるので調整
filename += String(timeinfo.tm_mon+1,DEC);//0からはじまるので調整
if(timeinfo.tm_mday<10){filename += "0";}
filename += String(timeinfo.tm_mday,DEC);
filename += ".csv";
M5.Lcd.print("Filename =");M5.Lcd.println(filename);
sheetname =""; // シート名初期化 以下、時分秒からシート名作成
if(timeinfo.tm_hour<10){sheetname = "0";}
sheetname += String(timeinfo.tm_hour,DEC);
if(timeinfo.tm_min<10){sheetname += "0";}
sheetname += String(timeinfo.tm_min,DEC);
if(timeinfo.tm_sec<10){sheetname += "0";}
sheetname += String(timeinfo.tm_sec,DEC);
M5.Lcd.print("Sheetname =");M5.Lcd.println(sheetname);
WiFi.disconnect();
delay(1000);
}
void LcdInit(){
M5.Lcd.clear(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(WHITE, BLACK);
M5.Lcd.setCursor(0, 0);
}
float readTemp(){
// エラーチェックは残しておく エラーが出た場合には10秒表示
// Check and print any faults
uint8_t fault = max31865.readFault();
if (fault) {
M5.Lcd.print("Fault 0x"); M5.Lcd.println(fault, HEX);
if (fault & MAX31865_FAULT_HIGHTHRESH) {
M5.Lcd.println("RTD High Threshold");
}
if (fault & MAX31865_FAULT_LOWTHRESH) {
M5.Lcd.println("RTD Low Threshold");
}
if (fault & MAX31865_FAULT_REFINLOW) {
M5.Lcd.println("REFIN- > 0.85 x Bias");
}
if (fault & MAX31865_FAULT_REFINHIGH) {
M5.Lcd.println("REFIN- < 0.85 x Bias - FORCE- open");
}
if (fault & MAX31865_FAULT_RTDINLOW) {
M5.Lcd.println("RTDIN- < 0.85 x Bias - FORCE- open");
}
if (fault & MAX31865_FAULT_OVUV) {
M5.Lcd.println("Under/Over voltage");
}
max31865.clearFault();
delay(1000);
}
//測定値を返す
return max31865.temperature(RNOMINAL, RREF);
}
void setHeater(int heaterPower){
if (heaterPower == 100){
digitalWrite(26, HIGH);//Relay ON
M5.Lcd.println("RelayOn");
}
else {digitalWrite(26, LOW);//Relay OFF
M5.Lcd.println("RelayOff");
}
delay(5 * 60 * 1000);//5分間隔
}
void senddata(){
LcdInit();
M5.Lcd.println("Send data to Google Spreadsheet");
connectingWiFi();
String values = filename;
values += ",";
values += sheetname;
values += ", 4";
for (int i=0;i<n_of_data;i++){
values += ",";
values += String(timeData[i],DEC);
values += ",";
values += String(bottleTempData[i],2);
values += ",";
values += String(ambTempData[i],2);
values += ",";
values += String(heaterData[i],2);
}
M5.Lcd.print("values =");
M5.Lcd.println(values);
M5.Lcd.print("Making Data Done\n");
delay(1000);
String response = postValues(values);
M5.Lcd.print("Response : ");
M5.Lcd.println(response);
delay(1000);
WiFi.disconnect();
M5.Lcd.clear();
}
void read_DHT22(){
int err = SimpleDHTErrSuccess;
if ((err = dht22.read2(&DHTtemp, &DHThumid, NULL)) != SimpleDHTErrSuccess) {
M5.Lcd.println("Read DHT22 failed, err="); M5.Lcd.println(err);delay(2000);
return;
}
return;
}
void setup(){
pinMode(26, OUTPUT);//Relay pin : 26
M5.begin();
M5.Lcd.setTextSize(2);
M5.Lcd.println("Temp Control Test this is setup()");
max31865.begin(MAX31865_3WIRE); // set to 2WIRE or 4WIRE as necessary
makeFilename();
delay(1000);
M5.Lcd.clear();
}
void loop() {
LcdInit();
float temp = readTemp();
bottleTempData[counter] = temp;
read_DHT22();
ambTempData[counter] = (float)DHTtemp;
if (temp < targetTemp){
heaterOn = true;
}
else{heaterOn = false;}
heaterData[counter] = heaterOn;
timeData[counter] = (millis() - startMillis )/1000; //シート名作成からの経過秒数
M5.Lcd.setCursor(0,50);
M5.Lcd.print("counter = "); M5.Lcd.println(counter);
M5.Lcd.print("timeData = "); M5.Lcd.println(timeData[counter]);
M5.Lcd.print("bottleTemp = "); M5.Lcd.println(bottleTempData[counter]);
M5.Lcd.print("ambientTemp = "); M5.Lcd.println(ambTempData[counter]);
M5.Lcd.print("targetTemp = "); M5.Lcd.println(targetTemp);
M5.Lcd.print("heaterOn = "); M5.Lcd.println(heaterOn);
if (heaterOn){setHeater(100);}
else {setHeater(0);}
LcdInit();
counter++;
if (counter >= n_of_data){senddata(); counter = 0;};
}
コメント