Tradutor

quinta-feira, 17 de outubro de 2013

Transforme sua velha maquina de arroz em um robô que faz deliciosos pratos utilizando o Arduino.

Navegando pela internet encontrei mais um projeto bastante interessante, pois através dele é possível hackear um velha panela de Arroz e transformá-la em um robô que produz uma serie de pratos deliciosos além do arroz através da técnica sous vide.


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.

Sous vide está rapidamente se tornando uma técnica de cozimento importante em muitos dos melhores restaurantes do mundo, pois combina princípios da gastronomia molecular com controles de temperatura industriais para gerenciar com precisão as reações químicas de cozimento.

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:

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.

 

Prepare o sensor


Nossos sensores DS18B20 à prova d'água são ótimos para imersão no liquido e medição temperatura , mas eles não são seguros para alimentos. Se você só vai cozinhar alimentos em um saco plástico, isso não importa tanto, mas se você cozinhar alimentos diretamente na água como ovos isso realmente importa. Nós sugerimos fortemente que utilize uma fita para vedar o sensor, use uma pistola de ar quente para encolher a manga plastica à prova d'água sobre o sensor.


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.

Se existir uma folga, Coloque uma braçadeira de cabo em torno do fio do sensor e a saída da tampa para evitar que deslizem mais profundamente na panela.

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:
  • 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:
  • 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.


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!

  1. / / ************************************************
  2. / / Loop de controle principal
  3. / /
  4. / / Todas as mudanças de estado passam por aqui
  5. / / ************************************************
  6. void loop ()
  7. {
  8. / / Espera por liberação de botão antes de mudar de estado
  9. while (ReadButtons (!) = 0) {}
  10. lcd.clear ();
  11. Serial.println (opState);
  12. switch (opState)
  13. {
  14. caso OFF:
  15. Off ();
  16. quebrar;
  17. caso SETP:
  18. Tune_Sp ();
  19. quebrar;
  20. caso RUN:
  21. Run ();
  22. quebrar;
  23. caso TUNE_P:
  24. TUNEP ();
  25. quebrar;
  26. caso TUNE_I:
  27. TuneI ();
  28. quebrar;
  29. caso TUNE_D:
  30. Tuned ();
  31. quebrar;
  32. }
  33. }

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.




  1. / / ************************************************
  2. / / Valor nominal de entrada do Estado
  3. / / UP / DOWN para mudar setpoint
  4. / / RIGHT para parâmetros de ajuste
  5. / / Esquerda para OFF
  6. / / Mudança para o ajuste 10x
  7. / / ************************************************
  8. vazio Tune_Sp ()
  9. {
  10. lcd.setBacklight (TEAL);
  11. lcd.print (F ("Set Temperatura:"));
  12. botões uint8_t = 0;
  13. while (true)
  14. {
  15. botões ReadButtons = ();
  16.  
  17. flutuar incrementar = 0,1;
  18. if (botões e BUTTON_SHIFT)
  19. {
  20. incremento * = 10;
  21. }
  22. if (botões e BUTTON_LEFT)
  23. {
  24. opState = RUN;
  25. voltar;
  26. }
  27. if (botões e BUTTON_RIGHT)
  28. {
  29. opState = TUNE_P;
  30. voltar;
  31. }
  32. if (botões e BUTTON_UP)
  33. {
  34. Setpoint + = incremento;
  35. retardo (200);
  36. }
  37. if (botões e BUTTON_DOWN)
  38. {
  39. Setpoint - = incremento;
  40. retardo (200);
  41. }
  42. if ((millis () - lastInput)> 3000) / / retorna para RUN após 3 segundos ociosas
  43. {
  44. opState = RUN;
  45. voltar;
  46. }
  47. lcd.setCursor (0,1);
  48. lcd.print (valor nominal);
  49. DoControl ();
  50. }
  51. }

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:

  1. // ************************************************
  2. // Save any parameter changes to EEPROM
  3. // ************************************************
  4. void SaveParameters()
  5. {
  6. if (Setpoint != EEPROM_readDouble(SpAddress))
  7. {
  8. EEPROM_writeDouble(SpAddress, Setpoint);
  9. }
  10. if (Kp != EEPROM_readDouble(KpAddress))
  11. {
  12. EEPROM_writeDouble(KpAddress, Kp);
  13. }
  14. if (Ki != EEPROM_readDouble(KiAddress))
  15. {
  16. EEPROM_writeDouble(KiAddress, Ki);
  17. }
  18. if (Kd != EEPROM_readDouble(KdAddress))
  19. {
  20. EEPROM_writeDouble(KdAddress, Kd);
  21. }
  22. }
  23.  
  24. // ************************************************
  25. // Load parameters from EEPROM
  26. // ************************************************
  27. void LoadParameters()
  28. {
  29. // Load from EEPROM
  30. Setpoint = EEPROM_readDouble(SpAddress);
  31. Kp = EEPROM_readDouble(KpAddress);
  32. Ki = EEPROM_readDouble(KiAddress);
  33. Kd = EEPROM_readDouble(KdAddress);
  34. // Use defaults if EEPROM values are invalid
  35. if (isnan(Setpoint))
  36. {
  37. Setpoint = 60;
  38. }
  39. if (isnan(Kp))
  40. {
  41. Kp = 500;
  42. }
  43. if (isnan(Ki))
  44. {
  45. Ki = 0.5;
  46. }
  47. if (isnan(Kd))
  48. {
  49. Kd = 0.1;
  50. }
  51. }
  52.  
  53.  
  54. // ************************************************
  55. // Write floating point values to EEPROM
  56. // ************************************************
  57. void EEPROM_writeDouble(int address, double value)
  58. {
  59. byte* p = (byte*)(void*)&value;
  60. for (int i = 0; i < sizeof(value); i++)
  61. {
  62. EEPROM.write(address++, *p++);
  63. }
  64. }
  65.  
  66. // ************************************************
  67. // Read floating point values from EEPROM
  68. // ************************************************
  69. double EEPROM_readDouble(int address)
  70. {
  71. double value = 0.0;
  72. byte* p = (byte*)(void*)&value;
  73. for (int i = 0; i < sizeof(value); i++)
  74. {
  75. *p++ = EEPROM.read(address++);
  76. }
  77. return value;
  78. }

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 ():

  1. / / Executar timer2 interromper a cada 15 ms
  2. TCCR2A = 0;
  3. TCCR2B = 1 << CS22 | 1 << CS21 | 1 << CS20;
  4.  
  5. / / Timer2 Overflow Interrupt Ativar
  6. 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é.

  1. / / ************************************************
  2. / / Temporizador de interrupção Handler
  3. / / ************************************************
  4. SINAL (TIMER2_OVF_vect)
  5. {
  6. if (opState == OFF)
  7. {
  8. digitalWrite (RelayPin, LOW) / / fazer revezamento certeza é fora
  9. }
  10. outro
  11. {
  12. DriveOutput ();
  13. }
  14. }
