// --------------------------------------------------------------------------------------------------------------------------------- //*************************************************** // Garden Project // 11/11/09 //*************************************************** // This library is necessary to communicate with the DS18B20 temperature sensor. // Remove all occurrences off " from the line below. #include "<"OneWire.h">" // The pin the temperature sensors will be connected too. #define TEMPSENSOR 3 #define TEMP_LSB 0 #define TEMP_MSB 1 #define TH_REG 2 #define USERBYTE_1 2 #define TL_REG 3 #define USERBYTE_2 3 #define CONFIG_REG 4 #define CRC 8 OneWire ds(TEMPSENSOR); byte present, i = 0; byte data[12], addr[8]; // The following 3 8 byte arrays are the addresses of my temperature sensors. // See this site for code that loops through all the sensors and writes their address // to the serial display. byte ts1[8] = {0x28, 0xFC, 0xF3, 0x2D, 0x02, 0x00, 0x00, 0x5B}; byte ts3[8] = {0x28, 0x82, 0x9A, 0x31, 0x02, 0x00, 0x00, 0x18}; byte ts2[8] = {0x28, 0x8D, 0x90, 0x31, 0x02, 0x00, 0x00, 0x81}; int temp_c_int, lightSensor, m1Sensor, m2Sensor, test_bit, set_bit, resolution_floor, inByte, val, aRead = 0; float temp_c_frac, temp_c, temp_1, temp_2, temp_3, temp_f, expon, temperature, moist1, moist2, light1 = 0; int span = 40; int potPin = 1; void setup() { Serial.begin(9600); pinMode(7, INPUT); pinMode(13, OUTPUT); // Setup the onboard LED pin as output. pinMode(11, OUTPUT); } void takeDS() { // I got this part of the code of the internet somewhere. I made minor changes for it to fit into my design. // It is decently commented, although some of what it does is a bit high level in my opinion. ds.reset(); ds.select(addr); ds.write(0x4E); // write scratchpad (starts at byte 2) // note: set high/low temp alarms by changing the next two values ds.write(0x4B); // default value of TH reg (user byte 1) ds.write(0x46); // default value of TL reg (user byte 2) // uncomment one of the following ds.write(0x7F); // 12-bit sampling resolution (default) //ds.write(0x5F); // 11-bit //ds.write(0x3F); // 10-bit //ds.write(0x1F); // 9-bit ds.reset(); ds.select(addr); ds.write(0x44,1); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not // we might do a ds.depower() here, but the reset will take care of it. present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad if (!present) { Serial.print("ERROR: selected device not present\n"); return; } for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } if (data[8] != OneWire::crc8(data,8)) { Serial.print("ERROR: CRC didn't match\n"); return; } /* compute the degrees in Celsius / integer part */ temp_c_int = 0; /* The measured temp is spread across two bytes of the returned data. * The integer part of the temp value is spread across the least 3 significant * bits of the most significant byte (MSB) and the most significant 4 of * the LSB. Here we shift those 7 bits into their proper place in our * result byte. * * note: could do this with 2 bit-shift / mask operations, alternatively */ set_bit = 6; for (test_bit = 2; test_bit >= 0; test_bit--) { temp_c_int |= ( ((data[TEMP_MSB] & (1 << test_bit)) >> test_bit) << set_bit ); set_bit--; } for (test_bit = 7; test_bit >= 4; test_bit--) { temp_c_int |= ( ((data[TEMP_LSB] & (1 << test_bit)) >> test_bit) << set_bit ); set_bit--; } /* compute the fractional part */ /* first figure out what resolution we're measuring in - varies between 1 and 4 bits * after the decimal (based on the contents of the CONFIG_REG byte): * bit 6 == 0 && bit 5 == 0 --> 9-bit resolution (ignore 3 least sig bits) * bit 6 == 0 && bit 5 == 1 --> 10-bit resolution (ignore 2 least sig bits) * bit 6 == 1 && bit 5 == 0 --> 11-bit resolution (ignore 1 least sig bits) * bit 6 == 1 && bit 5 == 1 --> 12-bit resolution */ if ((data[CONFIG_REG] & (1 << 5)) > 0) { if ((data[CONFIG_REG] & (1 << 4)) > 0) { // bits 6 and 5 are set resolution_floor = 0; } else { // bit 6 is set, 5 is clear resolution_floor = 1; } } else { if ((data[CONFIG_REG] & (1 << 4)) > 0) { // bits 6 is clear, 5 is set resolution_floor = 2; } else { // bit 6 and 5 are clear resolution_floor = 3; } } temp_c_frac = 0; for (test_bit = 3; test_bit >= resolution_floor; test_bit--) { if ((data[TEMP_LSB] & (1 << test_bit)) > 0) { expon = test_bit - 4; // will be negative temp_c_frac += pow(2,expon); } } /* put it all together */ temp_c = (float)temp_c_int + temp_c_frac; if ((data[TEMP_MSB] & (1 << 7)) > 0) { // the temp is negative temp_c *= -1; } } void loop() { // if we get a valid byte, read analog ins: if (Serial.available() > 0) { // get incoming byte: inByte = Serial.read(); // inByte = 'a'; // Use this if you want the loop to enter the case statement to test. digitalWrite(13, HIGH); // This is the LED on the arduino. This lets me know it received a byte. // The approach is that most of, if not all the intelligence lies on the PC side. The arduino loop is basically used // to wait and check for instructions via incoming byte from the PC. switch(inByte) { case 'a': // Got the send all status byte from PC. // Read the light sensor - Start light1 = 0; light1 = analogRead(0); light1 = 1023 - light1; // Read the light sensor - End // Read the moisture sensors - Start moist1 = 0; moist1 = analogRead(1); moist1 = 1023 - moist1; moist2 = 0; moist2 = analogRead(2); moist2 = 1023 - moist2; // Read the moisture sensors - End // Read the temperature sensors - Start for (int i = 0; i < 8 ; i++) { addr[i] = ts1[i]; // Set the address we want to read. } takeDS(); // Call the function that reads the sensor. temp_1 = temp_c * 10000; // Because we read the sensor with 12 bit resolution, it will return // a float with possibly 3 digits after the coma. Since we send the // info as a integer we times by 10000 and then on the PC we divide by // 10000. The other two sensors does the same. for (int i = 0; i < 8 ; i++) { addr[i] = ts2[i]; } takeDS(); temp_2 = temp_c * 10000; for (int i = 0; i < 8 ; i++) { addr[i] = ts3[i]; } takeDS(); temp_3 = temp_c * 10000; // Next we send all our results to the PC via the USB cable. Values are separated by ~. Our C# code on the PC // will read each char up to a ~ and reassemble the value. There might be better ways to do this, but I tried a // few methods to send the data to PC and most end up requiring the use of delays in either the arduino code // or the c# code. I found its best to make a call for the data by sending the "a" char. The arduino code takes // up to 10 sec to assemble the data and then sends. After requesting the data the c# code waits for data on the // serial port and then then reads it all in one string. We then have a c# function that disassembles the string // into individual data values. See the c# code for more details. Serial.print(light1, DEC); Serial.print("~"); Serial.print(temp_1, DEC); Serial.print("~"); Serial.print(temp_2, DEC); Serial.print("~"); Serial.print(temp_3, DEC); Serial.print("~"); Serial.print(moist1, DEC); Serial.print("~"); Serial.print(moist2, DEC); Serial.print("~"); // Serial.println(""); // I use this line when I want to view the serial data in the arduino IDE. break; case 'b': digitalWrite(11, HIGH); // Switch the relay light on. Program override light schedule. break; case 'c': digitalWrite(11, LOW); // Switch the relay light off. Program override light schedule. break; case 'd': // Future use. break; case 'e': // Future use. break; } digitalWrite(13, LOW); // Board is idle indicator. } } // ---------------------------------------------------------------------------------------------------------------------------------