Real Time Online Fermentation Temperature Monitor for ~$60

Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum

Help Support Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
I'm having fun watching my first fermentation (of a wine kit not beer). My carboy is just sitting here in my office and indeed it is working on getting appreciably hotter than ambient. It annoys me that people just say "10F hotter than ambient" but you know that depends on what ambient is, what the yeast is, how fermentable the wort is etc. This satisfies my inner geek.

https://cosm.com/feeds/97319
 
I'm having fun watching my first fermentation (of a wine kit not beer). My carboy is just sitting here in my office and indeed it is working on getting appreciably hotter than ambient. It annoys me that people just say "10F hotter than ambient" but you know that depends on what ambient is, what the yeast is, how fermentable the wort is etc. This satisfies my inner geek.

https://cosm.com/feeds/97319

Good!

I meant to ask you as well, were there any parts of the instructions that didnt make sense? Or bits i should add?
To me they seem good, but then again I made the thing so i may be assuming people know certain things.
 
My DS18B20s seem to have their power and ground switched compared to what you published which I know came from the spec sheet which is strange. When I followed your diagram they get blazing hot a few seconds after the board powers up. This is what I followed to get mine to work:
DS18S20-hookup.png


Also, I struggled with a few different implementations of the Cosm upload. Yours is far and away the easiest to get working. Not sure why I tried others!
 
No the schematic picture is right, its identical to what you show there..pin 1 (GND) is the left most pin with the flat side facing you...you have to look at the bottom view picture or else the flat picture makes no sense.

ds18b20.jpg


Finally got mine working again with just a single 4.7k resistor across the Data 3 and Pwr pins...gonna alter my schematic pic...
 
Now that you mention it I see it. My brain looked at the bottom view and immediately thought I was looking top down thus the switch. The isometric view is more clear to me regardless. I finished stuffing OneWire and Dallas into my project. I'm still doing minor cleaning up but I've gotten code which polls a single temperature sensor every 5 seconds down to a sketch size of 1,564 bytes and 30 bytes of SRAM. I'll post the code once I'm convinced that it is indeed optimized.
 
Well this project looks fun. I am thinking about putting this together however this would be my first go at arduino. I debated on using this setup or the teensypi setup. I decided to go with this for simplicity. From my understanding I could always adapt this setup to add the Raspberry Pi to run its own webpage.

Since this is my first go at arduino I was looking at ways of going solder free (at least on the board) Would adding these two items accomplish this? Or would it just over complicate things?

Arduino Prototype Shield + Mini Breadboard
Arduino Sensor Shield V4.0

Also have you thought about using a headphone jack type of connector on the thermowell so you could unplug the sensor?
 
Well this project looks fun. I am thinking about putting this together however this would be my first go at arduino. I debated on using this setup or the teensypi setup. I decided to go with this for simplicity. From my understanding I could always adapt this setup to add the Raspberry Pi to run its own webpage.

Since this is my first go at arduino I was looking at ways of going solder free (at least on the board) Would adding these two items accomplish this? Or would it just over complicate things?

Arduino Prototype Shield + Mini Breadboard
Arduino Sensor Shield V4.0

Also have you thought about using a headphone jack type of connector on the thermowell so you could unplug the sensor?

The sensor shield could work well, although you'd have to still make your own cables using their connectors, so it might be more trouble than its worth instead of just wiring in your own stereo jack/cat5/other wire for your cables.

The proto shield seems like a waste though as you would basically be using it to hold one 4.7k resistor and 2 wires per probe coming into it....

The stereo jack thing would work, i just chose to go with Cat5 since i have a ton of cables around.
 
Yeah after some looking around I realized I was mixing ideas. If you went with the protoshield you really don't need the Sensor shield and vise versa. The Sensor shield would work pretty good but I had a hard time finding the connectors on DXs website and I didn't feel like ordering from two vendors. Also most sites sell them in packs of 100 or so.

I will probably just stick with the basic setup for now and fine tune it later. My parts have been ordered, so the waiting begins.
 
The soldering isnt that bad, nothing that super small.

If you didnt want to solder onto the bottom, you could solder the wires high up on the male pins from the Ethernet shield that plug into the Arduino to mate the two together. There's probably a good 1cm or so of exposed header or more i'd guess...might make it easier to just wrap around that post and hit it with a bit of solder.

Doing this has its own dangers though, its possible that if you get solder onto the header tip, you may have a tough time mating the two as the pin will be a bit thicker...you'd have to keep those nice and clean.

The nice thing about soldering it all down is its permanency, i had nothing but trouble with wires popping out of the header pins for no obvious reason...you may have gotten the same issue with the proto board.
 
I tinned my three leads from my sensor cable and plugged those tinned ends directly into the top of my ethernet shield. I too didn't want to solder the bottom of the Uno and risk screwing it up with my poor soldering technique.

It took me a LOOONG time but I was able to adapt/pilfer/write a miniscule ethernet library which only knows how to connect to a server and fire data at it. It works perfectly for uploading CSV data to Cosm and only takes up 1,500 bytes of sketch. I also fooled around with using the EEPROM to store the strings that make up the HTTP Put to keep them out of either progmem or SRAM. Now I need to put it all together but I think I can shoehorn all the code into 4,000 bytes or less. This will leave a ton of free space for anything else one might like to include. I also found some code which implements a full blown webserver in an amazing 5k of sketch size! That will be useful when I move to a fermentation controller that can be adjusted from a smart phone ;)
 
...I finished stuffing OneWire and Dallas into my project. I'm still doing minor cleaning up but I've gotten code which polls a single temperature sensor every 5 seconds down to a sketch size of 1,564 bytes and 30 bytes of SRAM. I'll post the code once I'm convinced that it is indeed optimized.

Sure am glad I didn't post that code. I've learned a few more tricks and now have code which polls two sensors every 5 seconds that only consumes 932 bytes of sketch size and 23 bytes of SRAM. The standard setup() and loop() consumes 444 bytes and 9 bytes with no code in either routine. The code below does exactly the same thing, is perhaps more clear, and only uses 134 bytes of progmem and 0 bytes of SRAM.

Code:
int main() {
  // void setup() code goes here, this runs once only at board power up
  
  // Loop forever
  for(;;) {
    // void loop() code goes in here
  }
}
 
Lol your crazy, im interested in seeing your improved code.

Okay, here is OneWire and DallasTemperature mashed together and brutally optimized. I've removed every feature. No parasite mode (that is easy to add back), no CRC checks, no 10 bit temperatures. This is strictly 12-bit temperatures on an externally powered OneWire bus. This will support any number of sensors with almost no modification to the code.

928 bytes of program memory and 21 bytes of SRAM
Code:
// This sketch assumes two DS18B20s are on pin 3 and are not being operated in parasite mode.  Place your sensor ROMS on
// lines 27 and 28 and if your sensors are plugged into a different pin, adjust line 26 to match the pin you use
//
// ======================================================= Includes =======================================================
#include <util/delay.h>    // Using _delay_ms(#) rather than delay(#) saves 146 bytes of sketch size!

// ===================================================== Definitions ====================================================== 
#define PIN_TO_BASEREG(pin)             (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint8_t
#define IO_REG_ASM asm("r30")
#define DIRECT_READ(base, mask)         (((*(base)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask)   ((*(base+1)) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask)  ((*(base+1)) |= (mask))
#define DIRECT_WRITE_LOW(base, mask)    ((*(base+2)) &= ~(mask))
#define DIRECT_WRITE_HIGH(base, mask)   ((*(base+2)) |= (mask))
#define STARTCONVO      0x44  // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH     0xBE  // Read EEPROM
#define TEMP_LSB        0
#define TEMP_MSB        1
#define DEBUG  0  // Controls the inclusion or exclusion of serial debug information; 0 for no serial output 1 for serial output

// ============================================== Project scoped variables ================================================ 
IO_REG_TYPE bitmask;
volatile IO_REG_TYPE *baseReg;
const uint8_t pin = 3;                                                     // The pin on which to establish the OneWire bus
const uint8_t Sensor[2][8] =  {{ 0x28, 0xE0, 0xF7, 0x83, 0x04, 0x00, 0x00, 0x0F },      // Thermowell sensor
                                { 0x28, 0x68, 0xEF, 0x7D, 0x04, 0x00, 0x00, 0x62 }};    // Ambient room air sensor
uint8_t scratchPad[2];

// ========================================================================================================================

uint8_t read_bit(void) {
  IO_REG_TYPE mask=bitmask;
  volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
  uint8_t r;
  
  noInterrupts();
  DIRECT_MODE_OUTPUT(reg, mask);
  DIRECT_WRITE_LOW(reg, mask);
   _delay_us(3);
  DIRECT_MODE_INPUT(reg, mask);	// let pin float, pull up will raise
  _delay_us(10);
  r = DIRECT_READ(reg, mask);
  interrupts();
  _delay_us(53);
  return r;
}

uint8_t read() {
    uint8_t bitMask;
    uint8_t r = 0;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
	if ( read_bit()) r |= bitMask;
    }
    return r;
}

