Manual

do

Maker

.

com

Manômetro digital para cálculo de CO² da cerveja no priming

Manômetro digital para cálculo de CO² da cerveja no priming

Cálculo de CO2 da cerveja?

Esse artigo é um projeto completinho, pra você montar o produto e vender. Estou cheio de orgulho em tê-lo executado e espero que seja valorizado o tanto que merece, porque até onde eu sei, não existe um dispositivo para esse propósito ainda. Mas primeiro vamos às explicações.

A cerveja artesanal tem ocupado cada vez mais lugar no mercado nacional de cervejas . Sem entrar em detalhes sobre a cerveja, mas passando brevemente sobre as fases do processo, vou explicar a função desse manômetro digital.

Introdução

A produção de cerveja é dividida em 4 partes; braçagem, fermentação, maturação e priming. Essa última fase é o envase e re-fermentação, onde será formado o gás e espuma da cerveja. Cada estilo de cerveja tem uma carbonatação diferente (você pode encontrar a tabela procurando por "tabela de carbonatação por estilo"). A quantidade de gás e espuma depende da quantidade de açucar invertido utilizado no envase, além do tempo de refermentação e temperatura ambiente. Para dimensionar a quantidade de açucar invertido para o processo de priming, também há um cálculo que você encontra no google, fácil, fácil, mas vou deixar uma dica:

gL = V/0.286

O "V" é o volume. Supondo que você queira uma cerveja com 2.5 de volume de CO²:

gL = 2.5/0.286 = 8,7

É comum utilizar 8g/L.

Como fazemos envase, devemos dividir isso pela quantidade de cerveja que vai na garrafa. Um exemplo simples é a garrafa inglesa de 500ml, bastando então colocar 4ml de açucar invertido. Para tal, você precisa fazer o açucar invertido na mesma proporção de água e açúcar. Normalmente faço 1/2 litro de água para 500 gramas de açucar e coloco 3 gotas de ácido lático. Enfim, o objetivo desse artigo não é ensinar a fazer cerveja, mas tendo essa noção, já fica mais claro o propósito da medição de CO² produzido nessa última etapa da cerveja.

Durante uma semana, o priming estará produzindo o gás e a espuma da cerveja e isso aumentará a pressão dentro da garrafa; esse prazo pode ser estendido para até 2 semanas. Não importa se é uma garrafa ou um tambor de 50 litros; o volume de CO² é proporcional ao volume da cerveja, por isso a base de cálculo é a mesma. Só que os problemas começam por aí; muitas pessoas vão pela regra de deixar uma semana e às vezes poderiam deixar até 2 semanas fazendo priming para melhorar os resultados da carbonatação; ou, em alguns casos, reduzir o tempo para evitar a supercarbonatação, que pode inclusive explodir a garrafa. Mas são poucos os cervejeiros artesanais que tomam todos os cuidados necessários, incluindo o controle da carbonatação. Se quiser conhecer um pouco da divertida história da cerveja, recomendo esse link.

Medição do volume de CO²

Agora vou falar um pouco da parte que me motivou a criar esse projeto; se você olhar por aí, pode procurar "manômetro cervejeiro" ou "manômetro borracheiro", porque no final é exatamente a mesma coisa. O manômetro tem o propósito de medir a pressão pneumática em PSI, bar ou em ambos, mas o volume de CO² não tem nenhuma relação direta com essas unidades, portanto, quem tem o "manômetro cervejeiro", precisa no mínimo procurar no google por uma calculadora de carbonatação para ficar dia a dia aferindo o priming. E não pára por ai; é fundamental ter a temperatura do momento, ou seja, é necessário um manômetro e um termômetro de boa qualidade (mercúrio) e esses parâmetros devem ser passados para a calculadora online. Bem, na verdade se passa a temperatura e admirem - a carbonatação desejada, para então obter o PSI final e relacionar com o PSI do manômetro da garrafa. Então, por que não ter um dispositivo que faça por sí só todo o trabalho? Eu sinceramente não sei porque não existe nada pronto; aliás, não existia. E se você quiser, poderá fazer o seu também!

