EcoRenovator  

Go Back   EcoRenovator > Improvements > Geothermal & Heat Pumps
Advanced Search
 


Blog 60+ Home Energy Saving Tips Recent Posts Search Today's Posts Mark Forums Read


Reply
 
Thread Tools Display Modes
Old 11-08-15, 01:37 AM   #31
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

Here's a shot of the "prototype" control system... It's very cobbled, hasty, but seems to work as planned.



I am collecting relays, sensors, connectors and other components to build a more permanent control system. I got my hands on a bundle of DS18B20 digital temperature sensors. I connected 3 of them to a microcontroller with an SD card socket. Then I spent a little time developing some simple software to log temperature data to the SD card. It also allows me to dump the file across the serial port or USB cable back to the PC. Here's a graph of some raw data from Thursday.


Attached Thumbnails
Click image for larger version

Name:	13.jpg
Views:	891
Size:	542.0 KB
ID:	6201   Click image for larger version

Name:	_Temperatures 20151105.jpg
Views:	895
Size:	269.8 KB
ID:	6202  
TechShop is offline   Reply With Quote
The Following 2 Users Say Thank You to TechShop For This Useful Post:
AC_Hacker (11-08-15), Daox (11-09-15)
Old 11-08-15, 07:32 PM   #32
AC_Hacker
Supreme EcoRenovator
 
AC_Hacker's Avatar
 
Join Date: Mar 2009
Location: Portland, OR
Posts: 4,004
Thanks: 303
Thanked 723 Times in 534 Posts
Default

Quote:
Originally Posted by TechShop View Post
I am collecting relays, sensors, connectors and other components to build a more permanent control system. I got my hands on a bundle of DS18B20 digital temperature sensors. I connected 3 of them to a microcontroller with an SD card socket. Then I spent a little time developing some simple software to log temperature data to the SD card. It also allows me to dump the file across the serial port or USB cable back to the PC.

Pleas feel free to share code...

Best,

-AC
__________________
I'm not an HVAC technician. In fact, I'm barely even a hacker...
AC_Hacker is offline   Reply With Quote
Old 11-09-15, 01:21 AM   #33
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

Quote:
Originally Posted by AC_Hacker View Post
Pleas feel free to share code...

Best,

-AC

I'll do that. Some of the code resembles that PLC board at the moment. I'll add in some important comments and post it here.
TechShop is offline   Reply With Quote
Old 11-09-15, 02:35 AM   #34
skyking
Helper EcoRenovator
 
Join Date: May 2011
Location: Western Washington
Posts: 89
Thanks: 46
Thanked 20 Times in 13 Posts
Default

Have you installed the main loops? did you bore or go with a horizontal field?
skyking is offline   Reply With Quote
Old 11-09-15, 10:47 AM   #35
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

Quote:
Originally Posted by skyking View Post
Have you installed the main loops? did you bore or go with a horizontal field?
Not yet. For the winter, I will be running this system as-is. Currently I have a single 150' horizontal trench with a straight loop of 1-1/4" HDPE irrigation pipe. Where that loop re-enters the building, I have a 3" x 8" 50 plate heat exchanger. The loop water flows back to the GSHP through one side of that plate HX. There is a solenoid valve on the other side that opens when the GSHP is running, allowing well water to flow through the plate HX and dump into a pond.

Attached Thumbnails
Click image for larger version

Name:	14.jpg
Views:	864
Size:	71.9 KB
ID:	6204  
TechShop is offline   Reply With Quote
Old 11-16-15, 02:01 AM   #36
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default Arduino temperature datalogging code.

Here is a sketch for Arduino micro-controller boards that will log data from the inexpensive DS18B20 digital temperature sensors to your SD or Micro-SD card.

The source is included in this post, and the .ino file is attached. I had to rename it to .txt so that it would upload. Download it and rename it to "_DS18B20_SDLOG.ino", or just copy and paste the code into a new sketch in your Arduino IDE window.

It is a functional and useful program that stores raw data in the CSV format. You can also send some basic commands via a serial or USB connection to change settings and retrieve data from your log file.

