arduino based hvac lead-lag controller

arduino lead-lag hvac control
Installed lead-lag controller
arduino lead-lag hvac control
arduino lead-lag hvac control
arduino hvac lead-lag controller
lead-lag controller using arduino nano

My office is in a telecommunications facility full of routers, radios, fiber-optic equipment etc. Reliable environmental controls are essential as this equipment will fail in extreme heat. I have two 20,000 BTU AC units in my office that run on 250VAC. I created this lead-lag controller to turn on the lead AC unit based on the temperature setting, and if the lead unit has run for six minutes without cooling to the setting, turns on the lag unit as well until the temperature setting is reached.

arduino lead-lag hvac controller
arduino lead-lag hvac controller

I have the programming down and the device built and installed. Initial tests work as designed. I am using some 10A 250VAC power relays. With the unit running on high fan speed and the compressor going the AC units draw about 5 amps; well within the rage of the relays.

the lead / lag controller turns on/off the 250VAC outlets of the air conditioner units

Here is the code ->

// include the library code:
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "RTClib.h"
RTC_DS3231 rtc;

// Sensor input pin
#define DATA_PIN 2
// How many bits to use for temperature values: 9, 10, 11 or 12
#define SENSOR_RESOLUTION 9
// Index of sensors connected to data pin, default: 0
#define SENSOR_INDEX 0

OneWire oneWire(DATA_PIN);
DallasTemperature sensors(&oneWire);
DeviceAddress sensorDeviceAddress;

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 10, d5 = 9, d6 = 8, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int sample_num = 0;

//thermostat setting; init to 72
int setting = 70; 

//heat or cool mode
char mode = 'C';

//relay pins
const int ac1Pin = 5;
const int ac2Pin = 4;
//const int heatPin = 6;

//control buttons
const int modePin = 3;
const int decrPin = 6;
const int incrPin = 0;

//AC ON/OFF flags
int ac1_f = 0;
int ac2_f = 0;
int heat_f = 0;

//on timers
int ac1_timer = 0;
int ac2_timer = 0;

//RTC stuff
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  Serial.begin(9600);

  //AC1 relay
  pinMode(ac1Pin, OUTPUT); 
  pinMode(ac2Pin, OUTPUT);
  //pinMode(heatPin, OUTPUT);

  //set AC1 off
  digitalWrite(ac1Pin, LOW);
  digitalWrite(ac2Pin, LOW);
  //digitalWrite(heatPin, LOW);    

  //control buttons
  pinMode(modePin, INPUT);
  pinMode(decrPin, INPUT);
  pinMode(incrPin, INPUT);

  sensors.begin();
  sensors.getAddress(sensorDeviceAddress, 0);
  sensors.setResolution(sensorDeviceAddress, SENSOR_RESOLUTION);  

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }  
}

  //array to hold 12 readings; 5-per min
  float t_readings[12];

  //sum of readings
  float sum = 0.0;

  //avg of readings
  float average = 0.0;

