Custom Fermentation Controller

I have started work on my custom fermentation controller using peltier based thermo-electric elements. If you don’t know what that is, there is a good article available on wikipedia.

The benefit I will find is that for the (hopefully) short durations that the peltier will need to run, it will be more efficient than just running a fridge through an ATC. The only moving parts will be two cheap computer fans, so it should be cheaper to maintain should something break. It also means that the unit can be built to my exact requirements, which is always a bonus.

The unit it’s self will be a double ply wooden box with a full front door for inserting/removing the fermenting vessel. The box will most likely be made from 2×4′s, plywood (probably OSB on the inside), and a load of insulation. My basic idea is to build a frame out of 2×4′s, fit the internal paneling; fill the crevice with fiberglass, rockwool or polystyrene, and then fit the outer panels. The inside will most likely be sealed with a wood sealant, and then have the joints smeared with caulk, and then coated with a plastic resin coating. This will allow for restricted movement of air (like a fridge) and for waterproofing.

Electronically, the peltier element can both heat and cool by reversing the polarity on the cables. I have purchased almost all of the electronics for this (and some nice new tools :) ) and I’m just waiting on delivery. In it’s most basic sense, the peltier will be in one of three states, heating, cooling, or off. This will be accomplished using a H-Bridge setup built from FET’s (think of them as switches). If you would like more information on H-Bridges you can find some good information here.

The unit will also have a 4 digit LED 7 Segment display, a few LED’s, a few buttons for configuration, a buzzer for an alarm, and will probably be powered from a hacked computer PSU.

I have some of the parts I need here, and I have begun development and testing on them. Unfortunately, I don’t have the peltiers themselves, or the heatsinks for them, or the display, I do have a temperature sensor, but it is not the correct one. As such, I have been using a serial console and some LED’s to monitor the states.

Within the software, I have it set up such that a minimum and a maximum temperature can be specified. In addition to this, an alarm temperature can also be specified, in such that if the current temperature exceeds the minimum or the maximum by this value, an alarm sounds.

I think I’m babbling a little now….

Here’s what it does so far:
The unit powers up, and retrieves the user specified minimum, maximum and differential temperatures from memory (EEPROM).
The unit then measures the current temperature, and will activate the respective heating or cooling logic based on the current temperature (or do nothing if it is within bounds).
If the temperature is above or below the min/max temperature by the differential, it will sound off 3 beeps and continue to take corrective measures.
If the differential is set to 0, it will not sound the alarm.

There is also functionality to store the users parameters persistently between sessions.

Here’s what I have to make it do:
Display the current temperature on the display (which I don’t have yet).
Provide a configuration menu for the user to input the min/max/dif temperatures.
Provide a configuration menu for the user to input the cycle time and jitter time (explained below).
Port it from the development platform (Arduino) to a bog-standard microcontroller (PIC or AVR, or MSP430).
Design & Test the H-Bridge Circuitry.
Get some circuit boards made :)

The cycle time is the delay the system pauses for at the end of each cycle, realistically this code could run thousands of times a second, and there is no need. So rather than do that and waste power, the circuit will sample every X number of seconds. (Currently hard set to 1 second)

The jitter time (or dampening time for you engineers!) is when the system changes state from heating to idle, idle to heating, idle to cooling, or cooling to idle. Ideally, this delay should be slightly longer than the normal cycle time to allow for some measurable change to take effect. This will also cut out on a temperature being a point of a degree off, activating another state, then deactivating the state almost straight away, and ending up being a point of a degree out again a second later. (Currently hard set to 5 seconds).

Are there any other features that you think I should implement? It will be easier to do now than when I have it all done and have to rework code to add a new feature…

The Arduino Brain Machine

First off, if this thing messes up your brain, its your own fault and not my problem.

