1+ package com .opensourcewithslu .outputdevices ;
2+
3+ import java .io .IOException ;
4+ import java .util .Objects ;
5+
6+ import org .slf4j .Logger ;
7+ import org .slf4j .LoggerFactory ;
8+
9+ import com .pi4j .io .spi .Spi ;
10+
11+ /**
12+ * Helper for controlling a 74HC595 shift register over SPI (Pi4J).
13+ *
14+ * Provides simple operations to write a full byte to the shift register,
15+ * set or clear individual output bits, clear the register,
16+ * and read the last written state. All SPI communication is performed using
17+ * the provided {@link com.pi4j.io.spi.Spi} instance.
18+ *
19+ * The helper maintains an internal state byte reflecting the last value
20+ * written to the device.
21+ *
22+ */
23+ public class ShiftRegister74HC595Helper {
24+
25+ private static final Logger log = LoggerFactory .getLogger (ShiftRegister74HC595Helper .class );
26+ private final Spi spi ;
27+ private byte state = 0x00 ;
28+
29+ /**
30+ * Constructs a new ShiftRegister74HC595Helper with the specified SPI interface.
31+ *
32+ * Initializes the helper with a clean state (0x00) and sets up logging.
33+ *
34+ *
35+ * @param spi the SPI interface to use for communication with the shift register
36+ * @throws NullPointerException if spi is null
37+ */
38+ public ShiftRegister74HC595Helper (Spi spi ) {
39+ this .spi = Objects .requireNonNull (spi , "spi must not be null" );
40+ log .info ("74HC595 helper initialized; state=0x00" );
41+ }
42+
43+ /**
44+ * Shifts out a byte value to the 74HC595 shift register via SPI.
45+ *
46+ * This method writes the specified byte value to the shift register and
47+ * updates the internal state tracking. The operation is logged for debugging.
48+ *
49+ * @param value the byte value to shift out to the register
50+ * @throws IOException if the SPI write operation fails
51+ */
52+ public void shiftOut (byte value ) throws IOException {
53+ try {
54+ spi .write (value );
55+ this .state = value ;
56+ log .info ("Shifted out 0x{}" , toHex (this .state ));
57+ } catch (Exception e ) {
58+ String msg = String .format ("SPI write failed for 0x%s: %s" , toHex (value ), e .getMessage ());
59+ log .error (msg , e );
60+ throw new IOException (msg , e );
61+ }
62+ }
63+
64+ /**
65+ * Clears the shift register by setting all outputs to LOW (0x00).
66+ *
67+ * This is equivalent to calling {@code shiftOut((byte) 0x00)}.
68+ *
69+ * @throws IOException if the SPI write operation fails
70+ */
71+ public void clear () throws IOException {
72+ shiftOut ((byte ) 0x00 );
73+ log .info ("Shift register cleared" );
74+ }
75+
76+ /**
77+ * Sets or clears a specific bit in the shift register.
78+ *
79+ * This method modifies the internal state by setting or clearing the specified
80+ * bit index (0-7), then shifts out the updated state to the register.
81+ *
82+ * @param bitIndex the index of the bit to modify (0-7, where 0 is LSB)
83+ * @param value {@code true} to set the bit to HIGH, {@code false} to set to LOW
84+ * @throws IllegalArgumentException if bitIndex is not in the range 0-7
85+ * @throws IOException if the SPI write operation fails
86+ */
87+ public void setBit (int bitIndex , boolean value ) throws IOException {
88+ if (bitIndex < 0 || bitIndex > 7 ) {
89+ throw new IllegalArgumentException ("bitIndex must be 0..7; got " + bitIndex );
90+ }
91+ if (value ) {
92+ state |= (1 << bitIndex );
93+ } else {
94+ state &= ~(1 << bitIndex );
95+ }
96+ shiftOut (state );
97+ log .info ("Bit {} set to {}" , bitIndex , value );
98+ }
99+
100+ /**
101+ * Clears (sets to LOW) a specific bit in the shift register.
102+ *
103+ * This is a convenience method equivalent to calling {@code setBit(bitIndex, false)}.
104+ *
105+ * @param bitIndex the index of the bit to clear (0-7, where 0 is LSB)
106+ * @throws IllegalArgumentException if bitIndex is not in the range 0-7
107+ * @throws IOException if the SPI write operation fails
108+ */
109+ public void clearBit (int bitIndex ) throws IOException {
110+ setBit (bitIndex , false );
111+ log .info ("Bit {} cleared" , bitIndex );
112+ }
113+
114+ /**
115+ * Returns the current state of the shift register.
116+ *
117+ * This method returns the internally tracked state without performing
118+ * any SPI operations. The state reflects the last value written to
119+ * the shift register.
120+ *
121+ * @return the current state as a byte value
122+ */
123+ public byte getState () {
124+ return state ;
125+ }
126+
127+ /**
128+ * Converts a byte value to its hexadecimal string representation.
129+ *
130+ * This is a utility method used for logging purposes to display
131+ * byte values in a readable hexadecimal format.
132+ *
133+ * @param b the byte value to convert
134+ * @return a two-character uppercase hexadecimal string representation
135+ */
136+ private static String toHex (byte b ) {
137+ return String .format ("%02X" , b & 0xFF );
138+ }
139+ }
0 commit comments