Ainda antes de colocarmos a mão na massa, já vou explicar superficialmente o cálculo para o volume de CO² desejado ( e digo superficialmente porque envolve muita química e eu mal saberia explicar o que estudei pra fazer isso - não é minha área).

P = pressão

T = temperatura atual

V = volume de CO² desejado

Tendo essas variáveis, basta fazer a substituição e calcular diretamente operação a operação:

P = -16.6999 - 0.0101059 T + 0.00116512 T^2 + 0.173354 T V + 4.24267 V - 0.0684226 V^2

Supondo que você deseje fazer uma IPA (carbonatação "melhorada" em torno de 2,5 volumes de CO²) e no momento da medição a garrafa está em uma temperatura ambiente de 24 graus. Primeiro, converta graus Celsius para Fahrenheit:

T = 1.8\*24+32 = 75,2

P = -16.6999 - 0.0101059 \* 75.2 + 0.00116512 \* 75.2^2 + 0.173354 \* 75.2 \* 2.5 + 4.24267 \* 2.5 - 0.0684226 \* 2.5^2

Isso resulta em 31.89 PSI, que pode tranquilamente ser arredondado para 31.9 PSI. Então, quando seu manômetro chega a 31.9 PSI, você atingiu a carbonatação desejada, considerando sempre a temperatura a cada medição.

Conversão das unidades de pressão

Aí entra a questão; como medir o PSI de forma digital? - Eu digo que é bastante simples, bastando utilizar um barômetro como o BMP180 ou BMP280 e uma CPU/MCU. Foi por isso que escrevi o artigo anterior, utilizando o BMP180 com o ESP8266. Nesse artigo você encontra no código as conversões, mas vou explicá-las aqui.

A unidade padrão do sensor é Pascal, mas normalmente utiliza-se hecto Pascal na medição da pressão atmosférica, então pegamos o valor do barômetro em Pascal e dividimos por 10^2, obtendo o resultado em hecto Pascal.

Depois precisamos saber o valor em PSI, sendo que 1 PSI é igual a 0.000145038 Pascal. Basta multiplicar o Pascal pelo PSI ou então fazer hectoPascalPSI100. Mas não se preocupe, tudo isso já está disposto no código do artigo anterior, só vamos incrementar o conteúdo.

Material

Para esse projeto, os seguintes materiais serão necessários:

  • 1 ESP8266
  • 1 barômetro BMP180 ("tem" que ser o BMP180).
  • 10 CM de mangueira de silicone
  • 1 seringa de injeção para 20ml (diâmetro de 2cm ou qualquer uma que caiba o sensor dentro)
  • 1 fita Hellerman bem pequena (para prender firmemente a mangueira à ponta plástica da seringa)
  • cola quente
  • 1 tampa de silicone para garrafa ou uma rolha
  • fios para o wiring
  • alimentação para os componentes

Interface com o usuário

Dessa vez eu queria verazmente entregar algo pronto, ao nível da parte física. Já que é pra simplificar, que seja um resultado final realmente simples. E foi o que fiz. Passei uns dois dias pesquisando como fazer uma interface web bacana pra colocar dentro de um servidor web no ESP8266. Não achei nada que atendesse, mas pasmem, no próprio exemplo contido no framework do Sming tinha um belíssimo modelo de servidor web utilizando bootstrap! Não fiz muito com a interface, tentei deixar o mais original possível e só incrementei com informações advindas do ESP8266, mais especificamente da leitura e conversão do BMP180. Sequer removi os comentários no código de exemplo, também fiz apenas minhas implementações e daí mais uma vez defendo a utilização desse fantástico framework. Primeiro, um screenshot de um trecho da janela:

co2-project.webp

Na janela está primeiramente a opção de manter um LED ligado quando atingir a carbonatação desejada, assim sequer será necessário acessar a interface para saber como está a carbonatação. Logo abaixo dos botões do LED, explico sobre as condições do momento, como a pressão em PSI, temperatura em graus Celsius, estilo selecionado para carbonatação e a pressão necessária para atingir o objetivo. Depois, informações menos importantes como o IP pego por DHCP, números de reloads da página e status da carbonatação. Alguns campos são apenas texto estático, mas um pouco mais abaixo se faz a seleção do estilo a carbonatar, onde variáveis com valores predefinidos estão dispostas no código. E mais texto.