Getting back to my late night hackery, I was kinda bored last night and unable to sleep; but I was previously reading about brain machines (Thanks Hack A Day), they seem to be getting quite popular all of a sudden. So I decided to try my hand at it and I built a very basic pair of glasses, and a little bit of Arduino code to pulse the lights at a specific frequency. I did like the affect it had, it was very relaxing; which is strange to say the least as your having bright lights flashing in your eyes… Anyways, I then remembered Mitch Altman’s brain machine based on a POV kit from LadaAda and set out to find the source code for it. I was not disappointed, and immediately began porting it over to the Arduino. Now, keep in mind this was done at 3am with NO CAFFEINE and a bit of sleep deprivation thrown in so there probably will be bugs and discrepancies. The things I know of so far are:

  • No Button to start the cycle
  • Doesnt stop (or maybe it does and I just didn’t give it long enough)
  • Frequencies are not exactly the same (But I think they were a few points of a Hz off due to the timing of the AVR)

I have not tested the audio bit yet as I was too lazy to fire up the soldering iron again to build the audio filter circuit.

int leftEye = 13;
int rightEye = 12;
int leftEar = 11;
int rightEar = 10;
 
char brainType[] = {
  'b',
  'a',
  'b',
  'a',
  'b',
  'a',
  'b',
  'a',
  'b',
  'a',
  't',
  'a',
  't',
  'a',
  't',
  'a',
  't',
  'a',
  'b',
  'a',
  't',
  'd',
  't',
  'd',
  't',
  'd',
  't',
  'a',
  'b',
  'a',
  't',
  'a',
  'b',
  'a',
  'b',
  'a',
  'b',
  'a',
  'b',
  'a',
  'b',
  'a',
  'b',
  '0'
};
unsigned long int brainDuration[] = {
  600000 ,
  100000 ,
  200000 ,
  150000 ,
  150000 ,
  200000 ,
  100000 ,
  300000 ,
  50000 ,
  600000 ,
  100000 ,
  300000 ,
  200000 ,
  300000 ,
  300000 ,
  150000 ,
  600000 ,
  150000 ,
  10000 ,
  150000 ,
  600000 ,
  10000 ,
  100000 ,
  10000 ,
  100000 ,
  10000 ,
  300000 ,
  150000 ,
  10000 ,
  150000 ,
  300000 ,
  150000 ,
  10000 ,
  200000 ,
  50000 ,
  200000 ,
  150000 ,
  150000 ,
  200000 ,
  100000 ,
  250000 ,
  50000 ,
  600000
};
 
void delay_one_tenth_ms(unsigned long int ms) {
  delayMicroseconds(ms*100);
}
 
void blink_LEDs(int duration, int onTime, int offTime) {
  for(int i=0; i<(duration/(onTime+offTime)); i++) {
    digitalWrite(leftEye, HIGH);
    digitalWrite(rightEye, HIGH);
    delay_one_tenth_ms(onTime);
    digitalWrite(leftEye, LOW);
    digitalWrite(rightEye, LOW);
    delay_one_tenth_ms(offTime);
  }
}
 
void do_brainwave_element(int index) {
  if(brainType[index] == 'b') {
    blink_LEDs(brainDuration[index], 347, 347);
    tone(leftEar, 14);
  }
 
  else if (brainType[index] == 'a') {
    blink_LEDs(brainDuration[index], 451, 451);
    tone(leftEar, 11);
  }
 
  else if (brainType[index] == 't') {
    blink_LEDs(brainDuration[index], 835, 835);
    tone(leftEar, 6);
  }
 
  else if (brainType[index] == 'd') {
    blink_LEDs(brainDuration[index], 2253, 2253);
    tone(leftEar, 2);
  }
 
  else {
    return;
  }
}
 
void setup() {
  pinMode(leftEye, OUTPUT);
  pinMode(rightEye, OUTPUT);
}
 
void loop() {
  tone(rightEar, 400);
  int j=0;
  while(brainType[j] != 0) {
    do_brainwave_element(j);
    noTone(leftEar);
    j++;
  }
  noTone(rightEar);
}

Now, I didn’t bother trying to figure out why the Arduino IDE was fighting with me over the struct, so I just made two indexed arrays instead (remember, no caffeine). Also, this code is not complete. It is all based on Mitch Altman’s brain machine code, posted below.

