/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see .
*/
//#include "iface_cc2500.h"
#include "frskyd.h"
#include "config.h"
#include "Arduino.h"
Frsky_d::Frsky_d(CC2500 *cc2500)
{
this->calibration = 0x89;
this->state = FRSKY_BIND;
this->cc2500 = cc2500;
this->packet_count = 0;
// TODO real random randomSeed(analogRead(0));
randomSeed(42);
this->MProtocol_id_master = random(10);
this->MProtocol_id = this->MProtocol_id_master;
this->init_channel();
this->init_failsafe();
Serial.print(" multiprotocl id : ");
Serial.print(this->MProtocol_id);
Serial.print("\n");
}
void Frsky_d::init(bool bind)
{
this->init_cc2500(FRSKYD_cc2500_conf);
cc2500->write_register(CC2500_09_ADDR, bind ? 0x03 : rx_tx_addr[3]);
cc2500->write_register(CC2500_07_PKTCTRL1, 0x05);
cc2500->strobe(CC2500_SIDLE); // Go to idle...
//
cc2500->write_register(CC2500_0A_CHANNR, 0x00);
cc2500->write_register(CC2500_23_FSCAL3, 0x89);
cc2500->strobe(CC2500_SFRX);
//#######END INIT########
}
void Frsky_d::init_cc2500(const uint8_t *ptr)
{
for (uint8_t i = 0; i < 19; i++) {
uint8_t reg = FRSKY_common_startreg_cc2500_conf[i];
uint8_t val = ptr[i];
if (reg == CC2500_0C_FSCTRL0)
val = option;
cc2500->write_register(reg, val);
}
prev_option = option ; // Save option to monitor FSCTRL0 change
for (uint8_t i = 0; i < 17; i++) {
uint8_t reg = FRSKY_common_end_cc2500_conf[i][0];
uint8_t val = FRSKY_common_end_cc2500_conf[i][1];
cc2500->write_register(reg, val);
}
cc2500->set_wireless_mode(CC2500::wireless_tx);
cc2500->set_power();
cc2500->strobe(CC2500_SIDLE); // Go to idle...
}
void Frsky_d::build_bind_packet(void)
{
//11 03 01 d7 2d 00 00 1e 3c 5b 78 00 00 00 00 00 00 01
//11 03 01 19 3e 00 02 8e 2f bb 5c 00 00 00 00 00 00 01
packet[0] = 0x11;
packet[1] = 0x03;
packet[2] = 0x01;
packet[3] = rx_tx_addr[3];
packet[4] = rx_tx_addr[2];
uint16_t idx = ((state - FRSKY_BIND) % 10) * 5;
packet[5] = idx;
packet[6] = hopping_frequency[idx++];
packet[7] = hopping_frequency[idx++];
packet[8] = hopping_frequency[idx++];
packet[9] = hopping_frequency[idx++];
packet[10] = hopping_frequency[idx++];
packet[11] = 0x00;
packet[12] = 0x00;
packet[13] = 0x00;
packet[14] = 0x00;
packet[15] = 0x00;
packet[16] = 0x00;
packet[17] = 0x01;
}
void Frsky_d::frsky2way_data_frame()
{
//pachet[4] is telemetry user frame counter(hub)
//11 d7 2d 22 00 01 c9 c9 ca ca 88 88 ca ca c9 ca 88 88
//11 57 12 00 00 01 f2 f2 f2 f2 06 06 ca ca ca ca 18 18
packet[0] = 0x11; //Length
packet[1] = rx_tx_addr[3];
packet[2] = rx_tx_addr[2];
packet[3] = counter;//
#if defined TELEMETRY
packet[4] = telemetry_counter;
#else
packet[4] = 0x00;
#endif
packet[5] = 0x01;
//
packet[10] = 0;
packet[11] = 0;
packet[16] = 0;
packet[17] = 0;
for (uint8_t i = 0; i < 8; i++) {
uint16_t value;
value = convert_channel_frsky(i);
if (i < 4) {
packet[6 + i] = value & 0xff;
packet[10 + (i >> 1)] |= ((value >> 8) & 0x0f) << (4 * (i & 0x01));
} else {
packet[8 + i] = value & 0xff;
packet[16 + ((i - 4) >> 1)] |= ((value >> 8) & 0x0f) << (4 * ((i - 4) & 0x01));
}
}
}
uint16_t Frsky_d::init_frsky_2way(void)
{
Serial.print("init hop");
init_hop();
packet_count = 0;
#if defined TELEMETRY
init_frskyd_link_telemetry();
#endif
if (IS_BIND_IN_PROGRESS) {
init(true);
state = FRSKY_BIND;
} else {
state = FRSKY_BIND_DONE;
}
return 10000;
}
uint16_t Frsky_d::read_frsky_2way(void)
{
if (state < FRSKY_BIND_DONE) {
Serial.print("bind mode\n");
build_bind_packet();
cc2500->strobe(CC2500_SIDLE);
cc2500->write_register(CC2500_0A_CHANNR, 0x00);
cc2500->write_register(CC2500_23_FSCAL3, 0x89);
cc2500->strobe(CC2500_SFRX);//0x3A
cc2500->write_data(packet, packet[0] + 1);
if (IS_BIND_DONE) {
Serial.print("bind finished \n");
state = FRSKY_BIND_DONE;
} else {
//state += 1;
Serial.print("bind not done\n");
Serial.print("state :");
Serial.print(state);
Serial.print("\n");
}
return 9000;
}
if (state == FRSKY_BIND_DONE) {
Serial.print("bind done \n");
state = FRSKY_DATA2;
init(false);
counter = 0;
BIND_DONE;
} else if (state == FRSKY_DATA5) {
Serial.print("data 5 \n");
cc2500->strobe(CC2500_SRX);//0x34 RX enable
state = FRSKY_DATA1;
return 9200;
}
counter = (counter + 1) % 188;
if (state == FRSKY_DATA4) {
Serial.print("data 4 \n");
//telemetry receive
cc2500->set_wireless_mode(CC2500::wireless_rx);
cc2500->strobe(CC2500_SIDLE);
cc2500->write_register(CC2500_0A_CHANNR, hopping_frequency[counter % 47]);
cc2500->write_register(CC2500_23_FSCAL3, 0x89);
state++;
return 1300;
} else {
if (state == FRSKY_DATA1) {
Serial.print("data 1 \n");
len = cc2500->read_register(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
if (len && len <= (0x11 + 3)) { // 20bytes
cc2500->read_data(pkt, len); //received telemetry packets
#if defined(TELEMETRY)
if (pkt[len - 1] & 0x80) {
//with valid crc
packet_count = 0;
frsky_check_telemetry(pkt, len); //check if valid telemetry packets and buffer them.
}
#endif
} else {
packet_count++;
// restart sequence on missed packet - might need count or timeout instead of one missed
if (packet_count > 100) {
//~1sec
packet_count = 0;
#if defined TELEMETRY
telemetry_link = 0; //no link frames
pkt[6] = 0; //no user frames.
#endif
}
}
cc2500->set_wireless_mode(CC2500::wireless_tx);
cc2500->set_power(); // Set tx_power
}
cc2500->strobe(CC2500_SIDLE);
cc2500->write_register(CC2500_0A_CHANNR, hopping_frequency[counter % 47]);
if (prev_option != option) {
cc2500->write_register(CC2500_0C_FSCTRL0, option); // Frequency offset hack
prev_option = option ;
}
cc2500->write_register(CC2500_23_FSCAL3, 0x89);
cc2500->strobe(CC2500_SFRX);
frsky2way_data_frame();
cc2500->write_data(packet, packet[0] + 1);
state++;
}
return state == FRSKY_DATA4 ? 7500 : 9000;
}
uint16_t Frsky_d::convert_channel_frsky(uint8_t num)
{
// Channel value for FrSky (PPM is multiplied by 1.5)
uint16_t val = Channel_data[num];
return ((val*15)>>4)+1290;
}
void Frsky_d::init_hop(void)
{
uint8_t val;
uint8_t channel = rx_tx_addr[0]&0x07;
uint8_t channel_spacing = rx_tx_addr[1];
//Filter bad tables
if(channel_spacing<0x02)
channel_spacing+=0x02;
if(channel_spacing>0xE9)
channel_spacing-=0xE7;
if(channel_spacing%0x2F==0)
channel_spacing++;
hopping_frequency[0]=channel;
for(uint8_t i=1;i<50;i++) {
channel=(channel+channel_spacing) % 0xEB;
val=channel;
if((val==0x00) || (val==0x5A) || (val==0xDC))
val++;
hopping_frequency[i]=i>46?0:val;
}
}
void Frsky_d::init_channel(void)
{
for(uint8_t i=0;i