void write_bit(uint8_t v) {
  IO_REG_TYPE mask=bitmask;
  volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;

  if (v & 1) {
    noInterrupts();
    DIRECT_WRITE_LOW(reg, mask);
    DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
    _delay_us(10);
    DIRECT_WRITE_HIGH(reg, mask);	// drive output high
    interrupts();
    _delay_us(55);
  }
  else {
    noInterrupts();
    DIRECT_WRITE_LOW(reg, mask);
    DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
    _delay_us(65);
    DIRECT_WRITE_HIGH(reg, mask);	// drive output high
    interrupts();
    _delay_us(5);
  }
}

//
// Write a byte. The writing code uses the active drivers to raise the
// pin high, if you need power after the write (e.g. DS18S20 in
// parasite power mode) then set 'power' to 1, otherwise the pin will
// go tri-state at the end of the write to avoid heating in a short or
// other mishap.
//

void write(uint8_t v) {
    uint8_t bitMask;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) write_bit( (bitMask & v)?1:0);
    noInterrupts();
    DIRECT_MODE_INPUT(baseReg, bitmask);
    DIRECT_WRITE_LOW(baseReg, bitmask);
    interrupts();
}

uint8_t reset(void)
{
	IO_REG_TYPE mask = bitmask;
	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
	uint8_t r;
	uint8_t retries = 125;

	noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);
	interrupts();
	// wait until the wire is high... just in case
	do {
	  if (--retries == 0) return 0;
          _delay_us(2);
	} while ( !DIRECT_READ(reg, mask));

	noInterrupts();
	DIRECT_WRITE_LOW(reg, mask);
	DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
	interrupts();
        _delay_us(500);
	noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);	// allow it to float
        _delay_us(80);
	r = !DIRECT_READ(reg, mask);
	interrupts();
        _delay_us(420);
	return r;
}