void loop() {
  
  // set the cursor to (0,0):
  lcd.setCursor(0, 0);

  //reset sum and avg on each iteration
  sum = 0;
  average = 0;

  //12 sample loop; takes 1 min
  for(int x=0;x<12; x++){
    lcd.setCursor(0, 0);
    sensors.requestTemperatures();
    // Measurement may take up to 750ms

    float temperatureInCelsius = sensors.getTempCByIndex(SENSOR_INDEX);
    float temperatureInFahrenheit = sensors.getTempFByIndex(SENSOR_INDEX);
  
    Serial.print("Temperature: ");
    Serial.print(temperatureInCelsius, 1);
    Serial.print(" Celsius, ");
    Serial.print(temperatureInFahrenheit, 1);
    Serial.print(" x: ");
    Serial.print( x );
    Serial.println(" Fahrenheit"); 

    //push reading into array
    t_readings[x] = temperatureInFahrenheit;
  
    lcd.print(temperatureInFahrenheit);
    lcd.print("F");
    //lcd.setCursor(0, 1);
    //lcd.print(sample_num);
    //sample_num++;

    if(digitalRead(modePin) == LOW){
      Serial.println("MODE BUTTON PRESSED! ENTERING SETUP....");
      setTemp();
    }//end if mode pressed

    //print setting to lcd row 2
    lcd.setCursor(0, 1);
    lcd.print("Setting: ");
    lcd.print(setting);    

    //print mode
    lcd.setCursor(15,0);
    lcd.print(mode);

    delay(5000);  //sleep 5 sec
  }//end for loop 12 sample

  //average samples
  for(int i=0; i<12; i++){
    sum = sum + t_readings[i];
  }//end for avg
    Serial.print("sum: ");
    Serial.println(sum);
    average = sum / 12;
    Serial.print("avg: ");
    Serial.println(average);

    //AC relay control
    if(average > setting && mode == 'C'){
      digitalWrite(ac1Pin, HIGH);

      //flag logic
      if(ac1_f == 0){ ac1_f = 1;}

      //timer logic
      ac1_timer += 60;    //60 because of the 12 samples/min cycle
      Serial.print("AC UNIT 1 ON ");
      Serial.print(ac1_timer);
      Serial.print(" seconds ");

      if(ac1_timer > 60){//turn on ac2 after 30 minutes
        if(ac2_f == 0){ 
          ac2_f = 1; 
          digitalWrite(ac2Pin, HIGH);
        }
        else{
          //increment ac2_timer
          ac2_timer += 60;
        }
        Serial.print("AC UNIT 2 ON ");
        Serial.print(ac2_timer);
        Serial.print(" seconds ");
      }//end if turn on AC2

      //poll RTC for time
      DateTime now = rtc.now();
   
      Serial.print(now.year(), DEC);
      Serial.print('/');
      Serial.print(now.month(), DEC);
      Serial.print('/');
      Serial.print(now.day(), DEC);
      Serial.print(" "); 
      Serial.print(" (");
      Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
      Serial.print(") ");
      Serial.print(now.hour(), DEC);
      Serial.print(':');
      Serial.print(now.minute(), DEC);
      Serial.print(':');
      Serial.println(now.second(), DEC);
    }//end if
    if(average < setting && mode == 'C'){
      //turn AC1 OFF, reset timer & flags
      digitalWrite(ac1Pin, LOW);
      ac1_f = 0;
      ac1_timer = 0;
      Serial.print("AC UNIT 1 OFF ");

      //if on, turn off AC2
      if(ac2_f == 1){
          digitalWrite(ac2Pin, LOW);
          ac2_f = 0;
          ac2_timer = 0;
          Serial.print("AC UNIT 2 OFF ");
      }//end if
      
      //poll RTC for time
      DateTime now = rtc.now();
      
      Serial.print(now.year(), DEC);
      Serial.print('/');
      Serial.print(now.month(), DEC);
      Serial.print('/');
      Serial.print(now.day(), DEC);
      Serial.print(" "); 
      Serial.print(" (");
      Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
      Serial.print(") ");
      Serial.print(now.hour(), DEC);
      Serial.print(':');
      Serial.print(now.minute(), DEC);
      Serial.print(':');
      Serial.println(now.second(), DEC);      
    }//end if
    
    delay(2000); 
}

void setTemp(){
  int su_flag = 1;
  int mode_flag = 1;
  Serial.println("SETUP MODE ->");
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set temp: ");
  lcd.print(setting);
  delay(1000);
  //lcd.clear();
  while(su_flag == 1){
    if(digitalRead(decrPin) == LOW){
      setting--;
      lcd.setCursor(10, 0);
      lcd.print(setting);
    }//end if exit setup
    if(digitalRead(incrPin) == LOW){
      setting++;
      lcd.setCursor(10, 0);
      lcd.print(setting);
    }//end increment temp
    if(digitalRead(modePin) == LOW){
      //exit setup
      su_flag = 0;
    }//end exit setup mode
    delay(250);
  }//end while
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set mode: ");
  lcd.print(mode);
  delay(1500);
  while(mode_flag == 1){
    if(digitalRead(decrPin) == LOW){
      mode = 'C';
      lcd.setCursor(10, 0);
      lcd.print(mode);
    }//end if
    if(digitalRead(incrPin) == LOW){
      mode = 'H';
      lcd.setCursor(10, 0);
      lcd.print(mode);
    }//end if heat mode
    if(digitalRead(modePin) == LOW){
      mode_flag = 0;
    }//end if exit mode set
    delay(250);
  }//end while set mode
  lcd.clear();
}//end setTemp function

rssi vs. relative humidity

receive signal strength indication vs. relative humidity data logger
receive signal strength indication vs. relative humidity data logger

