My first Arduino is for heating my posterior.

by ModemJunki on Mon Mar 04, 2013 12:47 am

[edit]It's a finished and working project! You can jump to the description and updated code starting here->http://forums.adafruit.com/viewtopic.php?f=25&t=37662#p216017[/edit]

I bought an OEM configured remote starter with 1-mile range (2.2Km) from an electronics closeout for my car, and I wanted it to turn on the defroster and seat heaters if it was cold out. The online ad said it could!

Well it could - but it can only run one accessory, so the output had to be used for the security key emulator and the defrost activation was disabled. I was going to kit together a delayed relay and temperature controller (from a major kit Vendor) but then I thought, that going to take up too much space, and why not learn something new?

So I bought the Experimentation Kit here to get started. I've never worked with electronics before. I write some code (scripting in AutoIT) but not anything sophisticated. So, anyone who has suggestions for improvement feel free to speak up. :-)

For certain I think I can fit this onto a much smaller unit than an Uno as it hardly uses any pins or memory, so I might buy something less powerful for the final build. I have a Nano clone from ebay that I was going to use but I think I'll save that for another project I want to do after this.

Code: Select all | TOGGLE FULL SIZE
// My first Arduino effort
// HM March 2013
// This will be used to latch diode-isolated signal lines to ground
// in an automobile (12 volt) environment.
//
// The signal lines MUST NOT connect to the Arduino ground pins!
// Instead the signal must only pass through the relay contacts!
// Applying 12v to Arduino pins will let the magic smoke out!
//
// The signal lines will trigger onboard defrost and seat heater functions.
//
// I'm doing this because my remote start only has one output and
// I have to use it to power the security key emulator. It has
// the needed temperature circuit but it shares the internal relay
// and output wire with the function that powers the emulator.
// Only one or the other can be selected to run.
// I guess I was a bit too thrifty about buying it.

// Here is what this does
// 1) Waits "x" seconds defined in timer.setTimeout (in setup). During the wait, an LED pulses.
// 2) Evaluates the current temperature at the TMP36.
// 3) Compares the value of the temperature against variable "triggerTemp" using C (centigrade) or F (Farenheit)
// as defined in variable "CorF".

//#define DEBUG 1 // use this to enable serial monitoring

#include <SimpleTimer.h> ; // for the 30 second delay
//#include <avr/power.h> ; //for sleep mode, not implemented yet
//#include <avr/sleep.h> ; //for sleep mode, not implemented yet

int relayPin = 12; // relay
int relayInd = 13; // relay indication (hey, it's free)
int senseLED = 11; // the pin that the sense indicator LED is attached to
int senseLEDbrightness = 0; // how bright the sense indicator LED is
int senseLEDfadeAmount = 15; // how many points to fade the sense indicator LED by
int triggered = 0; // we will use this for state management of our indicator (sense)LED

// now lets define our target temperature.
char CorF = 'C'; // C for Centigrade, F for Farenheit
int triggerTemp = 5; // 68 F is 20 C (testing), 41 F is 5 C (production)

int temperaturePin = 0; //the analog pin the TMP36's Vout (sense) pin is connected to

SimpleTimer timer; //declare one timer only

void setup() {
   pinMode(relayPin, OUTPUT); 
   pinMode(relayInd, OUTPUT);
   digitalWrite(relayInd, LOW);   // make sure the relay indication is off, PIN13 defaults to HIGH
   pinMode(senseLED, OUTPUT); // declare pin 5 to be an output for power led

#ifdef DEBUG // if debug is on activate serial monitoring
   Serial.begin(9600);
   // welcome message
   //  Serial.println("One timer is triggered every 2 seconds");
   Serial.println("One timer is set to trigger only once after 30 seconds");
   Serial.println();
#endif

   timer.setTimeout(20000, TriggerIfCold); // delay 20 seconds for the car to start and power to be stable
   // timer.setTimeout(10000, TriggerIfCold); // for dev timing, long enough to grab temp controller and warm it up
}