int16_t ReadSensor(const uint8_t sensorIndex) {
  write(0x55);           // Choose ROM
  for(uint8_t i = 0; i < 8; i++) write(Sensor[sensorIndex][i]);
  write(READSCRATCH);
  scratchPad[TEMP_LSB] = read();
  scratchPad[TEMP_MSB] = read();
  reset();
  return (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
}

int main() {
  // This code will only run once, after each powerup or reset of the board
  
  // Set pin 3 to input
  //pinMode(pin, INPUT);        // This takes 136 bytes of sketch size
  //DDRD = DDRD | B11110100;    // This does the same thing and only takes 4 bytes of sketch size
                                // This does the same thing since pin 3 is set to input by default
  bitmask = PIN_TO_BITMASK(pin);
  baseReg = PIN_TO_BASEREG(pin);
  
  #if DEBUG
    Serial.begin(9600);
  #endif

  for (;;) {
     // Request a temperature reading from all Dallas sensors on the OneWire bus
    reset();
    write(0xCC);
    write(STARTCONVO);
    _delay_ms(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20(s)
    reset();
    
    // Read the temperatures previously requested
    int16_t rawTemperature1 = ReadSensor(0);  // Read the first sensor
    int16_t rawTemperature2 = ReadSensor(1);  // Read the second sensor
    
    #if DEBUG
      Serial.print( (float)rawTemperature1 * 0.1125+32,4);
      Serial.write(176);
      Serial.write('F');
      Serial.write('\t');
      Serial.print( (float)rawTemperature2 * 0.1125+32,4);
      Serial.write(176);
      Serial.print("F\n");
    #endif
    _delay_ms(5000);
  }  
}
 
Cosm feed of this code

Okay, I'm declaring victory! I'll probably keep tinkering a little just for my own enjoyment, but I have already slashed 86% of the sketch size and 66% of the SRAM usage out of the code posted on page 1 of this thread. This code will take a 12-bit temperature reading from 2 DS18B20s, put the temperature into a csv flavored HTTP put and fire it at the Cosm sever. I don't use DNS so in the event that the server's IP address ever changes line 154 will need to be modified to reflect the new IP address of the server. I also suspect that this code won't work for temperatures below 32°F (and I don't believe DallasTemperature.cpp will either looking at their code and the DS18B20 datasheet) but I would not expect that to be a big problem for HBT folks.

That said, I think this code would be very easy for a complete newbie to get up and running since you only edit the values laid out in the User-Specific Data block right at the top of the code.

3,256 bytes of program memory and 333 bytes of SRAM
Code:
// ======================================================= Includes =======================================================
#include <util/delay.h>    // Using _delay_ms(#) rather than delay(#) saves 146 bytes of sketch size!

// ================================================== User-Specific Data ==================================================
#define Sensor1 "Carboy_1_Temperature"                             // Enter whatever you wish to call your first sensor inside the double quotes
#define Sensor2 "Ambient_Temperature"                              // Enter whatever you wish to call your second sensor inside the double quotes
#define FEEDID "102607"                                            // Enter the feed ID assigned to you by Cosm.com inside the double quotes
#define APIKEY "xxxxxxxxD0kjGte5FxxxxxxxxxCSAKxva3hwZ3hCxxxxxxxx"  // Enter the feed APIKEY assigned to you by Cosm.com inside the double quotes
#define USERAGENT "Test"                                           // Enter
const uint8_t pin = 3;                                             // The pin that the data pin of the DS18B20s is connected to
const uint8_t Sensor[2][8] =  {{ 0x28, 0xE0, 0xF7, 0x83, 0x04, 0x00, 0x00, 0x0F },      // Thermowell sensor
                                { 0x28, 0x68, 0xEF, 0x7D, 0x04, 0x00, 0x00, 0x62 }};    // Ambient room air sensor

#define DELAY 5000 // The delay in milliseconds between sensor readings; the code below takes around a second to run total
                                
// ===================================================== Definitions ====================================================== 
#define PIN_TO_BASEREG(pin)             (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint8_t
#define IO_REG_ASM asm("r30")
#define DIRECT_READ(base, mask)         (((*(base)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask)   ((*(base+1)) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask)  ((*(base+1)) |= (mask))
#define DIRECT_WRITE_LOW(base, mask)    ((*(base+2)) &= ~(mask))
#define DIRECT_WRITE_HIGH(base, mask)   ((*(base+2)) |= (mask))
#define STARTCONVO      0x44  // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH     0xBE  // Read EEPROM
#define TEMP_LSB        0
#define TEMP_MSB        1
#define DEBUG  0  // Controls the inclusion or exclusion of serial debug information; 0 for no serial output 1 for serial output

// AVRJazz Mega328 SPI I/O
#define SPI_PORT PORTB
#define SPI_DDR  DDRB
#define SPI_CS   PORTB2

// Wiznet W5100 Op Code
#define WIZNET_WRITE_OPCODE 0xF0
#define WIZNET_READ_OPCODE 0x0F

// Wiznet W5100 Register Addresses
#define MR         0x0000      // Mode Register
#define GAR        0x0001      // Gateway Address: 0x0001 to 0x0004
#define SUBR       0x0005      // Subnet mask Address: 0x0005 to 0x0008
#define SAR        0x0009      // Source Hardware Address (MAC): 0x0009 to 0x000E
#define SIPR       0x000F      // Source IP Address: 0x000F to 0x0012
#define RMSR       0x001A      // RX Memory Size Register
#define TMSR       0x001B      // TX Memory Size Register

#define S0_MR	   0x0400      // Socket 0: Mode Register Address
#define S0_CR	   0x0401      // Socket 0: Command Register Address
#define S0_IR	   0x0402      // Socket 0: Interrupt Register Address
#define S0_SR	   0x0403      // Socket 0: Status Register Address
#define S0_DIPR    0x040C      // Socket 0: Destination IP Address: 0x040C to 0x040F
#define S0_DPORT   0x0410      // Socket 0: Destination Port: 0x0410 to 0x0411
#define S0_PORT    0x0404      // Socket 0: Source Port: 0x0404 to 0x0405
#define SO_TX_FSR  0x0420      // Socket 0: Tx Free Size Register: 0x0420 to 0x0421
#define S0_TX_RD   0x0422      // Socket 0: Tx Read Pointer Register: 0x0422 to 0x0423
#define S0_TX_WR   0x0424      // Socket 0: Tx Write Pointer Register: 0x0424 to 0x0425
#define S0_RX_RSR  0x0426      // Socket 0: Rx Received Size Pointer Register: 0x0425 to 0x0427
#define S0_RX_RD   0x0428      // Socket 0: Rx Read Pointer: 0x0428 to 0x0429

#define TXBUFADDR  0x4000      // W5100 Send Buffer Base Address
#define RXBUFADDR  0x6000      // W5100 Read Buffer Base Address

// S0_MR values
#define MR_CLOSE	  0x00    // Unused socket
#define MR_TCP		  0x01    // TCP
#define MR_UDP		  0x02    // UDP
#define MR_IPRAW	  0x03	  // IP LAYER RAW SOCK
#define MR_MACRAW	  0x04	  // MAC LAYER RAW SOCK
#define MR_PPPOE	  0x05	  // PPPoE
#define MR_ND		  0x20	  // No Delayed Ack(TCP) flag
#define MR_MULTI	  0x80	  // support multicating

// S0_CR values
#define CR_OPEN          0x01	  // Initialize or open socket
#define CR_LISTEN        0x02	  // Wait connection request in tcp mode(Server mode)
#define CR_CONNECT       0x04	  // Send connection request in tcp mode(Client mode)
#define CR_DISCON        0x08	  // Send closing reqeuset in tcp mode
#define CR_CLOSE         0x10	  // Close socket
#define CR_SEND          0x20	  // Update Tx memory pointer and send data
#define CR_SEND_MAC      0x21	  // Send data with MAC address, so without ARP process
#define CR_SEND_KEEP     0x22	  // Send keep alive message
#define CR_RECV          0x40	  // Update Rx memory buffer pointer and receive data

// S0_SR values
#define SOCK_CLOSED      0x00     // Closed
#define SOCK_INIT        0x13	  // Init state
#define SOCK_LISTEN      0x14	  // Listen state
#define SOCK_SYNSENT     0x15	  // Connection state
#define SOCK_SYNRECV     0x16	  // Connection state
#define SOCK_ESTABLISHED 0x17	  // Success to connect
#define SOCK_FIN_WAIT    0x18	  // Closing state
#define SOCK_CLOSING     0x1A	  // Closing state
#define SOCK_TIME_WAIT	 0x1B	  // Closing state
#define SOCK_CLOSE_WAIT  0x1C	  // Closing state
#define SOCK_LAST_ACK    0x1D	  // Closing state
#define SOCK_UDP         0x22	  // UDP socket
#define SOCK_IPRAW       0x32	  // IP raw mode socket
#define SOCK_MACRAW      0x42	  // MAC raw mode socket
#define SOCK_PPPOE       0x5F	  // PPPOE socket

#define TX_BUF_MASK      0x07FF   // Tx 2K Buffer Mask:
#define RX_BUF_MASK      0x07FF   // Rx 2K Buffer Mask:
#define NET_MEMALLOC     0x05     // Use 2K of Tx/Rx Buffer

#define TCP_PORT         80       // TCP/IP Port

#define MAX_BUF 210

// ============================================== Project scoped variables ================================================ 
IO_REG_TYPE bitmask;
volatile IO_REG_TYPE *baseReg;

uint8_t scratchPad[2];
uint8_t buf[MAX_BUF];  // The character buffer in which the HTTP Put will be constructed
// ========================================================================================================================

void SPI_Write(uint16_t addr,uint8_t data) {
  SPI_PORT &= ~(1<<SPI_CS);      // Activate the CS pin
  SPDR = WIZNET_WRITE_OPCODE;    // Start Wiznet W5100 Write OpCode transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = (addr & 0xFF00) >> 8;   // Start Wiznet W5100 Address High Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = addr & 0x00FF;          // Start Wiznet W5100 Address Low Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = data;                   // Start Data transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPI_PORT |= (1<<SPI_CS);       // CS pin is not active
}

unsigned char SPI_Read(uint16_t addr) {
  SPI_PORT &= ~(1<<SPI_CS);      // Activate the CS pin
  SPDR = WIZNET_READ_OPCODE;     // Start Wiznet W5100 Read OpCode transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = (addr & 0xFF00) >> 8;   // Start Wiznet W5100 Address High Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = addr & 0x00FF;          // Start Wiznet W5100 Address Low Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = 0x00;                   // Send Dummy transmission for reading the data
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPI_PORT |= (1<<SPI_CS);       // CS pin is not active
  return(SPDR);
}

void W5100_Init(void)
{
  // Ethernet Setup
  const unsigned char mac_addr[] = {0x00, 0x1D, 0x0D, 0x2C, 0x55, 0x3D};
  const unsigned char ip_addr[] = {192,168,11,8};
  const unsigned char sub_mask[] = {255,255,255,0};
  const unsigned char gtw_addr[] = {192,168,11,1};
  const unsigned char cosm_addr[] = {216,52,233,121};

  // Setting the Wiznet W5100 Mode Register: 0x0000
  SPI_Write(MR,0x80);            // MR = 0b10000000;

  // Setting the Wiznet W5100 Gateway Address (GAR): 0x0001 to 0x0004
  SPI_Write(GAR + 0,gtw_addr[0]);
  SPI_Write(GAR + 1,gtw_addr[1]);
  SPI_Write(GAR + 2,gtw_addr[2]);
  SPI_Write(GAR + 3,gtw_addr[3]);

  // Setting the Wiznet W5100 Source Address Register (SAR): 0x0009 to 0x000E
  SPI_Write(SAR + 0,mac_addr[0]);
  SPI_Write(SAR + 1,mac_addr[1]);
  SPI_Write(SAR + 2,mac_addr[2]);
  SPI_Write(SAR + 3,mac_addr[3]);
  SPI_Write(SAR + 4,mac_addr[4]);
  SPI_Write(SAR + 5,mac_addr[5]);

  // Setting the Wiznet W5100 Sub Mask Address (SUBR): 0x0005 to 0x0008
  SPI_Write(SUBR + 0,sub_mask[0]);
  SPI_Write(SUBR + 1,sub_mask[1]);
  SPI_Write(SUBR + 2,sub_mask[2]);
  SPI_Write(SUBR + 3,sub_mask[3]);

  // Setting the Wiznet W5100 IP Address (SIPR): 0x000F to 0x0012
  SPI_Write(SIPR + 0,ip_addr[0]);
  SPI_Write(SIPR + 1,ip_addr[1]);
  SPI_Write(SIPR + 2,ip_addr[2]);
  SPI_Write(SIPR + 3,ip_addr[3]);    

  // Setting the Wiznet W5100 RX and TX Memory Size (2KB),
  SPI_Write(RMSR,NET_MEMALLOC);
  SPI_Write(TMSR,NET_MEMALLOC);
  
  // Setting the Wiznet to connect to the Cosm server port 80
  SPI_Write(S0_DIPR + 0,cosm_addr[0]);
  SPI_Write(S0_DIPR + 1,cosm_addr[1]);
  SPI_Write(S0_DIPR + 2,cosm_addr[2]);
  SPI_Write(S0_DIPR + 3,cosm_addr[3]);
  SPI_Write(S0_DPORT,((TCP_PORT & 0xFF00) >> 8 ));
  SPI_Write(S0_DPORT + 1,(TCP_PORT & 0x00FF));
  SPI_Write(S0_PORT,((1 & 0xFF00) >> 8 ));    // Set the source port to 1
  SPI_Write(S0_PORT + 1,(1 & 0x00FF));        // Set the source port to 1
  
  // Set the Wiznet to use TCP
  SPI_Write(S0_MR,MR_TCP);                    // Set the mode to TCP  
}

void UploadDataPoint(void) {
  // Connect to the Cosm server and upload the data
  SPI_Write(S0_CR,CR_OPEN);
  while(SPI_Read(S0_CR));
  
  do {
    _delay_ms(1);
  } while(SPI_Read(S0_SR)!=SOCK_INIT);
  
  close(0);
  SPI_Write(S0_MR,MR_TCP);
  SPI_Write(S0_CR,CR_CONNECT);                   // Open Socket

  // Wait for Opening Process
  while(SPI_Read(S0_CR));
  
  do {
    _delay_ms(1);
  } while(SPI_Read(S0_SR)!=SOCK_ESTABLISHED);
  
  send(0,buf,strlen((char *)buf)); 
}

void close(uint8_t sock)
{
   if (sock != 0) return;

   // Send Close Command
   SPI_Write(S0_CR,CR_CLOSE);

   // Waiting until the S0_CR is clear
   while(SPI_Read(S0_CR));
}

void disconnect(uint8_t sock)
{
   if (sock != 0) return;

   // Send Disconnect Command
   SPI_Write(S0_CR,CR_DISCON);

   // Wait for Disconecting Process
   while(SPI_Read(S0_CR));
}

uint16_t send(uint8_t sock,const uint8_t *buf,uint16_t buflen)
{
    uint16_t ptr,offaddr,realaddr,txsize,timeout;   

    if (buflen <= 0 || sock != 0) return 0;

    // Make sure the TX Free Size Register is available
    txsize=SPI_Read(SO_TX_FSR);
    txsize=(((txsize & 0x00FF) << 8 ) + SPI_Read(SO_TX_FSR + 1));

    timeout=0;
    while (txsize < buflen) {
      _delay_ms(1);

     txsize=SPI_Read(SO_TX_FSR);
     txsize=(((txsize & 0x00FF) << 8 ) + SPI_Read(SO_TX_FSR + 1));

     // Timeout for approx 1000 ms
     if (timeout++ > 1000) {

       // Disconnect the connection
       disconnect(sock);
       return 0;
     }
   }	

   // Read the Tx Write Pointer
   ptr = SPI_Read(S0_TX_WR);
   offaddr = (((ptr & 0x00FF) << 8 ) + SPI_Read(S0_TX_WR + 1));
	
    while(buflen) {
      buflen--;
      // Calculate the real W5100 physical Tx Buffer Address
      realaddr = TXBUFADDR + (offaddr & TX_BUF_MASK);

      // Copy the application data to the W5100 Tx Buffer
      SPI_Write(realaddr,*buf);
      offaddr++;
      buf++;
    }

    // Increase the S0_TX_WR value, so it point to the next transmit
    SPI_Write(S0_TX_WR,(offaddr & 0xFF00) >> 8 );
    SPI_Write(S0_TX_WR + 1,(offaddr & 0x00FF));	

    // Now Send the SEND command
    SPI_Write(S0_CR,CR_SEND);

    // Wait for Sending Process
    while(SPI_Read(S0_CR));	

    return 1;
}

uint8_t read_bit(void) {
  IO_REG_TYPE mask=bitmask;
  volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
  uint8_t r;
  
  noInterrupts();
  DIRECT_MODE_OUTPUT(reg, mask);
  DIRECT_WRITE_LOW(reg, mask);
   _delay_us(3);
  DIRECT_MODE_INPUT(reg, mask);	// let pin float, pull up will raise
  _delay_us(10);
  r = DIRECT_READ(reg, mask);
  interrupts();
  _delay_us(53);
  return r;
}

uint8_t read() {
    uint8_t bitMask;
    uint8_t r = 0;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
	if ( read_bit()) r |= bitMask;
    }
    return r;
}

