or Connect
SmokingMeatForums.com › Forums › Smoking Supplies & Equipment › Smoker Builds › Other Builds › First Run Using New Smoker
New Posts  All Forums:Forum Nav:

First Run Using New Smoker

post #1 of 11
Thread Starter 

Howdy folks... just got done with the first run of sausage on my new homemade smoker.  It is constructed from a old sheet pan rackfrom a bakery with some sides/doors made from sheet steel and square bar.  The walls, doors and top are insulated with high temp insulation and it's powered by a 1500w replacement brinkmann element.  For temp control I put together an Arduino-based system that uses some Maverick probes and switches the element using a SSR.  Some pictures are below.  Here are some of the the pages I used for reference:

 

http://www.smokingmeatforums.com/forum/thread/90085/the-how-to-for-making-an-arduino-controlled-smoker

 

http://hruska.us/tempmon/

 

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1288631058

 

 

IMG_1617.jpg

 

 

IMG_1619.jpg

 

 

IMG_1618.jpg

 

 

keep an eye on things while I'm at work...

 

webpage.gif

 

 

graph.gif

 

 

 

And of course the end result........

 

IMG_1621.jpg

 

 

 

The sausage was not bad... will go heavier on the smoke and seasoning next time.  Overall though I am very happy with how the smoker performed.  It help temps very well except for that one spike which I think was maybe some chips catching on fire.  It does look like it's having trouble getting up around 160 but I'm thinking the 20 degree ambient temp is the main culprit.  Looking forward to tweaking the setup and churning out more sausage in the near future.


Edited by jeffesonm - 12/22/10 at 11:59am
post #2 of 11

Looks like you can handle a lot of sausage in that bad boy for sure

post #3 of 11

Now thats just cool.Ibet you could stack a few pounds of meat in that bad boy.....

post #4 of 11
Thread Starter 

For anyone else that comes across this and is trying to estimate what size heating element they need...

 

The 1500w element is having trouble keeping temps any higher than 160... fine for sausage but not for butts.  Ambient temp is around 30 F which I suspect doesn't help.  The element is currently at the top of the bottom half of the smoker, so I'd guess the internal volume it's heating is 28"x18"x30", just under 9 cu f if my math is right.

post #5 of 11

icon_cool.gif

Well your sausage looks awesome and the smoker looks great but I might think about maybe using a gas burner for the higher temp smokes that you want to do.

post #6 of 11

I'm going to attemp the same project that you have done, is there any tips you can give me on the code? I would like to be able to setup a sausage program that will increase the temp slowly up to 160 and then shutdown and posiibly alarm when the meat reaches 152. Also I have ordered some parts from sparkfun but asynclabs did not have the wishield, any suggestions?

post #7 of 11

Great looking build! Great looking sausage!

post #8 of 11
Thread Starter 

Some updates here...

 

Smoker has been working well overall.  It's getting a bit warmer here and the smoker gets up to 200-210 or so when the ambient temperature is around 70.  I'm going to try and seal things off better and hope that combined with another 10+ degrees of summer will put me into the 225 range for butts.  We may be re-doing our kitchen this summer so if I have the electrician around I may just go ahead and run a 220V outlet so I can just use an oven element and be done with it.

 

Guess I can't upload attachments so I'll post my current code in case it helps anyone.

 

 

post #9 of 11
Thread Starter 

 

 

Quote:
/*
BotulisMaster9000000
 
pins:                            LCD pins                          
1 - heating element / LED        1 -  ground            9 -        
2                                2 -  +5v              10 -        
3 - LCD RS                       3 -  pot              11 - DB4 (6)
4 - ethernet/sd                  4 -  RS (3)           12 - DB5 (7)
5 - LCD EN                       5 -  ground           13 - DB6 (8)
6 - LCD DB4                      6 -  EN (5)           14 - DB7 (9)
7 - LCD DB5                      7 -                   15 - +5v    
8 - LCD DB6                      8 -                   16 - ground 
9 - LCD DB7
10 - ethernet/sd
11 - ethernet/sd
12 - ethernet/sd
13 - ethernet/sd
 
*/
 
