GCC Code Coverage Report


Directory: ./
File: channeloutput/MCP23017.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 54 60 90.0%
Functions: 9 9 100.0%
Branches: 23 52 44.2%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the GPL v2 as described in the
10 * included LICENSE.GPL file.
11 */
12
13 /*
14 * This driver requires a MCP23017 I2C chip with the following connections:
15 *
16 * MCP Pin Connection
17 * ---------- ------------------------
18 * 9 (VDD) - Pi Pin 4 (5V)
19 * 10 (VSS) - Pi Pin 6 (Ground)
20 * 12 (SCL) - Pi Pin 5 (SCL)
21 * 13 (SDA) - Pi Pin 3 (SDA)
22 * 18 (Reset)- Pi Pin 4 (5V)
23 * 15 (A0) - Pi Pin 6 (Ground) (for 0x20 hex, 32 int device ID)
24 * 16 (A1) - Pi Pin 6 (Ground) (this can be configured in channelouputs.json)
25 * 17 (A2) - Pi Pin 6 (Ground) (if more than one MCP23017 is attached)
26 *
27 */
28
29 /*
30 * Sample channeloutputs.json config
31 *
32 * {
33 * "channelOutputs": [
34 * {
35 * "type": "MCP23017",
36 * "enabled": 1,
37 * "deviceID": 32,
38 * "startChannel": 1,
39 * "channelCount": 16
40 * }
41 * ]
42 * }
43 *
44 */
45 #include "fpp-pch.h"
46
47 #include "fpp-json.h"
48
49 #include "../log.h"
50
51 #include "MCP23017.h"
52
53 #define MCP23x17_IOCON 0x0A
54 #define MCP23x17_IODIRA 0x00
55 #define MCP23x17_IODIRB 0x01
56 #define MCP23x17_GPIOA 0x12
57 #define MCP23x17_GPIOB 0x13
58
59 #define IOCON_INIT 0x20
60
61 #define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
62 #define BYTETOBINARY(byte) \
63 (byte & 0x80 ? 1 : 0), \
64 (byte & 0x40 ? 1 : 0), \
65 (byte & 0x20 ? 1 : 0), \
66 (byte & 0x10 ? 1 : 0), \
67 (byte & 0x08 ? 1 : 0), \
68 (byte & 0x04 ? 1 : 0), \
69 (byte & 0x02 ? 1 : 0), \
70 (byte & 0x01 ? 1 : 0)
71
72 #include "Plugin.h"
73 class MCP23017Plugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
74 public:
75 1 MCP23017Plugin() :
76
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
3 FPPPlugins::Plugin("MCP23017") {
77 1 }
78 1 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
79
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 return new MCP23017Output(startChannel, channelCount);
80 }
81 };
82
83 extern "C" {
84 1 FPPPlugins::Plugin* createPlugin() {
85
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 return new MCP23017Plugin();
86 }
87 }
88
89 /*
90 *
91 */
92 1 MCP23017Output::MCP23017Output(unsigned int startChannel, unsigned int channelCount) :
93 ChannelOutput(startChannel, channelCount),
94 1 i2c(nullptr) {
95
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogDebug(VB_CHANNELOUT, "MCP23017Output::MCP23017Output(%u, %u)\n",
96 startChannel, channelCount);
97 1 }
98
99 /*
100 *
101 */
102 4 MCP23017Output::~MCP23017Output() {
103 2 LogDebug(VB_CHANNELOUT, "MCP23017Output::~MCP23017Output()\n");
104
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 if (i2c) {
105
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 delete i2c;
106 }
107 4 }
108
109 /*
110 *
111 */
112 1 int MCP23017Output::Init(Json::Value config) {
113 1 LogDebug(VB_CHANNELOUT, "MCP23017Output::Init(JSON)\n");
114
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (config["deviceID"].isString()) {
116 ✗ m_deviceID = std::atoi(config["deviceID"].asString().c_str());
117 } else {
118 1 m_deviceID = config["deviceID"].asInt();
119 }
120
121
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (m_deviceID < 0x20 || m_deviceID > 0x27) {
122 ✗ LogErr(VB_CHANNELOUT, "Invalid MSCP23017 Address: %X\n", m_deviceID);
123 ✗ return 0;
124 }
125
126 // 16 pins, one channel each; SendData shifts a bit per channel into two
127 // bytes, and past 40 channels the shift count passes the int's width
128
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (m_channelCount > 16) {
129 1 LogWarn(VB_CHANNELOUT, "MCP23017 has 16 pins, using 16 of the %u channels configured\n", m_channelCount);
130 1 m_channelCount = 16;
131 }
132
133
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 i2c = new I2CUtils(1, m_deviceID);
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!i2c->isOk()) {
135 ✗ LogErr(VB_CHANNELOUT, "Error opening I2C device for MCP23017 output\n");
136 ✗ return 0;
137 }
138
139 // Initialize
140 1 i2c->writeByteData(MCP23x17_IOCON, IOCON_INIT);
141
142 // Enable all pins for output
143 1 i2c->writeByteData(MCP23x17_IODIRA, 0b00000000);
144 1 i2c->writeByteData(MCP23x17_IODIRB, 0b00000000);
145
146
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return ChannelOutput::Init(config);
147 }
148
149 /*
150 *
151 */
152 1 int MCP23017Output::Close(void) {
153 1 LogDebug(VB_CHANNELOUT, "MCP23017Output::Close()\n");
154
155 1 return ChannelOutput::Close();
156 }
157
158 /*
159 *
160 */
161 30 int MCP23017Output::SendData(unsigned char* channelData) {
162 30 LogExcess(VB_CHANNELOUT, "MCP23017Output::SendData(%p)\n", channelData);
163
164 30 unsigned char* c = channelData;
165 30 int bank = 0;
166 30 int bankbox = 0;
167 30 int byte1 = 0;
168 30 int byte2 = 0;
169 30 int ch = 0;
170
171
2/2
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 30 times.
510 for (int x = 0; ch < m_channelCount; x++, ch++) {
172
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 477 times.
480 if (*(c++)) {
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (x < 8)
174 ✗ byte1 |= 0x1 << x;
175 else
176 3 byte2 |= 0x1 << (x - 8);
177 }
178 }
179
180 30 LogExcess(VB_CHANNELOUT,
181 "Byte1: 0b" BYTETOBINARYPATTERN ", Byte2: 0b" BYTETOBINARYPATTERN "\n",
182 BYTETOBINARY(byte1), BYTETOBINARY(byte2));
183
184 30 i2c->writeByteData(MCP23x17_GPIOA, byte1);
185 30 i2c->writeByteData(MCP23x17_GPIOB, byte2);
186
187 30 return m_channelCount;
188 }
189
190 /*
191 *
192 */
193 1 void MCP23017Output::DumpConfig(void) {
194 1 LogDebug(VB_CHANNELOUT, "MCP23017Output::DumpConfig()\n");
195
196 1 LogDebug(VB_CHANNELOUT, " deviceID: %X\n", m_deviceID);
197
198 1 ChannelOutput::DumpConfig();
199 1 }
200