Change current code to work with 74HC595

Hi there,

currently, I am working on some code which will later be used to illuminate a model kit.
This Model kit already comes with some light-effects, which I want to rework and change...

I already got some help on the code, since I do have a few very specific cases - and due to the amount of different inputs and outputs, I slowly run out of pins on my Arduino.

Honestly speaking, I am not very familiar with the Arduino - I am still learning some very basic stuff - and my programming skills are also not very good (especially in case of C / C++) - So I really do have my difficulties with all that stuff.

Anyway - to solve the issue with not having enought IO Pins available, I thought, I should give it a try with an 74HC595 ... I think, it should be possible

Since my main code will have some "fading" LEDs, I want to keep the PWM enabled pins for these LEDs on the Arduino - and change all LED, which does not need any PWM functionallity to the 74HC595 IC.

But - again - due to my lack of knowledge, I am struggling with changing my Code (and the classes provided by some others here from the forum) - to work with the IC.

At least, right now, I do not really have any Idea on how to start... :frowning:

Right now, this is some of the code I do have at the moment...
Since I am trying to rewrite the whole project, I decided to start again with a few basics...

/*
 Shift Register with 74HC595 possible
*/

// const byte _srData = 13;            // 74HC595-DS
// const byte _srLatch = 12;           // 74HC595-STCP
// const byte _srClock = 8;            // 74HC595-SHCP
// const byte _srClear;                // 74HC595-MR

// PWM functionallity not required
const byte _torpedolight = 4;       // will be moved to 74HC595-Q4
const byte _positionlight = 8;      // will be moved to 74HC595-Q2
const byte _navigationlight = 12;   // will be moved to 74HC595-Q1
const byte _beaconlight = 13;       // will be moved to 74HC595-Q3

const byte _torpedolightButton = A0;

const unsigned long _torpedolightOnTime = 120;
const unsigned long _torpedolightOffTime = 450;
const unsigned long _navigationlightOnTime = 500;
const unsigned long _navigationlightOffTime = 1000;
const unsigned long _positionlightOnTime = 500;
const unsigned long _positionlightOffTime = 500;
const unsigned long _beaconlightOnTime = 20;
const unsigned long _beaconlightOffTime = 1000;

unsigned long lastNavigationTime;
unsigned long lastPositionTime;

const byte _torpedolightBlinkAmount = 3;

//---------------------------------------------------------------------------------------------------------------------------------
class Flasher {
  protected:
    const byte _ledPin;
    const uint32_t _onPhase;
    const uint32_t _offPhase;
    uint32_t timeMarker = 0;

    enum _step : byte {Start, OnPhase, OffPhase};
    Flasher::_step step = _step::Start;

  public:
    Flasher(const byte ledPin, const unsigned long onPhase, const unsigned long offPhase): _ledPin(ledPin), _onPhase(onPhase), _offPhase(offPhase) {}

    void run(byte & blinkAmount) {
      uint32_t now = millis ();

      switch(step) {
        case _step::Start:
          if(blinkAmount) {
            digitalWrite(_ledPin, HIGH);
            step = _step::OnPhase;
            timeMarker = now;
          }
          break;
        case _step::OnPhase:
          if(now - timeMarker >= _onPhase) {
            digitalWrite (_ledPin, LOW);
            step = _step::OffPhase;
            timeMarker = now;
          }
          break;
        case _step::OffPhase:
          if(now - timeMarker >= _offPhase) {
            if(blinkAmount > 0) {
              blinkAmount --;
              step = _step::Start;
            }
          }
      }
    }

    void init() {
      pinMode(_ledPin, OUTPUT);
    }
};

//---------------------------------------------------------------------------------------------------------------------------------
enum FlashingGroup : byte {TORPEDO, BEACON};