//-------------------------------------------------------------------------------------------------
// main loop
void loop() {
   switch (triggered){
   case 0:
      pulse(); // LED "pulses" while waiting to evaluate temperature
      timer.run();// this is where the timer "polling" occurs
      break;
   case 1:
      analogWrite(senseLED, senseLEDbrightness);
      // doSleep(); // not implemented yet
      // the relay was triggered, we leave the sense LED on to show this
      break;
   case 2:
      analogWrite(senseLED, 0);
      // doSleep(); // not implemented yet
      // the relay was not triggered because the temperature was above the trigger
      // the we turn off the sense LED on to show this
      break;
   }
}

//-------------------------------------------------------------------------------------------------
// Sleep mode may not be necessary or desired
//void doSleep() {
//   set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here
//   sleep_enable();          // enables the sleep bit in the mcucr register
//                             // so sleep is possible. just a safety pin
// 
//   power_adc_disable();
//   power_spi_disable();
//   power_timer0_disable();
//   power_timer1_disable();
//   power_timer2_disable();
//   power_twi_disable();
//   
//   sleep_mode();            // here the device is actually put to sleep!!
//}


//-------------------------------------------------------------------------------------------------
// Trigger function for sensing
void TriggerIfCold() {
   int Degrees; // local
   Degrees = GetTemp(CorF); //C or F defined in declarations
   if (Degrees <= triggerTemp) { // it's colder then the trigger temp
      digitalWrite(relayPin, HIGH); // We trigger the relay on for half a second
      digitalWrite(relayInd, HIGH); // We toggle the onboard LED on too
      delay(500);
      digitalWrite(relayPin, LOW);
      digitalWrite(relayInd, LOW);
      triggered = 1;
   }
   else if (Degrees > triggerTemp) { // it's above our trigger temp and we won't be doing anything
      triggered = 2;
   } // we are done looking at the temperature
#ifdef DEBUG // if debug is on we can see this in the serial monitor
   Serial.print(Degrees);
   Serial.print(" degrees ");
   Serial.println(CorF);
   Serial.println("");         
   Serial.println("This timer only triggers once after 30 seconds"); 
   Serial.println("");       
#endif
}


//-------------------------------------------------------------------------------------------------
// Pulse sense LED, only to show that something is happening for the humans
void pulse() {   // set the brightness of pin 5:
   analogWrite(senseLED, senseLEDbrightness);   
   // change the brightness for next time through the loop:
   senseLEDbrightness = senseLEDbrightness + senseLEDfadeAmount;
   // reverse the direction of the fading at the ends of the fade:
   if (senseLEDbrightness == 0 || senseLEDbrightness == 255) {
      senseLEDfadeAmount = -senseLEDfadeAmount ;
   }     
   // wait for 30 milliseconds to see the dimming effect   
   delay(30);
}

//-------------------------------------------------------------------------------------------------
// calculate temperature in degrees F or C
int GetTemp(char CorF){
   float temperature = getVoltage(temperaturePin);          //getting the voltage reading from the temperature sensor
   switch (CorF){ // calculate depending on global variable
   case 'C':
      temperature = (temperature - .5) * 100;                //convert from 10 mv per degree with 500 mV offset to degrees C ((voltage - 500mV) times 100)
      break;
   case 'F':
      temperature = (((temperature - .5) * 100) * 1.8) + 32;   //convert to Farenheit for the backwards places
      break;
   }
   return temperature;
}

//-------------------------------------------------------------------------------------------------
// getVoltage() - returns the voltage on the analog input defined by pin
float getVoltage(int pin){
   return (analogRead(pin) * .004882814); //converting from a 0 to 1023 digital range
}
Last edited by ModemJunki on Sat Sep 07, 2013 11:32 pm, edited 1 time in total.
ModemJunki
 
Posts: 25
Joined: Sun Feb 17, 2013 10:52 pm

Re: My first Arduino is for heating my posterior.