#include <SPI.h>
#include <LiquidCrystal.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <Ethernet.h>
#include <Time.h> 
#include <Udp.h>
#include <Flash.h>
 
#define SERIAL
#define USE_LCD
#define USE_LOG
 
// ETHERNET SETUP
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 101 };
byte gateway[] = { 192, 168, 0, 1 };   //your router's IP address
byte subnet[] = { 255, 255, 255, 0 };    //subnet mask of the network 
 
Server server(580);
 
// NTP TIME SYNC SETUP
unsigned int localPort = 8888;      // local port to listen for UDP packets
byte timeServer[] = {192, 43, 244, 18}; // time.nist.gov NTP server
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
const  long timeZoneOffset = 18000L; // 60 seconds * 60 minutes * 5 hours
 
// SD CARD SETUP
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
 
#ifdef USE_LCD  // LCD SETUP
LiquidCrystal lcd(3, 5, 6, 7, 8, 9);
#endif // USE_LCD
 
// setup variables to track interval for logging/temp adjustment
long previousMillis = 0;        // will store last time logging/temp adjustment was called
unsigned int SECOND = 1000;
long MINUTE = SECOND*60;
long HOUR = MINUTE*60;
int interval = 2*SECOND;           // interval at which to log data/adjust temp
 
//int tempProgram[] = {75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75}; // bacon
int tempProgram[] = {110, 120, 130, 140, 150, 160, 165, 165, 165, 165, 165, 165, 165, 165, 165}; // sausage
//int tempProgram[] = {125, 135, 145, 155, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165}; // hot dogs
//int tempProgram[] = {130, 145, 145, 145, 160, 170, 170, 170, 170}; 
//int tempProgram[] = {100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}; // jerky
 
// setup variables to hold temp readings/targets
byte pitTemp = 0;
byte pitTarget = 0;
byte meatTemp = 0;
byte meatTarget = 152;
 
// heating element
byte elementStatus = HIGH;
unsigned int elementPin =  A5;
 
// html
FLASH_STRING(http_ok, "HTTP/1.1 200 OK");
FLASH_STRING(content_html, "Content-Type: text/html");
FLASH_STRING(html_open, "<html><head></head><body><form method=get><table border=1>");
FLASH_STRING(tr_time, "<tr><td>%2.2d:%2.2d</td><td colspan=2>%2.2d:%2.2d:%2.2d</td></tr>");
FLASH_STRING(tr_header, "<tr><td>&nbsp;</td><td><b>Temp</b></td><td><b>Target</b></td></tr>");
FLASH_STRING(tr_pit, "<tr><td><b>Pit</b></td><td>%2.2d</td><td><input type=text size=3 name=pit value=\"%2.2d\"></td></tr>");
FLASH_STRING(tr_meat, "<tr><td><b>Meat</b></td><td>%2.2d</td><td><input type=text size=3 name=meat value=\"%2.2d\"></td></tr>");
FLASH_STRING(tr_button, "<tr><td colspan=99 align=center><input type=submit value=Update></td></tr></table></form>");
FLASH_STRING(html_close, "</body></html>");
FLASH_STRING(http_404, "HTTP/1.1 404 Not Found");
FLASH_STRING(file_not_found, "404!");
FLASH_STRING(open_ul, "<ul>");
FLASH_STRING(open_li, "<li><a href=\"");
FLASH_STRING(end_a, "\">");
FLASH_STRING(close_a, "</a>");
FLASH_STRING(close_li, "</li>");
FLASH_STRING(close_ul, "</ul>");
FLASH_STRING(content_csv, "Content-Type: text/csv");
FLASH_STRING(h2_files, "<br><h2>Files:</h2><br>");
FLASH_STRING(attachment, "Content-disposition: attachment; filename=%s");
FLASH_STRING(open_del, "<a href=\"del=");
FLASH_STRING(close_del, "\">X</a>");
FLASH_STRING(temp_scale, "%2.2d:00 - %3d<br>");
 