You can paste the log data directly into Open Office Calc, Microsoft Excel or other similar programs to create a graph. The graph I've posted in this thread was created in about 3 minutes using Open Office Calc v4.12.

I provide this AS IS. This code is sloppy and was written in a hurry so that I could get up and running. I don't plan to take it any farther because I will not be using the Arduino in my final control scheme.

Currently I am using the 1.6.6 version of the Arduino IDE.
This sketch runs on on the Arduino "Duemilanove" board with the Atmega 328P although there isn't enough memory to run as full featured as I intended. I haven't had any major problems, but it may be unstable. It should run better on the Arduino boards that use the ATmega32U4 and others with more memory than the 328P.

I used the following two locations as reference material and also blatantly ripped off some of their code. If you want to learn about the one wire library or the SD library, these are good references:

OneWire Arduino Library, connecting 1-wire devices (DS18S20, etc) to Teensy
https://www.arduino.cc/en/Reference/SD
Attached Files
File Type: txt _DS18B20_SDLOG.txt (14.2 KB, 403 views)
TechShop is offline   Reply With Quote
Old 11-16-15, 02:06 AM   #37
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

Code:
/*
   DS18B20 temperature sensor datalogger.
   20151115 version
   Some code was borrowed and/or ispired by code from:
      -Arduino.cc reference for SD library
      -pjrc.com  for the one wire library (DS18B20 sensors).

   This sketch will run on some Arduino boards including the Duimilanove (with Atmega 328P) and the Sparkfun Pro-Micro 5v.
   With some minor tweaks to the pin assignments inside the SD library, it should run on the Arduino MEGA (2560 and other variants).
   Depending on your hardware, you'll have to change the value of #define SDMMCCSPIN.
   Some of the ethernet shields and other popular hardware use pin 4 for the SD CS pin.
   
   Connecting to the serial port at 9600 baud is also possible to monitor the data in CSV format
   commands are integers sent in CSV format.
   1    reads the datalog.txt file and sends it back through the serial port
   2    sends a timestamp to the arduino which is written into the file as text
        EXAMPLE:   2,14,30,59
          This saves the time stamp 14:30:59  (which would be 2:30:59 PM in 24 hour format).
   3    sends or requests the data refresh/logging interval.
        EXAMPLE:   3,0
          This requests the current log interval, it will be reported back via the serial port as text
        EXAMPLE:   3,45
          This changes the log interval to once every 45 seconds
   4    Request / Manage sensors list
        EXAMPLE:   4,0
          This requests the current list of sensors (at startup, all sensors are discovered and enabled (up to the quantity MAXSENSORSALLOWED limit defined below)
        EXAMPLE:   4,1,3
          This enables sensor number 3 fromt the list
        EXAMPLE:   4,2,3
          This disables sensor number 3 fromt the list
        EXAMPLE:   4,3
          This enables all available sensors in the list
        EXAMPLE:   4,4
          This disables all available sensors in the list
   9    This deletes the datalog.txt file on the SD card.  A new log is immediately started after the command completes.

   Commands can be stacked and will be executed sequentially.
        EXAMPLE: 1,3,15,4,2,2,5,2,22,30,00
        This will (1) read the datalog (3) Change the interval to 15 seconds (4) Disable sensor 2 (5) delete the file (2) log the timestamp @ 22:30:00.
        After execution of commands logging will resume with the settings change.
        Note:  I have not yet implimented a settings/script file at startup, so if you power cycle the arduino, the default settings will be used.


    Connect your DS18B20 temperature sensors to 5V and GND pins on the Arduino.
        Connect a 4.7K Ohm resistor between the 5V and Digital I/O pin #2 (Or whichever pin you have specified with #define SENSORPIN below).
        Connect the sensor data wire to Digital I/O pin #2 (Or whichever pin you have specified with #define SENSORPIN below).

*/

#include <SPI.h>
#include <SD.h>
#include <OneWire.h>
#define SENSORPIN 2     //which digital I/O pin are your DS18B20 sensors connected to?
#define SDMMCCSPIN 10   //Which digital I/O pin is your SD MMC card CS pin connected to?
#define MAXSENSORSALLOWED 7 // Maximum number of sensors allowed on the network.  Watch memory usage at compile time.
#define LOGFILENAME "datalog.txt" // name of the file to append with data.
//#define INIFILE "LOGSCRIPT.INI" // Name of the file for startup settings and script actions.  (This feature is not implimented yet).