by adafruit_support_bill on Mon Mar 04, 2013 7:17 am

Nice project. If you post a photo I'll see if we can get it in the blog. :D
User avatar
adafruit_support_bill
 
Posts: 28125
Joined: Sat Feb 07, 2009 9:11 am

Re: My first Arduino is for heating my posterior.

by ModemJunki on Mon Mar 04, 2013 10:59 am

I have a way to go yet - I have to build the final implementation (still waiting on some parts) and I have to get it into the car (which means: I have to get the garage cleaned out).

I plan to do the implementation this spring. Putting in the car starter in an unheated garage during winter was unpleasant.
ModemJunki
 
Posts: 25
Joined: Sun Feb 17, 2013 10:52 pm

Re: My first Arduino is for heating my posterior.

by ModemJunki on Sat Sep 07, 2013 11:07 pm

Well, it's installed (and after only a few months delay). I decided to wait until cold weather was approaching.

I got really great help over in the General Project Help to design the actual interface to the car. http://www.adafruit.com/forums/viewtopic.php?f=8&t=37806&start=30

Below is the final version and pictures of the build and install. My apologies that the circuit diagram is so crudely drawn in Visio, I hope it's understandable.
PWC_V0.5.png
PWC_V0.5.png (226.41 KiB) Viewed 885 times


I had originally targeted using an old 35mm film canister as the "chassis". I only had one film canister and our cat took off with it! I ended up housing the unit in a vintage pill bottle.

Adafruit has the Trinket now, I would have used one of those for this project if they were available at the time. I had already found a handful of these clone "New Version Pro Mini Module Atmega328 5v 16M" and started my build around it. The goal for the build was to keep the project as compact as possible while leaving room for future expansion as I learned. Now that I know more I can think of many alternatives, for example to create my my own board from scratch with a socket for the Arduino and put it all in a mint tin, or simply mounting the unit I have on a board and building around it. But using a shield did save me some grief - at one point, I zapped one of my boards with direct 12v power. Had I soldered it in place I would have had to rework a lot of pins.

This was a little expensive - the power supply and SSRs are overkill - but it was fun to make it so small. [edit] I forgot to mention that using the SSRs meant I did not have to put a diode (snubber) to protect the circuit from the relay coil. [/edit]

Here are the shield and the Arduino:
1_PWC_overview.JPG
1_PWC_overview.JPG (112.36 KiB) Viewed 885 times


Looking at the bottom of the shield, one of my lessons learned can be seen: DON'T draw the circuit with a Sharpie on the solder side. The ink makes the solder float!
3_PWC_bottom.JPG
3_PWC_bottom.JPG (110.43 KiB) Viewed 885 times
Last edited by ModemJunki on Sat Sep 07, 2013 11:36 pm, edited 2 times in total.
ModemJunki
 
Posts: 25
Joined: Sun Feb 17, 2013 10:52 pm

Re: My first Arduino is for heating my posterior.

by ModemJunki on Sat Sep 07, 2013 11:10 pm

Here you can see the last-minute add on (rework) of an LED - not really very elegant but it works.
2_PWC_top.JPG
2_PWC_top.JPG (108.47 KiB) Viewed 885 times


Assembled ....
4_PWC_assembled.JPG
4_PWC_assembled.JPG (98.55 KiB) Viewed 885 times


Installed in the "chassis" ....
5_PWC_chassis.JPG
5_PWC_chassis.JPG (80.6 KiB) Viewed 885 times
ModemJunki
 
Posts: 25
Joined: Sun Feb 17, 2013 10:52 pm

Re: My first Arduino is for heating my posterior.

by ModemJunki on Sat Sep 07, 2013 11:21 pm

And finally we have the unit installed in the car and the source code which was put into production. The dangling wires on the right are slack for pulling the unit out, they were tucked away after the picture was taken.

