• Please visit and share your knowledge at our sister communities:
  • If you have not, please join our official Homebrewing Facebook Group!

    Homebrewing Facebook Group

Real Time Online Fermentation Temperature Monitor for ~$60

Homebrew Talk

Help Support Homebrew Talk:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
These are what I got from DealExtreme: Arduino 5V Relay Module - Blue + Black
sku_121354_1.jpg


I went ahead and got 3 so they were only $1.88 each! I am not a patient man and waiting 4+ weeks for insanely slow Chinese post kills me but I do like getting these things on the cheap. Eventually these will show up and I can give them a lash and report back as to how they work.

Yea i always have mixed emotions buying cheap Chinese stuff, but the stuff is so incredibly cheap...:mug:

Brewing beer already isnt necessarily the cheapest hobby, it quickly grows in cost! :)

Ill look at those relays for sure...
 
I've been wanting to do this with a raspberry pi for a while. It would be around the same price and you would have a much powerful controller capable of running its own webserver.
 
@thomashp Look here: https://www.homebrewtalk.com/f235/raspberry-pi-temp-controller-344529/
Only for temps monitoring, RasPi is perfect. But for 'something' more (controlling fridge, heater, burners etc) some other hardware is needed(1wire setup, additional shields, ssr's). I'm hesitating between Arduino and Teensy (in fact, where I live 2nd option is more expensive-unfortunately).
 
I've been wanting to do this with a raspberry pi for a while. It would be around the same price and you would have a much powerful controller capable of running its own webserver.

The problem is also that Raspberry Pi's are hard to get a hold of, atleast in the US there is upwards of a 6+ week wait ...or your stuck paying $50 or more for people online gouging people with the ones they have.

You can get an Arduino anywhere ...that said it would work if you already have one!
 
I've been wanting to do this with a raspberry pi for a while. It would be around the same price and you would have a much powerful controller capable of running its own webserver.

I'm still learning but I do believe that Arduinos are capable of running their own web server as well.
 
Relays finally showed up yesterday and it was very easy and straightforward to get it up and running. For the moment I'm just switching my desk lamp on and off so I can't yet say whether or not it can handle a chest freezer. I've got some cheap 11" wide heat tape on the way so soon enough it will be switching that on and off as I piece things together. These relays are mechanical so there is a distinct clicking sound as they switch. That is irrelevant to me but for those who plan on rapid switching or require silence, you'll need to spend 10x as much to get a solid state relay + heatsink.
 
So i cant even get my own project back in working condition lol, not sure what im missing.

Basically set it up to have 2 sensors on a cat5 cable, seemed straight forward.

Make 2 sets of 2 wires, string a 4.7k resistor between them, solder them to the arduinos 5v and data pin 3, then the other ends into a cat5e jack, and same for 2 wires to the arduino gnd.

Cat5 Jack - Arduino Pin - Sensor Pin
Pin 1 - +5V - Sensor 1 Pwr
Pin 2 - Digital 3 - Sensor 1 Data
Pin 3 - Ground - Sensor 1 Gnd
Pin 4 - +5V - Sensor 2 Pwr
Pin 5 - Digital 3 - Sensor 2 Data
Pin 6 - Ground - Sensor 2 Gnd

Then i have my 2 sensor cables spliced/soldered onto the end of a cat5 cable i snipped matching the above pinouts..

Problem is, if both of my sensors are hooked up my arduino turns off after 2-5 seconds, which i assume means there is a short some where...but ive checked and double checked...if i unhook one of the sensors everything works, but both at the same time and it doesnt...

I cant help but wonder if i only need one 4.7k resistor? But i know how i had it before with one per sensor and it worked as i expected...

2013-03-01%2019.00.01.jpg

2013-03-01%2019.14.32.jpg


And yes i know horrible solder job my iron is a ghetto radio shack...theres no solder bridges between the arduino pins, as it it works fine with 1 hooked up.
 
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!
 
Yea i havent worked on condensing it, because there was still so much space on the Uno even with the extra libraries :)

Ill try messing with it and just use 1 resistor tommorrow..
 
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?
 
Back
Top