// lcd messages
FLASH_STRING(welcome_to, "welcome to");
FLASH_STRING(botul9m, "botulismaster9MM");
FLASH_STRING(web_req, "request");
FLASH_STRING(update_ln1, "%2.2d:%2.2d  %2.2d:%2.2d:%2.2d");
FLASH_STRING(update_ln2a, "pit %3d%c/%3d%c");
FLASH_STRING(update_ln2b, "meat %3d%c/%3d%c");
FLASH_STRING(sync_time, "syncing time");
FLASH_STRING(attmpt, "attempt");
FLASH_STRING(blank, "                ");
FLASH_STRING(udp_unavail, "UDP unavailable");
FLASH_STRING(time_set, "time set");
 
//store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
 
void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
}
 
#define BUFSIZ 120
 
void setup() {
  Serial.begin(9600); 
  Serial.print(welcome_to);
  Serial.println(botul9m);
 
  // lcd setup
#ifdef USE_LCD
  lcd.begin(16, 2);
  lcd.clear();
  lcd.home();
  lcd.print(welcome_to);
  lcd.setCursor(0, 1);
  lcd.print(botul9m);
  delay(1000);
#endif // USE_LCD
 
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with breadboards.
  pinMode(10, OUTPUT); // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH); // but turn off the W5100 chip!
 
  pinMode(elementPin, OUTPUT);
 
  // start ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  Udp.begin(localPort);
  server.begin();
 
  // initialize time
  setupTime();
  initializeLog();
}
 
void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis > interval) {
  
    // save the last time you log/adjust temp
    previousMillis = currentMillis;
 
    // read temp data
    pitTemp = thermister_temp(analogRead(4));
    meatTemp = thermister_temp(analogRead(3));
   
#ifdef USE_LOG
    logData();
#endif // USE_LOG
    adjustTargetTemp();
    adjustHeat();
#ifdef USE_LCD
    updateLCD();
#endif // USE_LCD
  }
  
  listenForClients();
}
 