I finally got around to deploying my rssi / temperature / relative humidity data logger and recorded nearly 6,000 samples of each metric over a five day period. This radio system operates in the ISM band, and is susceptible to propagation issues due to atmospheric conditions, especially humidity. The logic board provides a terminal that outputs a DC voltage that represents the rssi in dBm. My data logger uses a raspberry pi zero w, and reads the rssi voltage with an MCP3004 analog to digital converter using a bit-banged driver I wrote in perl. Each reading is timestamped using time derived from a DS3231 i2c real-time clock chip.

I use a HTU21 temperature / relative humidity sensor break out board from adafruit. This chip is MUCH MORE STABLE & RELIABLE than the DHT22 I was originally using.

temperature vs. relative humidity data capture
temperature vs. relative humidity data capture

For starters, here is the temperature in fahrenheit (red) vs. the relative humidity (green). Predictably, when the temperature rises, the relative humidity goes down and vice versa.

rssi level vs. relative humidity at 900MHz
rssi level vs. relative humidity at 900MHz

Now here is the relative humidity vs. the rssi. This radio is about 20 miles away from the transmitter over flat terrain with an output of 37dBm (5 watts), and has an average rssi of about -90dBm. You can see from the above graph that at about sample 4,000 when the relative humidity takes a noticeable dip that the rssi has a corresponding, albeit small, increase: precisely what we would expect to see.

Nothing surprising here. It just proves the effectiveness of my data logger device. It would be useful in situations where an RF path is going up and down due to atmospheric conditions or other obstructions, and real-time data would be useful in diagnosing problems and coming up with solutions like a higher gain antenna or increasing height.

homemade bluetooth work speaker that kicks

home made 25W stereo bluetooth speaker project
home made 25W stereo bluetooth speaker project

I have a huge project going on at work right now, and loud music is absolutely essential.  I ordered some Boss CH3220 140W speakers and used a 25W bluetooth audio amplifier board and whipped up in one evening a killer work speaker that kicks hard.

It is working great as is, but I am totally not done.  I am going to add a raspberry pi to play my saved mp3’s and roll some of my own features.

25W bluetooth audio amplifier board
25W bluetooth audio amplifier board

I am very impressed with the quality of the sound and how easy it was to pair with this board.  I tested the speakers with ‘dont tread on me’ by metallica.  The frequency response of these 3.5″ speakers is surprisingly very good.

data logging rssi on a GE MDS9710 with perl, dht22, and raspberry pi

pi zero w data logging temperature, rssi, and humidity
pi zero w data logging temperature, rssi, and humidity

I work on 900MHz point to multi-point data radio systems.  Humidity can greatly affect the propagation of rf waves at this frequency.  I wanted to do some data logging and analysis of how moisture in the air affected the received signal strength indication over time.

The GE MDS 9710 P70 radio system has an analog voltage test point that corresponds to the rssi on it’s interface board.  I used a DHT22 to measure the temperature and humidity,  an MCP3004  10-bit analog to digital converter to read the rssi voltage, and a pi zero W for the brains.

arduino nano every and DHT22. Readings displayed on 16X2 LCD
arduino nano every and DHT22. Readings displayed on 16X2 LCD

I originally implemented this project with an arduino nano every.   It worked great.  I was going to log the data by connecting the nano every to a pi zero via UART while a screen session was running on the pi with the screen output (STDOUT) saved to a file .  This method would require a logic level converter which I did not have on hand at the time, so I decided to implement the whole project on my pi zero.

It is much easier to use a DHT22 sensor with arduino using adafruit’s unified sensor library.

adafruit unified sensor library for DHT22
adafruit unified sensor library for DHT22

Here is the source:

 
// REQUIRES the following Arduino libraries:
// - DHT Sensor Library: https://github.com/adafruit/DHT-sensor-library
// - Adafruit Unified Sensor Lib: https://github.com/adafruit/Adafruit_Sensor

#include "DHT.h"
#include 
#define DHTPIN 2     // Digital pin connected to the DHT sensor

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);

const int rs = 12, en = 11, d4 = 10, d5 = 9, d6 = 8, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//number of samples
int sn = 0;

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("MDS RSSI Data   Logger");
  delay(2000);
  dht.begin();
}

