| 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 <errno.h> | ||
| 18 | #include <fcntl.h> | ||
| 19 | #include <stdio.h> | ||
| 20 | #include <stdlib.h> | ||
| 21 | #include <string.h> | ||
| 22 | #include <unistd.h> | ||
| 23 | |||
| 24 | #include "../log.h" | ||
| 25 | #include "../settings.h" | ||
| 26 | |||
| 27 | #include "SPInRF24L01.h" | ||
| 28 | #include "ChannelOutputSetup.h" | ||
| 29 | |||
| 30 | #ifdef USENRF | ||
| 31 | #include "RF24.h" | ||
| 32 | #else | ||
| 33 | #include "stdint.h" | ||
| 34 | |||
| 35 | #define rf24_datarate_e int | ||
| 36 | #define RF24_250KBPS 0 | ||
| 37 | #define RF24_1MBPS 1 | ||
| 38 | #define RF24_2MBPS 7 | ||
| 39 | #define RPI_V2_GPIO_P1_15 2 | ||
| 40 | #define RPI_V2_GPIO_P1_24 3 | ||
| 41 | #define RPI_V2_GPIO_P1_26 7 | ||
| 42 | #define BCM2835_SPI_SPEED_8MHZ 4 | ||
| 43 | #define RF24_CRC_16 5 | ||
| 44 | #define RF24_PA_MAX 6 | ||
| 45 | |||
| 46 | class RF24 { | ||
| 47 | public: | ||
| 48 | ✗ | RF24(int, int, int) {} | |
| 49 | ✗ | ~RF24() {} | |
| 50 | ✗ | void begin() {} | |
| 51 | ✗ | void setDataRate(int) {} | |
| 52 | ✗ | int getDataRate(void) { return RF24_250KBPS; } | |
| 53 | ✗ | void setRetries(int, int) {} | |
| 54 | ✗ | void setPayloadSize(int) {} | |
| 55 | ✗ | void setAutoAck(int) {} | |
| 56 | ✗ | void setChannel(int&) {} | |
| 57 | ✗ | void setCRCLength(int) {} | |
| 58 | ✗ | void openWritingPipe(const uint64_t&) {} | |
| 59 | ✗ | void openReadingPipe(int, const uint64_t&) {} | |
| 60 | ✗ | void setPALevel(int) {} | |
| 61 | ✗ | void flush_tx() {} | |
| 62 | ✗ | void powerUp() {} | |
| 63 | ✗ | void printDetails() {} | |
| 64 | ✗ | void write(char*, int) {} | |
| 65 | }; | ||
| 66 | |||
| 67 | #endif | ||
| 68 | |||
| 69 | //TODO: Check max channels | ||
| 70 | #define SPINRF24L01_MAX_CHANNELS 512 | ||
| 71 | const uint64_t pipes[2] = { | ||
| 72 | 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL | ||
| 73 | }; | ||
| 74 | |||
| 75 | #include "Plugin.h" | ||
| 76 | class SPInRF24L01Plugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin { | ||
| 77 | public: | ||
| 78 | 1 | SPInRF24L01Plugin() : | |
| 79 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
3 | FPPPlugins::Plugin("SPInRF24L01") { |
| 80 | 1 | } | |
| 81 | 1 | virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override { | |
| 82 |
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 SPInRF24L01Output(startChannel, channelCount); |
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | extern "C" { | ||
| 87 | 1 | FPPPlugins::Plugin* createPlugin() { | |
| 88 |
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 SPInRF24L01Plugin(); |
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | ///////////////////////////////////////////////////////////////////////////// | ||
| 93 | |||
| 94 | class SPInRF24L01PrivData { | ||
| 95 | public: | ||
| 96 | ✗ | SPInRF24L01PrivData() : | |
| 97 | ✗ | radio(nullptr), | |
| 98 | ✗ | speed(RF24_250KBPS), | |
| 99 | ✗ | channel(0) {} | |
| 100 | RF24* radio; | ||
| 101 | rf24_datarate_e speed; | ||
| 102 | int channel; | ||
| 103 | }; | ||
| 104 | |||
| 105 | ///////////////////////////////////////////////////////////////////////////// | ||
| 106 | |||
| 107 | 1 | SPInRF24L01Output::SPInRF24L01Output(unsigned int startChannel, unsigned int channelCount) : | |
| 108 | ChannelOutput(startChannel, channelCount), | ||
| 109 | 1 | data(nullptr) { | |
| 110 | 1 | } | |
| 111 | 4 | SPInRF24L01Output::~SPInRF24L01Output() { | |
| 112 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | if (data) { |
| 113 | ✗ | delete data; | |
| 114 | } | ||
| 115 | 4 | } | |
| 116 | |||
| 117 | 1 | int SPInRF24L01Output::Init(Json::Value config) { | |
| 118 | // Deprecated in FPP 10, scheduled for removal in FPP 11 (GitHub #2937) | ||
| 119 | 1 | LogWarn(VB_CHANNELOUT, "SPI-nRF24L01 output is deprecated and will be removed in FPP 11\n"); | |
| 120 | |||
| 121 | 1 | int chanNum = -1; | |
| 122 | 1 | rf24_datarate_e ss = RF24_250KBPS; | |
| 123 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (config.isMember("speed")) { |
| 124 | 1 | int i = config["speed"].asInt(); | |
| 125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (i == 0) { |
| 126 | ✗ | ss = RF24_250KBPS; | |
| 127 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | } else if (i == 1) { |
| 128 | ✗ | ss = RF24_1MBPS; | |
| 129 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | } else if (i == 2) { |
| 130 | ✗ | ss = RF24_2MBPS; | |
| 131 | } else { | ||
| 132 | 1 | LogErr(VB_CHANNELOUT, "Invalid speed %d from config\n", i); | |
| 133 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | AddOutputConfigWarning(40, "nRF24 radio output: invalid speed in configuration"); |
| 134 | 1 | return 0; | |
| 135 | } | ||
| 136 | } | ||
| 137 | ✗ | if (config.isMember("channel")) { | |
| 138 | ✗ | chanNum = config["channel"].asInt(); | |
| 139 | } | ||
| 140 | |||
| 141 | ✗ | if (chanNum > 83 && chanNum < 101) { | |
| 142 | ✗ | LogWarn(VB_CHANNELOUT, "FCC RESTRICTED FREQUENCY RANGE OF %dMHz\n", 2400 + chanNum); | |
| 143 | } | ||
| 144 | |||
| 145 | ✗ | if (chanNum < 0 || chanNum > 125) { | |
| 146 | ✗ | LogErr(VB_CHANNELOUT, "Invalid channel '%d'\n", chanNum); | |
| 147 | ✗ | AddOutputConfigWarning(40, "nRF24 radio output: invalid channel " + std::to_string(chanNum) + " (must be 0-125)"); | |
| 148 | ✗ | return 0; | |
| 149 | } | ||
| 150 | |||
| 151 | ✗ | RF24* radio = new RF24(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); | |
| 152 | ✗ | if (!radio) { | |
| 153 | ✗ | LogErr(VB_CHANNELOUT, "Failed to create our radio instance, unable to continue!\n"); | |
| 154 | ✗ | AddOutputConfigWarning(40, "nRF24 radio output: could not create radio instance"); | |
| 155 | ✗ | return 0; | |
| 156 | } | ||
| 157 | ✗ | data = new SPInRF24L01PrivData(); | |
| 158 | ✗ | data->channel = chanNum; | |
| 159 | ✗ | data->speed = ss; | |
| 160 | |||
| 161 | // This section of code was basically lifted from the initialization in | ||
| 162 | // Komby's RF1 library. This was tested on a Pi using a similar version | ||
| 163 | // of the RF24 library and works, so no further investigation to whether | ||
| 164 | // or not all of these are required was done. | ||
| 165 | ✗ | radio->begin(); | |
| 166 | |||
| 167 | // Attempt to detect whether or not the radio is present: | ||
| 168 | ✗ | radio->setDataRate(RF24_250KBPS); | |
| 169 | ✗ | if (radio->getDataRate() != RF24_250KBPS) { | |
| 170 | ✗ | LogErr(VB_CHANNELOUT, "Failed to detect nRF Radio by setting speed to 250k!\n"); | |
| 171 | ✗ | AddOutputConfigWarning(40, "nRF24 radio output: radio not detected (check wiring/SPI)"); | |
| 172 | ✗ | delete radio; | |
| 173 | ✗ | delete data; | |
| 174 | ✗ | data = nullptr; | |
| 175 | ✗ | return 0; | |
| 176 | } | ||
| 177 | |||
| 178 | ✗ | radio->setDataRate(data->speed); | |
| 179 | ✗ | radio->setRetries(0, 0); | |
| 180 | ✗ | radio->setPayloadSize(32); | |
| 181 | ✗ | radio->setAutoAck(0); | |
| 182 | ✗ | radio->setChannel(data->channel); | |
| 183 | ✗ | radio->setCRCLength(RF24_CRC_16); | |
| 184 | |||
| 185 | ✗ | radio->openWritingPipe(pipes[0]); | |
| 186 | ✗ | radio->openReadingPipe(1, pipes[1]); | |
| 187 | ✗ | radio->setPALevel(RF24_PA_MAX); | |
| 188 | |||
| 189 | ✗ | radio->flush_tx(); | |
| 190 | ✗ | radio->powerUp(); | |
| 191 | |||
| 192 | ✗ | data->radio = radio; | |
| 193 | |||
| 194 | ✗ | if (WillLog(LOG_DEBUG, VB_CHANNELOUT)) { | |
| 195 | ✗ | data->radio->printDetails(); | |
| 196 | } | ||
| 197 | |||
| 198 | ✗ | return ChannelOutput::Init(config); | |
| 199 | } | ||
| 200 | |||
| 201 | ✗ | int SPInRF24L01Output::Close(void) { | |
| 202 | ✗ | LogDebug(VB_CHANNELOUT, "SPInRF24L01Output::Close\n"); | |
| 203 | |||
| 204 | ✗ | delete data->radio; | |
| 205 | ✗ | data->radio = NULL; | |
| 206 | ✗ | return ChannelOutput::Close(); | |
| 207 | } | ||
| 208 | |||
| 209 | ✗ | int SPInRF24L01Output::SendData(unsigned char* channelData) { | |
| 210 | char packet[32]; | ||
| 211 | ✗ | int i = 0; | |
| 212 | ✗ | RF24* radio = data->radio; | |
| 213 | |||
| 214 | ✗ | for (i = 0; i * 30 < m_channelCount; i++) { | |
| 215 | ✗ | LogDebug(VB_CHANNELOUT, | |
| 216 | "Shipping off packet %d (offset: %d/%d, size: %d)\n", | ||
| 217 | i, i * 30, m_channelCount, | ||
| 218 | ((i * 30) + 30 > m_channelCount ? m_channelCount - i * 30 : 30)); | ||
| 219 | |||
| 220 | ✗ | bzero(packet, sizeof(packet)); | |
| 221 | |||
| 222 | ✗ | memcpy(packet, channelData + (i * 30), ((i * 30) + 30 > m_channelCount ? m_channelCount - i * 30 : 30)); | |
| 223 | ✗ | packet[30] = i; | |
| 224 | |||
| 225 | ✗ | radio->write(packet, 32); | |
| 226 | } | ||
| 227 | ✗ | return m_channelCount; | |
| 228 | } | ||
| 229 | |||
| 230 | ✗ | void SPInRF24L01Output::DumpConfig(void) { | |
| 231 | ✗ | ChannelOutput::DumpConfig(); | |
| 232 | ✗ | if (data) { | |
| 233 | ✗ | if (data->speed == RF24_2MBPS) | |
| 234 | ✗ | LogDebug(VB_CHANNELOUT, " speed : 2MBPS\n"); | |
| 235 | ✗ | else if (data->speed == RF24_1MBPS) | |
| 236 | ✗ | LogDebug(VB_CHANNELOUT, " speed : 1MBPS\n"); | |
| 237 | ✗ | else if (data->speed == RF24_250KBPS) | |
| 238 | ✗ | LogDebug(VB_CHANNELOUT, " speed : 250KBPS\n"); | |
| 239 | ✗ | LogDebug(VB_CHANNELOUT, " channel : %d\n", data->channel); | |
| 240 | } | ||
| 241 | ✗ | } | |
| 242 | |||
| 243 | ✗ | void SPInRF24L01Output::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) { | |
| 244 | ✗ | addRange(m_startChannel, m_startChannel + m_channelCount - 1); | |
| 245 | ✗ | } | |
| 246 |