The unit is connected to switched power so that it works either from the remote start or if I "key on" the ignition. I may switch it to be powered by the remote start unit instead, since there is a delay and if my posterior is cold I would jab the defrost and heater switches immediately with my finger. :mrgreen:
6_PWC_installed.JPG
6_PWC_installed.JPG (104.45 KiB) Viewed 885 times


Code: Select all | TOGGLE FULL SIZE
// Automotive Posterior Warming Control
// My first Arduino effort
// HM March 2013
// Last update 7-September-2013 (production, installed in car today!)
// This will be used to pulse or latch diode-isolated signal lines to ground
// in an automobile (12 volt) environment.
//
// The signal lines MUST NOT connect to the Arduino ground pins!
// Instead the signal must only pass through the relay contacts!
// Applying 12v to Arduino pins will let the magic smoke out!
// Use a relay (solid-state or otherwise).
//
// The signal lines will trigger onboard defrost and seat heater functions.
//
// I'm doing this because my remote start only has one output and
// I have to use it to power the security key emulator. It has
// the needed temperature circuit but it shares the internal relay
// and output wire with the function that powers the emulator.
// Only one or the other can be selected to run.
// I guess I was a bit too thrifty about buying it. :-|
// Nevertheless, I am determined to put my posterior onto pre-warmed seats.

// Here is what this does
// 1) Waits 20 seconds defined in delayTimer.setTimeout (in setup). During the wait, an LED pulses.
// 2) Evaluates the current temperature at the TMP36.
// 3) Compares the value of the temperature against variable "triggerTemp" using C (centigrade) or F (Farenheit) as defined in variable "CorF".
// 4) If the temperature is higher then the variable, the unit goes to sleep.
// 5) If the temperature is lower than the variable, triggers seat heaters with a simulated button press ("pulse") of 1/2 second.
// 6) If the temperature is lower than the variable, latches the defroster relay for 15 minutes. During the latch time the indicator LED flashes at 1Hz.
// 7) After the latch time period is up the relay is unlatched, the indicator LED is turned on (steady state) to indicate the trigger occurred, and the unit goes to sleep.
//
// For my vehicle, the interfaces being used are essentially 12V logic (soft buttons that ground milliamp current 12 volt signals).

//#define DEBUG 1 // use this (uncomment it) to enable serial monitoring

// both of the timer includes below are downloaded from the Arduino Playground.
// http://playground.arduino.cc/Code/SimpleTimer
// http://playground.arduino.cc/Code/Timer
#include "SimpleTimer.h"; // for the inital delay (wait for vehicle electrical system to stabilize)
#include "Timer.h"; // more timing functions
#include <avr/power.h> // for the sleep function
#include <avr/sleep.h> // for the sleep function

SimpleTimer delayTimer; //reusable delay timer
Timer defrostTimer; // timer function for our defrost pin trigger

int digitalDfrstrRelayPin = 4; // will latch defroster relay
int digitalDrivrHeaterPin = 6; // will pulse existing 12v logic control circuit for drivers side seat heater
int digitalPsngrHeaterPin = 8; // will pulse existing 12v logic control circuit for passenger side seat heater
int digitalIndicator = 13; // we will pulse this to indicate the unit is waiting and turn it on if we triggered the controls (free LED on pin 13!)
int analogTemperaturePin = 7; //the analog pin the TMP36's Vout (sense) pin is connected to

// now lets define our target temperature.
char CorF = 'C'; // C for Centigrade, F for Farenheit
int triggerTemp = 5; // 68 F is 20 C (testing), 41 F is 5 C (production)
//int triggerTemp = 22; // 68 F is 20 C (testing), 41 F is 5 C (debug in cold basement workshop)
// int triggerTemp = 200; // really high values if prototyping without the temperature sensor, use debug serial output to get a value you can use in this case

int dfrstTimerEventLatch; // defrost timer
int dfrstTimerEventIndicate;  // indication timer during defrost
int stopDfrst; // defrost timer stop function
long defrostLatchTimer=900000;  // we will countdown this many milliseconds after latching the defrost relay (15 minutes) - must be "long", too large for "int"!

// variables used to pulse digital pin in the pulse() function
unsigned int i=0;
boolean pulseRise=true;
int pulsePeriod=1000; // this will make the LED pulse at a moderate rate

int triggered=0; // used in the main loop to detect if we triggered the controls
int doSleep=0; // used to put the system into sleep mode later

// setup
void setup() {
   pinMode(digitalDfrstrRelayPin, OUTPUT); // setup digital pin as an output
   pinMode(digitalDrivrHeaterPin, OUTPUT); // setup digital pin as an output
   pinMode(digitalPsngrHeaterPin, OUTPUT); // setup digital pin as an output
   pinMode(digitalIndicator, OUTPUT); // setup digital pin as an output
   pinMode(analogTemperaturePin, INPUT); // setup analog pin as an input for temperature reading

   digitalWrite(digitalIndicator, LOW); // make sure the indication is off, e.g., PIN13 defaults to HIGH

#ifdef DEBUG // if debug is on activate serial monitoring
   Serial.begin(9600);
   Serial.println();
   Serial.println("Automotive Posterior Warming Control serial debug output is started");
   Serial.println();
   Serial.println("We are pulsing the indicator to show that the system is in a wait state.");
   Serial.println();
#endif

   delayTimer.setTimeout(20000, TriggerIfCold); // delay 20 seconds for the car to start and power to be stable
   //delayTimer.setTimeout(5000, TriggerIfCold); // for development timing, long enough to grab temperature probe and warm it up
}

//-------------------------------------------------------------------------------------------------
// main loop
void loop() {

   switch (triggered){
   case 0:
      pulse(); // built-in LED "pulses" while waiting to evaluate temperature
      delayTimer.run();// this is where the startup delay happens
      break;
   case 1:
      // evaluate the temperature and act if it's below the trigger
      defrostTimer.update(); // handle the triggers
      break;
   case 2:
      // nothing to do, we are above the desired ambient temperature, lets go to sleep now
      sleep();
      break;
   }
}

//-------------------------------------------------------------------------------------------------
// Trigger function for sensing
void TriggerIfCold() {
   int Degrees; // local
   Degrees = GetTemp(CorF); //C or F defined in declarations
   if (Degrees <= triggerTemp) { // it's colder then the trigger temp

#ifdef DEBUG // if debug is on we can see this in the serial monitor
      Serial.println("We will pulse the seat heater pins. ");
      Serial.println();         
#endif

      digitalWrite(digitalIndicator, HIGH); // we set the indicator LED on (non-pulsing) to show this
      digitalWrite(digitalDrivrHeaterPin, HIGH); // We trigger (pulse) the driver side pin for half a second
      digitalWrite(digitalPsngrHeaterPin, HIGH); // We trigger (pulse) the passenger side pin for half a second
      delay(500); // the half a second
      digitalWrite(digitalDrivrHeaterPin, LOW); // unlatch the pin
      digitalWrite(digitalPsngrHeaterPin, LOW); // unlatch the pin

#ifdef DEBUG // if debug is on we can see this in the serial monitor
      Serial.println("Now we will turn on the defrost relay pin.");
      Serial.println("While the defrost relay pin is energized, the indicator LED will blink.");
      Serial.println();         
#endif

      // Because I am directly pulling the control for the defrost relay to ground, the button on my dashboard does not light up (dash has it's own internal logic to illuminate the LED).
      dfrstTimerEventIndicate = defrostTimer.oscillate(digitalIndicator, 1000, HIGH); // setup a function in the defrost timer which will slow blink the indicator while defrost ON from the PWC
      dfrstTimerEventLatch = defrostTimer.oscillate(digitalDfrstrRelayPin, defrostLatchTimer, HIGH); // setup the defrost timer for the defrost trigger
      stopDfrst = defrostTimer.after(defrostLatchTimer, stopDefrost); // setup to stop the defrost timer functions

      triggered = 1; // to set the condition for the loop to read
   }
   else if (Degrees > triggerTemp) { // it's above our trigger temp and we won't trigger any pins

#ifdef DEBUG 
      Serial.println("No pins were triggered because the temperature was above the trigger level.");
      Serial.println("The indicator pin will be turned off to indicate the system took no action.");
      Serial.println();
#endif

      digitalWrite(digitalIndicator, LOW);
      triggered = 2;
   } // we are done looking at the temperature
}