/*
Sound & Light Machine
Firmware
for use with ATtiny2313
Make Magazine issue #10
Mitch Altman
19-Mar-07
*/
 
#include              // this contains all the IO port definitions
#include       // definitions for interrupts
#include           // definitions for power-down modes
#include        // definitions or keeping constants in program memory
 
#define TIMER0_PRESCALE_1 1
#define TIMER0_PRESCALE_8 2
#define TIMER0_PRESCALE_64 3
#define TIMER0_PRESCALE_256 4
#define TIMER0_PRESCALE_1024 5
#define TIMER1_PRESCALE_1 1
#define TIMER1_PRESCALE_8 2
#define TIMER1_PRESCALE_64 3
#define TIMER1_PRESCALE_256 4
#define TIMER1_PRESCALE_1024 5
 
/*
The hardware for this project is very simple:
     ATtiny2313 has 20 pins:
       pin 1   connects to serial port programming circuitry
       pin 10  ground
       pin 12  PB0 - Left eye LED1
       pin 13  PB1 - Right eye LED1
       pin 14  OC0A - Left ear speaker (base-frequency)
       pin 15  OC1A - Right ear speaker (Offset Frequencies for binaural beats)
       pin 17  connects to serial port programming circuitry
       pin 18  connects to serial port programming circuitry
       pin 19  connects to serial port programming circuitry
       pin 20  +3v
    All other pins are unused
 
    This firmware requires that the clock frequency of the ATtiny
      is the default that it is shipped with:  8.0MHz
*/
 
/*
The C compiler creates code that will transfer all constants into RAM when the microcontroller
resets.  Since this firmware has a table (brainwaveTab) that is too large to transfer into RAM,
the C compiler needs to be told to keep it in program memory space.  This is accomplished by
the macro PROGMEM (this is used, below, in the definition for the brainwaveTab).  Since the
C compiler assumes that constants are in RAM, rather than in program memory, when accessing
the brainwaveTab, we need to use the pgm_read_byte() and pgm_read_dword() macros, and we need
to use the brainwveTab as an address, i.e., precede it with "&".  For example, to access
brainwaveTab[3].bwType, which is a byte, this is how to do it:
     pgm_read_byte( &brainwaveTab[3].bwType );
And to access brainwaveTab[3].bwDuration, which is a double-word, this is how to do it:
     pgm_read_dword( &brainwaveTab[3].bwDuration );
*/
 
// table of values for meditation
//   start with lots of Beta (awake / conscious)
//   add Alpha (dreamy / trancy to connect with subconscious Theta that'll be coming up)
//   reduce Beta (less conscious)
//   start adding Theta (more subconscious)
//   pulse in some Delta (creativity)
//   and then reverse the above to come up refreshed
struct brainwaveElement {
  char bwType;  // 'a' for Alpha, 'b' for Beta, 't' for Theta, or 'd' for Delta ('0' signifies last entry in table
  unsigned long int bwDuration;  // Duration of this Brainwave Type (divide by 10,000 to get seconds)
} const brainwaveTab[] PROGMEM = {
  { 'b', 600000 },
  { 'a', 100000 },
  { 'b', 200000 },
  { 'a', 150000 },
  { 'b', 150000 },
  { 'a', 200000 },
  { 'b', 100000 },
  { 'a', 300000 },
  { 'b',  50000 },
  { 'a', 600000 },
  { 't', 100000 },
  { 'a', 300000 },
  { 't', 200000 },
  { 'a', 300000 },
  { 't', 300000 },
  { 'a', 150000 },
  { 't', 600000 },
  { 'a', 150000 },
  { 'b',  10000 },
  { 'a', 150000 },
  { 't', 600000 },
  { 'd',  10000 },
  { 't', 100000 },
  { 'd',  10000 },
  { 't', 100000 },
  { 'd',  10000 },
  { 't', 300000 },
  { 'a', 150000 },
  { 'b',  10000 },
  { 'a', 150000 },
  { 't', 300000 },
  { 'a', 150000 },
  { 'b',  10000 },
  { 'a', 200000 },
  { 'b',  50000 },
  { 'a', 200000 },
  { 'b', 150000 },
  { 'a', 150000 },
  { 'b', 200000 },
  { 'a', 100000 },
  { 'b', 250000 },
  { 'a',  50000 },
  { 'b', 600000 },
  { '0',      0 }
};
 
