NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » Beer FermentBot Phase 1

May 01, 2009
by DonNYC
DonNYC's Avatar

I have completed my first project!

I brew my own beer and need to control temperature during fermentation. I already have a temp controller but it does not do everything I want. I can only hold the set temp and I have to manually adjust it if I want to change over time. The problem is that I brew at our family vacation house 100 miles from where I live.

I have posted the code here. I am new to C programming and I realize that it is not the best code but it works.

Using the thermometer as a starting point I was able to have output pins trigger at specific temps. I have one pin for heating and one for cooling. I still need to work out the wiring for the relays.

Using the realtime clock I can program a temperature schedule. In the code I have it increase 4 degrees in ½ degree increments over 7 days.

I would like to add logging to a computer. I have data streaming to the serial port but it is dumping too much data to put it in all in a log file. My choices are to either have a script on the computer only log data at given intervals or program the NerdKit to only send data at fewer intervals. Any ideas?

Thanks,

Don

May 01, 2009
by mcai8sh4
mcai8sh4's Avatar

Don - you've amalgamated 2 of my favorite things... technology and beer! Sir, I salute you!

I'm loving the idea, and jealous that I don't brew my own (although here in the uk, there are plenty of microbreweries, so real beers always near.)

I'm just thinking out loud here, but regarding your data logging. I've just quickly looked at your code (and now appreciate how useful comments are :) ) You could strip your main function down a little into smaller functions (like one for heat, cool etc) this will initially make the main function a little clearer. Then within each function you could pass the details that you want to log (time...heat on...temp), then remove the other output to the serial port. This then will only give you the 'main events'. If you want more detail, maybe set a count variable, or use 'the_time' and an 'if' statement, so that you can get more information, but only every minute - or whatever you require. This would then reduce the amount of data you'd be logging.

The other method, as you've mentioned is just to have your computer control how often the data is logged.

From a computer point of view, a script or two could then be used (I'd use SED but I'm useless) to then just get the data that you need to know from the logs. You could also set up a little script to email you for main events (or, god forbid, errors). Although if an error occurs, you still have 100 miles to navigate to try and save anything.

Another thought, could you set up your computer to send an emergency shutoff if the worst should happen?

I hope you get everything sorted to your satisfaction. Best of luck to you.

May 01, 2009
by DonNYC
DonNYC's Avatar

I highly recommend you try brewing it is easy and fun. You should check out how to brew.

I think your idea of putting the serial log in a function for each switch is a good idea. I think I will also add a state switch variable so I can display on the LCD how many times it changed state.

Thanks,

Don

June 02, 2009
by DonNYC
DonNYC's Avatar

We have Fermentation!

nerd beer

Phase 1 is a go. I brewed a Belgian Blond on the weekend and it is fermenting away. I have programed the NerdKit (Code) to raise the fermentation temp by .5 degrees a day for a week. I have a relay set up to turn on a heater that is wrapped around the carboy. The basement is cool enough that I do not need to set up any cooling.

I will be back out there the weekend after this and I will let you all know how it went.

Thanks again for everyone who helped.

Don

June 02, 2009
by mcai8sh4
mcai8sh4's Avatar

Don - looking good! And a belgian blond - one of my favorites. Hope it tastes good.

June 03, 2009
by paulip
paulip's Avatar

But this will open the debate...duct tape vs electrical tape. I really think you should go with duct tape.

June 03, 2009
by mcai8sh4
mcai8sh4's Avatar

Nothing screams 'GOOD ENGINEERING' like duct-tape - it's just the professional thing to do!