//-------------------------------------------------------------------------------------------------
// function to stop the defrost timer functions
void stopDefrost()
{

#ifdef DEBUG 
   Serial.println("Now we will turn off the defrost relay pin.");
   Serial.println("The indicator pin will be illuminated to indicate the system triggered the pin functions.");
   Serial.println();
#endif

   defrostTimer.stop(dfrstTimerEventIndicate);
   defrostTimer.stop(dfrstTimerEventLatch);
   digitalWrite(digitalDfrstrRelayPin, LOW);
   digitalWrite(digitalIndicator, HIGH);   // the relay was triggered, we leave the indicator LED on to show this
   delay(1000);
   sleep();
}

//-------------------------------------------------------------------------------------------------
// Pulse indicator LED, only to show that something is happening for the humans
void pulse() {
   if(i == pulsePeriod)
   {
      i=1;
      pulseRise= !pulseRise;
   }
   if(pulseRise == false)
   {
      digitalWrite(digitalIndicator, LOW);
      delayMicroseconds(i);
      digitalWrite(digitalIndicator, HIGH);
      delayMicroseconds(pulsePeriod-i);
      i=i+1;
   }

   if(pulseRise == true)
   {
      digitalWrite(digitalIndicator, LOW);
      delayMicroseconds(pulsePeriod-i);
      digitalWrite(digitalIndicator, HIGH);
      delayMicroseconds(i);
      i=i+1;
   }
}

//-------------------------------------------------------------------------------------------------
// calculate temperature in degrees F or C
int GetTemp(char CorF){
   float temperature = getVoltage(analogTemperaturePin);          //getting the voltage reading from the temperature sensor
   switch (CorF){ // calculate depending on global variable
   case 'C':
      temperature = (temperature - .5) * 100;                //convert from 10 mv per degree with 500 mV offset to degrees C ((voltage - 500mV) times 100)
      break;
   case 'F':
      temperature = (((temperature - .5) * 100) * 1.8) + 32;   //convert to Farenheit for the backwards places
      break;
   }

#ifdef DEBUG // if debug is on we can see this in the serial monitor
   Serial.print("Detected ambient temperature of ");
   Serial.print(temperature);
   Serial.print(" degrees ");
   Serial.println(CorF);
   Serial.println();
#endif

   return temperature;
}

//-------------------------------------------------------------------------------------------------
// getVoltage() - returns the voltage on the analog input defined by pin
float getVoltage(int pin){
   return (analogRead(pin) * .004882814); //converting from a 0 to 1023 digital range
}

//-------------------------------------------------------------------------------------------------
// "Deep sleep" function, no wake event is implemented in this code!
void sleep(){

#ifdef DEBUG // if debug is on we can see this in the serial monitor
   Serial.println("The system will be set to sleep.");
   Serial.println();         
#endif

   delay(1000);
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   sleep_enable();
   sleep_mode();
}
ModemJunki
 
Posts: 25
Joined: Sun Feb 17, 2013 10:52 pm

Re: My first Arduino is for heating my posterior.

by adafruit_support_bill on Sun Sep 08, 2013 5:56 am

Nice! I like the pill-bottle enclosure.
User avatar
adafruit_support_bill
 
Posts: 28125
Joined: Sat Feb 07, 2009 9:11 am

Re: My first Arduino is for heating my posterior.

by EasternStarGeek on Sun Sep 08, 2013 7:04 am

Congratulations!
Very nicely done all the way round!
EasternStarGeek
 
Posts: 310
Joined: Mon Dec 13, 2010 12:39 pm
Location: Upper Eastern Tennessee