
O que é Sous Vide?
Sous vide é o que existe de mais moderno em cozimento de alimentos, através desta técnica é possível cozinhar alimentos de forma mais consistente e delicada como nunca antes feito.

Quer fazer comida deliciosa, perfeitamente cozida utilizando um robô ? Quem não quer? Este projeto irá mostrar-lhe como construir o seu próprio "Sous viduino", uma panela automatizada que faz ovos perfeitos, bifes suculentos e peixes tenros, sem toda a "escravização sobre um fogão." Tudo isso é possível devido a um recente avanço na tecnologia de cozimento, em vez de usar uma frigideira ou panela utiliza-se um "sous vide" (pronuncia-suu veed), máquina que aquece a comida em uma espécie de cruzamento entre uma jacuuzi e uma panela de barro.

Nós amamos boa comida tanto quanto amamos a ciência e a tecnologia, então é claro que teríamos que construir o nosso próprio controlador sous vide. Esse projeto transforma uma panela de arroz barata em um instrumento de cozinha de precisão capaz de manter a temperatura de cozimento dentro de + / - 0,1 C. Controlando precisamente a temperatura você pode garantir que os alimentos são cozidos exatamente para o nível desejado de cozimento e não-mais. Isso faz com que o método de cozimento sous vide seja perfeito para tudo, desde um ovo cozido até um bife cozido perfeitamente de borda a borda o que é meio raro.
Materiais para o projeto
O Controlador
Você não precisa de um microcomputador poderoso para conduzir esta configuração, nós selecionamos o Arduino para este projeto por causa da excelente PID (bem documentadas) e bibliotecas autotune disponíveis para ele. A Adafruit RGB / LCD shield com botões integrados também possui perfeita interface com o usuário facilitando seu manuseio.
Para construir este controlador, você vai precisará de:
- Maquina de arroz
- Arduino UNO
- Proto Shield ou Wing Shield
- RGB LCD Shield ( display positivo ou negativo)
- Interruptor de força
- Sensor de Temperatura DS18B20 para altas temperaturas , você também pode usar o tipo DS18B20 Waterproof padrão se não estiver pensando em aquecê-lo acima de 90C, que é muito raro para um Sous Vide.
- Tubos de calor Food Grade
- Cabo de Extensão Servo
- Conector de fio de energia JST In-Line: Masculino e Feminino
Você também vai precisar de algumas ferramentas básicas e materiais de solda:
- Cortadores de fio
- Descascadores de fios
- Ferro de solda
- Solda
- Tubos Enrole
- Pequena chave de fenda Phillips
Seleção da Panela
Tipo
Panelas de arroz e aparelhos Slow Cooker tem os elementos básicos que estamos procurando:- A vasilha para colocar água
- Um elemento de aquecimento elétrico
Capacidade
Você precisa de um recipiente substancialmente maior do que a comida que você pretende cozinhar nele. Ele precisa ter um grande volume suficiente de água com bastante espaço para circulação mantendo uma temperatura uniforme. Procure uma panela de arroz com capacidade de pelo menos 10 copos (20 xícaras) ou uma panela com pelo menos 4 litros de capacidade.
Controles
Uma vez que estamos preparando nosso próprio controle, escolha a panela com controle manual mais simples que encontrar. Uma com um interruptor simples ou botão de controle seria melhor. Queremos apenas ligá-lo no nível mais alto e conectá-lo a nosso controlador.
(Aqui na Adafruit utilizamos uma "Black and Decker RC880" que possui capacidade de 24 copos que é um tamanho bem grande.
Panelas com controles digitais não são adequadas a menos que você queira arrancar a parte eletrônica, pois o controlador funciona pulsando a alimentação elétrica da panela. Se você fizer isso com uma panela controlada digitalmente, ela só irá desligar!
Construir o controlador
Prepare o Sensor
A maioria dos sous vide são feitos com sacos plásticos fechados, uma exceção a isso são os ovos que são cozidos em suas conchas. Outras receitas (como caudas de lagosta cozido na manteiga ) cozinham o alimento imerso diretamente no líquido de cozimento. Para fazer com que o sensor de temperatura esteja em contato com o alimento de forma segura, corte um pedaço de fita plastica de alta temperatura e coloque envolvendo o sensor, conforme abaixo.
Construir o RGB LCD shield
Siga as instruções deste tutorial para construir e testar o escudo.
Construir o Wing Shield
Siga as instruções em este tutorial para construir o escudo asa.
Note-se que o interruptor, LEDs e resistências não são utilizados para o controlador sous vide, você pode salvar essas peças para o seu próximo projeto.

Instale o Sensor
Perfure ou amplie a abertura de ventilação do vapor para que o cabo do sensor poça ser colocado através da tampa. Posicione o sensor de modo que se estenda aproximadamente até o meio, dentro do recipiente de cosedura, quando a tampa é fechada.


Adicionar abraçadeiras adicionais a cada 4-6 polegas para prender o fio do sensor ao cabo de alimentação.
Terminando os fios do sensor
Vamos usar um cabo de extensão como uma maneira de fazer um sensor destacável. Não é obrigatório mas facilita na hora da retirada para limpeza.
- Descasque e prepare alguns comprimentos de heat-shrink.
- Corte um cabo de extensão servo pela metade.
- Solde a extremidade macho nos fios do sensor.
- Encolha o Heat-shrink para isolar.
A cor do fio do sensor de codificação (à direita) são um pouco estranhas, por isso vamos conectá-las para usar uma cor padrão de codificação para o conector de servo (direita).
- Preto <- Branco (terra)
- Red <- Laranja Stripe (+5 v)
- Branco <- listra azul (Sinal)
Adicione o Resistor
Com a metade fêmea da extensão servo:- Strip e estanho nas extremidades
- Soldar o resistor de 47K ohm que o acompanha entre os fios 5v (vermelho) e sinal (branco).
- Encolha o heat-shrink para isolar.
Empilhá-los!
Ligue o wing shield no arduino e o RGB shield LCD. Fixe a pilha em cima da caixa Elétrica e acrescente algumas presilhas de segurança.
Conecte os cabos
Conecte o cabo de alimentação macho JST no pino 7 e terra no wing shield.
Conecte o cabo JST feminino para os parafusos dos terminais Elétrico caixa. Assegure-se que a polaridade é a mesma que está na outra extremidade.
Ligue a metade macho do cabo na extensão servo:
Ligue a metade macho do cabo na extensão servo:
- Branco -> Pino 2 (Sinal)
- Vermelho -> Pino 3 (5v)
- Black -> Pino 4 (Terra)
Coloque tudo junto:
- Ligue as extremidades macho e fêmea do cabo de extensão servo.
- Conecte os cabos de JST macho e fêmea juntos.
- Ligue a panela ao conector de eletricidade
- Ligue a caixa Elétrica em uma tomada de parede.
Agora você está pronto para carregar algum software!
Software de Controle
As páginas a seguir irão orientá-lo através de vários aspectos do código do controlador, explicando como cada um funciona. A página final desta seção contém o esboço completo do controlador, pronto para fazer o upload para o seu controlador Sous Vide.
PID
Antes de começar a cozinhar temos para resolver um problema simples: como para manter a temperatura da água na panela a uma temperatura determinada durante um longo período de tempo. É realmente fácil de obter água fervervente (basta aquecê-lo até que comece a ferver) ou congelamento (resfriá-lo até que ele se solidifique), mas mantendo uma temperatura de água estável é um pouco mais difícil. A água esfria enquanto se aproxima da comida e a esquenta, mas o quão rápido ele esfria depende da quantidade de água, a temperatura da panela, a temperatura dos alimentos, etc. É difícil de fazer este controle manualmente então vamos automatizar usando controle de feedback PID
O que é um PID?
Precisamos que nosso microcontrolador controle o aquecedor para manter a temperatura estável. Pode-se aquecer a água ligando a panela de arroz (controle), e depois medir a temperatura com o nosso sensor à prova d'água (feedback). Estes componentes estão no meio de um algoritmo que liga-os juntos. O algoritmo PID é um tipo de controle de feedback, nesta aplicação a medição de temperatura é comparada com o valor nominal e a diferença entre eles é chamado erro. O erro é usado para calcular um ajuste para a saída, que controla o elemento de aquecimento.
O nome vem do PID dos três termos na equação utilizada para calcular a saída:
O nome vem do PID dos três termos na equação utilizada para calcular a saída:
- P - O termo proporcional olha para o atual estado do processo. O seu valor é proporcional ao erro de corrente.
- I - O termo integral olha para a história do processo. Seu valor é a integral dos erros do passado.
- D - O Derivative tenta prever o futuro do processo. O seu valor é o derivado ou a taxa de variação no erro ..
Estes três termos são pesos conhecidos como parâmetros de ajuste atribuído:. Kp, Ki e Kd Os três termos são somados para produzir a saída de controle.
A equação básica PID não é tão difícil de implementar e existem muitas implementações PID lá fora. Mas há um monte de 'pegadinhas' para se resolver em um PID com bom desempenho em uma aplicação do mundo real.
Essas dicas foram habilmente dirigidas por Brett Beauregard em sua Biblioteca PID Arduino . E claramente documentado em seu blog: Melhorar a PID para Iniciantes .
Graças a Brett Beauregard que deu permissão para usar essas imagens.
Ajuste Automático
Você já deve ter ouvido falar de "Auto Tuning" como uma maneira de filtrar a voz. Auto-sintonia de um controlador PID não é bem a mesma coisa - em vez de melhorar a sua voz cantando, ele pode ajudá-lo a definir os parâmetros de ajuste inicial para o seu controlador. Cada "sistema" tem diferentes parâmetros inerentes ao mundo físico. Por exemplo, o controlador de sous vide tem que explicar quantos Watts de potência a panela de arroz usa, o quão rápido ele leva para iniciar, o calor específico da água, etc
A função de autotune tenta caracterizar o desempenho do seu sistema de sous vide, interrompendo a saída do controlador para ver como ele responde.
A função de autotune tenta caracterizar o desempenho do seu sistema de sous vide, interrompendo a saída do controlador para ver como ele responde.
Com base na resposta do rompimento, o algoritmo calcula os parâmetros de ajuste Kp, Ki e Kd. Autotune não é perfeito e não pode encontrar a afinação ideal para o seu sistema, mas deverá colocá-lo no caminho.
Para mais detalhes sobre como o algoritmo funciona autotune, leia post de Brett sobre o tema aqui: Arduino PID Autotune Biblioteca.
Interface com usuário
A interface de usuário sous vide permite que você defina a temperatura de cozimento e faça os ajustes dos parâmetros de sintonia do PID. Ela é implementada como uma máquina de estado simples, cada estado implementa a 'tela' de interface de usuário. As máquinas de estado são muitas vezes utilizadas em projetos de microcontroladores semi-complexos onde se quer fazer um monte de configurações e atividades na ordem correta.
Os botões do shield são utilizados para navegar entre as diferentes telas. Após um período de inatividade, este retorna o sistema para a tela de "Run", para exibir o set-point de corrente (a temperatura que desejamos) e temperatura atual do banho, como mostrados abaixo:
Cada estado é implementado como uma função que é chamado, o circuito principal com base na variável opState. Para alterar estados, uma função de estado define opState para o novo estado e retorna ao circuito principal.
Se você estiver trabalhando em um projeto que tem um monte de coisas acontecendo, desenhando uma máquina de estado pode ser realmente útil para manter sua cabeça no caminho correto!
Se você estiver trabalhando em um projeto que tem um monte de coisas acontecendo, desenhando uma máquina de estado pode ser realmente útil para manter sua cabeça no caminho correto!
- / / ************************************************
- / / Loop de controle principal
- / /
- / / Todas as mudanças de estado passam por aqui
- / / ************************************************
- void loop ()
- {
- / / Espera por liberação de botão antes de mudar de estado
- while (ReadButtons (!) = 0) {}
- lcd.clear ();
- Serial.println (opState);
- switch (opState)
- {
- caso OFF:
- Off ();
- quebrar;
- caso SETP:
- Tune_Sp ();
- quebrar;
- caso RUN:
- Run ();
- quebrar;
- caso TUNE_P:
- TUNEP ();
- quebrar;
- caso TUNE_I:
- TuneI ();
- quebrar;
- caso TUNE_D:
- Tuned ();
- quebrar;
- }
- }
Cada função do Estado é responsável por atualizar a exibição e monitoramento, pressionando o botão além navegar entre as telas, também são utilizados para modificar os parâmetros de controle. Por exemplo, usa as teclas de cima e baixo para alterar o ponto de ajuste do estado valor de sintonia nominal, como indicado na figura e no código abaixo.
O botão 5 do shield é utilizado como uma chave de "mudança". Quando pressionadas simultaneamente com as teclas cima ou para baixo, ele aumenta ou diminui de 10 em vez de apenas 1. As outras telas de sintonia funcionam da mesma maneira.
- / / ************************************************
- / / Valor nominal de entrada do Estado
- / / UP / DOWN para mudar setpoint
- / / RIGHT para parâmetros de ajuste
- / / Esquerda para OFF
- / / Mudança para o ajuste 10x
- / / ************************************************
- vazio Tune_Sp ()
- {
- lcd.setBacklight (TEAL);
- lcd.print (F ("Set Temperatura:"));
- botões uint8_t = 0;
- while (true)
- {
- botões ReadButtons = ();
- flutuar incrementar = 0,1;
- if (botões e BUTTON_SHIFT)
- {
- incremento * = 10;
- }
- if (botões e BUTTON_LEFT)
- {
- opState = RUN;
- voltar;
- }
- if (botões e BUTTON_RIGHT)
- {
- opState = TUNE_P;
- voltar;
- }
- if (botões e BUTTON_UP)
- {
- Setpoint + = incremento;
- retardo (200);
- }
- if (botões e BUTTON_DOWN)
- {
- Setpoint - = incremento;
- retardo (200);
- }
- if ((millis () - lastInput)> 3000) / / retorna para RUN após 3 segundos ociosas
- {
- opState = RUN;
- voltar;
- }
- lcd.setCursor (0,1);
- lcd.print (valor nominal);
- DoControl ();
- }
- }
Dados persistentes
Você não terá que embutir os parâmetros de ajuste ou digitá-los toda vez que você usar o controlador, serão salvos na EEPROM do Arduino. Isso torna o código um pouco mais elegante, programando o controlador apenas uma vez, sempre que você ligar o seu controlador, ele irá lembrar as configurações a partir da última vez que você o usou e caso altere as configurações as mesmas passarão a ficar gravadas.
EEPROM só pode ser escrito um número finito de vezes (tipicamente 100.000), comparamos os conteúdos antes de escrever e gravaremos apenas se algo mudou. Essa funcionalidade é implementada nas seguintes funções auxiliares:
EEPROM só pode ser escrito um número finito de vezes (tipicamente 100.000), comparamos os conteúdos antes de escrever e gravaremos apenas se algo mudou. Essa funcionalidade é implementada nas seguintes funções auxiliares:
- // ************************************************
- // Save any parameter changes to EEPROM
- // ************************************************
- void SaveParameters()
- {
- if (Setpoint != EEPROM_readDouble(SpAddress))
- {
- EEPROM_writeDouble(SpAddress, Setpoint);
- }
- if (Kp != EEPROM_readDouble(KpAddress))
- {
- EEPROM_writeDouble(KpAddress, Kp);
- }
- if (Ki != EEPROM_readDouble(KiAddress))
- {
- EEPROM_writeDouble(KiAddress, Ki);
- }
- if (Kd != EEPROM_readDouble(KdAddress))
- {
- EEPROM_writeDouble(KdAddress, Kd);
- }
- }
- // ************************************************
- // Load parameters from EEPROM
- // ************************************************
- void LoadParameters()
- {
- // Load from EEPROM
- Setpoint = EEPROM_readDouble(SpAddress);
- Kp = EEPROM_readDouble(KpAddress);
- Ki = EEPROM_readDouble(KiAddress);
- Kd = EEPROM_readDouble(KdAddress);
- // Use defaults if EEPROM values are invalid
- if (isnan(Setpoint))
- {
- Setpoint = 60;
- }
- if (isnan(Kp))
- {
- Kp = 500;
- }
- if (isnan(Ki))
- {
- Ki = 0.5;
- }
- if (isnan(Kd))
- {
- Kd = 0.1;
- }
- }
- // ************************************************
- // Write floating point values to EEPROM
- // ************************************************
- void EEPROM_writeDouble(int address, double value)
- {
- byte* p = (byte*)(void*)&value;
- for (int i = 0; i < sizeof(value); i++)
- {
- EEPROM.write(address++, *p++);
- }
- }
- // ************************************************
- // Read floating point values from EEPROM
- // ************************************************
- double EEPROM_readDouble(int address)
- {
- double value = 0.0;
- byte* p = (byte*)(void*)&value;
- for (int i = 0; i < sizeof(value); i++)
- {
- *p++ = EEPROM.read(address++);
- }
- return value;
- }
Tempo de saída proporcional
Enquanto aquecimento da panela é controlado por um relé, não podemos usar uma saída PWM padrão para controlá-lo. PWM é uma maneira muito fácil e precisa de controlar o aquecimento, mas requer uma SSR mais cara. Existem alguns sistemas de feedback do PID que se beneficiam do controle PWM. Felizmente, o nosso sistema é uma grande banheira de água e a água esquenta e esfria muito lentamente, devido a esta "massa térmica" do sistema, o tempo de resposta é relativamente lento, de modo que pode-se usar uma forma muito lenta de PWM conhecido como "tempo de saída proporcional". Neste caso, a frequência dos impulsos é de 0,1 Hz ou uma vez muito, 10 segundos, é realmente um escala PWM muito, muito lenta.
Precisamos controlar o tempo de pulso com precisão, de modo que não sejam afetados por eventuais atrasos que podem haver no circuito principal, então usaremos um timer para gerar uma interrupção periódica. O temporizador é inicializado no setup ():
- / / Executar timer2 interromper a cada 15 ms
- TCCR2A = 0;
- TCCR2B = 1 << CS22 | 1 << CS21 | 1 << CS20;
- / / Timer2 Overflow Interrupt Ativar
- TIMSK2 | TOIE2 << = 1;
A rotina de serviço de interrupção é chamada uma vez a cada 15 milissegundos para atualizar a saída de relé.
- / / ************************************************
- / / Temporizador de interrupção Handler
- / / ************************************************
- SINAL (TIMER2_OVF_vect)
- {
- if (opState == OFF)
- {
- digitalWrite (RelayPin, LOW) / / fazer revezamento certeza é fora
- }
- outro
- {
- DriveOutput ();
- }
- }
O DriveOutput () função que implementa o tempo de saída proporcional.
- / / ************************************************
- / / Chamado pelo ISR cada 15ms para acionar a saída
- / / ************************************************
- vazio DriveOutput ()
- {
- tempo agora = millis ();
- / / Definir a saída
- / / "A tempo" é proporcional à saída do PID
- if (agora - windowStartTime> WindowSize)
- {/ / Hora de mudar a janela de Revezamento
- windowStartTime + = WindowSize;
- }
- if ((ONTIME> 100) && (ONTIME> (agora - windowStartTime)))
- {
- digitalWrite (RelayPin, HIGH);
- }
- outro
- {
- digitalWrite (RelayPin, LOW);
- }
- }
Juntando tudo!
Aqui está o esboço completo para o Controlador Adafruit Sous Vide
Você também pode obter o código mais recente (que pode ter atualizações ou melhorias) de Github em https://github.com/adafruit/Sous_Viduino
- //-------------------------------------------------------------------
- //
- // Sous Vide Controller
- // Bill Earl - for Adafruit Industries
- //
- // Based on the Arduino PID and PID AutoTune Libraries
- // by Brett Beauregard
- //------------------------------------------------------------------
- // PID Library
- #include <PID_v1.h>
- #include <PID_AutoTune_v0.h>
- // Libraries for the Adafruit RGB/LCD Shield
- #include <Wire.h>
- #include <Adafruit_MCP23017.h>
- #include <Adafruit_RGBLCDShield.h>
- // Libraries for the DS18B20 Temperature Sensor
- #include <OneWire.h>
- #include <DallasTemperature.h>
- // So we can save and retrieve settings
- #include <EEPROM.h>
- // ************************************************
- // Pin definitions
- // ************************************************
- // Output Relay
- #define RelayPin 7
- // One-Wire Temperature Sensor
- // (Use GPIO pins for power/ground to simplify the wiring)
- #define ONE_WIRE_BUS 2
- #define ONE_WIRE_PWR 3
- #define ONE_WIRE_GND 4
- // ************************************************
- // PID Variables and constants
- // ************************************************
- //Define Variables we'll be connecting to
- double Setpoint;
- double Input;
- double Output;
- volatile long onTime = 0;
- // pid tuning parameters
- double Kp;
- double Ki;
- double Kd;
- // EEPROM addresses for persisted data
- const int SpAddress = 0;
- const int KpAddress = 8;
- const int KiAddress = 16;
- const int KdAddress = 24;
- //Specify the links and initial tuning parameters
- PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
- // 10 second Time Proportional Output window
- int WindowSize = 10000;
- unsigned long windowStartTime;
- // ************************************************
- // Auto Tune Variables and constants
- // ************************************************
- byte ATuneModeRemember=2;
- double aTuneStep=500;
- double aTuneNoise=1;
- unsigned int aTuneLookBack=20;
- boolean tuning = false;
- PID_ATune aTune(&Input, &Output);
- // ************************************************
- // DiSplay Variables and constants
- // ************************************************
- Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
- // These #defines make it easy to set the backlight color
- #define RED 0x1
- #define YELLOW 0x3
- #define GREEN 0x2
- #define TEAL 0x6
- #define BLUE 0x4
- #define VIOLET 0x5
- #define WHITE 0x7
- #define BUTTON_SHIFT BUTTON_SELECT
- unsigned long lastInput = 0; // last button press
- byte degree[8] = // define the degree symbol
- {
- B00110,
- B01001,
- B01001,
- B00110,
- B00000,
- B00000,
- B00000,
- B00000
- };
- const int logInterval = 10000; // log every 10 seconds
- long lastLogTime = 0;
- // ************************************************
- // States for state machine
- // ************************************************
- enum operatingState { OFF = 0, SETP, RUN, TUNE_P, TUNE_I, TUNE_D, AUTO};
- operatingState opState = OFF;
- // ************************************************
- // Sensor Variables and constants
- // Data wire is plugged into port 2 on the Arduino
- // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
- OneWire oneWire(ONE_WIRE_BUS);
- // Pass our oneWire reference to Dallas Temperature.
- DallasTemperature sensors(&oneWire);
- // arrays to hold device address
- DeviceAddress tempSensor;
- // ************************************************
- // Setup and diSplay initial screen
- // ************************************************
- void setup()
- {
- Serial.begin(9600);
- // Initialize Relay Control:
- pinMode(RelayPin, OUTPUT); // Output mode to drive relay
- digitalWrite(RelayPin, LOW); // make sure it is off to start
- // Set up Ground & Power for the sensor from GPIO pins
- pinMode(ONE_WIRE_GND, OUTPUT);
- digitalWrite(ONE_WIRE_GND, LOW);
- pinMode(ONE_WIRE_PWR, OUTPUT);
- digitalWrite(ONE_WIRE_PWR, HIGH);
- // Initialize LCD DiSplay
- lcd.begin(16, 2);
- lcd.createChar(1, degree); // create degree symbol from the binary
- lcd.setBacklight(VIOLET);
- lcd.print(F(" Adafruit"));
- lcd.setCursor(0, 1);
- lcd.print(F(" Sous Vide!"));
- // Start up the DS18B20 One Wire Temperature Sensor
- sensors.begin();
- if (!sensors.getAddress(tempSensor, 0))
- {
- lcd.setCursor(0, 1);
- lcd.print(F("Sensor Error"));
- }
- sensors.setResolution(tempSensor, 12);
- sensors.setWaitForConversion(false);
- delay(3000); // Splash screen
- // Initialize the PID and related variables
- LoadParameters();
- myPID.SetTunings(Kp,Ki,Kd);
- myPID.SetSampleTime(1000);
- myPID.SetOutputLimits(0, WindowSize);
- // Run timer2 interrupt every 15 ms
- TCCR2A = 0;
- TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;
- //Timer2 Overflow Interrupt Enable
- TIMSK2 |= 1<<TOIE2;
- }
- // ************************************************
- // Timer Interrupt Handler
- // ************************************************
- SIGNAL(TIMER2_OVF_vect)
- {
- if (opState == OFF)
- {
- digitalWrite(RelayPin, LOW); // make sure relay is off
- }
- else
- {
- DriveOutput();
- }
- }
- // ************************************************
- // Main Control Loop
- //
- // All state changes pass through here
- // ************************************************
- void loop()
- {
- // wait for button release before changing state
- while(ReadButtons() != 0) {}
- lcd.clear();
- switch (opState)
- {
- case OFF:
- Off();
- break;
- case SETP:
- Tune_Sp();
- break;
- case RUN:
- Run();
- break;
- case TUNE_P:
- TuneP();
- break;
- case TUNE_I:
- TuneI();
- break;
- case TUNE_D:
- TuneD();
- break;
- }
- }
- // ************************************************
- // Initial State - press RIGHT to enter setpoint
- // ************************************************
- void Off()
- {
- myPID.SetMode(MANUAL);
- lcd.setBacklight(0);
- digitalWrite(RelayPin, LOW); // make sure it is off
- lcd.print(F(" Adafruit"));
- lcd.setCursor(0, 1);
- lcd.print(F(" Sous Vide!"));
- uint8_t buttons = 0;
- while(!(buttons & (BUTTON_RIGHT)))
- {
- buttons = ReadButtons();
- }
- // Prepare to transition to the RUN state
- sensors.requestTemperatures(); // Start an asynchronous temperature reading
- //turn the PID on
- myPID.SetMode(AUTOMATIC);
- windowStartTime = millis();
- opState = RUN; // start control
- }
- // ************************************************
- // Setpoint Entry State
- // UP/DOWN to change setpoint
- // RIGHT for tuning parameters
- // LEFT for OFF
- // SHIFT for 10x tuning
- // ************************************************
- void Tune_Sp()
- {
- lcd.setBacklight(TEAL);
- lcd.print(F("Set Temperature:"));
- uint8_t buttons = 0;
- while(true)
- {
- buttons = ReadButtons();
- float increment = 0.1;
- if (buttons & BUTTON_SHIFT)
- {
- increment *= 10;
- }
- if (buttons & BUTTON_LEFT)
- {
- opState = RUN;
- return;
- }
- if (buttons & BUTTON_RIGHT)
- {
- opState = TUNE_P;
- return;
- }
- if (buttons & BUTTON_UP)
- {
- Setpoint += increment;
- delay(200);
- }
- if (buttons & BUTTON_DOWN)
- {
- Setpoint -= increment;
- delay(200);
- }
- if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
- {
- opState = RUN;
- return;
- }
- lcd.setCursor(0,1);
- lcd.print(Setpoint);
- lcd.print(" ");
- DoControl();
- }
- }
- // ************************************************
- // Proportional Tuning State
- // UP/DOWN to change Kp
- // RIGHT for Ki
- // LEFT for setpoint
- // SHIFT for 10x tuning
- // ************************************************
- void TuneP()
- {
- lcd.setBacklight(TEAL);
- lcd.print(F("Set Kp"));
- uint8_t buttons = 0;
- while(true)
- {
- buttons = ReadButtons();
- float increment = 1.0;
- if (buttons & BUTTON_SHIFT)
- {
- increment *= 10;
- }
- if (buttons & BUTTON_LEFT)
- {
- opState = SETP;
- return;
- }
- if (buttons & BUTTON_RIGHT)
- {
- opState = TUNE_I;
- return;
- }
- if (buttons & BUTTON_UP)
- {
- Kp += increment;
- delay(200);
- }
- if (buttons & BUTTON_DOWN)
- {
- Kp -= increment;
- delay(200);
- }
- if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
- {
- opState = RUN;
- return;
- }
- lcd.setCursor(0,1);
- lcd.print(Kp);
- lcd.print(" ");
- DoControl();
- }
- }
- // ************************************************
- // Integral Tuning State
- // UP/DOWN to change Ki
- // RIGHT for Kd
- // LEFT for Kp
- // SHIFT for 10x tuning
- // ************************************************
- void TuneI()
- {
- lcd.setBacklight(TEAL);
- lcd.print(F("Set Ki"));
- uint8_t buttons = 0;
- while(true)
- {
- buttons = ReadButtons();
- float increment = 0.01;
- if (buttons & BUTTON_SHIFT)
- {
- increment *= 10;
- }
- if (buttons & BUTTON_LEFT)
- {
- opState = TUNE_P;
- return;
- }
- if (buttons & BUTTON_RIGHT)
- {
- opState = TUNE_D;
- return;
- }
- if (buttons & BUTTON_UP)
- {
- Ki += increment;
- delay(200);
- }
- if (buttons & BUTTON_DOWN)
- {
- Ki -= increment;
- delay(200);
- }
- if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
- {
- opState = RUN;
- return;
- }
- lcd.setCursor(0,1);
- lcd.print(Ki);
- lcd.print(" ");
- DoControl();
- }
- }
- // ************************************************
- // Derivative Tuning State
- // UP/DOWN to change Kd
- // RIGHT for setpoint
- // LEFT for Ki
- // SHIFT for 10x tuning
- // ************************************************
- void TuneD()
- {
- lcd.setBacklight(TEAL);
- lcd.print(F("Set Kd"));
- uint8_t buttons = 0;
- while(true)
- {
- buttons = ReadButtons();
- float increment = 0.01;
- if (buttons & BUTTON_SHIFT)
- {
- increment *= 10;
- }
- if (buttons & BUTTON_LEFT)
- {
- opState = TUNE_I;
- return;
- }
- if (buttons & BUTTON_RIGHT)
- {
- opState = RUN;
- return;
- }
- if (buttons & BUTTON_UP)
- {
- Kd += increment;
- delay(200);
- }
- if (buttons & BUTTON_DOWN)
- {
- Kd -= increment;
- delay(200);
- }
- if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
- {
- opState = RUN;
- return;
- }
- lcd.setCursor(0,1);
- lcd.print(Kd);
- lcd.print(" ");
- DoControl();
- }
- }
- // ************************************************
- // PID COntrol State
- // SHIFT and RIGHT for autotune
- // RIGHT - Setpoint
- // LEFT - OFF
- // ************************************************
- void Run()
- {
- // set up the LCD's number of rows and columns:
- lcd.print(F("Sp: "));
- lcd.print(Setpoint);
- lcd.write(1);
- lcd.print(F("C : "));
- SaveParameters();
- myPID.SetTunings(Kp,Ki,Kd);
- uint8_t buttons = 0;
- while(true)
- {
- setBacklight(); // set backlight based on state
- buttons = ReadButtons();
- if ((buttons & BUTTON_SHIFT)
- && (buttons & BUTTON_RIGHT)
- && (abs(Input - Setpoint) < 0.5)) // Should be at steady-state
- {
- StartAutoTune();
- }
- else if (buttons & BUTTON_RIGHT)
- {
- opState = SETP;
- return;
- }
- else if (buttons & BUTTON_LEFT)
- {
- opState = OFF;
- return;
- }
- DoControl();
- lcd.setCursor(0,1);
- lcd.print(Input);
- lcd.write(1);
- lcd.print(F("C : "));
- float pct = map(Output, 0, WindowSize, 0, 1000);
- lcd.setCursor(10,1);
- lcd.print(F(" "));
- lcd.setCursor(10,1);
- lcd.print(pct/10);
- //lcd.print(Output);
- lcd.print("%");
- lcd.setCursor(15,0);
- if (tuning)
- {
- lcd.print("T");
- }
- else
- {
- lcd.print(" ");
- }
- // periodically log to serial port in csv format
- if (millis() - lastLogTime > logInterval)
- {
- Serial.print(Input);
- Serial.print(",");
- Serial.println(Output);
- }
- delay(100);
- }
- }
- // ************************************************
- // Execute the control loop
- // ************************************************
- void DoControl()
- {
- // Read the input:
- if (sensors.isConversionAvailable(0))
- {
- Input = sensors.getTempC(tempSensor);
- sensors.requestTemperatures(); // prime the pump for the next one - but don't wait
- }
- if (tuning) // run the auto-tuner
- {
- if (aTune.Runtime()) // returns 'true' when done
- {
- FinishAutoTune();
- }
- }
- else // Execute control algorithm
- {
- myPID.Compute();
- }
- // Time Proportional relay state is updated regularly via timer interrupt.
- onTime = Output;
- }
- // ************************************************
- // Called by ISR every 15ms to drive the output
- // ************************************************
- void DriveOutput()
- {
- long now = millis();
- // Set the output
- // "on time" is proportional to the PID output
- if(now - windowStartTime>WindowSize)
- { //time to shift the Relay Window
- windowStartTime += WindowSize;
- }
- if((onTime > 100) && (onTime > (now - windowStartTime)))
- {
- digitalWrite(RelayPin,HIGH);
- }
- else
- {
- digitalWrite(RelayPin,LOW);
- }
- }
- // ************************************************
- // Set Backlight based on the state of control
- // ************************************************
- void setBacklight()
- {
- if (tuning)
- {
- lcd.setBacklight(VIOLET); // Tuning Mode
- }
- else if (abs(Input - Setpoint) > 1.0)
- {
- lcd.setBacklight(RED); // High Alarm - off by more than 1 degree
- }
- else if (abs(Input - Setpoint) > 0.2)
- {
- lcd.setBacklight(YELLOW); // Low Alarm - off by more than 0.2 degrees
- }
- else
- {
- lcd.setBacklight(WHITE); // We're on target!
- }
- }
- // ************************************************
- // Start the Auto-Tuning cycle
- // ************************************************
- void StartAutoTune()
- {
- // REmember the mode we were in
- ATuneModeRemember = myPID.GetMode();
- // set up the auto-tune parameters
- aTune.SetNoiseBand(aTuneNoise);
- aTune.SetOutputStep(aTuneStep);
- aTune.SetLookbackSec((int)aTuneLookBack);
- tuning = true;
- }
- // ************************************************
- // Return to normal control
- // ************************************************
- void FinishAutoTune()
- {
- tuning = false;
- // Extract the auto-tune calculated parameters
- Kp = aTune.GetKp();
- Ki = aTune.GetKi();
- Kd = aTune.GetKd();
- // Re-tune the PID and revert to normal control mode
- myPID.SetTunings(Kp,Ki,Kd);
- myPID.SetMode(ATuneModeRemember);
- // Persist any changed parameters to EEPROM
- SaveParameters();
- }
- // ************************************************
- // Check buttons and time-stamp the last press
- // ************************************************
- uint8_t ReadButtons()
- {
- uint8_t buttons = lcd.readButtons();
- if (buttons != 0)
- {
- lastInput = millis();
- }
- return buttons;
- }
- // ************************************************
- // Save any parameter changes to EEPROM
- // ************************************************
- void SaveParameters()
- {
- if (Setpoint != EEPROM_readDouble(SpAddress))
- {
- EEPROM_writeDouble(SpAddress, Setpoint);
- }
- if (Kp != EEPROM_readDouble(KpAddress))
- {
- EEPROM_writeDouble(KpAddress, Kp);
- }
- if (Ki != EEPROM_readDouble(KiAddress))
- {
- EEPROM_writeDouble(KiAddress, Ki);
- }
- if (Kd != EEPROM_readDouble(KdAddress))
- {
- EEPROM_writeDouble(KdAddress, Kd);
- }
- }
- // ************************************************
- // Load parameters from EEPROM
- // ************************************************
- void LoadParameters()
- {
- // Load from EEPROM
- Setpoint = EEPROM_readDouble(SpAddress);
- Kp = EEPROM_readDouble(KpAddress);
- Ki = EEPROM_readDouble(KiAddress);
- Kd = EEPROM_readDouble(KdAddress);
- // Use defaults if EEPROM values are invalid
- if (isnan(Setpoint))
- {
- Setpoint = 60;
- }
- if (isnan(Kp))
- {
- Kp = 850;
- }
- if (isnan(Ki))
- {
- Ki = 0.5;
- }
- if (isnan(Kd))
- {
- Kd = 0.1;
- }
- }
- // ************************************************
- // Write floating point values to EEPROM
- // ************************************************
- void EEPROM_writeDouble(int address, double value)
- {
- byte* p = (byte*)(void*)&value;
- for (int i = 0; i < sizeof(value); i++)
- {
- EEPROM.write(address++, *p++);
- }
- }
- // ************************************************
- // Read floating point values from EEPROM
- // ************************************************
- double EEPROM_readDouble(int address)
- {
- double value = 0.0;
- byte* p = (byte*)(void*)&value;
- for (int i = 0; i < sizeof(value); i++)
- {
- *p++ = EEPROM.read(address++);
- }
- return value;
- }
Ajustando
Diagrama de entrada Wikipedia: Controlador PID
Ajuste padrão
Os parâmetros de ajuste padrão no esboço são valores médios de alguns modelos diferentes de panela de arroz. Mas haverá variações mesmo entre duas panelas do mesmo modelo e do mesmo fabricante.
Auto Sintonia
A função de ajuste automático do esboço pode determinar parâmetros "aproximados" para sua panela. Você pode refinar o ajuste de lá. Para usar a função Autotune, primeiro deixe a panela de pré-aquecimento e atingir a temperatura nominal. Auto-ajuste funciona melhor se o sistema já está em um estado de equilíbrio. O estado de "Run" não permitirá que você invoque a auto-tuner, a menos que você esteja a 0,5 graus da temperatura nominal. Uma vez que a panela tenha se estabilizado ou perto do ponto de ajuste, pressione os botões DIREITA e SELEÇÃO simultaneamente. A luz de fundo ficará violeta para indicar que você está no modo de ajuste automático.
Seja paciente quando o sistema sintoniza si mesmo. Ele normalmente vai demorar uma hora ou mais para o algoritmo de auto-tune ser concluído. Quando o auto-ajuste estiver concluído, a luz de fundo irá retornar para o regime normal de funcionamento cromático. Os parâmetros de auto-tuned serão salvos na memória EEPROM, então eles estarão prontos para a próxima vez que for usá-lo.
Seja paciente quando o sistema sintoniza si mesmo. Ele normalmente vai demorar uma hora ou mais para o algoritmo de auto-tune ser concluído. Quando o auto-ajuste estiver concluído, a luz de fundo irá retornar para o regime normal de funcionamento cromático. Os parâmetros de auto-tuned serão salvos na memória EEPROM, então eles estarão prontos para a próxima vez que for usá-lo.
Sintonia manual
Como mencionado anteriormente, o auto-ajuste não é perfeito. Com um pouco de prática, você provavelmente pode chegar mais perto da afinação ideal para o seu fogão. Há muitos bons recursos na web que explicam sintonia PID. Aqui estão apenas alguns: PID Controlador de sintonia: um tutorial curto simples regras de sintonia do PID para ajustar o Kp, Ki e Kd parâmetros, use o botão direito para navegar entre as telas de ajuste. Os botões PARA CIMA e PARA BAIXO irão alterar o valor. Pressionando Select enquanto também pressionando UP ou DOWN irá alterar o valor 10x mais rápido.
Dica Sintonia Manual
Uma coisa a ter em conta é que o controle de temperatura em uma panela de arroz é não-linear e assimétrica . Você pode aplicar o calor, mas não há refrigeração ativa. Como resultado, a maioria das panelas de arroz levar um longo tempo para se recuperar de uma temperatura de superação . Por essa razão, geralmente é melhor procurar uma resposta superamortecido para evitar transbordo.
Cozinhar com ele!
A melhor parte de fazer a sua própria configuração de sous vide é a porção de testes, yum! Sous vide utiliza a menor da temperaturas normais de cozimento. Se não for feito com cuidado, isso pode criar condições que promovem o crescimento de bactérias nocivas. Para um excelente guia para sous vide temperaturas seguras de cozinha e práticas de manipulação de alimentos, bem como gráficos de tempo e temperatura, veja o livro de Douglas Baldwin e web-site " Um Guia Prático para Sous Vide Cooking "
Cozinhar um ovo "perfeito"!
Um primeiro teste bom para o seu fogão novo é um "ovo perfeito". Ovos não necessitam de preparação especial para sous vide. E, uma vez que eles são muito sensíveis a pequenas variações na temperatura de cozimento, são um bom teste para a precisão do seu controlador de temperatura. diferentes chefs têm idéias diferentes sobre o que exatamente constitui um ovo perfeito. Mas o controle preciso da temperatura do seu fogão sous vide vai deixar você atingir o seu ovo perfeito o tempo todo. http://www.edinformatics.com/math_science/science_of_cooking/eggs_sous_vide.htm
Cozinhar um bife!
Tal como acontece com um ovo, o controle preciso da temperatura do seu fogão irá permitir que você cozinhe bifes para o nível certo de cozimento com 100% de repetibilidade. Sem bordas mais exageradas e cruas no meio. O seu bife será cozido do mesmo jeito de ponta a ponta. Toste-os na grelha por apenas alguns segundos de cada lado antes de servir.
Cozinhe um peixe!
Peixe pode ser complicado para cozinhar. Apenas alguns segundos a mais na frigideira e irá de úmido a seco e quebradiço. Com sous vide, você pode ter certeza que ele ficará cozido perfeitamente, e não exagerado.
Transferências e linksHaddock Filetes com vagens e laranja molho de açafrão. (Um dos nossos favoritos!)Cozido 20 minutes@54.5C
Código Sous Viduino Arduino
Você pode obter o código mais recente do repositório github em https://github.com/adafruit/Sous_Viduino
Biblioteca de Downloads:
Você também vai precisar das bibliotecas para o sensor de temperatura DS18B20 e RGB LCD shield, verifique as páginas de produto e kit para obter detalhes sobre os itens
Biblioteca de Documentação:
Informações adicionais sobre o sous vide
Douglas Baldwin " Um Guia Prático para Sous Vide Cooking "
Leonardo diferenças no Temporizador
Cliente e membro do fórum ytoff57 portou com sucesso este código para o Leonardo. Uma diferença entre o Leonardo e o Arduino é o timer, então o código de interrupção do timer não funciona nessa plataforma. Leonardo testou as seguintes modificações baseadas na biblioteca TimerOne:
- # Include <TimerOne.h>
- / / ...
- void setup ()
- {
- / / ...
- Timer1 . initialize ( 15.000 );
- Timer1 . attachInterrupt ( TimerInterrupt );
- }
- / / ..
- anular TimerInterrupt ()
- {
- se ( opState == OFF )
- {
- digitalWrite ( RelayPin , LOW ); / / fazer revezamento certeza é fora
- }
- outro
- {
- DriveOutput ();
- }
- }
Fonte: http://learn.adafruit.com/sous-vide-powered-by-arduino-the-sous-viduino?view=all