esp32_ethernet_milight_hub/lib/Radio/LT8900MiLightRadio.cpp

452 lines
14 KiB
C++

/*
* MiLightRadioPL1167_LT89000.cpp
*
* Created on: 31 March 2017
* Author: WoodsterDK
*
* Very inspired by:
* https://github.com/pmoscetta/authometion-milight/tree/master/Authometion-MiLight
* https://bitbucket.org/robvanderveer/lt8900lib
*/
#include "LT8900MiLightRadio.h"
#include <SPI.h>
/**************************************************************************/
// Constructor
/**************************************************************************/
LT8900MiLightRadio::LT8900MiLightRadio(byte byCSPin, byte byResetPin, byte byPktFlag, const MiLightRadioConfig& config)
: _config(config),
_channel(0),
_currentPacketLen(0),
_currentPacketPos(0)
{
_csPin = byCSPin;
_pin_pktflag = byPktFlag;
pinMode(_pin_pktflag, INPUT);
if (byResetPin > 0) // If zero then bypass hardware reset
{
pinMode(byResetPin, OUTPUT);
digitalWrite(byResetPin, LOW);
delay(200);
digitalWrite(byResetPin, HIGH);
delay(200);
}
pinMode(_csPin, OUTPUT);
digitalWrite(_csPin, HIGH);
SPI.begin();
SPI.setDataMode(SPI_MODE1);
// The following speed settings depends upon the wiring and PCB
//SPI.setFrequency(8000000);
SPI.setFrequency(4000000);
SPI.setBitOrder(MSBFIRST);
//Initialize transceiver with correct settings
vInitRadioModule();
delay(50);
// Check if HW is connected
_bConnected = bCheckRadioConnection();
//Reset SPI MODE to default
SPI.setDataMode(SPI_MODE0);
_waiting = false;
}
/**************************************************************************/
// Checks the connection to the radio module by verifying a register setting
/**************************************************************************/
bool LT8900MiLightRadio::bCheckRadioConnection(void)
{
bool bRetValue = false;
uint16_t value_0 = uiReadRegister(0);
uint16_t value_1 = uiReadRegister(1);
if ((value_0 == 0x6fe0) && (value_1 == 0x5681))
{
#ifdef DEBUG_PRINTF
Serial.println(F("Radio module running correctly..."));
#endif
bRetValue = true;
}
else
{
#ifdef DEBUG_PRINTF
Serial.println(F("Failed initializing the radio module..."));
#endif
}
return bRetValue;
}
/**************************************************************************/
// Initialize radio module
/**************************************************************************/
void LT8900MiLightRadio::vInitRadioModule() {
bool bWriteDefaultDefault = true; // Is it okay to use the default power up values, without setting them
regWrite16(0x00, 0x6F, 0xE0, 7); // Recommended value by PMmicro
regWrite16(0x02, 0x66, 0x17, 7); // Recommended value by PMmicro
regWrite16(0x04, 0x9C, 0xC9, 7); // Recommended value by PMmicro
regWrite16(0x05, 0x66, 0x37, 7); // Recommended value by PMmicro
regWrite16(0x07, 0x00, 0x4C, 7); // PL1167's TX/RX Enable and Channel Register, Default channel 76
regWrite16(0x08, 0x6C, 0x90, 7); // Recommended value by PMmicro
regWrite16(0x09, 0x48, 0x00, 7); // PA Control register
regWrite16(0x0B, 0x00, 0x08, 7); // Recommended value by PMmicro
regWrite16(0x0D, 0x48, 0xBD, 7); // Recommended value by PMmicro
regWrite16(0x16, 0x00, 0xFF, 7); // Recommended value by PMmicro
regWrite16(0x18, 0x00, 0x67, 7); // Recommended value by PMmicro
regWrite16(0x1A, 0x19, 0xE0, 7); // Recommended value by PMmicro
regWrite16(0x1B, 0x13, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x20, 0x48, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x21, 0x3F, 0xC7, 7); // Recommended value by PMmicro
regWrite16(0x22, 0x20, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x23, 0x03, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x24, 0x72, 0x36, 7); // Sync R0
regWrite16(0x27, 0x18, 0x09, 7); // Sync R3
regWrite16(0x28, 0x44, 0x02, 7); // Recommended value by PMmicro
regWrite16(0x29, 0xB0, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x2A, 0xFD, 0xB0, 7); // Recommended value by PMmicro
if (bWriteDefaultDefault == true) {
regWrite16(0x01, 0x56, 0x81, 7); // Recommended value by PMmicro
regWrite16(0x0A, 0x7F, 0xFD, 7); // Recommended value by PMmicro
regWrite16(0x0C, 0x00, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x17, 0x80, 0x05, 7); // Recommended value by PMmicro
regWrite16(0x19, 0x16, 0x59, 7); // Recommended value by PMmicro
regWrite16(0x1C, 0x18, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x25, 0x00, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x26, 0x00, 0x00, 7); // Recommended value by PMmicro
regWrite16(0x2B, 0x00, 0x0F, 7); // Recommended value by PMmicro
}
}
/**************************************************************************/
// Set sync word
/**************************************************************************/
void LT8900MiLightRadio::vSetSyncWord(uint16_t syncWord3, uint16_t syncWord2, uint16_t syncWord1, uint16_t syncWord0)
{
uiWriteRegister(R_SYNCWORD1, syncWord0);
uiWriteRegister(R_SYNCWORD2, syncWord1);
uiWriteRegister(R_SYNCWORD3, syncWord1);
uiWriteRegister(R_SYNCWORD4, syncWord3);
}
/**************************************************************************/
// Low level register write with delay
/**************************************************************************/
void LT8900MiLightRadio::regWrite16(byte ADDR, byte V1, byte V2, byte WAIT)
{
digitalWrite(_csPin, LOW);
SPI.transfer(ADDR);
SPI.transfer(V1);
SPI.transfer(V2);
digitalWrite(_csPin, HIGH);
delayMicroseconds(WAIT);
}
/**************************************************************************/
// Low level register read
/**************************************************************************/
uint16_t LT8900MiLightRadio::uiReadRegister(uint8_t reg)
{
SPI.setDataMode(SPI_MODE1);
digitalWrite(_csPin, LOW);
SPI.transfer(REGISTER_READ | (REGISTER_MASK & reg));
uint8_t high = SPI.transfer(0x00);
uint8_t low = SPI.transfer(0x00);
digitalWrite(_csPin, HIGH);
SPI.setDataMode(SPI_MODE0);
return (high << 8 | low);
}
/**************************************************************************/
// Low level 16bit register write
/**************************************************************************/
uint8_t LT8900MiLightRadio::uiWriteRegister(uint8_t reg, uint16_t data)
{
uint8_t high = data >> 8;
uint8_t low = data & 0xFF;
digitalWrite(_csPin, LOW);
uint8_t result = SPI.transfer(REGISTER_WRITE | (REGISTER_MASK & reg));
SPI.transfer(high);
SPI.transfer(low);
digitalWrite(_csPin, HIGH);
return result;
}
/**************************************************************************/
// Start listening on specified channel and syncword
/**************************************************************************/
void LT8900MiLightRadio::vStartListening(uint uiChannelToListenTo)
{
_dupes_received = 0;
vSetSyncWord(_config.syncword3, 0,0,_config.syncword0);
//vSetChannel(uiChannelToListenTo);
_channel = uiChannelToListenTo;
vResumeRX();
delay(5);
}
/**************************************************************************/
// Resume listening - without changing the channel and syncword
/**************************************************************************/
void LT8900MiLightRadio::vResumeRX(void)
{
_dupes_received = 0;
uiWriteRegister(R_CHANNEL, _channel & CHANNEL_MASK); //turn off rx/tx
delay(3);
uiWriteRegister(R_FIFO_CONTROL, 0x0080); //flush rx
uiWriteRegister(R_CHANNEL, (_channel & CHANNEL_MASK) | _BV(CHANNEL_RX_BIT)); //enable RX
}
/**************************************************************************/
// Check if data is available using the hardware pin PKT_FLAG
/**************************************************************************/
bool LT8900MiLightRadio::bAvailablePin() {
return digitalRead(_pin_pktflag) > 0;
}
/**************************************************************************/
// Check if data is available using the PKT_FLAG state in the status register
/**************************************************************************/
bool LT8900MiLightRadio::bAvailableRegister() {
//read the PKT_FLAG state; this can also be done with a hard wire.
uint16_t value = uiReadRegister(R_STATUS);
if (bitRead(value, STATUS_CRC_BIT) != 0) {
#ifdef DEBUG_PRINTF
Serial.println(F("LT8900: CRC failed"));
#endif
vResumeRX();
return false;
}
return (value & STATUS_PKT_BIT_MASK) > 0;
}
/**************************************************************************/
// Read the RX buffer
/**************************************************************************/
int LT8900MiLightRadio::iReadRXBuffer(uint8_t *buffer, size_t maxBuffer) {
size_t bufferIx = 0;
uint16_t data;
if (_currentPacketLen == 0) {
if (! available()) {
return -1;
}
data = uiReadRegister(R_FIFO);
_currentPacketLen = (data >> 8);
_currentPacketPos = 1;
buffer[bufferIx++] = (data & 0xFF);
}
while (_currentPacketPos < _currentPacketLen && (bufferIx+1) < maxBuffer) {
data = uiReadRegister(R_FIFO);
buffer[bufferIx++] = data >> 8;
buffer[bufferIx++] = data & 0xFF;
_currentPacketPos += 2;
}
#ifdef DEBUG_PRINTF
printf_P(
PSTR("Read %d/%d bytes in RX, read %d bytes into buffer\n"),
_currentPacketPos,
_currentPacketLen,
bufferIx
);
#endif
if (_currentPacketPos >= _currentPacketLen) {
_currentPacketPos = 0;
_currentPacketLen = 0;
}
return bufferIx;
}
/**************************************************************************/
// Set the active channel for the radio module
/**************************************************************************/
void LT8900MiLightRadio::vSetChannel(uint8_t channel)
{
_channel = channel;
uiWriteRegister(R_CHANNEL, (_channel & CHANNEL_MASK));
}
/**************************************************************************/
// Startup
/**************************************************************************/
int LT8900MiLightRadio::begin()
{
vSetChannel(_config.channels[0]);
configure();
available();
return 0;
}
/**************************************************************************/
// Configure the module according to type, and start listening
/**************************************************************************/
int LT8900MiLightRadio::configure()
{
vInitRadioModule();
vSetSyncWord(_config.syncword3, 0,0,_config.syncword0);
vStartListening(_config.channels[0]);
return 0;
}
/**************************************************************************/
// Check if data is available
/**************************************************************************/
bool LT8900MiLightRadio::available()
{
if (_currentPacketPos < _currentPacketLen) {
return true;
}
return bAvailablePin() && bAvailableRegister();
}
/**************************************************************************/
// Read received data from buffer to upper layer
/**************************************************************************/
int LT8900MiLightRadio::read(uint8_t frame[], size_t &frame_length)
{
if (!available()) {
frame_length = 0;
return -1;
}
#ifdef DEBUG_PRINTF
Serial.println(F("LT8900: Radio was available, reading packet..."));
#endif
uint8_t buf[MILIGHT_MAX_PACKET_LENGTH];
int packetSize = iReadRXBuffer(buf, MILIGHT_MAX_PACKET_LENGTH);
if (packetSize > 0) {
frame_length = packetSize;
memcpy(frame, buf, packetSize);
}
vResumeRX();
return packetSize;
}
/**************************************************************************/
// Write data
/**************************************************************************/
int LT8900MiLightRadio::write(uint8_t frame[], size_t frame_length)
{
if (frame_length > sizeof(_out_packet) - 1) {
return -1;
}
memcpy(_out_packet + 1, frame, frame_length);
_out_packet[0] = frame_length;
SPI.setDataMode(SPI_MODE1);
int retval = resend();
yield();
SPI.setDataMode(SPI_MODE0);
if (retval < 0) {
return retval;
}
return frame_length;
}
/**************************************************************************/
// Handle the transmission to regarding to freq diversity and repeats
/**************************************************************************/
int LT8900MiLightRadio::resend()
{
byte Length = _out_packet[0];
for (size_t i = 0; i < MiLightRadioConfig::NUM_CHANNELS; i++)
{
sendPacket(_out_packet, Length, _config.channels[i]);
delayMicroseconds(DEFAULT_TIME_BETWEEN_RETRANSMISSIONS_uS);
}
return 0;
}
/**************************************************************************/
// The actual transmit happens here
/**************************************************************************/
bool LT8900MiLightRadio::sendPacket(uint8_t *data, size_t packetSize, byte byChannel)
{
if(_bConnected) // Must be connected to module otherwise it might lookup waiting for _pin_pktflag
{
if (packetSize < 1 || packetSize > 255)
{
return false;
}
uiWriteRegister(R_CHANNEL, 0x0000);
uiWriteRegister(R_FIFO_CONTROL, 0x8080); //flush tx and RX
digitalWrite(_csPin, LOW); // Enable PL1167 SPI transmission
SPI.transfer(R_FIFO); // Start writing PL1167's FIFO Data register
SPI.transfer(packetSize); // Length of data buffer: x bytes
for (byte iCounter = 0; iCounter < packetSize; iCounter++)
{
SPI.transfer((data[1+iCounter]));
}
digitalWrite(_csPin, HIGH); // Disable PL1167 SPI transmission
delayMicroseconds(10);
uiWriteRegister(R_CHANNEL, (byChannel & CHANNEL_MASK) | _BV(CHANNEL_TX_BIT)); //enable RX
//Wait until the packet is sent.
while (digitalRead(_pin_pktflag) == 0)
{
//do nothing.
}
return true;
}
return false;
}
const MiLightRadioConfig& LT8900MiLightRadio::config() {
return _config;
}