// This function delays the specified number of 1/10 milliseconds
void delay_one_tenth_ms(unsigned long int ms) {
  unsigned long int timer;
  const unsigned long int DelayCount=87;  // this value was determined by trial and error
 
  while (ms != 0) {
    // Toggling PD0 is done here to force the compiler to do this loop, rather than optimize it away
    for (timer=0; timer <= DelayCount; timer++) {PIND |= 0b0000001;};
    ms--;
  }
}
 
// This function blinks the LEDs (connected to PB0, PB1 - for Left eye, Right eye, respectively)
//   at the rate determined by onTime and offTime
//   and keeps them blinking for the Duration specified (Duration given in 1/10 millisecs)
// This function also acts as a delay for the Duration specified
void blink_LEDs( unsigned long int duration, unsigned long int onTime, unsigned long int offTime) {
  for (int i=0; i<(duration/(onTime+offTime)); i++) {     PORTB |= 0b00000011;          // turn on LEDs at PB0, PB1     delay_one_tenth_ms(onTime);   //   for onTime     PORTB &= 0b11111100;          // turn off LEDs at PB0, PB1     delay_one_tenth_ms(offTime);  //   for offTime   } } // This function starts the Offset Frequency audio in the Right ear through output OC1A  (using Timer 1) //   to create a binaural beat (between Left and Right ears) for a Brainwave Element //   (the base-frequency of 400.641Hz is already assumed to be playing in the Left ear before calling this function) //   and blinks the LEDs at the same frequency for the Brainwave Element //   and keeps it going for the Duration specified for the Brainwave Element // The timing for the Right ear is done with 16-bit Timer 1 (set up for CTC Mode, toggling output on each compare) //   Output frequency = Fclk / (2 * Prescale * (1 + OCR1A) ) = 8,000,000 / (2 * (1 + OCR1A) ) void do_brainwave_element(int index) {     char brainChr = pgm_read_byte(&brainwaveTab[index].bwType);     if (brainChr == 'b') {          // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)          // PORTB |= 0b10000000;       // Beta       // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type       //   to Right ear speaker through output OC1A (PB3, pin 15)       OCR1A = 9637;  // T1 generates 415.024Hz, for a binaural beat of 14.4Hz       // delay for the time specified in the table while blinking the LEDs at the correct rate       //   onTime = 34.7ms, offTime = 34.7ms --> 14.4Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 347, 347 );
      return;   // Beta
    }
 
    else if (brainChr == 'a') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b01000000;
      // Alpha
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9714;  // T1 generates 411.734Hz, for a binaural beat of 11.1Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 45.1ms, offTime = 45.0ms --> 11.1Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 451, 450 );
      return;   // Alpha
    }
 
    else if (brainChr == 't') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b00100000;
      // Theta
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9836;  // T1 generates 406.628Hz, for a binaural beat of 6.0Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 83.5ms, offTime = 83.5ms --> 6.0Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 835, 835 );
      return;   // Theta
    }
 
    else if (brainChr == 'd') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b00010000;
      // Delta
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9928;  // T1 generates 402.860Hz, for a binaural beat of 2.2Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 225.3ms, offTime = 225.3ms --> 2.2Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 2253, 2253 );
      return;   // Delta
    }
 
    // this should never be executed, since we catch the end of table in the main loop
    else {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b00000010;
      return;      // end of table
    }
}
 