void loop() {
  // Wait a few seconds between measurements.
  delay(2000);

  //increment sample number
  sn++;

  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(f)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    Serial1.println(F("Failed to read from DHT sensor!"));
    return;
  }

  //read rssi voltage
  int bv = analogRead(A0);
  float v = (5 * bv) / 1023.0;
  float rssi = v - 1.5;
  rssi = (rssi/0.8) * 20 - 120; 
  
  // Compute heat index in Fahrenheit (the default)
  float hif = dht.computeHeatIndex(f, h);

  //print to console
  Serial.print(F("Humidity: "));
  Serial.print(h);
  Serial.print(F("%  Temperature: "));
  Serial.print(f);
  Serial.print(F("°F  Heat index: "));
  Serial.print(hif);
  Serial.println(F("°F"));

  //print to pi
  Serial1.print("Humidity: ");
  Serial1.print(h);
  Serial1.print(F(" Temp: "));
  Serial1.print(f);
  Serial1.print("F RSSI: ");
  Serial1.print(rssi);
  Serial1.print("dBm");
  Serial1.print("\n");

  lcd.clear();
  lcd.print("Humidity:");
  lcd.print(h);
  lcd.print("%");
  lcd.setCursor(0,1);
  lcd.print(f);
  lcd.print("F ");
  //lcd.print(v);
  //lcd.print("V");
  lcd.print(rssi);
  lcd.print("dBm");
}

To use perl to interface with the DHT22 on my pi required the RPi::PIGPIO::Device::DHT22 module on cpan.  Before I could use this,  I needed to install PIGPIO and run it as sudo before executing my script.  PIGPIO is a VERY interesting library for controlling a pi’s gpio’s locally or remotely via socket, and I will hopefully find the time to explore more possibilities in the near future.  It is, however, somewhat of a resource hog.

pigpiod daemon cpu resources on a raspberry pi
pigpiod daemon cpu resources on a raspberry pi

Once all the modules are loaded on the pi and the PIGPIO daemon is fired up,  I just had to translate the ardino C code into perl.

 
#!/usr/bin/perl 
use strict;
require "/home/pi/gpio/MCP3004.pl";
use RPi::PIGPIO;
use RPi::PIGPIO::Device::DHT22;

=pod
loggs the temperature, humidity, and rssi of a
control point radio using a DHT22.  requires
PIGPIO daemon to be started first.  from home dir, 
cd/PIGPIO.  then, sudo pigpiod.  cd back into
gpio dir, and sudo perl CTC_datalogger.pl.  
=cut

my $pi = RPi::PIGPIO->connect('127.0.0.1');
my $dht22 = RPi::PIGPIO::Device::DHT22->new($pi,23);

#ADC channel
my @ch0 = (1,1,0,0,0);

#init ADC
init3004(3,4,5,6);

#open log file
open RD, ">", "ctc_data.log" or die $!;
#print table description
print RD "index,epoch,date,humidity,temperature,voltage,rssi\n";

my $i = 0;
for(;;){
	#formatted date string 
	my $time_str = `date`;
	print "$i\n";
	print $time_str;
	chomp($time_str);

	$dht22->trigger();
	my $deg_f = ($dht22->temperature * (9/5)) + 32;
	#$deg_f = sprintf("%.1f", $deg_f);
	$deg_f = int($deg_f);

	print "Temp: $deg_f \n";
	my $humidity = $dht22->humidity;
	$humidity = sprintf("%.1f", $humidity);
	print "Humidity: ".$dht22->humidity."%\n";
	int($deg_f);

	#take 5 samples from ADC and avg
	my $v_avg = 0;
	my $v_accum = 0;
	for(my $s=0;$s<5;$s++){
		my ($reading, $binval, $voltage ) = read3004(\@ch0, 50, 4.966);
		#print "binval: $binval\tvoltage: $voltage vdc\n";
		#print RD "$s\n$voltage\n";
		usleep(1000);

		$v_accum += $voltage; 
	}#end for
	$v_avg = $v_accum / 5;
	$v_avg = sprintf("%.2f", $v_avg);
	#print "avg: $v_avg\n";
	
	#calculate RSSI
	my $rssi = 0;
	$rssi = $v_avg - 1.5;
	$rssi = ($rssi / 0.8) * 20;
	$rssi -= 120;
	
	#format rssi
	$rssi = sprintf("%.2f", $rssi);
	print "rssi: $rssi dBm \n";
	print "-----------------------------------\n";

	#print to LCD
	`./lcd "Humidity: $humidity%     $deg_f F $rssi dBm"`;

	#print to log file
	my $log_str = '';
	my $uts = time;
	my $hr_time = localtime();
	$log_str = "$i,$uts,$hr_time,$humidity,$deg_f,$v_avg,$rssi\n";	
	select((select(RD), $|=1)[0]);
	print RD $log_str;

	sleep(2);
	
	$i++;
}#end for

