/*
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 .
*/
#if defined(HOTT_CC2500_INO)
#include "iface_cc2500.h"
//#define HOTT_FORCE_ID // Force ID of original dump
#define HOTT_TX_PACKET_LEN 50
#define HOTT_RX_PACKET_LEN 22
#define HOTT_PACKET_PERIOD 10000
#define HOTT_NUM_RF_CHANNELS 75
#define HOTT_COARSE 0
enum {
HOTT_START = 0x00,
HOTT_CAL = 0x01,
HOTT_DATA1 = 0x02,
HOTT_DATA2 = 0x03,
HOTT_RX1 = 0x04,
HOTT_RX2 = 0x05,
};
#ifdef HOTT_FW_TELEMETRY
#define HOTT_SENSOR_TYPE 6
#define HOTT_SENSOR_SEARCH_PERIOD 2000
uint8_t HOTT_sensor_cur=0;
uint8_t HOTT_sensor_pages=0;
uint8_t HOTT_sensor_valid=false;
uint8_t HOTT_sensor_ok[HOTT_SENSOR_TYPE];
uint8_t HOTT_sensor_seq=0;
#endif
#define HOTT_FREQ0_VAL 0x6E
// Some important initialization parameters, all others are either default,
// or not important in the context of transmitter
// FIFOTHR 00
// SYNC1 D3
// SYNC0 91
// PKTLEN 32 - Packet length, 50 bytes
// PKTCTRL1 04 - APPEND_STATUS on=RSSI+LQI, all other are receive parameters - irrelevant
// PKTCTRL0 44 - whitening, use FIFO, use CRC, fixed packet length
// ADDR 00
// CHANNR 10
// FSCTRL1 09 - IF
// FSCTRL0 00 - zero freq offset
// FREQ2 5C - synthesizer frequencyfor 26MHz crystal
// FREQ1 6C
// FREQ0 B9
// MDMCFG4 2D -
// MDMCFG3 3B -
// MDMCFG2 73 - disable DC blocking, MSK, no Manchester code, 32 bits sync word
// MDMCFG1 A3 - FEC enable, 4 preamble bytes, CHANSPC_E - 03
// MDMCFG0 AA - CHANSPC_M - AA
// DEVIATN 47 -
// MCSM2 07 -
// MCSM1 00 - always use CCA, go to IDLE when done
// MCSM0 08 - disable autocalibration, PO_TIMEOUT - 64, no pin radio control, no forcing XTAL to stay in SLEEP
// FOCCFG 1D
const PROGMEM uint8_t HOTT_init_values[] = {
/* 00 */ 0x2F, 0x2E, 0x2F, 0x00, 0xD3, 0x91, 0x32, 0x04,
/* 08 */ 0x44, 0x00, 0x00, 0x09, 0x00, 0x5C, 0x6C, HOTT_FREQ0_VAL + HOTT_COARSE,
/* 10 */ 0x2D, 0x3B, 0x73, 0xA3, 0xAA, 0x47, 0x07, 0x00,
/* 18 */ 0x08, 0x1D, 0x1C, 0xC7, 0x09, 0xF0, 0x87, 0x6B,
/* 20 */ 0xF0, 0xB6, 0x10, 0xEA, 0x0A, 0x00, 0x11
};
static void __attribute__((unused)) HOTT_rf_init()
{
CC2500_Strobe(CC2500_SIDLE);
for (uint8_t i = 0; i < 39; ++i)
CC2500_WriteReg(i, pgm_read_byte_near(&HOTT_init_values[i]));
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower();
}
static void __attribute__((unused)) HOTT_tune_chan()
{
CC2500_Strobe(CC2500_SIDLE);
CC2500_WriteReg(CC2500_0A_CHANNR, (rf_ch_num+1)*3);
CC2500_Strobe(CC2500_SCAL);
}
static void __attribute__((unused)) HOTT_tune_chan_fast()
{
CC2500_Strobe(CC2500_SIDLE);
CC2500_WriteReg(CC2500_0A_CHANNR, (rf_ch_num+1)*3);
CC2500_WriteReg(CC2500_25_FSCAL1, calData[rf_ch_num]);
}
static void __attribute__((unused)) HOTT_tune_freq()
{
if ( prev_option != option )
{
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
CC2500_WriteReg(CC2500_0F_FREQ0, HOTT_FREQ0_VAL + HOTT_COARSE);
prev_option = option ;
phase = HOTT_START; // Restart the tune process if option is changed to get good tuned values
}
}
// Hop table from the value sent in packet[0..1]: the C library rand() (low 16
// bits) seeded with it drives forward Fisher-Yates shuffles, first of the order
// of the 5 bands of 15 channels, then of the channels of each band. Hop i is in
// the band at position i%5 of that order.
static uint16_t __attribute__((unused)) HOTT_rand(uint16_t *seed)
{
*seed = *seed * 0x4E6D + 0x3039;
return *seed;
}
static void __attribute__((unused)) HOTT_hop_init(uint16_t seed)
{
uint8_t order[5], tmp, j;
for(uint8_t i=0; i<5; i++)
order[i] = i;
for(uint8_t i=0; i<5; i++)
{
j = i + HOTT_rand(&seed) % (5-i);
tmp = order[i]; order[i] = order[j]; order[j] = tmp;
}
for(uint8_t i=0; i>8;
HOTT_hop_init(pgm_read_word_near( &HOTT_hop_val[num_ch] ));
#ifdef HOTT_FORCE_ID
memcpy(rx_tx_addr,"\x7C\x94\x00\x0D\x50",5); //TX1
memcpy(rx_tx_addr,"\xEA\x4D\x00\x01\x50",5); //TX2
#endif
memset(&packet[30],0xFF,9);
packet[39]=0x07; // unknown and constant
if(IS_BIND_IN_PROGRESS)
{
memset(&packet[40],0xFA,5);
memcpy(&packet[45],rx_tx_addr,5);
}
else
{
memcpy(&packet[40],rx_tx_addr,5);
uint16_t addr=HOTT_EEPROM_OFFSET+RX_num*5;
debug("RXID: ");
for(uint8_t i=0;i<5;i++)
{
packet[45+i]=eeprom_read_byte((EE_ADDR)(addr+i));
debug(" %02X",packet[45+i]);
}
debugln("");
}
}
static void __attribute__((unused)) HOTT_prep_data_packet() {
static uint8_t upper = 0; // toggles between sending channels 1..8,9..12 and 1..8,13..16
packet[2] = hopping_frequency_no; // send next frequency to be used
packet[3] = upper; // indicate upper or lower channels (only supporting 16 channels)
#ifdef FAILSAFE_ENABLE
static uint8_t failsafe_count = 0; // failsafe packet state machine (need to send two packets)
if(IS_FAILSAFE_VALUES_on && IS_BIND_DONE) { // if TX wants to send failsafe data and RX is connected
failsafe_count++; // prepare to send next packet
if(failsafe_count >= 3) { // done sending two failsafe channel data packets
FAILSAFE_VALUES_off;
failsafe_count = 0;
}
}
else
failsafe_count = 0;
#endif
// Channels value are PPM*2, -100%=1100µs, +100%=1900µs, order TAER
//
// Note: failsafe packets are differnt to normal operation packets
// normal operation toggles between sending channels 1..8,9..12 and 1..8,13..16 using bit0 as high/low indicator in packet[3]
// while failsafe packets send channels 1..12, 13..24 (and probably 25..32) using bit0 in packet[3] as packet type indicator
// packet[3] = 0x40 -> failsafe packet with data for channels 1..12
// packet[3] = 0x41 -> failsafe packet with data for channels 13..24
//
uint16_t val;
for(uint8_t i = 0 ; i < 12*2 ; i += 2) { // working 12 channels (using 2 bytes per channel)
uint8_t chIndex = i >> 1 ; // normal operation channel number
uint8_t fschIndex = chIndex; // linear channel number for failsafe
if(upper && chIndex >= 8) chIndex += 4; // for normal operation toggle between channels 1..8,9..12 and 1..8,13..16
val = Channel_data[chIndex]; // get normal operation channel data
val = (((val << 2) + val) >> 2)+ 860*2; // convert channel data 0..2047 to 1720..4278 <-> -125%<->+125%
// val = (val*5/4+860*2)
#ifdef FAILSAFE_ENABLE
if(failsafe_count == 1 || failsafe_count == 2) {// failsafe data needs to be sent to RX
uint16_t fs = 0x8000; // default failsafe mode is hold
if(failsafe_count == 1) { // send fail safe packet containing channels 1..12
packet[3] = 0x40; // indicate packet has failsafe values for channels 1..12
fs = Failsafe_data[fschIndex]; // get failsafe channel data
} else { // send fail safe packet containing channels 13..24
packet[3] = 0x41; // indicate packet has failsafe values for channels 13..24
if(fschIndex < 4) // we only work 16 channels so send channels 13..16, rest default
fs = Failsafe_data[fschIndex+12]; // get failsafe channel data 13..16
}
if( fs == FAILSAFE_CHANNEL_HOLD || // treat HOLD and NOPULSES as channel hold
fs == FAILSAFE_CHANNEL_NOPULSES)
val = 0x8000; // set channel failsafe mode hold flag
else {
val = (((fs << 2) + fs) >> 2) +860*2; // convert channel data 0..2047 to 1720..4278 <-> -125%<->+125%
val |= 0x4000; // set channel failsafe mode position flag
}
}
#endif
packet[i + 4] = val; // first channel data at packet[4] and packet[5]
packet[i + 4+1] = val >> 8;
}
upper ^= 0x01; // toggle to upper and lower channels
packet[28] = 0x80; // no sensor
packet[29] = 0x02; // 0x02 when bind starts then when RX replies cycle in sequence 0x1A/22/2A/0A/12, 0x02 during normal packets, 0x01->text config menu, 0x0A->no more RX telemetry
#ifdef HOTT_FW_TELEMETRY
if(IS_BIND_DONE)
{
static uint8_t prev_SerialRX_val=0;
if(HoTT_SerialRX)
{//Text mode
uint8_t sensor=HoTT_SerialRX_val&0xF0;
if((sensor&0x80) && sensor!=0xF0 && (HoTT_SerialRX_val&0x0F) >= 0x07)
{//Valid Text query
if(sensor==0x80) HoTT_SerialRX_val&=0x0F; // RX only
if(prev_SerialRX_val!=HoTT_SerialRX_val)
{
prev_SerialRX_val=HoTT_SerialRX_val;
packet[28] = HoTT_SerialRX_val; // send the button being pressed only once
}
else
packet[28] = HoTT_SerialRX_val | 0x0F; // no button pressed
}
else
packet[28] = 0x0F; // RX, no button pressed
if(sub_protocol == HOTT_SYNC)
packet[29] = ((HOTT_sensor_seq+1)<<3) | 1; // Telemetry packet sequence
else
packet[29] = 0x01; // 0x01->Text config menu
}
else
{
packet[28] = 0x89+HOTT_sensor_cur; // 0x89/8A/8B/8C/8D/8E during normal packets
if(sub_protocol == HOTT_SYNC)
packet[29] = ((HOTT_sensor_seq+1)<<3) | 2; // Telemetry packet sequence
}
//debugln("28=%02X,29=%02X",packet[28],packet[29]);
}
#endif
CC2500_WriteReg(CC2500_06_PKTLEN, HOTT_TX_PACKET_LEN);
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, packet, HOTT_TX_PACKET_LEN);
#if 0
debug("RF:%02X P:",rf_ch_num);
for(uint8_t i=0;i=128:(a-256)/2-71dBm)
// Page 0 [16] = RX_LQI in %
// Page 0 [17] = RX_Voltage Min*10 in V
// Page 0 [18,19] = [19]<<8+[18]=max lost packet time in ms, max value seems 2s=0x7D0
// Page 0 [20] = rx events
//
// Config menu consists of the different telem pages put all together
// Page X [12] = seems like all the telem pages with the same value are going together to make the full config menu text. Seen so far 'a', 'b', 'c', 'd'
// Page X [13..21] = 9 ascii chars to be displayed, char is highlighted when ascii|0x80
// Screen display is 21 characters large which means that once the first 21 chars are filled go to the begining of the next line
// Menu commands are sent through TX packets:
// packet[28]= 0xXF=>no key press, 0xXD=>down, 0xXB=>up, 0xX9=>enter, 0xXE=>right, 0xX7=>left with X=0 or D
// packet[29]= 0xX1/0xX9 with X=0 or X counting 0,1,1,2,2,..,9,9
// Reduce telemetry to 14 bytes
packet_in[0]= packet_in[HOTT_RX_PACKET_LEN];
packet_in[1]= TX_LQI;
uint8_t hott_warnings = packet_in[10]; // save warnings from hott devices
bool send_telem=true;
HOTT_sensor_seq++; // Increment RX sequence counter
if(packet[29] & 1)
{ //Text mode
HOTT_sensor_seq %= 19; // 19 pages in Text mode
HOTT_sensor_pages = 0;
HOTT_sensor_valid = false;
packet_in[10] = 0x80; // Marking telem Text mode
packet_in[12] = 0;
for(uint8_t i=0; i>4)-9]=true;
}
else
{
HOTT_sensor_valid = false;
HOTT_sensor_pages = 0x1E; // Switch to next sensor
}
}
if(packet_in[11])
{ //Page != 0
if(HOTT_sensor_valid) // Valid
{
packet_in[10] = HOTT_sensor_cur+9; // Mark telem with sensor ID
HOTT_sensor_pages |= 1<=100)
{
TX_LQI=pps_counter;
if(pps_counter==0)
{ // lost connection with RX, power cycle? research sensors again.
HOTT_sensor_cur=3;
HOTT_sensor_seq=0;
HOTT_sensor_valid=false;
for(uint8_t i=0; i