Personalização

Talvez você queira fazer mais algumas modificações no código, e nesse caso será necessário preparar um ambiente para tal. No meu caso (sempre) uso Linux e as coisas tendem a ser mais fáceis assim.

O firmware com esse código faz uma validação a cada reset do ESP8266. Caso não encontre os arquivos necessários para o funcionamento do servidor web, ele faz download a partir de uma URL estática que está no código. Logo, se você quer mudar a interface, precisará também mudar as URLs de modo a fazê-lo buscar os arquivos em outro endereço. Para tal, instalei o servidor web nginx no Linux. Faça-o:

sudo apt-get update && sudo apt-get install nginx

Após instalado, copie os arquivos (dentre os quais é provável que você só mude o index) para /var/www/html/. Do mesmo modo, mude as linhas de código que apontam para esse endereço:

downloadClient.downloadFile("http://simple.anakod.ru/templates/index.html");

Deixe as 3 linhas com o endereço do seu computador, o qual você acabou de instalar o nginx. Por exemplo:

downloadClient.downloadFile("http://192.168.1.2/index.html");

Assim, quando você subir o firmware novinho, ele buscará pela primeira vez diretamente em seu servidor web e receberá as mudanças executadas por você.

Os arquivos que devem ser copiados podem ser previamente baixados das URLs originais do código. Simplesmente crie um diretório (co2, por exemplo) e então proceda com os comandos:

mkdir co2 && cd co2
wget -c http://simple.anakod.ru/templates/index.html
wget -c http://simple.anakod.ru/templates/bootstrap.css.gz
wget -c http://simple.anakod.ru/templates/jquery.js.gz

Modifique o que desejar, depois faça a cópia para /var/www/html para que em seu primeiro boot, o firmware busque no seu nginx.

...mas é necessário utilizar o Sming...

...e para isso, você precisará seguir os passos desses outros tutoriais antes de programar:

ESP8266 a lá Arduino com Sming em um Container Primeiros passo com o Sming dentro de um Container Conectar o NetBeans ao container Sming do ESP8266 Dicas rápidas com Sming no ESP8266

Depois que você tiver configurado todo seu ambiente para compilação do firmware, aí você poderá iniciar um novo projeto baseado em código existente, como exemplificado no artigo "Conectar o NetBeans ao container Sming do ESP8266". O código base pode ser copiado com esse comando:

scp -r root@172.17.0.2:/opt/sming/HttpServer_Bootstrap .

Use esse código base, não esqueça de mudar em Run a execução para "make flash". Lembre-se, "Run" pode ser encontrado em "Propriedades" do projeto e também está descrito no artigo supracitado.

O código utilizado nesse artigo pode ser baixado do github do projeto, de modo que você pode dispensar o download utilizando wget, pois coloquei os 3 arquivos no git, é só clonar e usar. Não tem o git instalado? Preceda o clone com sua instalação:

apt-get update && apt-get install git 
git clone https://github.com/DjamesSuhanko/BeerCO2meter.git

 

O projeto inteiro está nesse git, então até a cópia de dentro do container é dispensável; você poderá criar um novo projeto a partir do código contido em HttpServer_Bottstrap.

Uma característica interessante desse servidor web é a interface de fácil manipulação entre as variáveis web e as variáveis do código em C. Esses recursos facilitadores importantíssimos podem ser notados na função onIndex(), que é a função executada a cada carga da página html (quando você clica em algum recurso da página no browser).

O código ficou assim:

#include <user_config.h>
#include <SmingCore/SmingCore.h>
#include <Libraries/BMP180/BMP180.h>

#define WIFI_SSID "ColoqueSuaRede" // Put you SSID and Password here
#define WIFI_PWD "ColoqueSuaSenha"

//1 Pascal = 0,000145038 PSI
#define PSI 0.000145038
#define BAR 0.0689476
#define VOL 2.5 //serve para a maioria dos estilos

#define LED_PIN 4 // D4 no Wemos D1 Mini

#define ENGLISH   1.8
#define AMERICAN  2.5
#define BELGIAN   2.3
#define GERMANY   3.4
#define STRONG    3.8
#define DANGEROUS 4.0

HttpServer server;
int counter = 0;