close RD;

So far, it’s working great on the test bench.

pi zero rssi humidity data logger
pi zero rssi humidity data logger

Forgot to mention.  I used a DS3231 real time clock to keep time on the pi.  It works great and was easy to set up using this tutorial.

DS3231 RTC on pi zero
DS3231 RTC on pi zero

I will be deploying the data logger in the field this week and will post the results.  My bench test results are as follows.  Here is my humidity graph after nearly 9K samples.

dht22 humidity readings with pi zero w
dht22 humidity readings with pi zero w

And here is the rssi over the same dataset.  You can definitely see a higher rssi when the humidity is lower as expected.

rssi readings using raspberry pi
rssi readings using raspberry pi

family alarm clock project III

trinket Pro 5V 16MHz based alarm clock / stereo project using a PCF8523 real time clock breakout from adafruit
trinket Pro 5V 16MHz based alarm clock / stereo project using a PCF8523 real time clock breakout from adafruit

Nearly done with our family project.  It has been so much fun, and I hate that it’s nearly over, but Eli and Addy are pumped for another project.  The maker space  has been the new cool hangout:  loud music, chips + cheese dip, Teen titans Go, roller skating, and making a killer boom box that everybody loves.

trinket Pro 5V 16MHz based alarm clock / stereo project using a PCF8523 real time clock breakout from adafruit
the brains is a trinket Pro 5V 16MHz

This is my first project with a trinket Pro.  I love this platform.  Tiny, cheap, powerful,  easy to use; comparable to an arduino nano.  I did run out of GPIO pins (mostly due the the 16×2 LCD display), but got around it adding an MCP23008 for the alarm control buttons (also using libraries from adafruit) .

PCF8523 adafruit trinktet project
It’s adisaster on the inside, but pretty cool on the outside.

family alarm clock project II

We are making progress on our alarm clock / boom box project.  Eli soldered the 220 ohm resistors to the cathodes of the LEDs for the hours and Addy soldered the common ground wire to the resistors, hot glued them to the clock face, and tested them with a 6V battery.  I re-purposed an old POW3U prototype board for the 10 74HC595 shift registers we will need for the LEDs (they hate when I help!).  We put together what we have of the cabinet so far, and it looks pretty cool.  Not only that, it THUMPS!  Tested it with Wildflower from The Cult.  The kids are more enthused with seeing the progress.

The PCF8523 RTC breakout came in.  I downloaded the RTC libraries from Adafruit and ran the example scripts.  I am very impressed.  I will definitely buy more for other projects.  I connected it to an old CHIP pro I have and experimented with it just using i2cget commands reading the different registers and it seems to be an easy , straightforward RTC IC to use.  It is really coming together.

array of 74HC595 shift registers to drive our LED clock on a POW3U prototype board
array of 74HC595 shift registers to drive our LED clock on a POW3U prototype board

alarm clock / boom box cabinet front
alarm clock / boom box cabinet front

cabinet back view with sub woofer

family alarm clock project

Eli, Addy, and I are working on an arduino-based LED alarm clock project.  The design is totally Eli’s idea.  He wants 60 LEDs around the face of the clock for the minutes, and 12 LEDs on the top for the hour.  We gutted the audio system from one of their old gaming chairs that got tore up, and are using it for the sound.  I ordered a PCF8523 real time clock breakout board from adafruit to keep time, and are going to use a 5V 16MHz Trinket Pro for the brains. With over 72 LEDs to drive, we plan on using 10 74HC595 shift registers.  We are also going to use a 16X2 LCD display to show the time and maybe temperature.  So far, Eli has cut all the pieces out with a jig saw and he and Addy drilled all the holes for the LEDs.  Eli soldered a 220 Ohm resistor to the cathode of all the LEDs, and Addy used a glue gun to fasten them to the clock face.  I am new to arduino, so I am experimenting with using it to drive the LCD and the 74HC595’s.

Elegoo Uno driving a 16X2 LCD and 74595 shift register

family LED clock project

pi pet camera

We’re on spring break this week, and couldn’t get any takers to look after out guinea pig, Mon$ter.  The plan was to just put lots of extra food and water in his habitat.  I repurposed my pi-zero based front door security device (because we replaced it with a ring) and set it up to take a picture of Mon$ter every hour, and post it on my home web server so we can see how he’s doing.