O DriveOutput () função que implementa o tempo de saída proporcional.

  1. / / ************************************************
  2. / / Chamado pelo ISR cada 15ms para acionar a saída
  3. / / ************************************************
  4. vazio DriveOutput ()
  5. {
  6. tempo agora = millis ();
  7. / / Definir a saída
  8. / / "A tempo" é proporcional à saída do PID
  9. if (agora - windowStartTime> WindowSize)
  10. {/ / Hora de mudar a janela de Revezamento
  11. windowStartTime + = WindowSize;
  12. }
  13. if ((ONTIME> 100) && (ONTIME> (agora - windowStartTime)))
  14. {
  15. digitalWrite (RelayPin, HIGH);
  16. }
  17. outro
  18. {
  19. digitalWrite (RelayPin, LOW);
  20. }
  21. }

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

  1. //-------------------------------------------------------------------
  2. //
  3. // Sous Vide Controller
  4. // Bill Earl - for Adafruit Industries
  5. //
  6. // Based on the Arduino PID and PID AutoTune Libraries
  7. // by Brett Beauregard
  8. //------------------------------------------------------------------
  9.  
  10. // PID Library
  11. #include <PID_v1.h>
  12. #include <PID_AutoTune_v0.h>
  13.  
  14. // Libraries for the Adafruit RGB/LCD Shield
  15. #include <Wire.h>
  16. #include <Adafruit_MCP23017.h>
  17. #include <Adafruit_RGBLCDShield.h>
  18.  
  19. // Libraries for the DS18B20 Temperature Sensor
  20. #include <OneWire.h>
  21. #include <DallasTemperature.h>
  22.  
  23. // So we can save and retrieve settings
  24. #include <EEPROM.h>
  25.  
  26. // ************************************************
  27. // Pin definitions
  28. // ************************************************
  29.  
  30. // Output Relay
  31. #define RelayPin 7
  32.  
  33. // One-Wire Temperature Sensor
  34. // (Use GPIO pins for power/ground to simplify the wiring)
  35. #define ONE_WIRE_BUS 2
  36. #define ONE_WIRE_PWR 3
  37. #define ONE_WIRE_GND 4
  38.  
  39. // ************************************************
  40. // PID Variables and constants
  41. // ************************************************
  42.  
  43. //Define Variables we'll be connecting to
  44. double Setpoint;
  45. double Input;
  46. double Output;
  47.  
  48. volatile long onTime = 0;
  49.  
  50. // pid tuning parameters
  51. double Kp;
  52. double Ki;
  53. double Kd;
  54.  
  55. // EEPROM addresses for persisted data
  56. const int SpAddress = 0;
  57. const int KpAddress = 8;
  58. const int KiAddress = 16;
  59. const int KdAddress = 24;
  60.  
  61. //Specify the links and initial tuning parameters
  62. PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
  63.  
  64. // 10 second Time Proportional Output window
  65. int WindowSize = 10000;
  66. unsigned long windowStartTime;
  67.  
  68. // ************************************************
  69. // Auto Tune Variables and constants
  70. // ************************************************
  71. byte ATuneModeRemember=2;
  72.  
  73. double aTuneStep=500;
  74. double aTuneNoise=1;
  75. unsigned int aTuneLookBack=20;
  76.  
  77. boolean tuning = false;
  78.  
  79. PID_ATune aTune(&Input, &Output);
  80.  
  81. // ************************************************
  82. // DiSplay Variables and constants
  83. // ************************************************
  84.  
  85. Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
  86. // These #defines make it easy to set the backlight color
  87. #define RED 0x1
  88. #define YELLOW 0x3
  89. #define GREEN 0x2
  90. #define TEAL 0x6
  91. #define BLUE 0x4
  92. #define VIOLET 0x5
  93. #define WHITE 0x7
  94.  
  95. #define BUTTON_SHIFT BUTTON_SELECT
  96.  
  97. unsigned long lastInput = 0; // last button press
  98.  
  99. byte degree[8] = // define the degree symbol
  100. {
  101. B00110,
  102. B01001,
  103. B01001,
  104. B00110,
  105. B00000,
  106. B00000,
  107. B00000,
  108. B00000
  109. };
  110.  
  111. const int logInterval = 10000; // log every 10 seconds
  112. long lastLogTime = 0;
  113.  
  114. // ************************************************
  115. // States for state machine
  116. // ************************************************
  117. enum operatingState { OFF = 0, SETP, RUN, TUNE_P, TUNE_I, TUNE_D, AUTO};
  118. operatingState opState = OFF;
  119.  
  120. // ************************************************
  121. // Sensor Variables and constants
  122. // Data wire is plugged into port 2 on the Arduino
  123.  
  124. // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
  125. OneWire oneWire(ONE_WIRE_BUS);
  126.  
  127. // Pass our oneWire reference to Dallas Temperature.
  128. DallasTemperature sensors(&oneWire);
  129.  
  130. // arrays to hold device address
  131. DeviceAddress tempSensor;
  132.  
  133. // ************************************************
  134. // Setup and diSplay initial screen
  135. // ************************************************
  136. void setup()
  137. {
  138. Serial.begin(9600);
  139.  
  140. // Initialize Relay Control:
  141.  
  142. pinMode(RelayPin, OUTPUT); // Output mode to drive relay
  143. digitalWrite(RelayPin, LOW); // make sure it is off to start
  144.  
  145. // Set up Ground & Power for the sensor from GPIO pins
  146.  
  147. pinMode(ONE_WIRE_GND, OUTPUT);
  148. digitalWrite(ONE_WIRE_GND, LOW);
  149.  
  150. pinMode(ONE_WIRE_PWR, OUTPUT);
  151. digitalWrite(ONE_WIRE_PWR, HIGH);
  152.  
  153. // Initialize LCD DiSplay
  154.  
  155. lcd.begin(16, 2);
  156. lcd.createChar(1, degree); // create degree symbol from the binary
  157. lcd.setBacklight(VIOLET);
  158. lcd.print(F(" Adafruit"));
  159. lcd.setCursor(0, 1);
  160. lcd.print(F(" Sous Vide!"));
  161.  
  162. // Start up the DS18B20 One Wire Temperature Sensor
  163.  
  164. sensors.begin();
  165. if (!sensors.getAddress(tempSensor, 0))
  166. {
  167. lcd.setCursor(0, 1);
  168. lcd.print(F("Sensor Error"));
  169. }
  170. sensors.setResolution(tempSensor, 12);
  171. sensors.setWaitForConversion(false);
  172.  
  173. delay(3000); // Splash screen
  174.  
  175. // Initialize the PID and related variables
  176. LoadParameters();
  177. myPID.SetTunings(Kp,Ki,Kd);
  178.  
  179. myPID.SetSampleTime(1000);
  180. myPID.SetOutputLimits(0, WindowSize);
  181.  
  182. // Run timer2 interrupt every 15 ms
  183. TCCR2A = 0;
  184. TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;
  185.  
  186. //Timer2 Overflow Interrupt Enable
  187. TIMSK2 |= 1<<TOIE2;
  188. }
  189.  
  190. // ************************************************
  191. // Timer Interrupt Handler
  192. // ************************************************
  193. SIGNAL(TIMER2_OVF_vect)
  194. {
  195. if (opState == OFF)
  196. {
  197. digitalWrite(RelayPin, LOW); // make sure relay is off
  198. }
  199. else
  200. {
  201. DriveOutput();
  202. }
  203. }
  204.  
  205. // ************************************************
  206. // Main Control Loop
  207. //
  208. // All state changes pass through here
  209. // ************************************************
  210. void loop()
  211. {
  212. // wait for button release before changing state
  213. while(ReadButtons() != 0) {}
  214.  
  215. lcd.clear();
  216.  
  217. switch (opState)
  218. {
  219. case OFF:
  220. Off();
  221. break;
  222. case SETP:
  223. Tune_Sp();
  224. break;
  225. case RUN:
  226. Run();
  227. break;
  228. case TUNE_P:
  229. TuneP();
  230. break;
  231. case TUNE_I:
  232. TuneI();
  233. break;
  234. case TUNE_D:
  235. TuneD();
  236. break;
  237. }
  238. }
  239.  
  240. // ************************************************
  241. // Initial State - press RIGHT to enter setpoint
  242. // ************************************************
  243. void Off()
  244. {
  245. myPID.SetMode(MANUAL);
  246. lcd.setBacklight(0);
  247. digitalWrite(RelayPin, LOW); // make sure it is off
  248. lcd.print(F(" Adafruit"));
  249. lcd.setCursor(0, 1);
  250. lcd.print(F(" Sous Vide!"));
  251. uint8_t buttons = 0;
  252. while(!(buttons & (BUTTON_RIGHT)))
  253. {
  254. buttons = ReadButtons();
  255. }
  256. // Prepare to transition to the RUN state
  257. sensors.requestTemperatures(); // Start an asynchronous temperature reading
  258.  
  259. //turn the PID on
  260. myPID.SetMode(AUTOMATIC);
  261. windowStartTime = millis();
  262. opState = RUN; // start control
  263. }
  264.  
  265. // ************************************************
  266. // Setpoint Entry State
  267. // UP/DOWN to change setpoint
  268. // RIGHT for tuning parameters
  269. // LEFT for OFF
  270. // SHIFT for 10x tuning
  271. // ************************************************
  272. void Tune_Sp()
  273. {
  274. lcd.setBacklight(TEAL);
  275. lcd.print(F("Set Temperature:"));
  276. uint8_t buttons = 0;
  277. while(true)
  278. {
  279. buttons = ReadButtons();
  280.  
  281. float increment = 0.1;
  282. if (buttons & BUTTON_SHIFT)
  283. {
  284. increment *= 10;
  285. }
  286. if (buttons & BUTTON_LEFT)
  287. {
  288. opState = RUN;
  289. return;
  290. }
  291. if (buttons & BUTTON_RIGHT)
  292. {
  293. opState = TUNE_P;
  294. return;
  295. }
  296. if (buttons & BUTTON_UP)
  297. {
  298. Setpoint += increment;
  299. delay(200);
  300. }
  301. if (buttons & BUTTON_DOWN)
  302. {
  303. Setpoint -= increment;
  304. delay(200);
  305. }
  306. if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
  307. {
  308. opState = RUN;
  309. return;
  310. }
  311. lcd.setCursor(0,1);
  312. lcd.print(Setpoint);
  313. lcd.print(" ");
  314. DoControl();
  315. }
  316. }
  317.  
  318. // ************************************************
  319. // Proportional Tuning State
  320. // UP/DOWN to change Kp
  321. // RIGHT for Ki
  322. // LEFT for setpoint
  323. // SHIFT for 10x tuning
  324. // ************************************************
  325. void TuneP()
  326. {
  327. lcd.setBacklight(TEAL);
  328. lcd.print(F("Set Kp"));
  329.  
  330. uint8_t buttons = 0;
  331. while(true)
  332. {
  333. buttons = ReadButtons();
  334.  
  335. float increment = 1.0;
  336. if (buttons & BUTTON_SHIFT)
  337. {
  338. increment *= 10;
  339. }
  340. if (buttons & BUTTON_LEFT)
  341. {
  342. opState = SETP;
  343. return;
  344. }
  345. if (buttons & BUTTON_RIGHT)
  346. {
  347. opState = TUNE_I;
  348. return;
  349. }
  350. if (buttons & BUTTON_UP)
  351. {
  352. Kp += increment;
  353. delay(200);
  354. }
  355. if (buttons & BUTTON_DOWN)
  356. {
  357. Kp -= increment;
  358. delay(200);
  359. }
  360. if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
  361. {
  362. opState = RUN;
  363. return;
  364. }
  365. lcd.setCursor(0,1);
  366. lcd.print(Kp);
  367. lcd.print(" ");
  368. DoControl();
  369. }
  370. }
  371.  
  372. // ************************************************
  373. // Integral Tuning State
  374. // UP/DOWN to change Ki
  375. // RIGHT for Kd
  376. // LEFT for Kp
  377. // SHIFT for 10x tuning
  378. // ************************************************
  379. void TuneI()
  380. {
  381. lcd.setBacklight(TEAL);
  382. lcd.print(F("Set Ki"));
  383.  
  384. uint8_t buttons = 0;
  385. while(true)
  386. {
  387. buttons = ReadButtons();
  388.  
  389. float increment = 0.01;
  390. if (buttons & BUTTON_SHIFT)
  391. {
  392. increment *= 10;
  393. }
  394. if (buttons & BUTTON_LEFT)
  395. {
  396. opState = TUNE_P;
  397. return;
  398. }
  399. if (buttons & BUTTON_RIGHT)
  400. {
  401. opState = TUNE_D;
  402. return;
  403. }
  404. if (buttons & BUTTON_UP)
  405. {
  406. Ki += increment;
  407. delay(200);
  408. }
  409. if (buttons & BUTTON_DOWN)
  410. {
  411. Ki -= increment;
  412. delay(200);
  413. }
  414. if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
  415. {
  416. opState = RUN;
  417. return;
  418. }
  419. lcd.setCursor(0,1);
  420. lcd.print(Ki);
  421. lcd.print(" ");
  422. DoControl();
  423. }
  424. }
  425.  
  426. // ************************************************
  427. // Derivative Tuning State
  428. // UP/DOWN to change Kd
  429. // RIGHT for setpoint
  430. // LEFT for Ki
  431. // SHIFT for 10x tuning
  432. // ************************************************
  433. void TuneD()
  434. {
  435. lcd.setBacklight(TEAL);
  436. lcd.print(F("Set Kd"));
  437.  
  438. uint8_t buttons = 0;
  439. while(true)
  440. {
  441. buttons = ReadButtons();
  442. float increment = 0.01;
  443. if (buttons & BUTTON_SHIFT)
  444. {
  445. increment *= 10;
  446. }
  447. if (buttons & BUTTON_LEFT)
  448. {
  449. opState = TUNE_I;
  450. return;
  451. }
  452. if (buttons & BUTTON_RIGHT)
  453. {
  454. opState = RUN;
  455. return;
  456. }
  457. if (buttons & BUTTON_UP)
  458. {
  459. Kd += increment;
  460. delay(200);
  461. }
  462. if (buttons & BUTTON_DOWN)
  463. {
  464. Kd -= increment;
  465. delay(200);
  466. }
  467. if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
  468. {
  469. opState = RUN;
  470. return;
  471. }
  472. lcd.setCursor(0,1);
  473. lcd.print(Kd);
  474. lcd.print(" ");
  475. DoControl();
  476. }
  477. }
  478.  
  479. // ************************************************
  480. // PID COntrol State
  481. // SHIFT and RIGHT for autotune
  482. // RIGHT - Setpoint
  483. // LEFT - OFF
  484. // ************************************************
  485. void Run()
  486. {
  487. // set up the LCD's number of rows and columns:
  488. lcd.print(F("Sp: "));
  489. lcd.print(Setpoint);
  490. lcd.write(1);
  491. lcd.print(F("C : "));
  492.  
  493. SaveParameters();
  494. myPID.SetTunings(Kp,Ki,Kd);
  495.  
  496. uint8_t buttons = 0;
  497. while(true)
  498. {
  499. setBacklight(); // set backlight based on state
  500.  
  501. buttons = ReadButtons();
  502. if ((buttons & BUTTON_SHIFT)
  503. && (buttons & BUTTON_RIGHT)
  504. && (abs(Input - Setpoint) < 0.5)) // Should be at steady-state
  505. {
  506. StartAutoTune();
  507. }
  508. else if (buttons & BUTTON_RIGHT)
  509. {
  510. opState = SETP;
  511. return;
  512. }
  513. else if (buttons & BUTTON_LEFT)
  514. {
  515. opState = OFF;
  516. return;
  517. }
  518. DoControl();
  519. lcd.setCursor(0,1);
  520. lcd.print(Input);
  521. lcd.write(1);
  522. lcd.print(F("C : "));
  523. float pct = map(Output, 0, WindowSize, 0, 1000);
  524. lcd.setCursor(10,1);
  525. lcd.print(F(" "));
  526. lcd.setCursor(10,1);
  527. lcd.print(pct/10);
  528. //lcd.print(Output);
  529. lcd.print("%");
  530.  
  531. lcd.setCursor(15,0);
  532. if (tuning)
  533. {
  534. lcd.print("T");
  535. }
  536. else
  537. {
  538. lcd.print(" ");
  539. }
  540. // periodically log to serial port in csv format
  541. if (millis() - lastLogTime > logInterval)
  542. {
  543. Serial.print(Input);
  544. Serial.print(",");
  545. Serial.println(Output);
  546. }
  547.  
  548. delay(100);
  549. }
  550. }
  551.  
  552. // ************************************************
  553. // Execute the control loop
  554. // ************************************************
  555. void DoControl()
  556. {
  557. // Read the input:
  558. if (sensors.isConversionAvailable(0))
  559. {
  560. Input = sensors.getTempC(tempSensor);
  561. sensors.requestTemperatures(); // prime the pump for the next one - but don't wait
  562. }
  563. if (tuning) // run the auto-tuner
  564. {
  565. if (aTune.Runtime()) // returns 'true' when done
  566. {
  567. FinishAutoTune();
  568. }
  569. }
  570. else // Execute control algorithm
  571. {
  572. myPID.Compute();
  573. }
  574. // Time Proportional relay state is updated regularly via timer interrupt.
  575. onTime = Output;
  576. }
  577.  
  578. // ************************************************
  579. // Called by ISR every 15ms to drive the output
  580. // ************************************************
  581. void DriveOutput()
  582. {
  583. long now = millis();
  584. // Set the output
  585. // "on time" is proportional to the PID output
  586. if(now - windowStartTime>WindowSize)
  587. { //time to shift the Relay Window
  588. windowStartTime += WindowSize;
  589. }
  590. if((onTime > 100) && (onTime > (now - windowStartTime)))
  591. {
  592. digitalWrite(RelayPin,HIGH);
  593. }
  594. else
  595. {
  596. digitalWrite(RelayPin,LOW);
  597. }
  598. }
  599.  
  600. // ************************************************
  601. // Set Backlight based on the state of control
  602. // ************************************************
  603. void setBacklight()
  604. {
  605. if (tuning)
  606. {
  607. lcd.setBacklight(VIOLET); // Tuning Mode
  608. }
  609. else if (abs(Input - Setpoint) > 1.0)
  610. {
  611. lcd.setBacklight(RED); // High Alarm - off by more than 1 degree
  612. }
  613. else if (abs(Input - Setpoint) > 0.2)
  614. {
  615. lcd.setBacklight(YELLOW); // Low Alarm - off by more than 0.2 degrees
  616. }
  617. else
  618. {
  619. lcd.setBacklight(WHITE); // We're on target!
  620. }
  621. }
  622.  
  623. // ************************************************
  624. // Start the Auto-Tuning cycle
  625. // ************************************************
  626.  
  627. void StartAutoTune()
  628. {
  629. // REmember the mode we were in
  630. ATuneModeRemember = myPID.GetMode();
  631.  
  632. // set up the auto-tune parameters
  633. aTune.SetNoiseBand(aTuneNoise);
  634. aTune.SetOutputStep(aTuneStep);
  635. aTune.SetLookbackSec((int)aTuneLookBack);
  636. tuning = true;
  637. }
  638.  
  639. // ************************************************
  640. // Return to normal control
  641. // ************************************************
  642. void FinishAutoTune()
  643. {
  644. tuning = false;
  645.  
  646. // Extract the auto-tune calculated parameters
  647. Kp = aTune.GetKp();
  648. Ki = aTune.GetKi();
  649. Kd = aTune.GetKd();
  650.  
  651. // Re-tune the PID and revert to normal control mode
  652. myPID.SetTunings(Kp,Ki,Kd);
  653. myPID.SetMode(ATuneModeRemember);
  654. // Persist any changed parameters to EEPROM
  655. SaveParameters();
  656. }
  657.  
  658. // ************************************************
  659. // Check buttons and time-stamp the last press
  660. // ************************************************
  661. uint8_t ReadButtons()
  662. {
  663. uint8_t buttons = lcd.readButtons();
  664. if (buttons != 0)
  665. {
  666. lastInput = millis();
  667. }
  668. return buttons;
  669. }
  670.  
  671. // ************************************************
  672. // Save any parameter changes to EEPROM
  673. // ************************************************
  674. void SaveParameters()
  675. {
  676. if (Setpoint != EEPROM_readDouble(SpAddress))
  677. {
  678. EEPROM_writeDouble(SpAddress, Setpoint);
  679. }
  680. if (Kp != EEPROM_readDouble(KpAddress))
  681. {
  682. EEPROM_writeDouble(KpAddress, Kp);
  683. }
  684. if (Ki != EEPROM_readDouble(KiAddress))
  685. {
  686. EEPROM_writeDouble(KiAddress, Ki);
  687. }
  688. if (Kd != EEPROM_readDouble(KdAddress))
  689. {
  690. EEPROM_writeDouble(KdAddress, Kd);
  691. }
  692. }
  693.  
  694. // ************************************************
  695. // Load parameters from EEPROM
  696. // ************************************************
  697. void LoadParameters()
  698. {
  699. // Load from EEPROM
  700. Setpoint = EEPROM_readDouble(SpAddress);
  701. Kp = EEPROM_readDouble(KpAddress);
  702. Ki = EEPROM_readDouble(KiAddress);
  703. Kd = EEPROM_readDouble(KdAddress);
  704. // Use defaults if EEPROM values are invalid
  705. if (isnan(Setpoint))
  706. {
  707. Setpoint = 60;
  708. }
  709. if (isnan(Kp))
  710. {
  711. Kp = 850;
  712. }
  713. if (isnan(Ki))
  714. {
  715. Ki = 0.5;
  716. }
  717. if (isnan(Kd))
  718. {
  719. Kd = 0.1;
  720. }
  721. }
  722.  
  723.  
  724. // ************************************************
  725. // Write floating point values to EEPROM
  726. // ************************************************
  727. void EEPROM_writeDouble(int address, double value)
  728. {
  729. byte* p = (byte*)(void*)&value;
  730. for (int i = 0; i < sizeof(value); i++)
  731. {
  732. EEPROM.write(address++, *p++);
  733. }
  734. }
  735.  
  736. // ************************************************
  737. // Read floating point values from EEPROM
  738. // ************************************************
  739. double EEPROM_readDouble(int address)
  740. {
  741. double value = 0.0;
  742. byte* p = (byte*)(void*)&value;
  743. for (int i = 0; i < sizeof(value); i++)
  744. {
  745. *p++ = EEPROM.read(address++);
  746. }
  747. return value;
  748. }

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. 
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.

Lombo Tip-Strip com grelhado Zuccini Cozido 90 minutos @ 57C.

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.


Haddock Filetes com vagens e laranja molho de açafrão. (Um dos nossos favoritos!)
Cozido 20 minutes@54.5C
 Transferências e links  

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 




 Para Leonardo:

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:

  1. # Include <TimerOne.h>
  2. / / ...
  3. void setup ()
  4. {
  5. / / ...
  6. Timer1 . initialize ( 15.000 );
  7. Timer1 . attachInterrupt ( TimerInterrupt );
  8. }
  9. / / ..
  10. anular TimerInterrupt ()
  11. {
  12. se ( opState == OFF )
  13. {
  14. digitalWrite ( RelayPin , LOW ); / / fazer revezamento certeza é fora
  15. }
  16. outro
  17. {
  18. DriveOutput ();
  19. }
  20. }

 Fonte: http://learn.adafruit.com/sous-vide-powered-by-arduino-the-sous-viduino?view=all

Um comentário: