GCC Code Coverage Report


Directory: ./
File: channeloutput/GPIO.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 75 85 88.2%
Functions: 9 9 100.0%
Branches: 66 122 54.1%

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 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16
17 #include "../Warnings.h"
18 #include "../log.h"
19
20 #include "GPIO.h"
21 #include "ChannelOutputSetup.h"
22 #include "../common_mini.h"
23
24 #include "Plugin.h"
25 class GPIOPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
26 public:
27 9 GPIOPlugin() :
28
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
27 FPPPlugins::Plugin("GPIO") {
29 9 }
30 9 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
31
2/6
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
18 return new GPIOOutput(startChannel, channelCount);
32 }
33 };
34
35 extern "C" {
36 9 FPPPlugins::Plugin* createPlugin() {
37
2/6
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
18 return new GPIOPlugin();
38 }
39 }
40
41 /*
42 *
43 */
44 9 GPIOOutput::GPIOOutput(unsigned int startChannel, unsigned int channelCount) :
45 ChannelOutput(startChannel, channelCount),
46 9 m_GPIOPin(nullptr),
47 9 m_invertOutput(0),
48 9 m_pwm(0) {
49
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 LogDebug(VB_CHANNELOUT, "GPIOOutput::GPIOOutput(%u, %u)\n",
50 startChannel, channelCount);
51 9 }
52
53 /*
54 *
55 */
56 36 GPIOOutput::~GPIOOutput() {
57 18 LogDebug(VB_CHANNELOUT, "GPIOOutput::~GPIOOutput()\n");
58 36 }
59 9 int GPIOOutput::Init(Json::Value config) {
60 9 LogDebug(VB_CHANNELOUT, "GPIOOutput::Init()\n");
61
62 9 m_invertOutput = config["invert"].asInt();
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (config.isMember("softPWM")) {
64 ✗ m_pwm = config["softPWM"].asInt();
65 }
66
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (config.isMember("pwm")) {
67 6 m_pwm = config["pwm"].asInt();
68 }
69
70
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 if (config.isMember("pin")) {
71
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 std::string pinName = config["pin"].asString();
72
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 m_GPIOPin = PinCapabilities::getPinByName(pinName).ptr();
73
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if (m_GPIOPin == nullptr) {
74
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 LogErr(VB_CHANNELOUT, "GPIO Pin '%s' not found\n", pinName.c_str());
75
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 AddOutputConfigWarning(51, "GPIO Pin '" + pinName + "' not found");
76 3 return 0;
77 }
78
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 m_usedGPIOS.push_back(pinName);
79
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 } else {
80 2 int gpio = config["gpio"].asInt();
81 2 m_GPIOPin = PinCapabilities::getPinByGPIO(0, gpio).ptr();
82
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (m_GPIOPin == nullptr) {
83 2 LogErr(VB_CHANNELOUT, "GPIO %d not found\n", gpio);
84
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 AddOutputConfigWarning(51, "GPIO " + std::to_string(gpio) + " not found");
85 2 return 0;
86 }
87 ✗ m_usedGPIOS.push_back(m_GPIOPin->name);
88 }
89
90
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (m_pwm) {
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!m_GPIOPin->supportPWM()) {
92 ✗ LogErr(VB_CHANNELOUT, "GPIO Pin does now support PWM\n");
93 ✗ return 0;
94 }
95 1 m_GPIOPin->setupPWM(25500);
96
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (m_invertOutput)
98 ✗ m_GPIOPin->setPWMValue(25500);
99 else
100 1 m_GPIOPin->setPWMValue(0);
101 } else {
102
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
12 m_GPIOPin->configPin("gpio", true);
103
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (m_invertOutput)
105 ✗ m_GPIOPin->setValue(1);
106 else
107 3 m_GPIOPin->setValue(0);
108 }
109
110
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 return ChannelOutput::Init(config);
111 }
112
113 /*
114 *
115 */
116 4 int GPIOOutput::Close(void) {
117 4 LogDebug(VB_CHANNELOUT, "GPIOOutput::Close()\n");
118
119 4 return ChannelOutput::Close();
120 }
121
122 /*
123 *
124 */
125 683 int GPIOOutput::SendData(unsigned char* channelData) {
126
1/2
✓ Branch 0 taken 683 times.
✗ Branch 1 not taken.
683 LogExcess(VB_CHANNELOUT, "GPIOOutput::SendData(%p)\n", channelData);
127
128 683 std::string warning;
129 try {
130
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 615 times.
683 if (m_pwm) {
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (m_invertOutput)
132 ✗ m_GPIOPin->setPWMValue(100 * (int)(255 - channelData[0]));
133 else
134
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 m_GPIOPin->setPWMValue(100 * (int)(channelData[0]));
135 } else {
136
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 615 times.
615 if (m_invertOutput)
137 ✗ m_GPIOPin->setValue(!channelData[0]);
138 else
139
2/2
✓ Branch 0 taken 613 times.
✓ Branch 1 taken 2 times.
615 m_GPIOPin->setValue(channelData[0]);
140 }
141
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 } catch (const std::exception& e) {
142
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6 warning = std::string("Exception in GPIO output: ") + std::string(e.what());
143 2 } catch (...) {
144 ✗ warning = std::string("Exception in GPIO output");
145 ✗ }
146
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 681 times.
683 if (!warning.empty()) {
147 // A timed warning, refreshed while the error keeps happening: it
148 // comes down by itself once the pin works again. lastWarning was
149 // never set before, so the banner was re-added every frame and never
150 // removed. Refreshing at most every 10s keeps this off the frame path.
151
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 uint64_t now = GetTimeMS();
152
5/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (warning != lastWarning || now - lastWarningMS > 10000) {
153
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 WarningHolder::AddWarningTimeout(30, 51, warning);
154
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 lastWarning = warning;
155 1 lastWarningMS = now;
156 }
157 }
158
159 683 return m_channelCount;
160 683 }
161
162 /*
163 *
164 */
165 4 void GPIOOutput::DumpConfig(void) {
166 4 LogDebug(VB_CHANNELOUT, "GPIOOutput::DumpConfig()\n");
167
168
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogDebug(VB_CHANNELOUT, " GPIO Pin : %s\n", m_GPIOPin ? m_GPIOPin->name.c_str() : "");
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 LogDebug(VB_CHANNELOUT, " Inverted Output: %s\n", m_invertOutput ? "Yes" : "No");
170
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 LogDebug(VB_CHANNELOUT, " PWM : %s\n", m_pwm ? "Yes" : "No");
171
172 4 ChannelOutput::DumpConfig();
173 4 }
174