String carbonation_status  = "Em andamento";
bool activate_alarm        = false;//confirma se o led de alarme deve ficar ligado
float pressure_in_psi      = 0.0;//variavel que guarda a pressão em PSI
float currentTemperature   = 0.0;//temperatura em graus Celsius
String beer_style          = "Nenhum";//estilo monitorado
float CO2                  = 0.0;//carbonatacao selecionada
float pressure_target      = 0.0;//PSI necessário para atingir o objetivo
float farenheit            = 0.0;//guarda a conversão de Celsius para Fahrenheit

BMP180 barometer;//instância do barômetro
Timer getValues;//timer de leitura do BMP180

float calculateCO2(float volume){
    float pressure_needed = -16.6999 - 0.0101059 * farenheit + 0.00116512 * pow(farenheit,2) + 0.173354 * farenheit * volume + 4.24267 * volume - 0.0684226 * pow(volume,2);
    return pressure_needed;
}

void bmp180CB(){
    //pressao em hPa
    float currentPressure = (float) barometer.GetPressure()/100.0;

    //converte para PSI
    pressure_in_psi = (float) currentPressure*PSI*100.0;
    float pressure_in_bar = (float) currentPressure/1000.0;

    //Temperatura em graus Celsius
    currentTemperature = barometer.GetTemperature();

    Serial.print("\tTemperaturas: ");
    Serial.print(currentTemperature);
    Serial.print(" C (");

    //converte Celsius para Fahrenheit
    farenheit = 1.8*currentTemperature+32;
    
    pressure_target = calculateCO2(CO2);
    
    Serial.print(farenheit);
    Serial.println(" F)");

    Serial.print("Pressao atual em hPa: ");
    Serial.println(currentPressure);
    Serial.print("Pressao atual em PSI: ");
    Serial.println(pressure_in_psi);
    Serial.print("Pressao atual em BAR: ");
    Serial.println(pressure_in_bar);
    Serial.print("Pressao ideal para o estilo: ");
    Serial.print(pressure_target);
    Serial.println(" PSI");
    Serial.println("-------------------");
    
    if (pressure_target <= pressure_in_psi && pressure_target > 0){
        if (activate_alarm){
            digitalWrite(LED_PIN,HIGH);
        }
        carbonation_status = "Concluido";
    }
    else if (pressure_target < 0){
        carbonation_status = "Não iniciado";
    }
    else{
        carbonation_status = "Em andamento";
    }
}

void onIndex(HttpRequest &request, HttpResponse &response)
{
    counter++;
    bool led = request.getQueryParameter("led") == "on";
    //digitalWrite(LED_PIN, led);
    activate_alarm = led;

    beer_style = request.getQueryParameter("style");

    if (beer_style == "english"){
        CO2 = ENGLISH;
    }
    else if (beer_style == "american"){
        CO2 = AMERICAN;
    }
    else if (beer_style == "germany"){
        CO2 = GERMANY;
    }
    else if (beer_style == "belgian"){
        CO2 = BELGIAN;
    }
    else if (beer_style ==  "high"){
        CO2 = STRONG;
    }
    else if (beer_style == "dangerous"){
        CO2 = DANGEROUS;
    }
    bmp180CB();

    TemplateFileStream *tmpl = new TemplateFileStream("index.html");
    auto &vars               = tmpl->variables();
    vars["counter"]          = String(counter);
    vars["IP"]               = WifiStation.getIP().toString();
    vars["PSI_CARBON"]       = String(pressure_in_psi);
    vars["TEMPERATURE"]      = String(currentTemperature); 
    vars["BEER_STYLE"]       = beer_style;
    vars["PSI_TO_TARGET"]    = pressure_target;
    vars["CARB_STATUS"]      = carbonation_status;
    response.sendTemplate(tmpl); // this template object will be deleted automatically
}

void onHello(HttpRequest &request, HttpResponse &response)
{
    response.setContentType(ContentType::HTML);
    // Use direct strings output only for small amount of data (huge memory allocation)
    response.sendString("Sming. Let's do smart things.");
}

