I've recently started working on a custom spa pool controller, to allow WiFi control of temperatures, blowers, and automatic heating/filtration cycles using an Arduino.

The bulk of the project is done at this stage, but I've run into an issue whilst reverse engineering the existing control panel of the spa to integrate with my custom controller.

The control panel has three LEDs, one bi-colour LED, and a triple 7-segment display, all of which are controlled by two HEF4094B shift registers. One shift register (U2) takes input from my Arduino on the data line, and another (U1) has its data line connected to the serial output of U1.

I took the time to go through each of the parallel outputs on the shift registers, to figure out which outputs controlled which regions of the control panel:

U1 - Data wire connected to U2 serial output
QP0 - Display segment G
QP1 - Display segment C
QP2 - Display DP
QP3 - Display segment D
QP4 - Display segment B
QP5 - Display segment F
QP6 - Display segment E
QP7 - Display segment A

U2 - Data wire connected to Arduino
QP0 - Bi-colour LED (Green)
QP1 - Red LED
QP2 - Yellow LED
QP3 - Bi-colour LED (Red)
QP4 - Green LED
QP5 - Display digit 1 select
QP6 - Display digit 3 select
QP7 - Display digit 2 select

Based on this, I tried a quick test (based on an example found here) to iterate through the outputs on U2, setting an individual bit high every 500 ms:

const byte COL_COUNT = 9;
//array to hold the data
unsigned char sequence[COL_COUNT] = {B00000000, B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
//unsigned char sequence[COL_COUNT] = {1, 2, 4, 8, 16, 32, 64, 128};
//unsigned char sequence[COL_COUNT] = {0x01, 0x02, 0x04, 0x8, 0x10, 0x20, 0x40, 0x80};
//Define which pins will be used for the shift register control
//can be any digital pin on the Arduino
int latchPin = 8;  //Pin connected to STR(pin 1) of HEF4094
int clockPin = 12; //Pin connected to CP(pin 3) of HEF4094
int dataPin = 11;  //Pin connected to D(pin 2) of HEF4094
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
void loop() { 
  for (int col = 0; col < COL_COUNT; col++) {
    digitalWrite(latchPin, HIGH); // Pull latch HIGH to send data
    shiftOut(dataPin, clockPin, MSBFIRST, sequence[col]); // Send the data
    digitalWrite(latchPin, LOW); // Pull latch LOW to stop sending data

The output after running this example was not as I expected. I did expect the 7-segment displays to output garbage, but I would have expected the LEDs to turn on in the order listed above (i.e QP0, QP1, QP2, QP3 etc). This was instead the output I got, with the input binary listed below each state:

each possible output with 9 different binary inputs

This is my first time using shift registers in a project, and I wonder if I am misunderstanding how they work here. If anyone can figure out why the register is outputting multiple bits high at once, and not setting outputs high in any sort of coherent order, your input would be much appreciated!

  • \$\begingroup\$ The Strobe signal should be used as a "latch" (as you define). In most cases you would have the latch signal low (in this case), then serially load the data (to the 4094), then pulse the latch high-low to transfer all the data to the output storage registers. If the latch is high during serial loading the data is passing directly to the output pins as the data shifts in. With the latch low the data in the storage register remains unchanged. The difference of holding the latch high rather than pulsing it may be hard to see with a fast clock, but it may cause flickering on the LEDs or segments. \$\endgroup\$
    – Nedd
    Commented Oct 1, 2022 at 7:29
  • \$\begingroup\$ Note that my comment above is in addition to the answer from Justme. \$\endgroup\$
    – Nedd
    Commented Oct 1, 2022 at 7:38

1 Answer 1


The two 8-bit shift registers are connected in to a chain so as a whole the PCB is really just one 16-bit shift register.

Your code only shifts out 8-bit bytes and thus whatever you send will get shifted from one register to the second without considering what there is.

To update the whole chain of shift registers, you need to shift out two bytes, first the data for U1 and then for U2.

Also it might be that the 7-segments are active low. See how the first segment has all segments on because data is all zeroes, and next two segments have only one segment off at a time, as your data has only one bit on.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.