diff --git a/IO_DNOU8.h b/IO_DNOU8.h
new file mode 100644
index 0000000..b73f1f0
--- /dev/null
+++ b/IO_DNOU8.h
@@ -0,0 +1,113 @@
+/*
+ * © 2022, Chris Harlow. All rights reserved.
+ *
+ * This file is part of DCC++EX API
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * It is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with CommandStation. If not, see .
+ */
+#ifndef IO_DNOU8_h
+ #define IO_DNOU8_h
+#include
+#include "IODevice.h"
+#define GET_BIT(bit) ((_pinValues[bit/8] >>(bit%8) == 1))
+#define SET_BIT(bit) _pinValues[bit/8] |= 1<<(bit%8)
+#define CLR_BIT(bit) _pinValues[bit/8] &= ~(1<<(bit%8))
+
+class IO_DNOU8 : public IODevice {
+public:
+ static void create(VPIN firstVpin, int nPins, byte latchPin, byte clockPin, byte dataPin )
+ {
+ new IO_DNOU8( firstVpin, nPins, latchPin, clockPin, dataPin);
+ }
+
+protected:
+ IO_DNOU8(VPIN firstVpin, int nPins, byte latchPin, byte clockPin, byte dataPin) :
+ IODevice(firstVpin, nPins) {
+ _latchPin=latchPin;
+ _clockPin=clockPin;
+ _dataPin=dataPin;
+ _nShiftBytes=(nPins+7)/8; // rounded up to multiples of 8 bits
+ _pinValues=(byte*) calloc(_nShiftBytes,1);
+ // Connect to HAL so my _write, _read and _loop will be called as required.
+ IODevice::addDevice(this);
+ }
+
+// Called by HAL to start handling this device
+ void _begin() override {
+ _xmitPending=true; // will cause transmission of all zeros first time out
+ _deviceState = DEVSTATE_NORMAL;
+ pinMode(_latchPin,OUTPUT);
+ pinMode(_clockPin,OUTPUT);
+ pinMode(_dataPin,OUTPUT);
+ _display();
+ }
+
+void _loop(unsigned long currentMicros) override {
+ (void)currentMicros; // Not needed
+
+ if (!_xmitPending) return; // Nothing to do
+
+ #ifdef DIAG_IO
+ // We will create a DIAG on the fly
+ USB_SERIAL.print("<* DNOU8 ");
+ #endif
+
+ // stream out the bitmap (highest pin first)
+ _xmitPending=false;
+ digitalWrite(_latchPin, LOW);
+
+ for (int xmitBit=_nShiftBytes*8 -1; xmitBit>=0; xmitBit--) {
+ bool bit=GET_BIT(xmitBit);
+ #ifdef DIAG_IO
+ USB_SERIAL.print(bit?'1':'0');
+ #endif
+ digitalWrite(_dataPin,bit);
+ digitalWrite(_clockPin,HIGH);
+ digitalWrite(_clockPin,LOW);
+ }
+
+ digitalWrite(_latchPin, HIGH);
+ #ifdef DIAG_IO
+ USB_SERIAL.print(" *>\n");
+ #endif
+ }
+
+ void _write(VPIN vpin, int value) override {
+ int pin = vpin - _firstVpin;
+ bool oldval=GET_BIT(pin);
+ bool newval=value!=0;
+ if (newval==oldval) return; // no change
+ if (newval) SET_BIT(pin);
+ else CLR_BIT(pin);
+ _xmitPending=true; // shift register will be sent on next _loop()
+ }
+
+ int _read(VPIN vpin) override {
+ int pin = vpin - _firstVpin;
+ return GET_BIT(pin);
+ }
+
+ void _display() override {
+ DIAG(F("IO_DNOU8 Configured on VPins:%d-%d"), (int)_firstVpin,
+ (int)_firstVpin+_nPins-1);
+ }
+
+private:
+ bool _xmitPending=false;
+ int _nShiftBytes=0;
+ VPIN _latchPin,_clockPin,_dataPin;
+ byte* _pinValues;
+};
+
+#endif
\ No newline at end of file