void write_bit(uint8_t v) {
  IO_REG_TYPE mask=bitmask;
  volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;

  if (v & 1) {
    noInterrupts();
    DIRECT_WRITE_LOW(reg, mask);
    DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
    _delay_us(10);
    DIRECT_WRITE_HIGH(reg, mask);	// drive output high
    interrupts();
    _delay_us(55);
  }
  else {
    noInterrupts();
    DIRECT_WRITE_LOW(reg, mask);
    DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
    _delay_us(65);
    DIRECT_WRITE_HIGH(reg, mask);	// drive output high
    interrupts();
    _delay_us(5);
  }
}

void write(uint8_t v) {
    uint8_t bitMask;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) write_bit( (bitMask & v)?1:0);
    noInterrupts();
    DIRECT_MODE_INPUT(baseReg, bitmask);
    DIRECT_WRITE_LOW(baseReg, bitmask);
    interrupts();
}

uint8_t reset(void) {
	IO_REG_TYPE mask = bitmask;
	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
	uint8_t r;
	uint8_t retries = 125;

	noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);
	interrupts();
	// wait until the wire is high... just in case
	do {
	  if (--retries == 0) return 0;
          _delay_us(2);
	} while ( !DIRECT_READ(reg, mask));

	noInterrupts();
	DIRECT_WRITE_LOW(reg, mask);
	DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
	interrupts();
        _delay_us(500);
	noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);	// allow it to float
        _delay_us(80);
	r = !DIRECT_READ(reg, mask);
	interrupts();
        _delay_us(420);
	return r;
}

int16_t ReadSensor(const uint8_t sensorIndex) {
  write(0x55);           // Choose ROM
  for(uint8_t i = 0; i < 8; i++) write(Sensor[sensorIndex][i]);
  write(READSCRATCH);
  scratchPad[TEMP_LSB] = read();
  scratchPad[TEMP_MSB] = read();
  reset();
  return (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
}

// Converts the raw temperature from a DS18B20 directly to a string containing the temperature in °F
// avoids unnecessary floating point math, float variables, and casts
// TODO: May not work properly with temperatures below 32°F
void RawTempToString(uint16_t raw, char * buffer) {
  uint32_t intTemp;
  uint8_t numChars;
  
  intTemp=raw*1125ul+320000ul;    // Hold the temperature
  if(raw<605 && raw>=0) {  // We're looking at a positive number with two digits
    numChars = 7;
  }
  else if (raw>=605) {
    numChars = 8;
  }
  
   // Write the null termination, the first four digits, and then the decimal point
  buffer[numChars--]='\0';
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]='.';
    
  // Continue writing the left of the decimal place until we run out of intTemp
  do {
    buffer[numChars--]=intTemp%10 + '0';
    intTemp/=10;
  } while (intTemp);
}

void itoa(long inp, char * buf) {
  long temp = inp;
  uint8_t numChars=0;
  boolean isNegative=false;
  
  // Check to see if there is a negative sign
  if(temp<0){
    isNegative=true;
    numChars++;
    temp*=-1;
  }
  
  do {
        numChars++;
        temp /= 10;
  } while ( temp );
  
  // Write the negative sign if present and the terminating null character
  temp=inp;
  buf[numChars]=0;
  if(isNegative) {
    temp*=-1;
    buf[0]='-';
  }
  
  int i = numChars - 1;
  do {
      buf[i--] = temp%10 + '0';
      temp /= 10;
  } while (temp);
}

void BuildPut(uint16_t T1, uint16_t T2) {
  // Convert raw temperatures into strings
  char Temp1[8];
  char Temp2[8];
  RawTempToString(T1, (char *)Temp1);
  RawTempToString(T2, (char *)Temp2);
  
  // Set up data buffer to hold the csv data
  char * dataBuffer[strlen(Sensor1)+strlen(Temp1)+strlen(Sensor2)+strlen(Temp2)+4];
 
  strcpy((char *)dataBuffer,Sensor1);
  strcat_P((char *)dataBuffer,PSTR(","));
  strcat((char *)dataBuffer,Temp1);
  strcat_P((char *)dataBuffer,PSTR("\n"));
  strcat((char *)dataBuffer,Sensor2);
  strcat_P((char *)dataBuffer,PSTR(","));
  strcat((char *)dataBuffer,Temp2);
  strcat_P((char *)dataBuffer,PSTR("\n"));
  
  // Convert the length of the data buffer into a string
  char dataLength[2];
  itoa(strlen((char *)dataBuffer)-1,(char *)dataLength);
  
  // Construct the HTTP Put
  strcpy_P((char *)buf,PSTR("PUT /v2/feeds/"));
  strcat((char *)buf,FEEDID);
  strcat_P((char *)buf,PSTR(".csv HTTP/1.1\nHost: api.cosm.com\n"));
  strcat_P((char *)buf,PSTR("X-ApiKey: "));
  strcat((char *)buf,APIKEY);
  strcat_P((char *)buf,PSTR("\nUser-Agent: "));
  strcat((char *)buf,USERAGENT);
  strcat_P((char *)buf,PSTR("\nContent-Length: "));
  strcat((char *)buf,dataLength);
  strcat_P((char *)buf,PSTR("\n\n"));
  strcat((char *)buf,(char *)dataBuffer);
}

int main() {
  // This code will only run once, after each powerup or reset of the board
  
  // Set pin 3 to input
  //pinMode(pin, INPUT);        // This takes 136 bytes of sketch size
  //DDRD = DDRD | B11110100;    // This does the same thing and only takes 4 bytes of sketch size
                                // This does the same thing since pin 3 is set to input by default
  bitmask = PIN_TO_BITMASK(pin);
  baseReg = PIN_TO_BASEREG(pin);
  
  SPI_DDR = (1<<PORTB3)|(1<<PORTB5)|(1<<PORTB2);  // Set MOSI (PORTB3),SCK (PORTB5) and PORTB2 (SS) as output, others as input  
  SPI_PORT |= (1<<SPI_CS);                        // CS pin is not active
  SPCR = (1<<SPE)|(1<<MSTR);                      // Enable SPI, Master Mode 0
  
  #if DEBUG
    Serial.begin(9600);
  #endif
  
  W5100_Init();
  
  for (;;) {
     // Request a temperature reading from all Dallas sensors on the OneWire bus
    reset();
    write(0xCC);
    write(STARTCONVO);
    _delay_ms(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20(s)
    reset();
    
    // Read the temperatures previously requested
    int16_t rawTemperature1 = ReadSensor(0);  // Read the first sensor
    int16_t rawTemperature2 = ReadSensor(1);  // Read the second sensor
    
    BuildPut(rawTemperature1, rawTemperature2);
    
    #if DEBUG
      Serial.print((char *)buf);
    #endif
    
    UploadDataPoint();
    _delay_ms(DELAY);
  }  
}
 
Okay, I'm declaring victory! I'll probably keep tinkering a little just for my own enjoyment, but I have already slashed 86% of the sketch size and 66% of the SRAM usage out of the code posted on page 1 of this thread. This code will take a 12-bit temperature reading from 2 DS18B20s, put the temperature into a csv flavored HTTP put and fire it at the Cosm sever. I don't use DNS so in the event that the server's IP address ever changes line 154 will need to be modified to reflect the new IP address of the server. I also suspect that this code won't work for temperatures below 32°F (and I don't believe DallasTemperature.cpp will either looking at their code and the DS18B20 datasheet) but I would not expect that to be a big problem for HBT folks.

That said, I think this code would be very easy for a complete newbie to get up and running since you only edit the values laid out in the User-Specific Data block right at the top of the code.

3,256 bytes of program memory and 333 bytes of SRAM
Code:
// ======================================================= Includes =======================================================
#include <util/delay.h>    // Using _delay_ms(#) rather than delay(#) saves 146 bytes of sketch size!

// ================================================== User-Specific Data ==================================================
#define Sensor1 "Carboy_1_Temperature"                             // Enter whatever you wish to call your first sensor inside the double quotes
#define Sensor2 "Ambient_Temperature"                              // Enter whatever you wish to call your second sensor inside the double quotes
#define FEEDID "102607"                                            // Enter the feed ID assigned to you by Cosm.com inside the double quotes
#define APIKEY "xxxxxxxxD0kjGte5FxxxxxxxxxCSAKxva3hwZ3hCxxxxxxxx"  // Enter the feed APIKEY assigned to you by Cosm.com inside the double quotes
#define USERAGENT "Test"                                           // Enter
const uint8_t pin = 3;                                             // The pin that the data pin of the DS18B20s is connected to
const uint8_t Sensor[2][8] =  {{ 0x28, 0xE0, 0xF7, 0x83, 0x04, 0x00, 0x00, 0x0F },      // Thermowell sensor
                                { 0x28, 0x68, 0xEF, 0x7D, 0x04, 0x00, 0x00, 0x62 }};    // Ambient room air sensor