Electrical tape should be used for what it was made for... sticking over cuts on your fingers! (DISCLAIMER : you probably shouldn't do that!)

June 03, 2009
by wayward
wayward's Avatar
Nothing screams [GOOD ENGINEERING] like duct-tape

Oh, I thought you said "ducked ape". 'Cause I had one in my project, but it didn't scream "GOOD ENGINEERING". It screamed something incomprehensible.

(quietly leaves the room)

June 03, 2009
by DonNYC
DonNYC's Avatar

For this project I prefer electrical tape because I have to remove it easily.

And forget about duct tape. Real men use gaffers tape.

June 04, 2009
by mcai8sh4
mcai8sh4's Avatar

Agree - duct tape is the poor mans gaffer tape

June 04, 2009
by AVRGirl
AVRGirl's Avatar

As an Aussie - I can appreciate good beer - I only wish I could be there drinking it with you. Fantastic project!!

As for Duct Tape vs Electrical - Remember the catch cry "Spare the Duct Tape, spoil the project!!" Only girlies use gaffer tape - for waxing their legs!!

June 17, 2009
by DonNYC
DonNYC's Avatar

Beer is done fermenting. The NerdKit worked like a charm.

done

I hit my final gravity reading of 1.010. It started at 1.068 so that makes it about 7.6% ABV exactly where I wanted it. It stayed in the temp range I wanted. In the future I plan on having it hooked up to a laptop so I can get some live logging of it.

I have keged it and it is carbonating/conditioning now. I will a have a tasting this weekend.

The picture is upside down because I soldered my own board and I added a header so the LCD can plug directly in the board. Worked great but unfortunately it is upside down. It is ok for now, I am planning on putting the whole thing in an enclosure now that it works.

Don

June 18, 2009
by mcai8sh4
mcai8sh4's Avatar

Don - looking good. The beer sounds quite strong for my tastes, but it's still making me want to nip to the pub. I wonder if there's a way to invert the text so it's the right way up.

Well done!

July 06, 2009
by DonNYC
DonNYC's Avatar

Well I screwed up something in my recipe/process and I am getting an off flavor. I am sending it out to some experts for an analysis.

The good news is that NerdKit performed flawlessly. I am very happy with how it worked.

I should be brewing again in a week or so. I will keep you all up to date.

Don

August 27, 2012
by g33kDAD
g33kDAD's Avatar

Don,

Would you mind posting the code again? I'm doing a similar project but need to cool the fermentation down but keep a steady temp. Glad I'm not the only person who thought of this.

You ever figure out what gave the off flavors?

August 31, 2012
by g33kDAD
g33kDAD's Avatar

Had a feeling I wouldn't get a response, no biggie. I'll post what I've accomplished so far as this could help others with their projects or decide to tackle this one.

Overall goal is to ferment beer(around 70degress) during summer in Florida - ohh yea in my garage. Found plans to make "son of fermentation chiller" online - http://home.roadrunner.com/~brewbeer/chiller/chiller.PDF

Instead of using the thermostat, I'm using the nerdkit. I taken code from the example projects and the thermostat project to create this.

I'll post pictures of the nerdkit this weekend.

// tempsensor.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600
#define FAN_ON  0
#define FAN_OFF 1

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input
// PC1 -- Button to control status LCD screen
// PC4 -- FAN Control. Powers 12v Fans through a relay

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);  
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // enable internal pullup resistor on PC1 (the status button)
  PORTC |= (1<<PC1);

  // FAN as output
  DDRC |= (1<<PC4);

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint8_t i;  
  uint8_t b;                //bit to help count fan cycles
  uint8_t status = 0;       //Used for animation purposes
  uint8_t ani = 0;          //Used for animation purposes
  uint16_t last_sample = 0;
  uint16_t fan_count = 0;   //Record how many times the Fan turned on
  double this_temp;
  double temp_avg;  
  double desired_temp = 70.0;  // Keep the temp under
  double diff_temp = 2.0;  //When cooling it must reach the desired temp minus this temp before fan off
  double highest_temp = desired_temp;  // Record the highest temp - start with something
  double lowest_temp = desired_temp;  // Record the lowest temp - start with something

  // Other Variables
  uint8_t state = FAN_OFF;  // state number!
  uint16_t timeout = 200;   // timeout, in centiseconds

  // loop keeps looking forever  
  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    // Compare actual and desired temperature. Check the current temp and compare to lowest/highest temps

    if (temp_avg >= desired_temp) {state = FAN_ON;} // Turn on FAN

    if (temp_avg < desired_temp - diff_temp) {state = FAN_OFF;} // Turn off FAN

    if (temp_avg < lowest_temp) {lowest_temp = temp_avg;}  //Record the lowest Temp

    if (temp_avg > highest_temp) {highest_temp = temp_avg;}  //Record the highest Temp

    if((PINC & (1<<PC1)) == 0) // Show status message
    {

    //Display Fan Count
    //Display Lowest Temp
    //Display Highest Temp

    lcd_home();
    lcd_line_one();  // Title with animation
        switch(status) {
            case 0:                     
            lcd_write_string(PSTR("   * Brew STATUS *  "));       
            status = 1;
            break;      
            case 1:                     
            lcd_write_string(PSTR("  *  Brew STATUS  * "));       
            status = 2;
            break;  
            case 2:                     
            lcd_write_string(PSTR(" *   Brew STATUS   *"));       
            status = 3;
            break;  
            case 3:                     
            lcd_write_string(PSTR("  *  Brew STATUS  * "));       
            status = 0;
            break;          
            }

    lcd_line_two();
    lcd_write_string(PSTR("Cooled:  "));
    lcd_write_int16(fan_count);  // Display the fan count
    lcd_write_string(PSTR("  Times   "));

    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("Highest Temp:   %.1f"), highest_temp);  // Display Highest Temp

    lcd_line_four();
    fprintf_P(&lcd_stream, PSTR("Lowest Temp:    %.1f"), lowest_temp);   // Display Lowest Temp

    delay_ms(190);  // Slow down the animation

    } else {
    // write normal message to LCD
    lcd_home();
    lcd_line_one();
    fprintf_P(&lcd_stream, PSTR("Current Temp:   %.1f"), temp_avg);     // Display Current Temp

    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Desired Temp:   %.1f"), desired_temp); // Display Desired Temp

    lcd_line_three();

    if(timeout == 0) {
      switch(state) {
        case FAN_ON:   
          // If Fan is on, display the cool down temp to turn off fan   
          // Show a simple animation during the cool down
          switch(ani) {
            case 0:
            fprintf_P(&lcd_stream, PSTR("*   Cooling to: %.1f"), desired_temp - diff_temp);
            ani = 1;
            break;      
            case 1:                     
            fprintf_P(&lcd_stream, PSTR(" *  Cooling to: %.1f"), desired_temp - diff_temp);
            ani = 2;
            break;  
            case 2:                     
            fprintf_P(&lcd_stream, PSTR("  * Cooling to: %.1f"), desired_temp - diff_temp);
            ani = 3;
            break;  
            case 3:                     
            fprintf_P(&lcd_stream, PSTR("   *Cooling to: %.1f"), desired_temp - diff_temp);
            ani = 0;
            break;          
            }

          PORTC |= (1<<PC4);  // Send Signal to Relay
          timeout = 50;       // repeat in .5 second
          b = 1;    // bit used to count cooling cycle
          break;
        case FAN_OFF:         
          lcd_write_string(PSTR("OFF - Temp is good  "));         
          PORTC &= ~(1<<PC4);   // Ensure the signal to relay is off
          timeout = 500;        // Check again in 5 seconds   
          if(b == 1)    // check bit to count towards a successful cool cycle
            {
            fan_count = fan_count +1; //Count Cooling down times
            b = 0;      // Reset the bit
            }
          ani = 0;      // Reset the animation bit 
          break;
        }
    }

    lcd_line_four();
    lcd_write_int16_centi(timeout); // Show the countdown before next check
    lcd_write_string(PSTR(" sec. left      "));

     // delay a bit and decrement timeout
    delay_ms(95);
    timeout = timeout - 10; 
    } 
  }
  return 0;
}

