268 lines
7.8 KiB
C++
268 lines
7.8 KiB
C++
#include "radio.h"
|
||
|
||
/*
|
||
* Package structure:
|
||
* 0 – 7: Preamble (see constant above)
|
||
* 8 – 10: Remote ID
|
||
* 11 – 11: Separator (0xFF)
|
||
* 12 – 12: Sequence counter
|
||
* 13 – 14: Command ID + options
|
||
* 15 – 16: CRC16 checksum
|
||
*/
|
||
|
||
Radio::Radio(uint8_t ce, uint8_t csn)
|
||
{
|
||
this->radio = RF24(ce, csn);
|
||
}
|
||
|
||
Radio::~Radio()
|
||
{
|
||
this->radio.stopListening();
|
||
this->radio.powerDown();
|
||
}
|
||
|
||
bool Radio::addRemote(Remote *remote)
|
||
{
|
||
if (this->num_remotes >= constants::MAX_REMOTES)
|
||
{
|
||
Serial.println("[Radio] Could not add remote, because too many remotes are saved!");
|
||
Serial.println("[Radio] Please check if you actually want to save more than " + String(constants::MAX_REMOTES, DEC) + " remotes.");
|
||
Serial.println("[Radio] If you do, increase MAX_REMOTES in constants.h and recompile.");
|
||
return false;
|
||
}
|
||
if (this->num_package_ids >= constants::MAX_SERIALS)
|
||
{
|
||
Serial.println("[Radio] Could not add remote, because too many serials are saved!");
|
||
Serial.println("[Radio] Please check if you actually want to save more than " + String(constants::MAX_SERIALS, DEC) + " serials.");
|
||
Serial.println("[Radio] If you do, increase MAX_SERIALS in constants.h and recompile.");
|
||
return false;
|
||
}
|
||
this->remotes[this->num_remotes] = remote;
|
||
this->num_remotes++;
|
||
this->package_ids[this->num_package_ids].serial = remote->getSerial();
|
||
this->package_ids[this->num_package_ids].package_id = 0;
|
||
this->num_package_ids++;
|
||
Serial.print("[Radio] Remote ");
|
||
Serial.print(remote->getSerialString());
|
||
Serial.println(" added!");
|
||
return true;
|
||
}
|
||
|
||
bool Radio::removeRemote(Remote *remote)
|
||
{
|
||
for (int i = 0; i < this->num_remotes; i++)
|
||
{
|
||
if (this->remotes[i] == remote)
|
||
{
|
||
for (int j = 0; j < this->num_package_ids; j++)
|
||
{
|
||
if (this->package_ids[j].serial == remote->getSerial())
|
||
{
|
||
for (int k = j; k < this->num_package_ids - 1; k++)
|
||
{
|
||
this->package_ids[k] = this->package_ids[k + 1];
|
||
}
|
||
this->num_package_ids--;
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (int j = i; j < this->num_remotes - 1; j++)
|
||
{
|
||
this->remotes[j] = this->remotes[j + 1];
|
||
}
|
||
this->num_remotes--;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void Radio::sendCommand(uint32_t serial, byte command, byte options)
|
||
{
|
||
PackageIdForSerial *package_id = nullptr;
|
||
for (int i = 0; i < this->num_package_ids; i++)
|
||
{
|
||
if (this->package_ids[i].serial == serial)
|
||
{
|
||
package_id = &this->package_ids[i];
|
||
break;
|
||
}
|
||
}
|
||
if (package_id == nullptr)
|
||
{
|
||
if (this->num_package_ids >= constants::MAX_SERIALS)
|
||
{
|
||
Serial.println("[Radio] Could not send command, because too many serials are saved!");
|
||
Serial.println("[Radio] Please check if you actually want to save more than " + String(constants::MAX_SERIALS, DEC) + " serials.");
|
||
Serial.println("[Radio] If you do, increase MAX_SERIALS in constants.h and recompile.");
|
||
return;
|
||
}
|
||
package_id = &this->package_ids[this->num_package_ids];
|
||
package_id->serial = serial;
|
||
package_id->package_id = 0;
|
||
this->num_package_ids++;
|
||
}
|
||
|
||
byte data[17] = {0};
|
||
memcpy(data, Radio::preamble, sizeof(Radio::preamble));
|
||
data[8] = (serial & 0xFF0000) >> 16;
|
||
data[9] = (serial & 0x00FF00) >> 8;
|
||
data[10] = serial & 0x0000FF;
|
||
data[11] = 0xFF;
|
||
data[12] = ++package_id->package_id;
|
||
data[13] = command;
|
||
data[14] = options;
|
||
|
||
this->crc.restart();
|
||
this->crc.add(data, sizeof(data) - 2);
|
||
uint16_t checksum = this->crc.calc();
|
||
data[15] = (checksum & 0xFF00) >> 8;
|
||
data[16] = checksum & 0x00FF;
|
||
|
||
Serial.print("[Radio] Sending command: 0x");
|
||
for (int i = 0; i < 17; i++)
|
||
{
|
||
Serial.print(data[i], HEX);
|
||
}
|
||
Serial.println();
|
||
|
||
this->radio.stopListening();
|
||
for (int i = 0; i < 20; i++)
|
||
{
|
||
this->radio.write(&data, sizeof(data), true);
|
||
delay(10);
|
||
}
|
||
this->radio.startListening();
|
||
}
|
||
|
||
void Radio::sendCommand(uint32_t serial, byte command)
|
||
{
|
||
return this->sendCommand(serial, command, 0x0);
|
||
}
|
||
|
||
void Radio::setup()
|
||
{
|
||
uint retries = 0;
|
||
while (!this->radio.begin())
|
||
{
|
||
Serial.println("[Radio] nRF24 not responding! Is it wired correctly?");
|
||
delay(1000);
|
||
retries++;
|
||
if (retries > 60)
|
||
ESP.restart();
|
||
}
|
||
|
||
Serial.println("[Radio] Setting up radio...");
|
||
this->radio.failureDetected = false;
|
||
|
||
this->radio.openReadingPipe(0, Radio::address);
|
||
|
||
this->radio.setChannel(68);
|
||
this->radio.setDataRate(RF24_2MBPS);
|
||
this->radio.disableCRC();
|
||
this->radio.disableDynamicPayloads();
|
||
this->radio.setPayloadSize(17);
|
||
this->radio.setAutoAck(false);
|
||
this->radio.setRetries(15, 15);
|
||
|
||
this->radio.openWritingPipe(Radio::address);
|
||
|
||
this->radio.startListening();
|
||
Serial.println("[Radio] done!");
|
||
}
|
||
|
||
void Radio::loop()
|
||
{
|
||
if (this->radio.failureDetected)
|
||
{
|
||
Serial.println("[Radio] Failure detected!");
|
||
delay(1000);
|
||
this->setup();
|
||
delay(1000);
|
||
}
|
||
|
||
if (this->radio.available())
|
||
this->handlePackage();
|
||
}
|
||
|
||
void Radio::handlePackage()
|
||
{
|
||
// Read raw data, append a 5 and shift it. See
|
||
// https://github.com/lamperez/xiaomi-lightbar-nrf24?tab=readme-ov-file#baseband-packet-format
|
||
// on why that is necessary.
|
||
byte raw_data[18] = {0};
|
||
this->radio.read(&raw_data, sizeof(raw_data));
|
||
byte data[17] = {0x5};
|
||
for (int i = 0; i < 17; i++)
|
||
{
|
||
if (i == 0)
|
||
data[i] = 0x50 | raw_data[i] >> 5;
|
||
else
|
||
data[i] = ((raw_data[i - 1] >> 1) & 0x0F) << 4 | ((raw_data[i - 1] & 0x01) << 3) | raw_data[i] >> 5;
|
||
}
|
||
|
||
// Check if preamble matches. Ignore package otherwise.
|
||
if (memcmp(data, Radio::preamble, sizeof(Radio::preamble)))
|
||
return;
|
||
|
||
// Make sure the checksum of the package is correct.
|
||
this->crc.restart();
|
||
this->crc.add(data, sizeof(data) - 2);
|
||
uint16_t calculated_checksum = this->crc.calc();
|
||
uint16_t package_checksum = data[15] << 8 | data[16];
|
||
if (calculated_checksum != package_checksum)
|
||
{
|
||
Serial.println("[Radio] Ignoring pacakge with wrong checksum!");
|
||
return;
|
||
}
|
||
|
||
// Check if package is coming from a observed remote.
|
||
Remote *remote = nullptr;
|
||
uint32_t serial = data[8] << 16 | data[9] << 8 | data[10];
|
||
for (int i = 0; i < this->num_remotes; i++)
|
||
{
|
||
|
||
if (serial == this->remotes[i]->getSerial())
|
||
{
|
||
remote = this->remotes[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (remote == nullptr)
|
||
{
|
||
Serial.print("[Radio] Ignoring package with unknown serial: 0x");
|
||
Serial.print(serial, HEX);
|
||
Serial.println("");
|
||
return;
|
||
}
|
||
|
||
// Make sure the same package was not handled before.
|
||
uint8_t package_id = data[12];
|
||
PackageIdForSerial *package_id_for_serial = nullptr;
|
||
for (int i = 0; i < this->num_package_ids; i++)
|
||
{
|
||
if (this->package_ids[i].serial == serial)
|
||
{
|
||
package_id_for_serial = &this->package_ids[i];
|
||
break;
|
||
}
|
||
}
|
||
if (package_id_for_serial == nullptr)
|
||
{
|
||
Serial.print("[Radio] Could not find latest package id for serial 0x");
|
||
Serial.print(serial, HEX);
|
||
Serial.println("!");
|
||
return;
|
||
}
|
||
if (package_id <= package_id_for_serial->serial && package_id > package_id_for_serial->serial - 64)
|
||
{
|
||
Serial.println("[Radio] Ignoring package with too low package number!");
|
||
return;
|
||
}
|
||
package_id_for_serial->package_id = package_id;
|
||
|
||
Serial.println("[Radio] Package received!");
|
||
remote->callback(data[13], data[14]);
|
||
} |