#define DELAY 5000 // The delay in milliseconds between sensor readings; the code below takes around a second to run total
                                
// ===================================================== Definitions ====================================================== 
#define PIN_TO_BASEREG(pin)             (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint8_t
#define IO_REG_ASM asm("r30")
#define DIRECT_READ(base, mask)         (((*(base)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask)   ((*(base+1)) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask)  ((*(base+1)) |= (mask))
#define DIRECT_WRITE_LOW(base, mask)    ((*(base+2)) &= ~(mask))
#define DIRECT_WRITE_HIGH(base, mask)   ((*(base+2)) |= (mask))
#define STARTCONVO      0x44  // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH     0xBE  // Read EEPROM
#define TEMP_LSB        0
#define TEMP_MSB        1
#define DEBUG  0  // Controls the inclusion or exclusion of serial debug information; 0 for no serial output 1 for serial output

// AVRJazz Mega328 SPI I/O
#define SPI_PORT PORTB
#define SPI_DDR  DDRB
#define SPI_CS   PORTB2

// Wiznet W5100 Op Code
#define WIZNET_WRITE_OPCODE 0xF0
#define WIZNET_READ_OPCODE 0x0F

// Wiznet W5100 Register Addresses
#define MR         0x0000      // Mode Register
#define GAR        0x0001      // Gateway Address: 0x0001 to 0x0004
#define SUBR       0x0005      // Subnet mask Address: 0x0005 to 0x0008
#define SAR        0x0009      // Source Hardware Address (MAC): 0x0009 to 0x000E
#define SIPR       0x000F      // Source IP Address: 0x000F to 0x0012
#define RMSR       0x001A      // RX Memory Size Register
#define TMSR       0x001B      // TX Memory Size Register

#define S0_MR	   0x0400      // Socket 0: Mode Register Address
#define S0_CR	   0x0401      // Socket 0: Command Register Address
#define S0_IR	   0x0402      // Socket 0: Interrupt Register Address
#define S0_SR	   0x0403      // Socket 0: Status Register Address
#define S0_DIPR    0x040C      // Socket 0: Destination IP Address: 0x040C to 0x040F
#define S0_DPORT   0x0410      // Socket 0: Destination Port: 0x0410 to 0x0411
#define S0_PORT    0x0404      // Socket 0: Source Port: 0x0404 to 0x0405
#define SO_TX_FSR  0x0420      // Socket 0: Tx Free Size Register: 0x0420 to 0x0421
#define S0_TX_RD   0x0422      // Socket 0: Tx Read Pointer Register: 0x0422 to 0x0423
#define S0_TX_WR   0x0424      // Socket 0: Tx Write Pointer Register: 0x0424 to 0x0425
#define S0_RX_RSR  0x0426      // Socket 0: Rx Received Size Pointer Register: 0x0425 to 0x0427
#define S0_RX_RD   0x0428      // Socket 0: Rx Read Pointer: 0x0428 to 0x0429

#define TXBUFADDR  0x4000      // W5100 Send Buffer Base Address
#define RXBUFADDR  0x6000      // W5100 Read Buffer Base Address

// S0_MR values
#define MR_CLOSE	  0x00    // Unused socket
#define MR_TCP		  0x01    // TCP
#define MR_UDP		  0x02    // UDP
#define MR_IPRAW	  0x03	  // IP LAYER RAW SOCK
#define MR_MACRAW	  0x04	  // MAC LAYER RAW SOCK
#define MR_PPPOE	  0x05	  // PPPoE
#define MR_ND		  0x20	  // No Delayed Ack(TCP) flag
#define MR_MULTI	  0x80	  // support multicating

// S0_CR values
#define CR_OPEN          0x01	  // Initialize or open socket
#define CR_LISTEN        0x02	  // Wait connection request in tcp mode(Server mode)
#define CR_CONNECT       0x04	  // Send connection request in tcp mode(Client mode)
#define CR_DISCON        0x08	  // Send closing reqeuset in tcp mode
#define CR_CLOSE         0x10	  // Close socket
#define CR_SEND          0x20	  // Update Tx memory pointer and send data
#define CR_SEND_MAC      0x21	  // Send data with MAC address, so without ARP process
#define CR_SEND_KEEP     0x22	  // Send keep alive message
#define CR_RECV          0x40	  // Update Rx memory buffer pointer and receive data

// S0_SR values
#define SOCK_CLOSED      0x00     // Closed
#define SOCK_INIT        0x13	  // Init state
#define SOCK_LISTEN      0x14	  // Listen state
#define SOCK_SYNSENT     0x15	  // Connection state
#define SOCK_SYNRECV     0x16	  // Connection state
#define SOCK_ESTABLISHED 0x17	  // Success to connect
#define SOCK_FIN_WAIT    0x18	  // Closing state
#define SOCK_CLOSING     0x1A	  // Closing state
#define SOCK_TIME_WAIT	 0x1B	  // Closing state
#define SOCK_CLOSE_WAIT  0x1C	  // Closing state
#define SOCK_LAST_ACK    0x1D	  // Closing state
#define SOCK_UDP         0x22	  // UDP socket
#define SOCK_IPRAW       0x32	  // IP raw mode socket
#define SOCK_MACRAW      0x42	  // MAC raw mode socket
#define SOCK_PPPOE       0x5F	  // PPPOE socket

#define TX_BUF_MASK      0x07FF   // Tx 2K Buffer Mask:
#define RX_BUF_MASK      0x07FF   // Rx 2K Buffer Mask:
#define NET_MEMALLOC     0x05     // Use 2K of Tx/Rx Buffer

#define TCP_PORT         80       // TCP/IP Port

#define MAX_BUF 210

// ============================================== Project scoped variables ================================================ 
IO_REG_TYPE bitmask;
volatile IO_REG_TYPE *baseReg;

uint8_t scratchPad[2];
uint8_t buf[MAX_BUF];  // The character buffer in which the HTTP Put will be constructed
// ========================================================================================================================

void SPI_Write(uint16_t addr,uint8_t data) {
  SPI_PORT &= ~(1<<SPI_CS);      // Activate the CS pin
  SPDR = WIZNET_WRITE_OPCODE;    // Start Wiznet W5100 Write OpCode transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = (addr & 0xFF00) >> 8;   // Start Wiznet W5100 Address High Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = addr & 0x00FF;          // Start Wiznet W5100 Address Low Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = data;                   // Start Data transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPI_PORT |= (1<<SPI_CS);       // CS pin is not active
}

unsigned char SPI_Read(uint16_t addr) {
  SPI_PORT &= ~(1<<SPI_CS);      // Activate the CS pin
  SPDR = WIZNET_READ_OPCODE;     // Start Wiznet W5100 Read OpCode transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = (addr & 0xFF00) >> 8;   // Start Wiznet W5100 Address High Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = addr & 0x00FF;          // Start Wiznet W5100 Address Low Bytes transmission
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPDR = 0x00;                   // Send Dummy transmission for reading the data
  while(!(SPSR & (1<<SPIF)));    // Wait for transmission complete
  SPI_PORT |= (1<<SPI_CS);       // CS pin is not active
  return(SPDR);
}

void W5100_Init(void)
{
  // Ethernet Setup
  const unsigned char mac_addr[] = {0x00, 0x1D, 0x0D, 0x2C, 0x55, 0x3D};
  const unsigned char ip_addr[] = {192,168,11,8};
  const unsigned char sub_mask[] = {255,255,255,0};
  const unsigned char gtw_addr[] = {192,168,11,1};
  const unsigned char cosm_addr[] = {216,52,233,121};

  // Setting the Wiznet W5100 Mode Register: 0x0000
  SPI_Write(MR,0x80);            // MR = 0b10000000;

  // Setting the Wiznet W5100 Gateway Address (GAR): 0x0001 to 0x0004
  SPI_Write(GAR + 0,gtw_addr[0]);
  SPI_Write(GAR + 1,gtw_addr[1]);
  SPI_Write(GAR + 2,gtw_addr[2]);
  SPI_Write(GAR + 3,gtw_addr[3]);

  // Setting the Wiznet W5100 Source Address Register (SAR): 0x0009 to 0x000E
  SPI_Write(SAR + 0,mac_addr[0]);
  SPI_Write(SAR + 1,mac_addr[1]);
  SPI_Write(SAR + 2,mac_addr[2]);
  SPI_Write(SAR + 3,mac_addr[3]);
  SPI_Write(SAR + 4,mac_addr[4]);
  SPI_Write(SAR + 5,mac_addr[5]);

  // Setting the Wiznet W5100 Sub Mask Address (SUBR): 0x0005 to 0x0008
  SPI_Write(SUBR + 0,sub_mask[0]);
  SPI_Write(SUBR + 1,sub_mask[1]);
  SPI_Write(SUBR + 2,sub_mask[2]);
  SPI_Write(SUBR + 3,sub_mask[3]);

  // Setting the Wiznet W5100 IP Address (SIPR): 0x000F to 0x0012
  SPI_Write(SIPR + 0,ip_addr[0]);
  SPI_Write(SIPR + 1,ip_addr[1]);
  SPI_Write(SIPR + 2,ip_addr[2]);
  SPI_Write(SIPR + 3,ip_addr[3]);    

  // Setting the Wiznet W5100 RX and TX Memory Size (2KB),
  SPI_Write(RMSR,NET_MEMALLOC);
  SPI_Write(TMSR,NET_MEMALLOC);
  
  // Setting the Wiznet to connect to the Cosm server port 80
  SPI_Write(S0_DIPR + 0,cosm_addr[0]);
  SPI_Write(S0_DIPR + 1,cosm_addr[1]);
  SPI_Write(S0_DIPR + 2,cosm_addr[2]);
  SPI_Write(S0_DIPR + 3,cosm_addr[3]);
  SPI_Write(S0_DPORT,((TCP_PORT & 0xFF00) >> 8 ));
  SPI_Write(S0_DPORT + 1,(TCP_PORT & 0x00FF));
  SPI_Write(S0_PORT,((1 & 0xFF00) >> 8 ));    // Set the source port to 1
  SPI_Write(S0_PORT + 1,(1 & 0x00FF));        // Set the source port to 1
  
  // Set the Wiznet to use TCP
  SPI_Write(S0_MR,MR_TCP);                    // Set the mode to TCP  
}