OneWire ds(SENSORPIN);  // DS18B20 temperature sensors connected to SENSORPIN, set it above in the definitions.

boolean logging = true;
unsigned long G_TimePoint = 0;
byte G_LogInterval = 30;
byte G_SensorQty = 0;
byte G_CurSensor = 1;
String G_dString = "";   // make a string for assembling the data to log
byte G_AddressList[MAXSENSORSALLOWED][8];
boolean G_ActiveSensors[MAXSENSORSALLOWED];

void setup() {
  byte s, i;
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    delay(100); // wait for serial port to connect. Needed for native USB port only
  }
  for (s = 0; s < MAXSENSORSALLOWED; s++) {
    G_ActiveSensors[s] = false;  // make sure we start out expecting to log no sensors (we haven't found any sensors yet).
  }
  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(SDMMCCSPIN)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  SetCheckInterval(G_LogInterval);
  File LogFile = SD.open(LOGFILENAME, FILE_WRITE);
  if (LogFile) {
    LogFile.println("START NEW DATA RECORD");
    Serial.println("START NEW DATA RECORD");
    Serial.println(LOGFILENAME);
    LogFile.println("INTERVAL = " + String(G_LogInterval));
    LogFile.close();
  }
  else {
    Serial.println("Error opening " + String(LOGFILENAME));
  }
  G_SensorQty = FillAddressList(MAXSENSORSALLOWED);     //call FillAddressList() for initial sensor discovery
  Serial.print(String(G_SensorQty) + " Sensors found on pin " + String(SENSORPIN) + ":\n");
  if (G_SensorQty > 0) {
    for (s = 0; s < G_SensorQty; s++) {
      G_ActiveSensors[s] = true; // log every sensor found by default.  (User can change this with a command)
    }
  }
  SensorList(true);
}

The remainder of the sketch is in my next post
TechShop is offline   Reply With Quote
Old 11-16-15, 02:07 AM   #38
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

Please see my previous post for the first half of this Arduino source code

Code:
void loop() {
  byte s, ScriptCommand;
  while (Serial.available() > 0) { // if a command was sent via the serial port, parse it and execute the appropriate action.
    ScriptCommand = Serial.parseInt();
    delay(1000); // wait for the rest of the command to fill the serial buffer.
    switch (ScriptCommand) {
      case 1: // dump contents of data file to serial port
        DumpLogFileToSerial();
        break;
      case 2: // add a timestamp to the record
        SensorList(true);
        SaveTimeStamp();
        break;
      case 3: // change sensor/logging interval
        if (Serial.available() > 0) {
          SetCheckInterval(byte(Serial.parseInt()));
        }
        break;
      case 4:
        if (Serial.available() > 1) {
          switch (Serial.parseInt()) {
            case 0:
              SensorList(true); // using the 4,0 command (WITH zero suffix) appends the sensor list to the log file.
              break;
            case 1:
              SetCheckActiveSensors(1, Serial.parseInt());  //enable a sensor by it's list number
              break;
            case 2:
              SetCheckActiveSensors(2, Serial.parseInt());  //disable a sensor by it's list number
              break;
            case 3:    // Enable all available sensors
              if (G_SensorQty > 0) {
                for (s = 0; s < G_SensorQty; s++) {
                  G_ActiveSensors[s] = true;
                }
              }
              break;
            case 4:    // Disable all available sensors
              if (G_SensorQty > 0) {
                for (s = 0; s < G_SensorQty; s++) {
                  G_ActiveSensors[s] = false; 
                }
              }            
              break;
            default:
              break;
          }
        }
        else {
          SensorList(false);  // using the 4 command (WITHOUT the zero suffix) does NOT append the sensor list to the log file.
        }
        break;
      case 6: // Pause logging
        logging = !logging;
        if (logging) Serial.println("\nLogging resumed.");
        break;
      case 9: // delete the file
        DeleteLogFile();
        break;
      default: //invalid command
        //do nothing
        break;
    }
    G_dString = ""; // reset the data output string if this is to be the first in the list of sensors
  }
  if (millis() >= G_TimePoint) {
    if (logging) {
      G_TimePoint = G_TimePoint + G_LogInterval * 1000;
      for (G_CurSensor = 1; G_CurSensor <= G_SensorQty;) {
        if (G_ActiveSensors[G_CurSensor - 1]) { //does the user want to log this sensor?
          ReadTempSensors(G_CurSensor - 1);
        }
        G_CurSensor++;
      }
      LogSensorData();
      G_CurSensor = 1;
    }
    else {
      G_TimePoint = G_TimePoint + 1000; // Use a 1 second interval when logging is suspended
      Serial.println("\nLogging suspended.  Send 6 to resume.");
      Serial.println("Interval = " + String(G_LogInterval));
      SensorList(true);
    }
  }
}