** I'm NOT a programmer so please if you see anything that can be improved - please let me know. Thank you

September 02, 2012
by pcbolt
pcbolt's Avatar

@g33kDAD

Code looks pretty good. The animation will eat up some program memory since each 20-charcter string is slightly different and needs to be store separately, but since the program is small enough it should be no problem. Actually it helps the readability of the code the way it is now. If ever needed to, you could use the "lcd_goto_position()" function to print the animation.

Anyway...let us know how the project turns out.

(And you're right about not getting a response...it's hard to revive a 3-year old thread. Most of the "old-timers" rarely post anymore.)

September 02, 2012
by Ralphxyz
Ralphxyz's Avatar

Personally I like reviving old threads.

Now g33kDAD probable would have gotten the same response from a new thread but in reviving the old thread people get to see the additional dialog from the original post.

Plus who knows possible some of the original posters might still have the email reminder and they might just say hey I ought to see what is happening on the Nerdkits forum.

Some of the original posters really contributed to my learning and I miss them.

Of course these are the type projects I like to see in the Nerdkits Community Library where there is a ready reference.

Ralph

September 02, 2012
by g33kDAD
g33kDAD's Avatar

Appreciate the info pcbolt. I'll look into that function as it does make more sense that way. The program works great but have yet to try full scale over 10-14 day period.

I replied to this thread since its related to my project (though slightly different) but more importantly it is referenced many places. Since the previous code was removed, I wanted to share my code as it might help anyone new in the future.

Thanks! g33kDAD

Post a Reply

Please log in to post a reply.

Did you know that you can build a digital read out (DRO) for a lathe or milling machine? Learn more...