void UploadDataPoint(void) {
  // Connect to the Cosm server and upload the data
  SPI_Write(S0_CR,CR_OPEN);
  while(SPI_Read(S0_CR));
  
  do {
    _delay_ms(1);
  } while(SPI_Read(S0_SR)!=SOCK_INIT);
  
  close(0);
  SPI_Write(S0_MR,MR_TCP);
  SPI_Write(S0_CR,CR_CONNECT);                   // Open Socket

  // Wait for Opening Process
  while(SPI_Read(S0_CR));
  
  do {
    _delay_ms(1);
  } while(SPI_Read(S0_SR)!=SOCK_ESTABLISHED);
  
  send(0,buf,strlen((char *)buf)); 
}

void close(uint8_t sock)
{
   if (sock != 0) return;

   // Send Close Command
   SPI_Write(S0_CR,CR_CLOSE);

   // Waiting until the S0_CR is clear
   while(SPI_Read(S0_CR));
}

void disconnect(uint8_t sock)
{
   if (sock != 0) return;

   // Send Disconnect Command
   SPI_Write(S0_CR,CR_DISCON);

   // Wait for Disconecting Process
   while(SPI_Read(S0_CR));
}

uint16_t send(uint8_t sock,const uint8_t *buf,uint16_t buflen)
{
    uint16_t ptr,offaddr,realaddr,txsize,timeout;   

    if (buflen <= 0 || sock != 0) return 0;

    // Make sure the TX Free Size Register is available
    txsize=SPI_Read(SO_TX_FSR);
    txsize=(((txsize & 0x00FF) << 8 ) + SPI_Read(SO_TX_FSR + 1));

    timeout=0;
    while (txsize < buflen) {
      _delay_ms(1);

     txsize=SPI_Read(SO_TX_FSR);
     txsize=(((txsize & 0x00FF) << 8 ) + SPI_Read(SO_TX_FSR + 1));

     // Timeout for approx 1000 ms
     if (timeout++ > 1000) {

       // Disconnect the connection
       disconnect(sock);
       return 0;
     }
   }	

   // Read the Tx Write Pointer
   ptr = SPI_Read(S0_TX_WR);
   offaddr = (((ptr & 0x00FF) << 8 ) + SPI_Read(S0_TX_WR + 1));
	
    while(buflen) {
      buflen--;
      // Calculate the real W5100 physical Tx Buffer Address
      realaddr = TXBUFADDR + (offaddr & TX_BUF_MASK);

      // Copy the application data to the W5100 Tx Buffer
      SPI_Write(realaddr,*buf);
      offaddr++;
      buf++;
    }

    // Increase the S0_TX_WR value, so it point to the next transmit
    SPI_Write(S0_TX_WR,(offaddr & 0xFF00) >> 8 );
    SPI_Write(S0_TX_WR + 1,(offaddr & 0x00FF));	

    // Now Send the SEND command
    SPI_Write(S0_CR,CR_SEND);

    // Wait for Sending Process
    while(SPI_Read(S0_CR));	

    return 1;
}

uint8_t read_bit(void) {
  IO_REG_TYPE mask=bitmask;
  volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
  uint8_t r;
  
  noInterrupts();
  DIRECT_MODE_OUTPUT(reg, mask);
  DIRECT_WRITE_LOW(reg, mask);
   _delay_us(3);
  DIRECT_MODE_INPUT(reg, mask);	// let pin float, pull up will raise
  _delay_us(10);
  r = DIRECT_READ(reg, mask);
  interrupts();
  _delay_us(53);
  return r;
}

uint8_t read() {
    uint8_t bitMask;
    uint8_t r = 0;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
	if ( read_bit()) r |= bitMask;
    }
    return r;
}

void write_bit(uint8_t v) {
  IO_REG_TYPE mask=bitmask;
  volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;

  if (v & 1) {
    noInterrupts();
    DIRECT_WRITE_LOW(reg, mask);
    DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
    _delay_us(10);
    DIRECT_WRITE_HIGH(reg, mask);	// drive output high
    interrupts();
    _delay_us(55);
  }
  else {
    noInterrupts();
    DIRECT_WRITE_LOW(reg, mask);
    DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
    _delay_us(65);
    DIRECT_WRITE_HIGH(reg, mask);	// drive output high
    interrupts();
    _delay_us(5);
  }
}

void write(uint8_t v) {
    uint8_t bitMask;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) write_bit( (bitMask & v)?1:0);
    noInterrupts();
    DIRECT_MODE_INPUT(baseReg, bitmask);
    DIRECT_WRITE_LOW(baseReg, bitmask);
    interrupts();
}

uint8_t reset(void) {
	IO_REG_TYPE mask = bitmask;
	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
	uint8_t r;
	uint8_t retries = 125;

	noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);
	interrupts();
	// wait until the wire is high... just in case
	do {
	  if (--retries == 0) return 0;
          _delay_us(2);
	} while ( !DIRECT_READ(reg, mask));

	noInterrupts();
	DIRECT_WRITE_LOW(reg, mask);
	DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
	interrupts();
        _delay_us(500);
	noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);	// allow it to float
        _delay_us(80);
	r = !DIRECT_READ(reg, mask);
	interrupts();
        _delay_us(420);
	return r;
}

int16_t ReadSensor(const uint8_t sensorIndex) {
  write(0x55);           // Choose ROM
  for(uint8_t i = 0; i < 8; i++) write(Sensor[sensorIndex][i]);
  write(READSCRATCH);
  scratchPad[TEMP_LSB] = read();
  scratchPad[TEMP_MSB] = read();
  reset();
  return (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
}

// Converts the raw temperature from a DS18B20 directly to a string containing the temperature in °F
// avoids unnecessary floating point math, float variables, and casts
// TODO: May not work properly with temperatures below 32°F
void RawTempToString(uint16_t raw, char * buffer) {
  uint32_t intTemp;
  uint8_t numChars;
  
  intTemp=raw*1125ul+320000ul;    // Hold the temperature
  if(raw<605 && raw>=0) {  // We're looking at a positive number with two digits
    numChars = 7;
  }
  else if (raw>=605) {
    numChars = 8;
  }
  
   // Write the null termination, the first four digits, and then the decimal point
  buffer[numChars--]='\0';
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]=intTemp%10 + '0';
  intTemp/=10;
  buffer[numChars--]='.';
    
  // Continue writing the left of the decimal place until we run out of intTemp
  do {
    buffer[numChars--]=intTemp%10 + '0';
    intTemp/=10;
  } while (intTemp);
}

void itoa(long inp, char * buf) {
  long temp = inp;
  uint8_t numChars=0;
  boolean isNegative=false;
  
  // Check to see if there is a negative sign
  if(temp<0){
    isNegative=true;
    numChars++;
    temp*=-1;
  }
  
  do {
        numChars++;
        temp /= 10;
  } while ( temp );
  
  // Write the negative sign if present and the terminating null character
  temp=inp;
  buf[numChars]=0;
  if(isNegative) {
    temp*=-1;
    buf[0]='-';
  }
  
  int i = numChars - 1;
  do {
      buf[i--] = temp%10 + '0';
      temp /= 10;
  } while (temp);
}

void BuildPut(uint16_t T1, uint16_t T2) {
  // Convert raw temperatures into strings
  char Temp1[8];
  char Temp2[8];
  RawTempToString(T1, (char *)Temp1);
  RawTempToString(T2, (char *)Temp2);
  
  // Set up data buffer to hold the csv data
  char * dataBuffer[strlen(Sensor1)+strlen(Temp1)+strlen(Sensor2)+strlen(Temp2)+4];
 
  strcpy((char *)dataBuffer,Sensor1);
  strcat_P((char *)dataBuffer,PSTR(","));
  strcat((char *)dataBuffer,Temp1);
  strcat_P((char *)dataBuffer,PSTR("\n"));
  strcat((char *)dataBuffer,Sensor2);
  strcat_P((char *)dataBuffer,PSTR(","));
  strcat((char *)dataBuffer,Temp2);
  strcat_P((char *)dataBuffer,PSTR("\n"));
  
  // Convert the length of the data buffer into a string
  char dataLength[2];
  itoa(strlen((char *)dataBuffer)-1,(char *)dataLength);
  
  // Construct the HTTP Put
  strcpy_P((char *)buf,PSTR("PUT /v2/feeds/"));
  strcat((char *)buf,FEEDID);
  strcat_P((char *)buf,PSTR(".csv HTTP/1.1\nHost: api.cosm.com\n"));
  strcat_P((char *)buf,PSTR("X-ApiKey: "));
  strcat((char *)buf,APIKEY);
  strcat_P((char *)buf,PSTR("\nUser-Agent: "));
  strcat((char *)buf,USERAGENT);
  strcat_P((char *)buf,PSTR("\nContent-Length: "));
  strcat((char *)buf,dataLength);
  strcat_P((char *)buf,PSTR("\n\n"));
  strcat((char *)buf,(char *)dataBuffer);
}

