• Some of the links on this forum allow SMF, at no cost to you, to earn a small commission when you click through and make a purchase. Let me know if you have any questions about this.

First Run Using New Smoker

jeffesonm

Smoke Blower
102
10
Joined Mar 3, 2008
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/fo...ow-to-for-making-an-arduino-controlled-smoker

http://hruska.us/tempmon/

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







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





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



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.
 
Last edited:

scarbelly

Epic Pitmaster
OTBS Member
14,318
71
Joined Jul 26, 2009
Looks like you can handle a lot of sausage in that bad boy for sure
 

bamaboy

Meat Mopper
SMF Premier Member
298
11
Joined Sep 26, 2010
Now thats just cool.Ibet you could stack a few pounds of meat in that bad boy.....
 

jeffesonm

Smoke Blower
102
10
Joined Mar 3, 2008
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.
 

mballi3011

Epic Pitmaster
OTBS Member
SMF Premier Member
14,478
55
Joined Mar 12, 2009


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.
 

smokedinthenw

Newbie
4
10
Joined Apr 28, 2011
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?
 

SmokinAl

SMF Hall of Fame Pitmaster
Staff member
Moderator
OTBS Member
★ Lifetime Premier ★
45,959
7,057
Joined Jun 22, 2009
Great looking build! Great looking sausage!
 

jeffesonm

Smoke Blower
102
10
Joined Mar 3, 2008
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.
 
Last edited:

jeffesonm

Smoke Blower
102
10
Joined Mar 3, 2008
/*

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, "<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> </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, "");

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 = 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 = 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);

          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 == ' ') continue;

      if (i == 8) {

        client.print('.');

      }

      client.print(p.name);

    }

    client.println(end_a);

    // print file name with possible blank fill

    for (uint8_t i = 0; i < 11; i++) {

      if (p.name == ' ') continue;

      if (i == 8) {

        client.print('.');

      }

      client.print(p.name);

    }

    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 == ' ') continue;

      if (i == 8) {

        client.print('.');

      }

      client.print(p.name);

    }

    client.print(close_del);

    client.println(close_li);

  }

  client.println(close_ul);

}

#endif
[/hr]
[hr][/hr]
 

SmokinAl

SMF Hall of Fame Pitmaster
Staff member
Moderator
OTBS Member
★ Lifetime Premier ★
45,959
7,057
Joined Jun 22, 2009
 

Latest posts

Hot Threads

Top Bottom
  AdBlock Detected

We noticed that you're using an ad-blocker, which could block some critical website features. For the best possible site experience please take a moment to disable your AdBlocker.