boolean ReadTempSensors(byte q) { //read data from sensor q from the array of addresses: G_AddressList[q]
  boolean SignBit;
  int TReading, Tc_100, Whole, Fract;
  byte i, HighByte, LowByte, data[12];
  ds.reset();
  ds.select(G_AddressList[q]);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end

  delay(800);     // maybe 750ms is enough, maybe not

  ds.reset();
  ds.select(G_AddressList[q]);
  ds.write(0xBE);         // Read Scratchpad

  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
  }
  //Serial.print(" CRC=");
  // Serial.println( OneWire::crc8( data, 8), HEX);
  LowByte = data[0];
  HighByte = data[1];
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25
  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;
  if (SignBit) // If its negative
  {
    G_dString = G_dString + "-" + (Tc_100 * .018 + 32) + ",";
  }
  else {
    G_dString = G_dString + (Tc_100 * .018 + 32) + ",";
  }
  return true;
}

void LogSensorData() {
  File LogFile = SD.open(LOGFILENAME, FILE_WRITE);
  if (LogFile) {
    LogFile.println(G_dString);  // This is where we actually write all of the sensor data to the file
    LogFile.close();
    // print to the serial port too:
    Serial.println(G_dString);
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening " + String(LOGFILENAME));
  }
  G_dString = "";
  return;
}

void DumpLogFileToSerial() {
  File LogFile = SD.open(LOGFILENAME, FILE_READ);
  if (LogFile) {
    Serial.println("\n<<<READING DATA FILE>>>");
    while (LogFile.available()) {
      Serial.write(LogFile.read());
    }
    LogFile.close();
    Serial.println("<<<END OF DATA FILE>>>");
    Serial.println("\nResume logging task");
    return;
  }
}

void SaveTimeStamp() {
  byte hours, minutes, seconds;
  boolean badstamp = true;
  if (Serial.available() >= 3) { //check to see if a time was sent
    hours = Serial.parseInt();
    minutes = Serial.parseInt();
    seconds = Serial.parseInt();
    badstamp = false;
    if (hours > 24 || hours < 0) {
      Serial.println("Invalid time format.  Hours out of range.");
      Serial.println("Hours must be 0 - 23.");
      badstamp = true;
    }
    if (minutes > 59 || minutes < 0) {
      Serial.println("Invalid time format.  Minutes out of range.");
      Serial.println("Minutes must be 0 - 59.");
      badstamp = true;
    }
    if (seconds > 59 || seconds < 0) {
      Serial.println("Invalid time format.  Seconds out of range.");
      Serial.println("Seconds must be 0 - 59.");
      badstamp = true;
    }
    if (!badstamp) {
      File LogFile = SD.open(LOGFILENAME, FILE_WRITE);
      if (LogFile) {
        G_dString = "TIME:" + String(hours) + ":" + String(minutes) + ":" + String(seconds);
        if (LogFile.println(G_dString)) {
          G_TimePoint = millis();
          Serial.println("Timestamp saved:  " + G_dString);
        }
        else {
          Serial.println("Timestamp failed.");
          return;
        }
        LogFile.close();
        return;
      }
      else {
        Serial.println("FILE NOT AVAILABLE");
        return;
      }
    }
    else {
      return;
    }
  }
  else {
    Serial.println("Invalid timestamp or none provided.");
    Serial.println("hours = " + String(hours) + " Minutes = " + String(minutes) + " seconds = " + String(seconds));
  }
  return;
}