void onFile(HttpRequest &request, HttpResponse &response)
{
    String file = request.getPath();
    if (file[0] == '/')
        file = file.substring(1);

    if (file[0] == '.')
        response.forbidden();
    else
    {
        response.setCache(86400, true); // It's important to use cache for better performance.
        response.sendFile(file);
    }
}

void startWebServer()
{
    server.listen(80);
    server.addPath("/", onIndex);
    server.addPath("/hello", onHello);
    server.setDefaultHandler(onFile);

    Serial.println("\r\n=== WEB SERVER STARTED ===");
    Serial.println(WifiStation.getIP());
    Serial.println("==============================\r\n");
}

Timer downloadTimer;
HttpClient downloadClient;
int dowfid = 0;
void downloadContentFiles(){
    if (downloadClient.isProcessing()){
        return; // Please, wait.  
    }  

    if (downloadClient.isSuccessful()){
        dowfid++; // Success. Go to next file!
    }

    downloadClient.reset(); // Reset current download status

    if (dowfid == 0){
        downloadClient.downloadFile("http://192.168.1.7/index.html");
    }
    else if (dowfid == 1){
        downloadClient.downloadFile("http://192.168.1.7/bootstrap.css.gz");
    }
    else if (dowfid == 2){
        downloadClient.downloadFile("http://192.168.1.7/jquery.js.gz");
    }
    else{
            // Content download was completed
            downloadTimer.stop();
            startWebServer();
    }
}

// Will be called when WiFi station was connected to AP
void connectOk()
{
    Serial.println("I'm CONNECTED");

    if (!fileExist("index.html") || !fileExist("bootstrap.css.gz") || !fileExist("jquery.js.gz"))
    {
        // Download server content at first
        downloadTimer.initializeMs(3000, downloadContentFiles).start();
    }
    else
    {
        startWebServer();
    }
}

void sta_if(){
    WifiStation.enable(true);
    WifiStation.config(WIFI_SSID, WIFI_PWD);
    WifiAccessPoint.enable(false);

    // Run our method when station was connected to AP
    WifiStation.waitConnection(connectOk);
}

void init(){
    pinMode(LED_PIN, OUTPUT);

    Serial.begin(115200); // 115200 by default
    Serial.systemDebugOutput(true); // Enable debug output to serial
    
    //loadVectors();
    
    sta_if();

    Wire.begin();
    
    if(!barometer.EnsureConnected()){
        Serial.println("Nao pude me conectar ao BMP180.");
    }
    
    barometer.Initialize();
    barometer.PrintCalibrationData();
    
    //Change CPU freq. to 160MHZ
    System.setCpuFrequency(eCF_160MHz);
    Serial.print("New CPU frequency is:");
    Serial.println((int)System.getCpuFrequency());
    
    Serial.print("Iniciando leitura");
    getValues.initializeMs(60000,bmp180CB).start();
}

Assembling

Agora é hora de montar o projeto.Eu ia descrever, mas isso  pode ser feito de muitas maneiras, então resolvi mostrar as imagens da montagem no video. Em seguida, mostro a interface, a interação da interface com o código do ESP8266 e vice-versa. A alimentação pode ser feita pela USB do computador, alimentação por carregador de telefone ou através de um power bank, mas nesse último caso, lembre-se de que o ESP8266 está a 160MHz de clock e ligado 24x7. Não repare meu erro grotesco chamando "chaves" de "colchetes". Também não repare a palavra "djavuzejável".

https://youtu.be/690iW4IcSBw

Para concluir, esse post será atualizado com um sensor específico para pressão pneumática, portanto recomendo que aguarde o próximo artigo relacionado antes de montar o seu, e explicarei as diferenças.

Atualização

O sensor BMP180 só mede até 110kPa, de modo que o resultado é de apenas 16 psi. Mas sem problemas; basta substituí-lo pelo sensor MPX5700, que é um sensor de pressão pneumática. Logo mais coloco os artigos relacionados a esse sensor, porque ele serve pra outra coisa legal; medir a quantidade de cerveja da bombona. Não percam, sigam o site clicando ali na coluna da direita em nossa página do facebook, inscrevam-se no news letter (também ali em cima à direita) e é só aguardar!

Inscreva-se no nosso newsletter, alí em cima à direita e receba novos posts por email.

Próximo post a caminho!

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.