Несколько лет я ставлю несколько чанов для брожения квасов и медовух. Сырье использую разное, дрожжи (если использую), то тоже разные - подбираю, смотрю, стараюсь обеспечить оптимальные условия для них, что не всегда легко, т.к. в своей “продукции” я не использую вообще свекольный сахар, только естественные моносахара и на крайний случай декстрозу для повторного дображивания, стараясь обходиться без кислотного гидролиза полисахаров вовсе.
Понятно, что при таком подходе всяческие рецепты из интернета без понимания не очень годятся. Впрочем, в целом они мне уже и не очень нужны, т.к. в целом процесс я понимаю и контролирую. Что, впрочем, требует постоянных измерений. Я постоянно контролирую PH сусла, его температуру, соленость изначального сырья, влажности и температуру в помещения брожения, уровень сахара на всех этапах и скорость брожения.
Какое-то время я обходился бумажкой и ручкой для записи всего этого дела и по ней контролировал сроки проверки сусла, время снятия с осадка и так далее. Пока не стал путаться.
В итоге мне это надоело и за пару часов я быстро накидал себе сервис для контроля брожения.

Во-первых, мне хотелось иметь в одном месте полноценную базу данных по каждому чану, чтобы более детально и без потерь сравнивать и делать выводы. Самое же главное, - чтобы не забывать. Такое уже со мной случалось - как артефакт в подвале до сих пор стоят четыре 38 литровых чана, не снятые вовремя с осадка, и превратившися в брагу.
Во-вторых, хотелось автоматизировать наиболее значимые измерения - температуру в помещениях и в месте, куда выведана вентиляция подвала, а также самого сусла. Это дело благое, так как позволяет по динамике более детальной, чем “когда на глаза попадется и вспомнишь”, показателей много чего понять. Что именно и как - это тема совсем отдельная, поэтому пока только про сервис.
В-третьих, хотелось бы иметь напоминания автоматические о том, что пора предпринять какие-то действия, ну и предупреждения: чтобы не забыть и при необходимости делигировать. В варианте “на бумажке” делигировать работу с чанами тяжело - надо все самому в голове держать.
Решил не измудряться и в качестве БД использовать Google Sheets, настроив там экспорт в CSV, а все необходимые мне расчетные формулы забив прямо в таблицу. Это достаточно удобно - не надо мудрить интерфейсы, все понятно и, главное, - быстро.
Поля получились следующие:
sensor_id - uid датчика температуры
Название емкости
Объем емкости
Дата постановки на брожение
Объем сока
Начальная масса добавленного меда
Начальная масса добавленной фруктозы
Начальная масса добавленной декстрозы
Начальная масса добавленных полисахаров
PH
Сахаристость
Дата снятия с осадка
Дата постановки на повторное брожение
Дата повторного снятия с осадка
Дата купажа
Дата добавления фруктозы
Масса добавления фруктозы
Дата добавления декстрозы
Масса добавления декстрозы
Дата добавления полисахаров
Масса добавления полисахаров
Комментарии
Архив - флаг того, что процесс закончен
Все массы добавляемого сырья и даты снятия с осадка и добавления при этом рассчитываются сразу по шаблону типа сырья в Google Sheets. Хоть речь о примитивных формулах, это позволяет не загружать голову и ничего не записывать.
В комментарии при этом пишу значимую информацию - например, особенности сырья или название и марку применяемых дрожжей, потом анализирую по итогам, просто проставляя на чанах, а потом и на бутылках (после купажа), маркером номер. Таким образом, через год-два можно воссоздать всю историю напитка и сделать выводы.
Измерения построил максимально просто - использовал ESP 8266 и самые ходовые 1-wire термометры DS1820 для чанов (чтобы не тянуть к каждому по отдельному проводу), а для измерения влажности в помещениях - три датчика DHT11. Показания раз в 7 секунд отдаю на сервер по TCP. Ниже код сбора показаний с датчиков - все там банально за исключением того, что json с показаниями передаю в Base64. Это вызвано просто тем, что не хотелось менять специфические настройки файрволла у себя на сервере.
local results = {} -- DS1822 results
-- Read DS1822
local function readout(temp)
for addr, temp in pairs(temp) do
local uid = string.format('%s',('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(addr:byte(1,8)))
results[uid] = temp
print(uid,temp)
end
end
local function read_sensors()
t:read_temp(readout, ds_pin, t.C)
end
-- Read the DHT11 sensor
local function dht_read(dhtpin)
local status, temp, humi, temp_dec, humi_dec = dht11.read(dhtpin)
if status == dht.OK then
print("DHT Temperature:"..temp..";".."Humidity:"..humi)
return {['temp'] = temp, ['humi'] = humi}
elseif status == dht.ERROR_CHECKSUM then
print( "DHT Checksum error." )
elseif status == dht.ERROR_TIMEOUT then
print( "DHT timed out." )
end
return {}
end
-- Data for TCP server
function data_out()
read_sensors()
local pack = {}
pack['id'] = uid
pack['room'] = dht_read(dhtpin_room)
pack['cellar'] = dht_read(dhtpin_cellar)
pack['weather'] = dht_read(dhtpin_weather)
pack['sensors'] = results
return encoder.toBase64(sjson.encode(pack))
end
На сервере как всегда OpenResty, для хранения и обработки результатов с датчиков использовал встраиваемую FlatDB. Немного интерфейсов и все готово. Для Google даже API не использовал - просто по крону дергаю CSV и все.
Уведомления зашил прямо в код разбора CSV и приема показаний с датчиков:
for line in io.lines(ngx.var.document_root..'/tanks.csv') do
if count>0 then
local data = array.explode(line,',')
results[count] = {notification = {typ = 'is-success', text = 'Брожение протекает нормально'}, status = 'is-primary'}
for n,value in pairs(data) do
local key,text = array.first(fields[n])
if key:find('date_') and value ~='' and pcall(date(),value) and date(value) < date() then
results[count].notification = {typ = 'is-danger', text = text}
end
results[count][key] = {value = value, text = text, order = n}
end
if not db[results[count].sensor_id.value] then
db[results[count].sensor_id.value] = {
{temp = 0, date = date():fmt('%Y-%d-%m')},
{temp = 0, date = date():fmt('%Y-%d-%m')}
}
db:save()
end
results[count].stat = db[results[count].sensor_id.value]
if (results[count]['date_blending'].value ~= '' and pcall(date(),results[count]['date_blending'].value) and date(results[count]['date_blending'].value) < date()) or (results[count]['date_end'].value ~= '' and date(results[count]['date_end'].value) < date()) then
results[count].status = 'is-success'
end
end
count = count + 1
end
При приеме данных с датчиков просто проверяю пределы и записываю в БД уже с уведомлением:
local limits = {
weather = {
temp = {
optimal = {0,10},
critical = {-25,35}
},
warm = {
optimal = {40,60},
critical = {10,90}
}
},
cellar = {
temp = {
optimal = {10,12},
critical = {8,14}
},
warm = {
optimal = {65,80},
critical = {50,90}
}
},
room = {
temp = {
optimal = {18,24},
critical = {16,28}
},
warm = {
optimal = {60,75},
critical = {50,80}
}
}
}
Посмотреть можно тут: http://chan.ulgrad.ru/
Программная часть тривиальна и проста, и трудозатраты на нее - это реально пара часов, но вот с конструктивом размещения датчиков дело другое - хотя из обвязки для всего этого нужен всего один резистор, заставляет задуматься вопрос другой - как и где лучше мерить в чане температуру сусла и как герметично и химически нейтрально оформить датчики?