int main(void) {
 
  TIMSK = 0x00;  // no Timer interrupts enabled
  DDRB = 0xFF;   // set all PortB pins as outputs
  PORTB = 0x00;  // all PORTB output pins Off
 
  // start up Base frequency = 400.641Hz on Left ear speaker through output OC0A (using Timer 0)
  //   8-bit Timer 0 OC0A (PB2, pin 14) is set up for CTC mode, toggling output on each compare
  //   Fclk = Clock = 8MHz
  //   Prescale = 256
  //   OCR0A = 38
  //   F = Fclk / (2 * Prescale * (1 + OCR0A) ) = 400.641Hz
  TCCR0A = 0b01000010;  // COM0A1:0=01 to toggle OC0A on Compare Match
                        // COM0B1:0=00 to disconnect OC0B
                        // bits 3:2 are unused
                        // WGM01:00=10 for CTC Mode (WGM02=0 in TCCR0B)
  TCCR0B = 0b00000100;  // FOC0A=0 (no force compare)
                        // F0C0B=0 (no force compare)
                        // bits 5:4 are unused
                        // WGM2=0 for CTC Mode (WGM01:00=10 in TCCR0A)
                        // CS02:00=100 for divide by 256 prescaler
  OCR0A = 38;  // to output 400.641Hz on OC0A (PB2, pin 14)
 
  // set up T1 to accept Offset Frequencies on Right ear speaker through OC1A (but don't actually start the Timer 1 here)
  //   16-bit Timer 1 OC1A (PB3, pin 15) is set up for CTC mode, toggling output on each compare
  //   Fclk = Clock = 8MHz
  //   Prescale = 1
  //   OCR0A = value for Beta, Alpha, Theta, or Delta (i.e., 9520, 9714, 9836, or 9928)
  //   F = Fclk / (2 * Prescale * (1 + OCR0A) )
  TCCR1A = 0b01000000;  // COM1A1:0=01 to toggle OC1A on Compare Match
                        // COM1B1:0=00 to disconnect OC1B
                        // bits 3:2 are unused
                        // WGM11:10=00 for CTC Mode (WGM13:12=01 in TCCR1B)
  TCCR1B = 0b00001001;  // ICNC1=0 (no Noise Canceller)
                        // ICES1=0 (don't care about Input Capture Edge)
                        // bit 5 is unused
                        // WGM13:12=01 for CTC Mode (WGM11:10=00 in TCCR1A)
                        // CS12:10=001 for divide by 1 prescaler
  TCCR1C = 0b00000000;  // FOC1A=0 (no Force Output Compare for Channel A)
                        // FOC1B=0 (no Force Output Compare for Channel B)
                        // bits 5:0 are unused
 
  // loop through entire Brainwave Table of Brainwave Elements
  //   each Brainwave Element consists of a Brainwave Type (Beta, Alpha, Theta, or Delta) and a Duration
  // Seeing the LEDs blink and hearing the binaural beats for the sequence of Brainwave Elements
  //   synchs up the user's brain to follow the sequence (hopefully it is a useful sequence)
  int j = 0;
  while (pgm_read_byte(&brainwaveTab[j].bwType) != '0') {  // '0' signifies end of table
    do_brainwave_element(j);
    j++;
  }
 
  // Shut down everything and put the CPU to sleep
  TCCR0B &= 0b11111000;  // CS02:CS00=000 to stop Timer0 (turn off audio in Right ear speaker)
  TCCR1B &= 0b11111000;  // CS12:CS10=000 to stop Timer1 (turn off audio in Left ear speaker)
  MCUCR |= 0b00100000;   // SE=1 (bit 5)
  MCUCR |= 0b00010000;   // SM1:0=01 to enable Power Down Sleep Mode (bits 6, 4)
  delay_one_tenth_ms(10000);  // wait 1 second
  PORTB = 0x00;          // turn off all PORTB outputs
  DDRB = 0x00;           // make PORTB all inputs
  sleep_cpu();           // put CPU into Power Down Sleep Mode
}

LED Matrix Displays

*Updated small bug where I forgot to enable the system afterwards…

I’ve managed to pick up a few LED matrix displays from Sure Electronics that use the HT1632 LED Matrix driver, as such, I have written a new library for the Arduino project to make use of the displays, I haven’t done anything in relation to generating characters for them or doing any scrolling, but I can now pretty much call all of the features of the handy driver. There is still some work to do on cleaning up the library and adding some more functionality, but it does indeed work as it should. Here is the source…

/*
 
    Holtech HT1632 Driver Class (Header)
    Values taken from Datasheet...
 
*/
 
