| 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 |