void listenForClients() {
  Client client = server.available();
  if (client) {
    char buffer[BUFSIZ];
    Serial.println(web_req);
    int index = 0;
 
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c != '\n' && c != '\r') {
          buffer[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
 
          continue;            // continue to read more data!
        }
        buffer[index] = 0;
 
        if (strstr(buffer, "?") != 0) { // update pit/meat targets if needed
          String s = String(buffer);
          String p = s.substring(s.indexOf("pit=")+4, s.indexOf("meat=")-1);
          char p_char[p.length()+1];
          p.toCharArray(p_char, sizeof(p_char));
          pitTarget = atoi(p_char);
          String m = s.substring(s.indexOf("meat=")+5, s.indexOf("HTTP")-1);
          char m_char[m.length()+1];
          m.toCharArray(m_char, sizeof(m_char));
          meatTarget = atoi(m_char);
        }
 
#ifdef USE_LOG
#else
        if (strstr(buffer, "GET /del=") !=0 ) { // handle delete
          char filename[13];
          for(int i=0; i<=11; i++){
            filename[i] = buffer[i+9];
          }
          filename[12] = 0;
          if (!file.open(&root, filename, O_WRITE)) {
            //Serial.print("Can't open "); 
            //Serial.println(filename);
            //error("file.open failed");
          }
          if (!file.remove()) {
            //Serial.print("file.remove failed");
          }
        }else if (strstr(buffer, ".CSV") != 0) { // serve up file
          char filename[13];
          for(int i=0; i<=11; i++){
            filename[i] = buffer[i+5];
          }
          filename[12] = 0;
          if (! file.open(&root, filename, O_READ)) { // can't read file
            client.println(http_404);
            client.println(content_html);
            client.println();
            client.println(file_not_found);
          } else {  // can read file
            client.println(http_ok);
            client.println(content_csv);
            attachment.copy(buffer);
            char buf[55];
            sprintf(buf, buffer, filename);
            client.println(buf);
            client.println();
            
            int16_t c;
            while ((c = file.read()) > 0) {
                //Serial.print((char)c); // uncomment the serial to debug (slow!)
                client.print((char)c);
            }
            file.close();
          }
          break;
 
        }
#endif //USE_LOG
 
        client.println(http_ok);
        client.println(content_html);
        client.println("");
        client.println(html_open);
        tr_time.copy(buffer);
        long et = millis();
        int hr = et/HOUR;
        int min = et%HOUR/MINUTE;
        int sec = (et%MINUTE)/1000;
        sprintf(buffer, buffer, hour(), minute(), hr, min, sec);
        client.println(buffer);
        client.print(tr_header);
        tr_pit.copy(buffer);
        sprintf(buffer, buffer, pitTemp, pitTarget);
        client.println(buffer);
        tr_meat.copy(buffer);
        sprintf(buffer, buffer, meatTemp, meatTarget);
        client.println(buffer);
        client.println(tr_button);
#ifdef USE_LOG
#else
        client.println(h2_files);
        ListFiles(client, 0);
#endif
        for(int i =0; i < 10; i++) {
          temp_scale.copy(buffer);
          sprintf(buffer, buffer, i, tempProgram[i]);
          client.println(buffer);
        }
        client.println(html_close);
        break;
 
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}
 
int thermister_temp(int aval) {
double R, T;
 
// These were calculated from the thermister data sheet
// A = 2.3067434E-4;
// B = 2.3696596E-4;
// C = 1.2636414E-7;
//
// This is the value of the other half of the voltage divider
// Rknown = 22200;
// Do the log once so as not to do it 4 times in the equation
// R = log(((1024/(double)aval)-1)*(double)22200);
R = log((1 / ((1024 / (double) aval) - 1)) * (double) 22200);
// Compute degrees C
T = (1 / ((2.3067434E-4) + (2.3696596E-4) * R + (1.2636414E-7) * R * R * R)) - 273.25;
// return degrees F
return ((int) ((T * 9.0) / 5.0 + 32.0));
}
 
void initializeLog() {
  if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("volume.init failed");
  
  // open root directory
  if (!root.openRoot(&volume)) error("openRoot failed");
 
#ifdef USE_LOG
  // create a new file
  char name[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    name[6] = i/10 + '0';
    name[7] = i%10 + '0';
    if (file.open(&root, name, O_CREAT | O_EXCL | O_WRITE)) break;
  }
  file.open(&root, name, O_CREAT | O_EXCL | O_WRITE);
  if (!file.isOpen()) error ("file.create");
  //Serial.print(F("Logging to: "));
  lcd.setCursor(0, 1);
  lcd.print(name);
  delay(1000);
 
  // write header
  file.writeError = 0;
  file.print(F("time,pitTemp,pitTarget,meatTemp,meatTarget,elementStatus"));
  file.println();
  if (file.writeError || !file.sync()) {
    error("write header failed");
  }
#endif // USE_LOG
}
 
// log data to the SD card
void logData() {
  char line[60];
  file.writeError = 0;
  sprintf(line, "%2.2d:%2.2d:%2.2d,%3d,%3d,%3d,%3d,%3d", hour(), minute(), second(),pitTemp, pitTarget, meatTemp, meatTarget, elementStatus);
  file.print(line);
  file.println();  
  if (!file.sync()) error("sync failed");
}
 
void updateLCD() {
#ifdef USE_LCD
  char line[20];
  lcd.clear();
  lcd.home();
  long et = millis();
  int hr = et/HOUR;
  int min = et%HOUR/MINUTE;
  int sec = (et%MINUTE)/1000;
  
  update_ln1.copy(line);
  sprintf(line, line, hour(), minute(), hr, min, sec);
  lcd.print(line);
  lcd.setCursor(0, 1);
  if(second() % 6 < 3) {
    update_ln2a.copy(line);
    sprintf(line, line, pitTemp, 0xDF, pitTarget, 0xDF);
  } else {
    update_ln2b.copy(line);
    sprintf(line, line, meatTemp, 0xDF, meatTarget, 0xDF);
  }
  lcd.print(line);
#endif // USE_LCD
}
 
 
// adjustHeat
void adjustHeat() {
    if ((pitTarget) > pitTemp) {
        elementStatus = HIGH;
    } else {
        elementStatus = LOW;
    }
    if(pitTemp < 0) {
      elementStatus = LOW; // if something is wrong turn off the element
    }
    digitalWrite(elementPin, elementStatus);
}
 
 
// adjustHeat
void adjustTargetTemp() {
  int hr = millis()/HOUR;
  //int hr = millis()%HOUR/MINUTE;
  if(hr > sizeof(tempProgram))
    hr = sizeof(tempProgram)-1;
  pitTarget = tempProgram[hr];
  //pitTarget = 75;
}
 
void setupTime() {
  char buffer[20];
 
  Serial.println(sync_time); 
#ifdef USE_LCD
  lcd.clear();
  lcd.home();
  lcd.print(sync_time);
  lcd.setCursor(0,1);
  lcd.print(attmpt);
#endif // USE_LCD
  
  for(int i=1; i <=5; i++) {
    Serial.println(i);
#ifdef USE_LCD
    lcd.setCursor(1, 8);
    lcd.print(i);
//    attmpt.copy(buffer);
//    sprintf(buffer, buffer, i); 
//    lcd.print(buffer);
#endif // USE_LCD
 
    sendNTPpacket(timeServer); // send an NTP packet to a time server
  
     // wait to see if a reply is available
    delay(1000);  
    if ( Udp.available() ) {  
      Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
  
      //the timestamp starts at byte 40 of the received packet and is four bytes,
      // or two words, long. First, extract the two words:
      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
      // combine the four bytes (two words) into a long integer
      // this is NTP time (seconds since Jan 1 1900):
      unsigned long secsSince1900 = highWord << 16 | lowWord;
      // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
      const unsigned long seventyYears = 2208988800UL;     
      // subtract seventy years:
      unsigned long epoch = secsSince1900 - seventyYears - timeZoneOffset;  
      setTime(epoch);
      Serial.println(time_set);
      break;
    } else {
      Serial.println(udp_unavail);
#ifdef USE_LCD
      lcd.setCursor(0, 1);
      lcd.print(udp_unavail);
#endif // USE_LCD
      delay(2000); 
    }
  }
  if (timeStatus()== timeNotSet) {
    Serial.println("time not set... defaulting");
#ifdef USE_LCD
    lcd.setCursor(0, 1);
    lcd.print("fail. defaulting");
#endif // USE_LCD
    setTime(1262347900);
  }
}
 
// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(byte *address)
{
 
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
 
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:   
  Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123
}
 
#ifdef USE_LOG
#else
void ListFiles(Client client, uint8_t flags) {
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
  dir_t p;
  char buffer[15];
  
  root.rewind();
  client.println(open_ul);
  while (root.readDir(p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;
 
    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;
 
    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
 
    // print any indent spaces
    client.println(open_li);
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print(p.name[i]);
    }
    client.println(end_a);
    
    // print file name with possible blank fill
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print(p.name[i]);
    }
    
    client.println(close_a);
    
    if (DIR_IS_SUBDIR(&p)) {
      client.print('/');
    }
 
    // print modify date/time if requested
    if (flags & LS_DATE) {
       root.printFatDate(p.lastWriteDate);
       client.print(' ');
       root.printFatTime(p.lastWriteTime);
    }
    // print size if requested
    if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
      client.print(' ');
      client.print(p.fileSize);
    }
    
    // print file name with possible blank fill
    client.print(open_del);
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print(p.name[i]);
    }
    client.print(close_del);
    
    client.println(close_li);
  }
  client.println(close_ul);
}
#endif
 

 

post #10 of 11

Okay it just got way to Sci-Fi for me! huh.gif LOL

post #11 of 11

huh.gif

New Posts  All Forums:Forum Nav:
  Return Home
  Back to Forum: Other Builds
SmokingMeatForums.com › Forums › Smoking Supplies & Equipment › Smoker Builds › Other Builds › First Run Using New Smoker