#ifndef HT1632_h
#define HT1632_h
 
//Data Mode
#define HT1632_CTL_COMMAND      0x04    //Preceeds all _COMMANDS_ to the system
#define HT1632_CTL_WRITE        0x05    //Write data to the RAM
#define HT1632_CTL_READ         0x06    //Read data from the RAM
 
//Command Mode
#define HT1632_CMD_SYSDIS       0x00    //Turn off system oscillator and LED duty cycle generator
#define HT1632_CMD_SYSEN        0x01    //Turn on system oscillator
#define HT1632_CMD_LEDOFF       0x02    //Turn off LED duty cycle generator
#define HT1632_CMD_LEDON        0x03    //Turn on LED duty cycle generator
#define HT1632_CMD_BLINKOFF     0x08    //Turn off blinking function
#define HT1632_CMD_BLINKON      0x09    //Turn on blinking function
#define HT1632_CMD_SLAVEMODE    0x10    //Set slave mode and clock source from external clock, the system clock input from OSC pin and synchronous signal input from SYN pin
#define HT1632_CMD_RCMASTER     0x18    //Set master mode anc clock source from on-chip RC oscillator, the system clock output to OSC pin and synchronous signal output SYN pin
#define HT1632_CMD_EXTCLK       0x1C    //System clock source, external
#define HT1632_CMD_COM00        0x20    //N-MOS open drain output and 8 COM option
#define HT1632_CMD_COM01        0x24    //N-MOS open drain output and 8 COM option
#define HT1632_CMD_COM10        0x28    //P-MOS open drain output and 8 COM option
#define HT1632_CMD_COM11        0x2C    //P-MOS open drain output and 16 COM option
 
//Command Mode - PWM Settings
#define HT1632_CMD_PWM01        0xA0    //PWM 1/16 Duty
#define HT1632_CMD_PWM02        0xA1    //PWM 2/16 Duty
#define HT1632_CMD_PWM03        0xA2    //PWM 3/16 Duty
#define HT1632_CMD_PWM04        0xA3    //PWM 4/16 Duty
#define HT1632_CMD_PWM05        0xA4    //PWM 5/16 Duty
#define HT1632_CMD_PWM06        0xA5    //PWM 6/16 Duty
#define HT1632_CMD_PWM07        0xA6    //PWM 7/16 Duty
#define HT1632_CMD_PWM08        0xA7    //PWM 8/16 Duty
#define HT1632_CMD_PWM09        0xA8    //PWM 9/16 Duty
#define HT1632_CMD_PWM10        0xA9    //PWM 10/16 Duty
#define HT1632_CMD_PWM11        0xAA    //PWM 11/16 Duty
#define HT1632_CMD_PWM12        0xAB    //PWM 12/16 Duty
#define HT1632_CMD_PWM13        0xAC    //PWM 13/16 Duty
#define HT1632_CMD_PWM14        0xAD    //PWM 14/16 Duty
#define HT1632_CMD_PWM15        0xAE    //PWM 15/16 Duty
#define HT1632_CMD_PWM16        0xAF    //PWM 16/16 Duty
 
#include "wProgram.h"
 
class HT1632 {
    public:
        HT1632(byte data, byte wclock, byte chip0, byte chip1=NULL, byte chip2=NULL, byte chip3=NULL, byte rclock=NULL);
        void ChipSelect(byte chip);
        void ChipRelease(byte chip);
        void SendCommand(byte command, byte chip=0);
        void WriteBits(byte bits, byte mask);
        byte ReadData(byte address, byte chip);
        void Clear(byte chip=0);
        void WriteData(byte address, byte data, byte chip=0);
        void Init(byte chip=0);
        void BlinkMode(bool blink=false, byte chip=0);
 
    private:
        byte _DATA;
        byte _WCLOCK;
        byte _RCLOCK;
        byte _CHIP0;
        byte _CHIP1;
        byte _CHIP2;
        byte _CHIP3;
};
 
#endif
/*
 
    Holtech HT1632 Driver Class
    Some functionality inspired by westfw from this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1225239439
 
*/
 
