Source Count Item
www.oshpark.com ..... 1 .... Circuit board
www.jameco.com ...... 2 .... Red LED 655nm, 3mm, Jameco #2186603
www.jameco.com ...... 1 .... 10uf 25v Electrolytic Capacitor, #94212
www.jameco.com ...... 1 .... 0.1uf 50v ceramic capacitor, #2162581
www.jameco.com ...... 1 .... LM7805, #51262
www.jameco.com ...... 1 .... 18 pin socket, #112231
www.jameco.com ...... 8 .... 10k 5% 1/4 watt resistors, #691104
www.jameco.com ...... 1 .... straight header*, #103393
www.jameco.com ...... 1 .... jumper*, #112432
www.mouser.com ...... 1 .... RUEF400 resettable fuse, Mouser #650-RUEF400
www.mouser.com ...... 1 .... PIC 16F88 (This must be programmed to work), #579-PIC16F88-I/P
www.mouser.com ...... 1 .... 22k 1% 1/4 watt resistors, #603-MFR-25FRF52-22K
www.mouser.com ...... 1 .... 1N4001 Diode
www.sparkfun.com .... 2 .... 10K Linear Potentiometers
www.sparkfun.com .... 2 .... MOSFET FQP30N06L
www.ebay.com ........ 1 .... Water resistant project box with clear lid
www.ebay.com ........ 6 .... RCA jacks (4 to 8 - 6 fit the box nicely)
www.ebay.com ........ 2 .... Potentiometer Knobs
www.ebay.com ........ 5 .... RCA jack caps (optional to protect and plug unused RCA jacks)
www.ebay.com ........ 1 .... Yellow LED, 3mm, Super Bright (likely any super bright will work)
www.radioshack.com... 1 .... 12v auto power plug with switch and fuse
#include <16F88.h> #device ADC=8 // return ADC values 0-255 #fuses INTRC,NOWDT,PUT,NOBROWNOUT,NOMCLR,NOLVP #use delay(internal=8000000) // 8MHz #define POT1_PIN PIN_A0 // POT 1 #define POT2_PIN PIN_A1 // POT 2 #define BAT_VIN_PIN PIN_A3 // Battery in to check voltage through voltage divider #define POT_JUMPER PIN_A7 // connect to +5v (with a 10K resistor) to use both pots or ground for one #define PWM_PIN PIN_B0 // LED for low battery #define HEATER1_PIN PIN_B1 // MOSFET 1 #define HEATER2_PIN PIN_B2 // MOSFET 2 #define DELAY100 100 // 1/10th of a sec #define DELAY_LEDBLINK 500 // 1/2 second #define POTMIN 0 // minimum return value of the ADC when reading the POT #define POTMAX 255 // maximum return value of the ADC when reading the POT // the voltage divider handles 0-18.5 volts, which should be overkill for any battery // This scales down to 0-5v for the pic to handle (using a 27K and 10K voltage divider) // 11v is 84.6% of 13v (full battery) so 84.6% of 255 (ADC return max) is 215. // This will be calculated in the program so we can enter the voltage here. // #define VIN_MAX 16 // Max volts allowed (should be no more than about 13v from a car battery) #define VIN_WARN 10.5 // low volts to start the LED warning #define VIN_CUTOFF 10.1 // low volts to start the power cutoff #define ADC_MAX 255 #define YELLOW_LED_OFF 0 // 0 (off) - 255 (bright) #define YELLOW_LED_ON 25 // 0 (off) - 255 (bright) Use 250 for a standard brightness LED, 25 for high brightness LED #define ONEPOT 1 // how many pots are in use - depends on POT_JUMPER value #define TWOPOTS 2 #define FULLPULSE 92 // longer number makes for a longer total peak to peak // 93 is about 3 seconds. See below for a full explanation //#define FULLPULSE 153 // longer number makes for a longer total peak to peak // 153 is about 5 seconds. See below for a full explanation //#pragma config CCPMX = RB0 // CCP1 Pin Selection bit (CCP1 function on RB0) #use pwm(CCP1,TIMER=2,FREQUENCY=1000,DUTY=50) // Approximate FULLPULSE value for full pulse seconds. // Each second is about 30.6 (round up or down to whole number) // // SECONDS FULLPULSE SECONDS FULLPULSE // 1 31 | 6 184 // 2 61 | 7 214 // 3 92 | 8 245 // 4 122 | 9 275 // 5 153 | 10 306 // // The interrupt (FULLPULSE) math is: // The timer is incremented at (CLOCK/4)/RTCC_DIV. // The timer is incremented (8000000/4)/256 or 7812.5 times a second. // The interrupt happens every 256 increments. // With these numbers the interrupt happens 7812.5/256 or 30.517578125 times a second. // Multiply that by our FULLPULSE for the actual full pulse time: // 60/30.517578125 = once every 1.96608 us // 1.96608 * 256 = 503 us seconds = 8.388608 seconds // 1.96608 * 153 = 300 us seconds = 5 seconds // 1.96608 * 62 = 121.9 us seconds = ~2 seconds // PIC 16F88 // ------------- // -| A2 A1 |- 10k Pot #2 input // Battery VIN -| A3 A0 |- 10k Pot #1 input // -| A4 A7 |- Pot Count (+5v with 10k resistor=2, GND=1) // RESET -| A5/MCLR A6 |- // GND -| VSS VDD |- +5v // PWM Batt LED -| B0 B7 |- // MOSFET 1 -| B1 B6 |- // MOSFET 2 -| B2 B5 |- // -| B3 B4 |- // -------------
/***************************************************************** Dew Heater Controller code for a PIC 16F88 By Allen Maroney http://www.allenmaroney.com 6/4/2015 SPAC Optical Lab http://www.telescopelab.com PIC 16F88 ------------- -| A2 A1 |- 10k Pot #2 input Battery VIN -| A3 A0 |- 10k Pot #1 input -| A4 A7 |- Pot Count (+5v with 10k resistor=2, GND=1) RESET -| A5/MCLR A6 |- GND -| VSS VDD |- +5v PWM Batt LED -| B0 B7 |- MOSFET 1 -| B1 B6 |- MOSFET 2 -| B2 B5 |- -| B3 B4 |- ------------- *******************************************************************/ #include <Dew Heater Controller - Manual.h> // need these to be global because they are set in main and used in the interrupt int16 width1, width2; int8 potreset; // prototypes void Read_Pot(int8 *pot1, int8 *pot2, int8 potcount); int8 Read_BatteryVoltage(int8 adcwarnval, int8 adcoffval); // ********************* must be before main() #INT_RTCC void tick_interrupt(void) { static int16 loop = 0, pulse1=0, pulse2=0; if(loop++ > FULLPULSE || potreset) { loop = 0; potreset = 0; pulse1 = 0; pulse2 = 0; } // this 'if' uses the pulse length of pot 1 // the >1 makes sure that the lowest setting turns the heater off // at the highest setting there is a 1/250th of a pulse off blink to let you know that everything is working if(pulse1 <= width1 && width1 > 1) { output_high(HEATER1_PIN); pulse1++; } else { output_low(HEATER1_PIN); } // this 'if' uses the pulse length of pot 2 (or the same as pulse 1 if only one pot is set up) // the >1 makes sure that the lowest setting turns the heater off // at the highest setting there is a 1/250th of a pulse off blink to let you know that everything is working if(pulse2 <= width2 && width2 > 1) { output_high(HEATER2_PIN); pulse2++; } else { output_low(HEATER2_PIN); } } void main() { int8 pot1=0, pot2=0, oldpot1=0, oldpot2=0, potcount=ONEPOT; int8 adcwarnval=0, adcoffval=0, poweron=1; width1=0; // set the default of the globals width2=0; potreset=0; set_tris_a(0b10001011); // A0-A1, A3, A7 inputs; A2, A4-A5 output set_tris_b(0b00000000); // B0-B7 all output output_low(HEATER1_PIN); output_low(HEATER2_PIN); delay_ms(DELAY100); // let the internal R/C clock charge up and stabilize // setup ADC ports for the pots on A0 and A1 // The Jumper connects to A2 // The voltage divider feeds A3 setup_adc_ports(sAN0|sAN1|sAN3|VSS_VDD); setup_adc(ADC_CLOCK_INTERNAL); /// see if we are using one pot or two if(input(POT_JUMPER) ) // POT_JUMPER connected to 5v use two pots { // blink each LED back and forth a few time if we are using two pots potcount = TWOPOTS; output_high(HEATER1_PIN); output_low(HEATER2_PIN); delay_ms(DELAY_LEDBLINK); output_low(HEATER1_PIN); output_high(HEATER2_PIN); delay_ms(DELAY_LEDBLINK); output_high(HEATER1_PIN); output_low(HEATER2_PIN); } else // POT_JUMPER connect to GROUND (default) { // blink both LEDs on for a second at the same time if we use one pot potcount=ONEPOT; output_high(HEATER1_PIN); output_high(HEATER2_PIN); delay_ms(DELAY_LEDBLINK); } delay_ms(DELAY_LEDBLINK); output_low(HEATER1_PIN); // turn off the LEDS output_low(HEATER2_PIN); delay_ms(DELAY_LEDBLINK); // calculate the minimum threshold for the low battery LED to start lighting up // we only need to calculate these once adcwarnval = (int8)((double)(ADC_MAX-1) * ((double)VIN_WARN/(double)VIN_MAX)); adcoffval = (int8)((double)(ADC_MAX-1) * ((double)VIN_CUTOFF/(double)VIN_MAX)); //// Setup CCP to PWM Mode setup_ccp1(CCP_PWM); setup_timer_2((Long int)T2_DIV_BY_16,255,1); set_pwm1_duty(YELLOW_LED_ON); // set up the interrupt set_rtcc(0); setup_counters(RTCC_INTERNAL, RTCC_DIV_256); enable_interrupts(INT_RTCC); enable_interrupts(GLOBAL); while(true) { poweron = Read_BatteryVoltage(adcwarnval, adcoffval); if( poweron ) // as long as the battery is > VIN_CUTOFF { Read_Pot(&pot1, &pot2, potcount); // stop jittering if(oldpot1 < pot1-1 || oldpot1 > pot1+1 || oldpot2 < pot2-1 || oldpot2 > pot2+1 ) { width1 = (int16)((float)FULLPULSE * ((float)pot1/(float)POTMAX)); // pot1 values 0-255 width2 = (int16)((float)FULLPULSE * ((float)pot2/(float)POTMAX)); // pot2 values 0-255 oldpot1 = pot1; oldpot2 = pot2; potreset = 1; } } else // low battery turns off the mosfets { width1=0; // turn off power to mosfet 1 width2=0; // turn off power to mosfet 2 if( pot1 > 2) // forces going into the pot if() should the power go back up { oldpot1 = pot1-2; } else { oldpot1 = pot1+2; } } delay_ms(DELAY100); // 1/10th sec } } // ********************* START READ POT void Read_Pot(int8 *pot1, int8 *pot2, int8 potcount) { //////////////// READ 10K POTS ////////////////// Set_adc_channel(0); // Select Analog port connected to pot1 (A0) delay_us(20); *pot1 = read_adc(); // Read value if( *pot1 < POTMIN+1 ) { *pot1 = POTMIN+1; } if( *pot1 > POTMAX-1 ) { *pot1 = POTMAX-1; } if( potcount == TWOPOTS ) { Set_adc_channel(1); // Select Analog port connected to pot2 (A1) delay_us(20); *pot2 = read_adc(); // Read value if( *pot2 < POTMIN+1 ) { *pot2 = POTMIN+1; } if( *pot2 > POTMAX-1 ) { *pot2 = POTMAX-1; } } else { // pot #2 not used so return pot 1's value *pot2 = *pot1; } } // ********************* START READ_BATTERYVOLTAGE int8 Read_BatteryVoltage(int8 adcwarnval, int8 adcoffval) { static int8 vin=ADC_MAX; // start with no warnings static int8 ledon=YELLOW_LED_ON, ledctr=0, lowvoltjitterwarn=0, lowvoltjittercutoff=0; //////////////// BATTERY VOLTAGE IN ////////////////// Set_adc_channel(3); // Select Analog port connected to A3 delay_us(20); if(!input_state(HEATER1_PIN) && !input_state(HEATER2_PIN)) // only update voltage when the heaters are off { vin = read_adc(); // Read value } // once the voltage drops to the warn or cutoff level // this raises the value (if>0) to get back to the higher // state to reduce blinking between states. adcwarnval += lowvoltjitterwarn; adcoffval += lowvoltjittercutoff; if( vin > adcwarnval ) { // all is well, > 11V lowvoltjitterwarn=0; // back to normal for warn lowvoltjittercutoff=0; // back to normal for cutoff set_pwm1_duty(YELLOW_LED_OFF); return(1); // power on to the heaters } else if( vin > adcoffval ) { // low power warning > 10v <= 11v turn yellow LED on solid lowvoltjitterwarn=1; lowvoltjittercutoff=0; // back to normal for cutoff set_pwm1_duty(YELLOW_LED_ON); return(1); // power on to the heaters } else // <= 10v { // cut off power, blink yellow led lowvoltjittercutoff=1; if(ledctr++ > 5 ) // 5 = 1/2 sec blinks (10 = 1 sec blinks) { if(ledon == YELLOW_LED_ON) // if yellow led currently on then turn it off { ledon=YELLOW_LED_OFF; } else { ledon=YELLOW_LED_ON; } set_pwm1_duty(ledon); ledctr=0; } return(0); // power OFF to the heaters } }