void SetCheckInterval(byte tempint) {
  if (tempint > 4) {
    G_TimePoint = G_TimePoint - G_LogInterval * 1000;
    G_LogInterval = tempint;
    G_TimePoint = G_TimePoint + G_LogInterval * 1000;
    Serial.println("Interval changed to: " + String(G_LogInterval));
    File LogFile = SD.open(LOGFILENAME, FILE_WRITE);
    if (LogFile) {
      LogFile.println("INTERVAL = " + String(G_LogInterval));
      LogFile.close();
    }
  }
  else if (tempint < 5) {
    Serial.println("Interval must be 5 seconds or larger.");
    Serial.println("Interval = " + String(G_LogInterval));
  }
  return;
}

void SensorList(boolean w) {// present a list of available sensors
  byte i, sc;
  File LogFile = SD.open(LOGFILENAME, FILE_WRITE);
  for (sc = 0; sc < G_SensorQty; sc++) {
    delay(20);
    Serial.print(String(sc + 1) + ") ");
    if (w) {
      if (LogFile) LogFile.print(String(sc + 1) + ") ");
    }
    for ( i = 0; i < 8; i++) {
      Serial.print(G_AddressList[sc][i], HEX);
      if (w) {
        if (LogFile) LogFile.print(G_AddressList[sc][i], HEX);
        delay(20);
      }
    }
    if (G_ActiveSensors[sc]) { // Is this sensor being logged?
      Serial.println(" - Logging");   // Notify the user that this sensor is being logged.
      if (w) {
        if (LogFile) LogFile.println(" - Logging");
      }
    }
    else {
      Serial.println(" - Ignored");   // Notify the user that this sensor is not being logged.
      if (w) {
        if (LogFile) LogFile.println(" - Ignored");
      }
    }
  }
  if (LogFile) {
    delay(20);
    LogFile.close();
  }
  Serial.println("");
  return;
}

int FillAddressList(int howmanysensorstoadd) {// fill an array with a list of sensor addresses
  byte i,  s = 0;
  byte addr[8];
  ds.reset_search();
  while ( ds.search(addr) && s < howmanysensorstoadd) {
    for ( i = 0; i < 8; i++) {
      G_AddressList[s][i] = addr[i];
    }
    s++; // increment the count for the next sensor (if there is one)
  }
  return s;
}

void SetCheckActiveSensors(byte c, byte s) {
  if (s > 0 && s <= G_SensorQty) {  // only do something if the sensor number is in the correct range
    if (c == 1) { // User sent the command to enable a sensor
      G_ActiveSensors[s - 1] = true; // enable the sensor by setting it true
    }
    if (c == 2) { // User sent the command to disable a sensor
      G_ActiveSensors[s - 1] = false;   // disable the sensor by setting it false
    }
  }
  else {
    Serial.println("Unknown sensor referenced in command");
  }
  SensorList(true);
  return;
}

void DeleteLogFile() {
  SD.remove(LOGFILENAME);
  Serial.println("FILE DELETED");
  return;
}
TechShop is offline   Reply With Quote
Old 11-16-15, 02:45 AM   #39
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

So the next portion of this project will be a departure from the faithful Arduino in favor of the Raspberry Pi. I'll load it up with Raspian and Apache web server. This will serve as the brains behind my system and serve up the user interface. The web-based U/I will allow me control from anywhere.

Also, I may write some software that uses the weather forecast data to gain an edge in system efficiency. I found this data source for custom generated XML weather forecasts via NOAA.GOV:


http://www.wrh.noaa.gov/forecast/xml...lon=-122.25500

If you study the URL, you'll see that you can change the lat/lon coordinates, duration and time interval to suit your particular needs:

HTML Code:
http://www.wrh.noaa.gov/forecast/xml/xml.php?duration=48&interval=1&lat=45.63200&lon=-122.25500

TechShop is offline   Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 03:20 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Ad Management by RedTyger
Inactive Reminders By Icora Web Design