int main() {
  // This code will only run once, after each powerup or reset of the board
  
  // Set pin 3 to input
  //pinMode(pin, INPUT);        // This takes 136 bytes of sketch size
  //DDRD = DDRD | B11110100;    // This does the same thing and only takes 4 bytes of sketch size
                                // This does the same thing since pin 3 is set to input by default
  bitmask = PIN_TO_BITMASK(pin);
  baseReg = PIN_TO_BASEREG(pin);
  
  SPI_DDR = (1<<PORTB3)|(1<<PORTB5)|(1<<PORTB2);  // Set MOSI (PORTB3),SCK (PORTB5) and PORTB2 (SS) as output, others as input  
  SPI_PORT |= (1<<SPI_CS);                        // CS pin is not active
  SPCR = (1<<SPE)|(1<<MSTR);                      // Enable SPI, Master Mode 0
  
  #if DEBUG
    Serial.begin(9600);
  #endif
  
  W5100_Init();
  
  for (;;) {
     // Request a temperature reading from all Dallas sensors on the OneWire bus
    reset();
    write(0xCC);
    write(STARTCONVO);
    _delay_ms(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20(s)
    reset();
    
    // Read the temperatures previously requested
    int16_t rawTemperature1 = ReadSensor(0);  // Read the first sensor
    int16_t rawTemperature2 = ReadSensor(1);  // Read the second sensor
    
    BuildPut(rawTemperature1, rawTemperature2);
    
    #if DEBUG
      Serial.print((char *)buf);
    #endif
    
    UploadDataPoint();
    _delay_ms(DELAY);
  }  
}

Compilers are awesome, considering that's quite a bit more code than mine but its so much more streamlined for the compiler it becomes tiny!
 
Agreed! I wonder what a blackbelt in C/C++ could pull off. I bet I'm doing a lot of stuff stupidly but C is like my 4th programming language. Still now there is TONS of room left to do cool stuff like a PID controller and a webserver for the remote setting of set points :)
 
Still waiting my parts to ship. I am not much of a code writer but it seems easy enough to allow for more sensors. The plan is to have three to four sensors. One for each fermenter and then an ambient.
Do you see any issue with this idea?
 
None at all. You'll just need to expand the 2D sensor array to hold the extra roms and add some extra sensor names. Then in int main() you'll need a few extra

Code:
int16_t rawTemperature2 = ReadSensor(1);  // Read the second sensor

and finally you'll need to modify BuildPut to string together the extra sensor data

I suppose I could make the sensor names a 2D array and design the code to automatically loop through all sensor ROMs and string the data together. Or I could make a data type to hold the sensor ROM and name and just loop through an array of those...
 
If you're really interested in saving code space, you could reduce the precision to 9 bits, still be within the +-0.5 deg C accuracy of the chip, and speed up your chip read times by a factor of almost 5 (I use 125ms for 9 bit vs. 750mc for 12 bit). I mean seriously, when is a ten-thousandth of a degree going to make a difference?? :confused:
 
If you're really interested in saving code space, you could reduce the precision to 9 bits, still be within the +-0.5 deg C accuracy of the chip, and speed up your chip read times by a factor of almost 5 (I use 125ms for 9 bit vs. 750mc for 12 bit). I mean seriously, when is a ten-thousandth of a degree going to make a difference?? :confused:

Yeah, ±0.5°C (±0.9°F) isn't nearly precise enough for me. A 12-bit reading still only reads to the nearest 0.1125°F which really means that I should only be reporting temperatures to the nearest 0.1°F. I was always taught the precision you attach to a measurement is the same as that of the most significant digit of the error associated with that measurement. Besides, in my experiments taking a temperature reading every second really did cause internal heating of the DS18B20s. At a reading every 3-5 seconds I don't observe any extra heating. The spike in my carboy temperature at 21:15:00 is nothing more than a result of polling the temperature sensor too frequently. As soon as I stopped pestering it, it cooled back down to the true temperature. I care more about reading the real temperature than reading it every second; nothing is changing very quickly in my system anyway.

EDIT: I forgot the mention that I shaved another 400 bytes of program memory by tweaking my itoa and RawTemperatureToString routines.
 
If you're really interested in saving code space, you could reduce the precision to 9 bits, still be within the +-0.5 deg C accuracy of the chip, and speed up your chip read times by a factor of almost 5 (I use 125ms for 9 bit vs. 750mc for 12 bit). I mean seriously, when is a ten-thousandth of a degree going to make a difference?? :confused:

For brewing purposes though, really whats the point of 125ms delay vs 750ms delay? We aren't sending this thing to be on the International Space Station where milliseconds matter for life and death. The worst any of these sensors are going to see is a keezer or ferm chamber :)

Realistically even polling every 5 seconds like we are is way overkill, you could do every minute and still get the same usefulness out of the data.

Because we arent trying to push the sensors delay to the limit, i'd rather sacrifice speed that we dont care about for sensitivity, which we do. Granted having .11F accuracy is way overkill too.
 
In my case, I'm polling up to 36 separate devices, so a 750ms delay each starts to add up.

I'm also not sitting there in a delay() either. I set a timer per device, and do a read when the timer expires.
 
In my case, I'm polling up to 36 separate devices, so a 750ms delay each starts to add up.

I'm also not sitting there in a delay() either. I set a timer per device, and do a read when the timer expires.

First of all bravo Sir! ANY project which takes 36 temperature sensors must be chocked full of win! Just making sure but you do know that writing STARTCONVO onto the OneWire bus makes all the sensors on the bus take a temperature reading and store the results in their EEPROM (scratchpad) right? Just wait 750 ms and then poll each of the 36 one after the other with no further delay. I hacked my code to take 36 readings and it completes them in 300 ms total!

Code:
reset();
    write(0xCC);
    write(STARTCONVO);
    _delay_ms(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20(s)
    reset();
    
    // Read the temperatures previously requested
    int16_t rawTemperature1, rawTemperature2;
    long startTime, endTime;
    startTime = millis();
    for (uint8_t i=0;i<=18;i++) {
      rawTemperature1 = ReadSensor(0);  // Read the first sensor
      rawTemperature2 = ReadSensor(1);  // Read the second sensor
    }
    endTime = millis();
    Serial.print("36 temperature readings took ");
    Serial.print(endTime-startTime);
    Serial.println(" ms");
 
First of all bravo Sir! ANY project which takes 36 temperature sensors must be chocked full of win!

Actually it can be a mix of DS18B20 Temp Sensors and DS2406+ digital switches. I'm also looking at other possible 1-Wire devices as well. The project is here, and my website is here. I'm currently adding hardware and code to support multiple 4x20 HD44780-compatible LCDs over an I2C interface.

Just making sure but you do know that writing STARTCONVO onto the OneWire bus makes all the sensors on the bus take a temperature reading and store the results in their EEPROM (scratchpad) right? Just wait 750 ms and then poll each of the 36 one after the other with no further delay. I hacked my code to take 36 readings and it completes them in 300 ms total!

I appreciate you pointing that out, I re-checked the datasheets for both devices and there doesn't appear to be any overlapping commands that might collide with one another, but I haven't checked any of the other devices yet. For the time being, I think using MATCHROM is a better approach.:rockin:
 
I got one a while back, could not get it to work to my satisfaction. It may be OK if all you're doing is pushing info to COSM, but it took a lot of overhead to use the Arduino as a web-server.
 
Good to know that you've got at least some code for it. I found an EtherShield libraray which it seems like is the most developed/functional for that chip. I'm hoping between the datasheet and the chunks of code that I can find that I can do for it what I did for the w5100. I've read that this chip is really only appropriate for client situations rather than as a server.
 
I came across Atmel AVR4027: Tips and Tricks to Optimize Your C Code for 8-bit AVR Microcontrollers and applied what I could to my fermentation monitor code to tremendous effect. As I speculated I was doing some things stupidly especially global variables which burn up SRAM big time! My newest code now takes up 2,812 bytes of program memory and 121 bytes of SRAM. Before applying the tips I used 2,856 bytes of program memory (not a big difference here) and 333 bytes of SRAM (HUGE difference here). I also added some escapes from a few infinite loops which periodically caused the code to hang until board reset.
 
I used one 4.7k on each of my sensors as well Fuzze. I later learned that I just need one per grouping but my project has been working just fine with one per DS18B20.

By the way, I'm working on compacting sketch size and SRAM usage by stuffing just those parts of DallasTemperature.cpp and DallasTemperature.h into my sketch that I actually need. I know the compiler is supposed to remove unreferenced code but so far I was able to shave almost 2k off my sketch size just be deep sixing Dallas. I may give the same treatment to OneWire later this week if I'm feeling ambitious. This is my very simple code to read a 12 bit temperature off a DS18B20 and spit it to the serial monitor in F.

Code:
#include <OneWire.h>

// Data wire is plugged into pin 3 on the Arduino
#define ONE_WIRE_BUS 3
#define STARTCONVO      0x44  // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH     0xBE  // Read EEPROM
#define TEMP_LSB        0
#define TEMP_MSB        1
uint8_t testTherm[8]= { 0x28, 0x83, 0xF5, 0x83, 0x04, 0x00, 0x00, 0x16 };
uint8_t scratchPad[8];

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

void setup()
{
  // This code will only run once, after each powerup or reset of the board
  Serial.begin(9600);
  Serial.print("Setup");
}

void loop() {
 
  // Request temperatures function from Dallas
  //Serial.print("Requesting\n");
  oneWire.reset();
  oneWire.skip();
  oneWire.write(STARTCONVO, false);
  
  // Read the temperature from the DS18B20
  delay(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20
  //Serial.print("Reading\n");
  oneWire.reset();
  oneWire.select(testTherm);
  oneWire.write(READSCRATCH);
  scratchPad[TEMP_LSB] = oneWire.read();
  scratchPad[TEMP_MSB] = oneWire.read();
  oneWire.reset();
  int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
  Serial.print( (float)rawTemperature * 0.0625*1.8+32,4);
  Serial.write(176);
  Serial.print("F\n");
  delay(5000);

}

I'm going through this trouble because DallasTemperature is way bigger than what we need it to be and I want to have plenty of room on my chip for doing cooler stuff like output to an LCD and controlling stuff. Of course this sketch would be a heck of a lot smaller if I wasn't using the Serial monitor. With that commented out the sketch size is 1,738 bytes and uses 39 bytes of SRAM!

The OneWire protocol has been looking a little portly to me for a while now so I went one step further with it and rewrote all the read_bit, read, write_bit, write, and reset functions to twiddle ports directly rather than doing it through the #defines that OneWire uses. I removed a little of the flexibility in that now one must run their OneWire bus on Port D (digital pins 2-7). I took the code that reads a temperature all the way down to 504 bytes of program memory and 8 bytes of SRAM usage which is crazy given a blank Arduino sketch takes 444 and 9!
 
So I tried this. I bought an UNO, Ethershield, and two super cheap DS18B20's from deal extreme. (I have plenty of wire and resistors lying around the house. Grand total was like $30.)

It worked beautifully. Both temp probes were surprisingly accurate. I calibrated them against my thermopen, and found I only needed a .2F offset on one and a .6F offset on the other.

The problem came when I climbed into my attic to run an Ethernet cord to the garage -- I'm not that motivated. Wireless it is.

WIFI sheilds are expensive, so I decided to drop $35 on a Raspberry Pi, and hook up a $5 USB wifi nic I have around. Updates to come on that front.

For now, here are the two temp probes monitoring my closet temp. https://cosm.com/feeds/120160
 
Ok, the raspberry pi arrived. i bought a $2 ribbon cable for its GPIO pins, and shoved my temp probe wires directly into the other end of the ribbon cable.

Raspbian is an easy install, and the wifi card was supported out of the box.

After a couple hours of learning some rudimentary python, I had it pushing temps to COSM again.

The last piece of the puzzle was getting it to auto-login and, auto launch VNC, and auto-run my script. At this point, should the power ever drop, or the Pi reboots, it'll fire right back up.

I'll post some brief instructions and include my Python scripts tomorrow. If I ever get around to it, I'll write blog post that really details all the packages you need and what not.

https://cosm.com/feeds/120160

There's my feed. That's the current temp of my Stone Enjoy By IPA clone
 
Alright, I had some more fun today. I threw a new page together on my blog. I'm polling both my datastreams from COSM for the current datapoint with a little script. The script updates every 10 seconds, so the text value should always be up-to-date. The graphs are embed from the Graph Builder on COSM. Those update on every page refresh.

http://www.bertusbrewery.com/p/temp-monitor.html

Here's my python code below. You'd first need to install the python-dev, python-pip, and EEML packages. I'll eventually do a full write up that includes more of a step-by-step.

The Raspberry Pi was definitely more involved that the Arduino, but it's a much more powerful little box with more options (cheap wifi, desktop environment, VNC, etc).
Code:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import glob
import time
import eeml

# Setting up the temp probes
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

# The two 28-XXXX numbers below are my two temp probes
# You'll need to replace those with your own
base_dir = '/sys/bus/w1/devices/'
device1_folder = glob.glob(base_dir + '28-00000480c6e8')[0]
device1_file = device1_folder + '/w1_slave'
device2_folder = glob.glob(base_dir + '28-000004837542')[0]
device2_file = device2_folder + '/w1_slave'

# Insert your API KEY below.
API_KEY = 'PutYourAPIKeyHere,LeaveTheApostrophes' 
# Replace the XX's with your FEED ID, e.g. 120160
FEED = XXXXX

API_URL = '/v2/feeds/{feednum}.xml' .format(feednum = FEED)
def read_temp_raw1():
    f = open(device1_file, 'r')
    lines1 = f.readlines()
    f.close()
    return lines1

def read_temp_raw2():
    f = open(device2_file, 'r')
    lines2 = f.readlines()
    f.close()
    return lines2


def read_temp1():
    lines1 = read_temp_raw1()
    while lines1[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines1 = read_temp_raw1()
    equals_pos = lines1[1].find('t=')
    if equals_pos != -1:
        temp_string = lines1[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        temp_f = temp_c * 9.0 / 5.0 + 32
# temp_f is our temp in F, but it's a floating integer. 
# we want a rounded number. Replace the number 1 below with 2
# if you want your value in hundreths not teths.
        temp_r = "%.1f" % round(temp_f,1)
        return temp_r

def read_temp2():
    lines2 = read_temp_raw2()
    while lines2[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines2 = read_temp_raw2()
    equals_pos = lines2[1].find('t=')
    if equals_pos != -1:
        temp_string = lines2[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        temp_f = temp_c * 9.0 / 5.0 + 31.77
# Same deal as above, we're rounding our reading for probe2
        temp_r = "%.1f" % round(temp_f,1)
        return temp_r

# Doing two things here, first printing the temps in the
# terminal window. Secondly, sending the data to COSM
while True:
	print(read_temp1(), read_temp2())	
        pac = eeml.Pachube(API_URL, API_KEY)
# Change 'FermentationTemp' to whatever you want
        pac.update ([eeml.Data('FermentationTemp', read_temp1(), unit=eeml.Fahrenheit())])
# FridgeTemp is my second Stream name, change as well
        pac.update ([eeml.Data('FridgeTemp', read_temp2(), unit=eeml.Fahrenheit())])
        pac.put()	
# This polls every 7 seconds. You could go longer if you like. 
        time.sleep(7)
 
Finally getting around to turning this into a full fledged temperature controller.

Bought this thing called a Powerswitch Tail II, which can switch up to 120V @ 15A and doesnt require any real wiring but takes in the Microcontroller outputs from Arduino...the 15A being key obviously as many freezers spike up to 10A or more when the compressor comes on, so this should give a bit of margin so the SSR doesnt get super hot or blown out...
PowerSwitch%20Tail%20II.jpg


For $25 bucks its not bad, considering its an all in 1 solution with the connectors, and all the protection circuitry...even though i feel fairly confident playing with 110V mains voltage, every time you wire something up you always have(or should have) that voice in your head telling you not to screw up and fry yourself.

I think in most cases though for a ferm fridge you only use one, either its plugged into the fridge or its plugged into a light source...never both at the same time. So this should work really well.
 
Well isnt this just lovely.

I go to bed last night and everything is fine.

This morning i wake up and COSM does not exist, they are now known as Xively or something....No emails prior..nothing. Just one email at 6am this morning saying that they are transitioning to Xively...what the hell is that. I know its a free service and what not, but why the hell didnt they send this information out weeks or months ago so that we could prepare our feeds? Both of my feeds are stagnant now presumably failing to upload data, and im stuck at work so i cant fix them for the day...bah. It looks like Scottlands is still functioning, so maybe i just need to restart my Arduino to get it to reconnect.

My data stream isnt working, and their new webpage looks like crap.
Im not sure if i can customize the graphs better, but overall i dont like their new setup it seems inferior....im seriously kinda pissed right now ;)
 
Odd, my feeds are still working. They were pachube before COSM, so it's not a shocker to me. The URLs and APIs redirect, so I wonder why it's not working for you?
 
Well isnt this just lovely.

I go to bed last night and everything is fine.

This morning i wake up and COSM does not exist, they are now known as Xively or something....No emails prior..nothing. Just one email at 6am this morning saying that they are transitioning to Xively...what the hell is that. I know its a free service and what not, but why the hell didnt they send this information out weeks or months ago so that we could prepare our feeds? Both of my feeds are stagnant now presumably failing to upload data, and im stuck at work so i cant fix them for the day...bah. It looks like Scottlands is still functioning, so maybe i just need to restart my Arduino to get it to reconnect.

My data stream isnt working, and their new webpage looks like crap.
Im not sure if i can customize the graphs better, but overall i dont like their new setup it seems inferior....im seriously kinda pissed right now ;)

That is why I chose to store my own data and create my own graphs.
 
Back
Top