rasspberry pi pet cam
Screenshot of my web browser pet cam app

pi zero w with camera and 12VDC lead-acid backup battery
pi zero w with camera and 12VDC lead-acid backup battery facing Mon$ter’s cage

Here’s the simple script.  It makes some system calls to some other scripts I wrote to scp the images to the web server.

DIY Home security with Raspberry pi zero w + Perl

I got a ring doorbell for Christmas.  It was a very thoughtful gift and I gotta admit it is very slick.  However, for some time, I have been using a home made security system based on the raspberry pi zero w and perl.

home made home security node based on pi zero w
home made home security node based on pi zero w

I designed it so that each node sends udp messages to a udp + LAMP server. I can view all the events on the server’s web interface.

UDP server receiving message from a node
UDP server receiving message from a node

The front-door node also has a TMP36 temperature sensor read by an MCP3008 adc, and a raspberry pi camera.  When the door is opened, it takes a picture two seconds later, and uploads it to the LAMP server.

diy home security web interface
diy home security web interface

I set up port forwarding on my ISP router so I can view my LAMP server from anywhere.

The udp client and server scripts are incredibly simple.

perl udp client script
perl udp client script

perl udp server script
perl udp server script

tmp36 + MCP3008 schematic
tmp36 + MCP3008 schematic

entry contact and LCD schematic
entry contact and LCD schematic

I go into somewhat more detail on my hackster account.  Off-the-shelf home security and automation products are fairly inexpensive and easy to set up nowadays, but for me, it’s just so much fun to build my own from scratch.

analyzing SPI on an MCP3208 12-bit analog to digital converter

The MCP3208 12-bit analog-to-digitial converter is the basis of my battery load test analyzer and online monitoring system which I call pid3A .  As I have mentioned before,  I implemented a bit-banged SPI driver for the mcp3208 in the perl programming language.  I use the analog-to-digital converters (adc’s) to sample the voltages of each cell in the battery plant at regular intervals to make graphs of the discharge curves.

MCP3208 Analog to digital converter
MCP3208 Analog to digital converter

pid3A is in it’s 2nd prototype and can accurately measure up to 24 cell voltages at a time with it’s three 8-channel mcp3208’s.  Occasionally, I will burn up a single channel on one of the adc’s, rendering the whole chip useless for my application.

I have written a lot of diagnostic software for pid3A to make sure the adc’s are functioning properly before I begin an IEEE450  load test.  To test all 24 channels, I made a test plug that puts 2.5VDC on each channel, takes 5 samples, and give the the binary output code and actual voltage of each sample.  It then gives me a color-coded go/no-go visual indication.  If there are any bad channels, I just change out the adc chip and re-run the diagnostic test.

mcp3208 test plug
pid3A test plug

Here is a sample of the output:

mcp3208 diagnostics
diagnostic testing of each ADC channel

You can see that the adc output codes and voltages are very steady.  The voltage is a function of the digital output code of the adc.  Above, most of the output codes are 2049.  Knowing this, and the reference voltage of 4.998VDC, you can calculate the actual voltage using this equation:

mcp3208 digital output code equation
mcp3208 digital output code equation

This is absolutely necessary for troubleshooting quickly, but sometimes I need to know what is going on at a microscopic level.  My multi-channel logic analyzer is the perfect tool to look at the underlying SPI protocol.

IEEE450 battery load test analyzer
IEEE450 battery load test analyzer

I know this just looks like a mess of wires, but I have my logic analyzer hooked up to the data-in, data-out, chip select, and clock lines of one of my mcp3208 chips.  Here is a close up of a frame from my diagnostic script:

SPI examination using PulseView
SPI examination using PulseView

This shows the driver clocking in 19 pulses.  The first few bits on the MOSI line tell the adc to use single (as opposed to differential) mode, and sample channel 001.  The rest of the MOSI bits are ‘don’t care’ bits, so my driver just set them all high.  The MISO line is the mcp3208 clocking out the actual binary output code.  In this case it is 2045, which in binary is 011111111101.  The slave select line goes low to initiate a sample, and goes high when finished.  PulseView shows me precisely what is happening, and lets me easily analyze the SPI protocol for proper function per the datasheet below:

mcp3208 SPI protocol framing
mcp3208 SPI protocol framing