Flasher flashingGroup[] = {
  {_torpedolight, _torpedolightOnTime, _torpedolightOffTime},
  {_beaconlight, _beaconlightOnTime, _beaconlightOffTime},
};

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup()
{
  // Debug - might be disabled if we run out of pins...
  Serial.begin(115200);
  Serial.println(F("Start..."));

  lastNavigationTime = millis();
  lastPositionTime = lastNavigationTime;

// The Pins that will be used by the 74HC595 should be set to OUTPUT
  // pinMode(_srData, OUTPUT);
  // pinMode(_srClock, OUTPUT);
  // pinMode(_srLatch, OUTPUT);

// Current PINs for the LED - set to Output
  pinMode(_srClear, OUTPUT);
  pinMode(_navigationlight, OUTPUT);
  pinMode(_positionlight, OUTPUT);

// The Button that will trigger the "Torpedo Light" needs to be set as an Input...
  pinMode(_torpedolightButton, INPUT_PULLUP);

// Init of the "Flash" Class, used for the Beacon Light and the Torpedo Light...
  for(Flasher &flash : flashingGroup) {
    flash.init();
  }
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void loop()
{
/*
Since the loop() will include much more code later, it should be considered to use as many single functions as possible...
*/

// I do have a single function for all blinking lights... 
  BlinkingLightGroup(1);      // Blinking Beacon Light
  BlinkingLightGroup(2);      // Blinking Torpedo Light
  BlinkingLightGroup(3);      // Blinking Navigation & Position Lights
}

//---------------------------------------------------------------------------------------------------------------------------------
void BlinkingLightGroup(byte type) {
  static byte _beaconflash = 0;
  static byte _torpedoamount = 0;
  switch (type) {
    case 1:                                     // This will set the beacon light to "infinite"
      _beaconflash = 1;
      break;

    case 2:                                     // The Torpedo Light should only be activated if a button is pressed, and then blink three times in a row
      if(!digitalRead(_torpedolightButton))
      {
        _torpedoamount = _torpedolightBlinkAmount;    
      }
      break;

    case 3:                                     // Due to the specific timings between position- and navigation lights, this will no longer be handled by the 'Flasher"-Class.
                                                // Instead, I am using this functionallity - which can either be handled within this case-block, or moved to another function as well in order to get a better readability.
      unsigned long istTime = millis();
      if (istTime - lastNavigationTime >= _navigationlightOnTime && digitalRead(_navigationlight))
      {
        lastNavigationTime += _navigationlightOnTime;
        digitalWrite(_navigationlight, LOW);
      }
      if (istTime - lastNavigationTime >= _navigationlightOffTime && !digitalRead(_navigationlight))
      {
        lastNavigationTime += _navigationlightOffTime;
        digitalWrite(_navigationlight, HIGH);
      }
      if (istTime - lastPositionTime >= _positionlightOnTime && digitalRead(_positionlight))
      {
        lastPositionTime += _positionlightOnTime;
        digitalWrite(_positionlight, LOW);
      }
      if (istTime - lastPositionTime >= _positionlightOffTime && !digitalRead(_positionlight))
      {
        lastPositionTime += _positionlightOffTime;
        digitalWrite(_positionlight, HIGH);
      }
      break;
  }

  flashingGroup[BEACON].run(_beaconflash);
  flashingGroup[TORPEDO].run(_torpedoamount);
}

//---------------------------------------------------------------------------------------------------------------------------------

So, can anyone provide some support, what / where and how I could do the changes to use the 74HC595?

Thank you so much - any help is much appreciated :slight_smile:

Hi,
To be able to help, we need to know your need.
You want to use a 74HC595 with Arduino to control LEDs.
On each 595 you can turn on up to 8 LEDs.
So far so good.
How many 595 will be used in the project?
What is the behavior of these LEDs?
Will they all light up at the same time?
Or will they be lit separately following some procedure?
Describe in detail what your project should look like.

Hi @ruilviana, thanks for your reply.

at the moment, I am not quite sure how many 595 are required at all... but theoretically, it should be OK with having one of them (while using the PWM Pins on the Arduino for other LEDs).

If you have a look to the Source Code which I have attached above, most of your questions should be anwered by the comments I've made in the code.

there are different "rules" that will trigger when a led should start blinking... so the main issue I have at the moment is the logic when / where I need to create the pattern that should be forwarded to the 595...

For changes you have to rewrite an entire shift register. You can reflect that requirement in a ShiftRegister class that accepts changed settings from Flasher::run() and outputs the new register value after all channels have been updated.

Eventually add Flasher (or similar) references to the ShiftRegister class so that it can call the individual run() methods and finally update the register.

ok, so with each "blink" I need to rewrite all outputs on the shift register... which means, that I need to create new patterns every time when one single LED needs to change...

this will become a difficult task for me at the moment, but I think, I'll give it a try... somehow...

No.

For a change you currently have to call run(). So you only have to call all run() methods, remember all changes and afterwards update the shift register.

Hi,
Have you checked the 595 library?

Tom... :grinning: :+1: :coffee: :australia:

Only when want/need to see the changed LEDs. If they were blinking at different rates. For example.

Otherwise, as said, you can make all the changes you want, then ship out the entire set of bits.

I did a similar thing and just for total flexibility wrote code that allowed me to think about the LEDs at a higher level as individual unrelated things, so just wrote the whole thing anytime I did the equivalent of digitalWrite on any of them.

But I had the time to do that: write out the whole thing on any and every change.

In my case it was a number of neopixels used not as a strip but as independent unrelated lamps.

a7

Hi @TomGeorge, thanks for the advice.
I haven't yet checked the Library - but will do so :slight_smile:

@alto777 - yes, there are different patterns set for the LEDs... one should blink for 120ms, then should be off for 450ms (three times, on button push)

Then, another one should blink permanently - on = 20ms, off 1000ms

and then, there are two other group of LEDs which should blink as following:
LED_1 -> On = 500ms Off = 1000ms
LED_2 -> On = 500ms Off = 500ms
so that they should be some kind of "switching" pattern ... LED 2 blinks two times and within the same time, LED 1 should blink one time.

All of this should be done "in parallel" - So it would be required to know the current state of an LED whenever I need to change the state of another one.

Right. That's what software is good at.

Write at some kind of data structure that "knows" all the LED states to change any one of them.

You could use the bits in an int or char variable, or something more elaborate.

Turning on and off an LED from the higher level manipulates that data structure.

Whenever you write, put the whole thing out to the 74HC595. Again andled in the code informing the entire mechaism.

You could be a bit <-- see what I did there? more clever and have that intervening code check to see if your write actually changed the bit, or whatever stands for the state of the LED you wrote to, and make up its own mind (!) whether or not a push to the 74HC595 was necessary.

a7

Take a close look at this.

uint8_t theLEDs;                    // hold state for 8 LEDs on the 74HC595

void myVirtualLED(uint8_t vPin, bool onOff)
{
	if (onOff) {
		theLEDs |= 1 << vPin;		// set bit number vPin 
	}
	else {
		theLEDs &= ~(1 << vPin);	// clear bit number vPin
	}
	
	shipEmOut();					// put theLEDs out to the 74HC595
}

I'm supposed to be working on my taxes. :expressionless:

added: myVirtualLED() is the one place to adjust for how you wired you LEDs, that is whether a HIGH would make the lit or not.

And it coud be more clever as suggested, and then also return a bool to say whether it did, or did not, write to the 74HC595 if that would be of any use. If not, the returned value could just be ignored.

added: I've recently learnt how the use of namespaces might mean you could do this without changing much in the original code by catching digitalWrite() before it went off where it would have gone, and doing its job and never actually calling through to the "real" digitalWrite.

But that's still something I can't just dash off before going to the beach getting back to my taxes.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.