Skip to content

Commit 07507a5

Browse files
committed
Drafted load lib idea
1 parent 43715e4 commit 07507a5

2 files changed

Lines changed: 264 additions & 0 deletions

File tree

lib/MCP23008T/MCP23008T.hpp

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#pragma once
2+
3+
#include <Adafruit_BusIO_Register.h>
4+
#include <Adafruit_I2CDevice.h>
5+
#include <cstdint>
6+
7+
class MCP23008T {
8+
public:
9+
static constexpr uint16_t REG_IODIR_ADDR = 0x00;
10+
static constexpr uint16_t REG_IPOL_ADDR = 0x01;
11+
static constexpr uint16_t REG_GPINTEN_ADDR = 0x02;
12+
static constexpr uint16_t REG_DEFVAL_ADDR = 0x03;
13+
static constexpr uint16_t REG_INTCON_ADDR = 0x04;
14+
static constexpr uint16_t REG_IOCON_ADDR = 0x05;
15+
static constexpr uint16_t REG_GPPU_ADDR = 0x06;
16+
static constexpr uint16_t REG_INTF_ADDR = 0x07;
17+
static constexpr uint16_t REG_INTCAP_ADDR = 0x08;
18+
static constexpr uint16_t REG_GPIO_ADDR = 0x09;
19+
static constexpr uint16_t REG_OLAT_ADDR = 0x0A;
20+
21+
static constexpr uint8_t ONE_BYTE = 1;
22+
23+
MCP23008T(uint8_t address, TwoWire *wire = &Wire) : device(address, wire) {}
24+
25+
bool begin() {
26+
if (!device.begin()) {
27+
return false;
28+
}
29+
30+
return true;
31+
}
32+
33+
/**
34+
* @details When a bit is set, the corresponding pin becomes an input (1).
35+
* When a bit is clear (0), the corresponding pin becomes an output.
36+
*
37+
* @returns Returns true on success, false otherwise
38+
*/
39+
inline bool setIODir(uint32_t value) { return regIODIR.write(value); }
40+
41+
/**
42+
* @details If a bit is set, the corresponding GPIO register bit will
43+
* reflect the inverted value on the pin.
44+
*
45+
* @returns Returns true on success, false otherwise
46+
*/
47+
inline bool setInputPolarity(uint32_t value) {
48+
return regIPOL.write(value);
49+
}
50+
51+
/**
52+
* @details If a bit is set, the corresponding pin is enabled for
53+
* interrupt-on-change. The DEFVAL and INTCON registers must also be
54+
* configured if any pins are enabled for interrupt-on-change.
55+
*
56+
* @returns Returns true on success, false otherwise
57+
*/
58+
inline bool setIntOnChange(uint32_t value) {
59+
return regGPINTEN.write(value);
60+
}
61+
62+
/**
63+
* @details The default comparison value is configured in the DEFVAL
64+
* register. If enabled (via GPINTEN and INTCON) to compare against the
65+
* DEFVAL register, an opposite value on the associated pin will cause an
66+
* interrupt to occur.
67+
*
68+
* @returns Returns true on success, false otherwise
69+
*/
70+
inline bool setDefaultCompVal(uint32_t value) {
71+
return regDEFVAL.write(value);
72+
}
73+
74+
/**
75+
* @details The INTCON register controls how the associated pin value is
76+
* compared for the interrupt-on-change feature. If a bit is set, the
77+
* corresponding I/O pin is compared against the associated bit in the
78+
* DEFVAL register. If a bit value is clear, the corresponding I/O pin is
79+
* compared against the previous value.
80+
*
81+
* @returns Returns true on success, false otherwise
82+
*/
83+
inline bool setIntOnChange(uint32_t value) {
84+
return regINTCON.write(value);
85+
}
86+
87+
/**
88+
* @details The IOCON register contains several bits for configuring the
89+
* device:
90+
* - The Sequential Operation (SEQOP) controls the incrementing function of
91+
* the Address Pointer. If the Address Pointer is disabled, the Address
92+
* Pointer does not automatically increment after each byte is clocked
93+
* during a serial transfer. This feature is useful when it is desired to
94+
* continuously poll (read) or modify (write) a register. • The Slew Rate
95+
* (DISSLW) bit controls the slew rate function on the SDA pin. If
96+
* enabled, the SDA slew rate will be controlled when driving from a high
97+
* to a low.
98+
* - The Hardware Address Enable (HAEN) control bit enables/disables the
99+
* hardware address pins (A1, A0) on the MCP23S08. This bit is not used on
100+
* the MCP23008. The address pins are always enabled on the MCP23008.
101+
* - The Open-Drain (ODR) control bit enables/disables the INT pin for
102+
* open-drain configuration.
103+
* - The Interrupt Polarity (INTPOL) control bit sets the polarity of the
104+
* INT pin. This bit is functional only when the ODR bit is cleared,
105+
* configuring the INT pin as active push-pull.
106+
* bit 7-6 Unimplemented: Read as ‘0’
107+
* bit 5 SEQOP: Sequential Operation Mode
108+
* 1 = Sequential operation disabled, Address Pointer does not increment
109+
* 0 = Sequential operation enabled, Address Pointer increments
110+
* bit 4 DISSLW: Slew Rate Control Bit for SDA Output
111+
* 1 = Slew rate disabled
112+
* 0 = Slew rate enabled
113+
* bit 3 HAEN: Hardware Address Enable (MCP23S08 only)
114+
* Address pins are always enabled on MCP23008.
115+
* 1 = Enables the MCP23S08 address pins
116+
* 0 = Disables the MCP23S08 address pins
117+
* bit 2 ODR: This bit configures the INT pin as an open-drain output
118+
* 1 = Open-drain output (overrides the INTPOL bit)
119+
* 0 = Active driver output (INTPOL bit sets the polarity)
120+
* bit 1 INTPOL: This bit sets the polarity of the INT output pin
121+
* 1 = Active-high
122+
* 0 = Active-low
123+
* bit 0 Unimplemented: Read as ‘0’
124+
*
125+
* @returns Returns true on success, false otherwise
126+
*/
127+
inline bool setConfig(uint32_t value) { return regIOCON.write(value); }
128+
129+
/**
130+
* @details The GPPU register controls the pull-up resistors for the PORT
131+
* pins. If a bit is set and the corresponding pin is configured as an
132+
* input, the corresponding PORT pin is internally pulled up with a 100 k
133+
* resistor
134+
*
135+
* @returns Returns true on success, false otherwise
136+
*/
137+
inline bool setPullUp(uint32_t value) { return regGPPU.write(value); }
138+
139+
/**
140+
* @details The INTF register reflects the interrupt condition on the
141+
* PORT pins of any pin that is enabled for interrupts via
142+
* the GPINTEN register. A ‘set’ bit indicates that the
143+
* associated pin caused the interrupt.
144+
*
145+
* This register is ‘read-only’. Writes to this register will be
146+
* ignored.
147+
*
148+
* @returns Returns 0xFFFFFFFF on failure, value otherwise
149+
*/
150+
inline uint32_t readIntFlag(void) { return regINTF.read(); }
151+
152+
/**
153+
* @details The INTCAP register captures the GPIO port value at the time the
154+
* interrupt occurred. The register is ‘read-only’ and is updated only when
155+
* an interrupt occurs. The register will remain unchanged until the
156+
* interrupt is cleared via a read of INTCAP or GPIO.
157+
*
158+
* @returns Returns 0xFFFFFFFF on failure, value otherwise
159+
*/
160+
inline uint32_t readIntCap(void) { return regIntCap.read(); }
161+
162+
/**
163+
* @details The GPIO register reflects the value on the port. Reading from
164+
* this register reads the port. Writing to this register modifies the
165+
* Output Latch (OLAT) register.
166+
*
167+
* @returns Returns true on success, false otherwise
168+
*/
169+
inline bool setGPIO(uint32_t value) { return regGPIO.write(value); }
170+
171+
/**
172+
* @details The OLAT register provides access to the output latches. A read
173+
* from this register results in a read of the OLAT and not the port itself.
174+
* A write to this register modifies the output latches that modify the pins
175+
* configured as outputs.
176+
*
177+
* @returns Returns true on success, false otherwise
178+
*/
179+
inline bool setOLAT(uint32_t value) { return regOLAT.write(value); }
180+
181+
private:
182+
Adafruit_I2CDevice device;
183+
184+
Adafruit_BusIO_Register regIODIR = Adafruit_BusIO_Register(
185+
&device, REG_IODIR_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
186+
Adafruit_BusIO_Register regIPOL = Adafruit_BusIO_Register(
187+
&device, REG_IPOL_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
188+
Adafruit_BusIO_Register regGPINTEN = Adafruit_BusIO_Register(
189+
&device, REG_GPINTEN_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
190+
Adafruit_BusIO_Register regDEFVAL = Adafruit_BusIO_Register(
191+
&device, REG_DEFVAL_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
192+
193+
Adafruit_BusIO_Register regINTCON = Adafruit_BusIO_Register(
194+
&device, REG_INTCON_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
195+
Adafruit_BusIO_Register regIOCON = Adafruit_BusIO_Register(
196+
&device, REG_IOCON_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
197+
Adafruit_BusIO_Register regGPPU = Adafruit_BusIO_Register(
198+
&device, REG_GPPU_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
199+
Adafruit_BusIO_Register regINTF = Adafruit_BusIO_Register(
200+
&device, REG_INTF_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
201+
202+
Adafruit_BusIO_Register regIntCap = Adafruit_BusIO_Register(
203+
&device, REG_INTCAP_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
204+
Adafruit_BusIO_Register regGPIO = Adafruit_BusIO_Register(
205+
&device, REG_GPIO_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
206+
Adafruit_BusIO_Register regOLAT = Adafruit_BusIO_Register(
207+
&device, REG_OLAT_ADDR, ONE_BYTE, LSBFIRST, ONE_BYTE);
208+
};

src/main.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,44 @@
77
// #include "2026Core/Net/NetAdapter_A.hpp"
88
#include "esp_log.h"
99
#include <Arduino.h>
10+
#include "MCP23008T.hpp"
1011

1112
/* Config */
1213
static constexpr char *TAG = "LoMa";
14+
static constexpr uint8_t LOAD_ADDR = 0x00; // I2C address
15+
16+
/* Global Objects */
17+
MCP23008T loadDevice(LOAD_ADDR, &Wire);
18+
bool loadConfigured = false;
1319

1420
/* Function Prototypes */
21+
bool configureLoad() {
22+
if (!loadDevice.begin()) {
23+
ESP_LOGE(TAG, "Failed to initialize MCP23008T device at 0x%02X",
24+
LOAD_ADDR);
25+
return false;
26+
}
27+
28+
// First six pins outputs, made last two inputs as that's the default
29+
if (!loadDevice.setIODir(0b1100'0000)) {
30+
ESP_LOGE(TAG, "Failed to set IODIR on MCP23008T at 0x%02X", LOAD_ADDR);
31+
return false;
32+
}
33+
34+
// Disable sequential operation, other settings default
35+
if (!loadDevice.setConfig(0b0010'0000)) {
36+
ESP_LOGW(TAG, "Failed to set IOCON on MCP23008T at 0x%02X", LOAD_ADDR);
37+
}
38+
39+
// Set all outputs low, note that pin 0 is inverted
40+
if (!loadDevice.setGPIO(0b0000'0000)) {
41+
ESP_LOGE(TAG, "Failed to set GPIO on MCP23008T at 0x%02X", LOAD_ADDR);
42+
return false;
43+
}
44+
45+
// Else: success
46+
return true;
47+
}
1548

1649
/**
1750
* MARK: Setup
@@ -20,6 +53,9 @@ static constexpr char *TAG = "LoMa";
2053
void setup() {
2154
pinMode(LED::LED_PIN, OUTPUT);
2255

56+
// Configure Devices
57+
loadConfigured = configureLoad();
58+
2359
// Set up tasks
2460
xTaskCreate(vTaskStatusLED, // Task function
2561
"Status LED", // Name of the task (for debugging)
@@ -28,6 +64,7 @@ void setup() {
2864
1, // Priority of the task
2965
nullptr // Task handle
3066
);
67+
xTaskCreate(vTaskConfigure, "Cfg", 1024, nullptr, 50, nullptr);
3168
}
3269

3370
/**
@@ -46,6 +83,25 @@ void vTaskStatusLED(void *pvParameters) {
4683
}
4784
}
4885

86+
void vTaskConfigure(void *pvParameters) {
87+
while (true) {
88+
if (!loadConfigured) {
89+
if (loadDevice.begin()) {
90+
ESP_LOGI(TAG, "MCP23008T initialized successfully.");
91+
loadConfigured = true;
92+
} else {
93+
ESP_LOGE(
94+
TAG,
95+
"Failed to initialize MCP23008T. Retrying in 5 seconds...");
96+
delay(5000);
97+
}
98+
} else {
99+
// Sleep for 30 seconds
100+
delay(30000);
101+
}
102+
}
103+
}
104+
49105
/**
50106
* @brief Task to handle Telnet connections
51107
*/

0 commit comments

Comments
 (0)