#include "wProgram.h"
#include "HT1632.h"
 
HT1632::HT1632(byte data, byte wclock, byte chip0, byte chip1, byte chip2, byte chip3, byte rclock) {
    //Set the I/O Directions
    pinMode(data, OUTPUT);
    pinMode(wclock, OUTPUT);
    if(rclock) {
        pinMode(rclock, OUTPUT);
        _RCLOCK = rclock;
    }
 
    pinMode(chip0, OUTPUT);
    digitalWrite(chip0, HIGH);
    _CHIP0 = chip0;
 
    if(chip1) {
        pinMode(chip1, OUTPUT);
        digitalWrite(chip1, HIGH);
        _CHIP1 = chip1;
    }
 
    if(chip2) {
        pinMode(chip2, OUTPUT);
        digitalWrite(chip2, HIGH);
        _CHIP2 = chip2;
    }
 
    if(chip3) {
        pinMode(chip3, OUTPUT);
        digitalWrite(chip3, HIGH);
        _CHIP2 = chip2;
    }
 
    _DATA = data;
    _WCLOCK = wclock;
}
 
void HT1632::ChipSelect(byte chip) {
    switch(chip) {
        case 0:
            digitalWrite(_CHIP0, LOW);
            break;
        case 1:
            digitalWrite(_CHIP1, LOW);
            break;
        case 2:
            digitalWrite(_CHIP2, LOW);
            break;
        case 3:
            digitalWrite(_CHIP3, LOW);
            break;
        }
}
 
void HT1632::ChipRelease(byte chip) {
    switch(chip) {
        case 0:
            digitalWrite(_CHIP0, HIGH);
            break;
        case 1:
            digitalWrite(_CHIP1, HIGH);
            break;
        case 2:
            digitalWrite(_CHIP2, HIGH);
            break;
        case 3:
            digitalWrite(_CHIP3, HIGH);
            break;
    }
}
 
void HT1632::WriteBits(byte bits, byte mask) {
    while (mask) {
        digitalWrite(_WCLOCK, LOW);
        if (bits & mask) {
            digitalWrite(_DATA, HIGH);
        } else {
            digitalWrite(_DATA, LOW);
        }
        digitalWrite(_WCLOCK, HIGH);
        mask >>= 1;
    }
}
 
void HT1632::SendCommand(byte command, byte chip) {
    ChipSelect(chip);
    WriteBits(HT1632_CTL_COMMAND, 1<<2);//3 bit command id
    WriteBits(command, 1<<7);//8 bit command
    WriteBits(0,1);//There's one extra bit in commands that dont matter...
    ChipRelease(chip);
}
 
byte HT1632::ReadData(byte address, byte chip) {
    //Do stuff here to read the data from the chip...
}
 
void HT1632::Clear(byte chip) {
    ChipSelect(chip);
    for(int i=0; i<256; i++) {
        WriteData(i, 0);
    }
    ChipRelease(chip);
}
 
void HT1632::WriteData(byte address, byte data, byte chip) {
    ChipSelect(chip);//Select the chip...
    //Send the WRITE command...
    WriteBits(HT1632_CTL_WRITE, 1<<2);  //3 bit command
    //Send the Address...
    WriteBits(address, 1<<6);   //7 bit address
    //Send the data...
    WriteBits(data, 1<<3); //4 bit data
    ChipRelease(chip);//Release the chip...
}
 
void HT1632::Init(byte chip) {
    SendCommand(HT1632_CMD_SYSDIS); //Disable system
    SendCommand(HT1632_CMD_COM11); //PMOS drivers
    SendCommand(HT1632_CMD_RCMASTER); //Master mode
    SendCommand(HT1632_CMD_SYSEN); //System Enable
    SendCommand(HT1632_CMD_LEDON); //Enable the display
    Clear(chip);
}
 
void HT1632::BlinkMode(bool blink, byte chip) {
    if(blink) {
        SendCommand(HT1632_CMD_BLINKON, chip);
    } else {
        SendCommand(HT1632_CMD_BLINKOFF, chip);
    }
}