How to control many shift register outputs


I thought about making an automated plant waterer using an Attiny85 IC and 2 shift registers (74HC595). With this setup, I can water virtually an unlimited number of plants using just an IC with 5 digital output pins. I understand that I need to use the millis() function because each plant needs to be watered at different rates. Do I need to worry about rollover? One way I thought of going about this is by somehow combining the two sketches (attached below). In the first sketch, I have a problem with making all the digitalWrite functions compatible with the shift registers. How exactly would the programming work? Thanks in advance for everyone’s help.

char pin[5] = {0, 1, 2, 3, 4};                          //Change pins for arduino uno
boolean state[5] = {LOW, LOW, LOW, LOW, LOW};
unsigned long previousMillis[5] = {0, 0, 0, 0, 0};

unsigned long offTime[5] = {2, 7, 7, 7, 4};             //adjust as needed, units in days, unsigned long because of multiplication in functions
unsigned long onTime[5] = {20, 15, 15, 20, 15};         //adjust as needed, units in seconds

void setup() {
  for(char i=0; i<=4; i++) pinMode(pin[i], OUTPUT);

void loop() {

  for(char i=0; i<=4; i++) {
    unsigned long currentMillis = millis();
    offTime[i] = offTime[i]*86400;
    offTime[i] = offTime[i]*1000;
    onTime[i] = onTime[i]*1000;
    if((state[i] == HIGH) && (currentMillis - previousMillis[i] >= onTime[i])) //if ledState is high and time has passed
      state[i] = LOW;                     // Turn it off
      previousMillis[i] = currentMillis;  // Remember the time
      digitalWrite(pin[i], state[i]);     // Update the motor
    else if ((state[i] == LOW) && (currentMillis - previousMillis[i] >= offTime[i]))
    state[i] = HIGH;                      // turn it on
    previousMillis[i] = currentMillis;    // Remember the time
    digitalWrite(pin[i], state[i]);       // Update the motor
boolean array1[8] = {1, 0, 0, 0, 0, 0, 0, 0};     //this sketch cascades a lit LED
boolean array2[8] = {0, 1, 0, 0, 0, 0, 0, 0};     //delete the arrays to conform to different sketches
boolean array3[8] = {0, 0, 1, 0, 0, 0, 0, 0};
boolean array4[8] = {0, 0, 0, 1, 0, 0, 0, 0};
boolean array5[8] = {0, 0, 0, 0, 1, 0, 0, 0};
boolean array6[8] = {0, 0, 0, 0, 0, 1, 0, 0};
boolean array7[8] = {0, 0, 0, 0, 0, 0, 1, 0};
boolean array8[8] = {0, 0, 0, 0, 0, 0, 0, 1};

char dataPin = 2;
char clockPin = 3;
char latchPin = 4;
char clearPin = 5;

void setup() {
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(clearPin, OUTPUT);

  digitalWrite(clearPin, HIGH);   //it only clears if it's 'LOW', otherwise it needs to be 'HIGH'

void loop() {
  for(char i=7; i>=0; i--) {       //use 2 dimensional arrays for 'array1', 'array2', etc?
    if(array1[i] == 0) {
      digitalWrite(clockPin, HIGH);
      delay(10);                  //ppf 1; If it works, shorten to its limit, otherwise lengthen this value
      digitalWrite(clockPin, LOW);
    if(array1[i] == 1) {
      digitalWrite(dataPin, HIGH);
      delay(10);                  //ppf 1
      digitalWrite(clockPin, HIGH);
      digitalWrite(dataPin, LOW);
      digitalWrite(clockPin, LOW);
  Latch();                        //'Clear()' will be used to show 'array2' after this line


void Latch() {
  digitalWrite(latchPin, HIGH);
  delay(10);                      //ppf 1
  digitalWrite(latchPin, LOW);

void Clear() {
  digitalWrite(clearPin, LOW);    //don't change
  delay(10);                      //ppf 1
  digitalWrite(clearPin, HIGH);   //don't change

If you want us to read your code, please post it as per the forum guidelines in the sticky post.

Rollover of millis() occurrs at about 45 to 50 days. If you code correctly, it will not be a problem.

I do not understand why you want to use an attiny85 to drive two 74hc595 shift registers. That is 3 chips instead of one. An atmega328 would be easier, in terms of hardware and software.

However, if you need to use the output pins to drive small solenoid valves or pumps, and the current is not too great, you could use tpic6a595 shift registers instead of 74hc595. The chips can handle higher voltages and currents, so you may not need transistors/MOSFETs/relays to control the pumps/valves.

Sorry about the code; it's my first post.

I have a spare Attiny85 and I only need to use 4 digital output pins. Also, I'm not too familiar with using the atmega328.

Thanks for the advice regarding the tpic6a595 shift registers. I'll consider using them.

You should read the sticky post before you make your first post.

Do you have an Uno or Nano? If so, those use an atmega328, so in fact you are familiar with them. They are just as easy to use as attiny85 but have more pins, so you may not need separate shift registers.

Thanks for posting your code correctly. It is not well written code and contains errors, I think. But I understand what you want to achieve. The code would be easier to understand if you learn about the bitWrite() and shiftOut() functions.

I clicked "how to read this forum" but I guess that wasn't the sticky post you were referring to. Yeah, I should've provided some context with the sketches. Ignore arrays 2-8.

I have an Arduino Uno. I also want to pursue the Attiny approach because I want a general solution to future problems that require a shift register, but I'll use the atmega328 if no solution can be found.

How could I improve my code? Also, 'ppf' in the comments stands for 'potential point of failure'. I use these because if a sketch doesn't work, then I can go back to places where I wasn't too sure about the code working.

Thanks for suggesting the bitWrite() and shiftOut() functions.

Here's another way of visualizing what I'm trying to do: I want to have LEDs flash at different rates. I also want to use an Attiny85 and 2 shift registers because I can have 4 outputs control any number of LEDs. The reason I'm having trouble is because I can't use the traditional digitalWrite() functions in the first sketch (I'm controlling LEDs through the shift register, not using the traditional arduino pin numbers). My proposed solution is to use variables for each LED and assign each of them a boolean value.

So create an array of 2 bytes long for as many time intervals as you have.
At each time interval, shift out the next 2 bytes with the on/off levels needed.
Shifting 1 into an output position turns on the TPIC6x595 output to sink current thru the LED.

I’d go with a larger part, one that supports I2C as well to read an RTC chip.
Expand the array to have a time as well.
Read the time, when it’s >= the next time in the array then shift out the next data.
Update to the next time to compare to.

If you are going to have a real time clock then I would suggest you use a MCP23017 port expander that will give you up to 16 outputs which can use the same I2C bus.
You can use the ATtiny85 for this.

If you do not use a real time clock then the free running oscillator on the AT 85 will drift and you daily times will drift.

I can't use the traditional digitalWrite() functions

What you need to do is to set the state of the bits in the 2 bytes that you are going to output to the shift registers. The easy way to do this is to use the bitWrite() function. You can also do it using bit masking

Set all 16 bits to the value that you want and shift them out. The shift register output pins will then be set to the corresponding state as the bits in the 2 bytes.