Files
lightbar2mqtt/radio.cpp

268 lines
7.8 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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]);
}