2 This project is free software: you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation, either version 3 of the License, or
5 (at your option) any later version.
7 Multiprotocol is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
15 // Compatible with PROPEL 74-Z Speeder Bike.
17 #if defined(PROPEL_NRF24L01_INO)
19 #include "iface_nrf24l01.h"
21 //#define PROPEL_FORCE_ID
23 #define PROPEL_INITIAL_WAIT 500
24 #define PROPEL_PACKET_PERIOD 10000
25 #define PROPEL_BIND_RF_CHANNEL 0x23
26 #define PROPEL_PAYLOAD_SIZE 16
27 #define PROPEL_SEARCH_PERIOD 50 //*10ms
28 #define PROPEL_BIND_PERIOD 1500
29 #define PROPEL_PACKET_SIZE 14
30 #define PROPEL_RF_NUM_CHANNELS 4
31 #define PROPEL_ADDRESS_LENGTH 5
32 #define PROPEL_DEFAULT_PERIOD 20
41 static uint16_t __attribute__((unused)) PROPEL_checksum()
57 uint8_t sum = packet[0];
58 for (uint8_t i = 1; i < PROPEL_PACKET_SIZE - 2; i++)
61 byte_bits_t in = { .byte = sum };
62 byte_bits_t out = { .byte = sum };
64 out.bits.d = !(in.bits.d ^ in.bits.h);
65 out.bits.c = (!in.bits.c && !in.bits.d && in.bits.g)
66 || (in.bits.c && !in.bits.d && !in.bits.g)
67 || (!in.bits.c && in.bits.g && !in.bits.h)
68 || (in.bits.c && !in.bits.g && !in.bits.h)
69 || (in.bits.c && in.bits.d && in.bits.g && in.bits.h)
70 || (!in.bits.c && in.bits.d && !in.bits.g && in.bits.h);
71 out.bits.b = (!in.bits.b && !in.bits.c && !in.bits.d)
72 || (in.bits.b && in.bits.c && in.bits.g)
73 || (!in.bits.b && !in.bits.c && !in.bits.g)
74 || (!in.bits.b && !in.bits.d && !in.bits.g)
75 || (!in.bits.b && !in.bits.c && !in.bits.h)
76 || (!in.bits.b && !in.bits.g && !in.bits.h)
77 || (in.bits.b && in.bits.c && in.bits.d && in.bits.h)
78 || (in.bits.b && in.bits.d && in.bits.g && in.bits.h);
79 out.bits.a = (in.bits.a && !in.bits.b)
80 || (in.bits.a && !in.bits.c && !in.bits.d)
81 || (in.bits.a && !in.bits.c && !in.bits.g)
82 || (in.bits.a && !in.bits.d && !in.bits.g)
83 || (in.bits.a && !in.bits.c && !in.bits.h)
84 || (in.bits.a && !in.bits.g && !in.bits.h)
85 || (!in.bits.a && in.bits.b && in.bits.c && in.bits.g)
86 || (!in.bits.a && in.bits.b && in.bits.c && in.bits.d && in.bits.h)
87 || (!in.bits.a && in.bits.b && in.bits.d && in.bits.g && in.bits.h);
89 return (sum << 8) | (out.byte & 0xff);
92 static void __attribute__((unused)) PROPEL_bind_packet(bool valid_rx_id)
94 memset(packet, 0, PROPEL_PACKET_SIZE);
97 memcpy(&packet[1], rx_tx_addr, 4); // only 4 bytes sent of 5-byte address
98 if (valid_rx_id) memcpy(&packet[5], rx_id, 4);
99 packet[9] = rf_ch_num; // hopping table to be used when switching to normal mode
100 packet[11] = 0x05; // unknown, 0x01 on TX2??
102 uint16_t check = PROPEL_checksum();
103 packet[12] = check >> 8;
104 packet[13] = check & 0xff;
106 NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT)));
109 NRF24L01_WritePayload(packet, PROPEL_PACKET_SIZE);
112 static void __attribute__((unused)) PROPEL_data_packet()
114 memset(packet, 0, PROPEL_PACKET_SIZE);
117 packet[1] = convert_channel_16b_limit(THROTTLE, 0x2f, 0xcf);
118 packet[2] = convert_channel_16b_limit(RUDDER , 0xcf, 0x2f);
119 packet[3] = convert_channel_16b_limit(ELEVATOR, 0x2f, 0xcf);
120 packet[4] = convert_channel_16b_limit(AILERON , 0xcf, 0x2f);
121 packet[5] = 0x40; //might be trims but unsused
122 packet[6] = 0x40; //might be trims but unsused
123 packet[7] = 0x40; //might be trims but unsused
124 packet[8] = 0x40; //might be trims but unsused
126 {//need to send a couple of default packets after bind
128 packet[10] = 0x80; // LEDs
132 packet[9] = 0x02 // Always fast speed, slow=0x00, medium=0x01, fast=0x02, 0x03=flight training mode
133 | GET_FLAG( CH14_SW, 0x03) // Flight training mode
134 | GET_FLAG( CH10_SW, 0x04) // Calibrate
135 | GET_FLAG( CH12_SW, 0x08) // Take off
136 | GET_FLAG( CH8_SW, 0x10) // Fire
137 | GET_FLAG( CH11_SW, 0x20) // Altitude hold=0x20
138 | GET_FLAG( CH6_SW, 0x40) // Roll CW
139 | GET_FLAG( CH7_SW, 0x80); // Roll CCW
140 packet[10] = GET_FLAG( CH13_SW, 0x20) // Land
141 | GET_FLAG( CH9_SW, 0x40) // Weapon system activted=0x40
142 | GET_FLAG(!CH5_SW, 0x80); // LEDs
144 packet[11] = 5; // unknown, 0x01 on TX2??
146 uint16_t check = PROPEL_checksum();
147 packet[12] = check >> 8;
148 packet[13] = check & 0xff;
150 NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
151 hopping_frequency_no &= 0x03;
153 NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT)));
155 NRF24L01_WritePayload(packet, PROPEL_PACKET_SIZE);
158 static void __attribute__((unused)) PROPEL_RF_init()
160 NRF24L01_Initialize();
162 NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x7f);
163 NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x3f); // AA on all pipes
164 NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x3f); // Enable all pipes
165 NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x36); // retransmit 1ms, 6 times
166 NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *)"\x99\x77\x55\x33\x11", PROPEL_ADDRESS_LENGTH); //Bind address
167 NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x99\x77\x55\x33\x11", PROPEL_ADDRESS_LENGTH); //Bind address
168 NRF24L01_WriteReg(NRF24L01_05_RF_CH, PROPEL_BIND_RF_CHANNEL);
169 NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); // Enable dynamic payload length
170 NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); // Enable all features
172 NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
175 const uint8_t PROGMEM PROPEL_hopping []= { 0x47,0x36,0x27,0x44,0x33,0x0D,0x3C,0x2E,0x1B,0x39,0x2A,0x18 };
176 static void __attribute__((unused)) PROPEL_initialize_txid()
181 //random hopping channel table
182 rf_ch_num=random(0xfefefefe)&0x03;
183 for(uint8_t i=0; i<3; i++)
184 hopping_frequency[i]=pgm_read_byte_near( &PROPEL_hopping[i + 3*rf_ch_num] );
185 hopping_frequency[3]=0x23;
187 #ifdef PROPEL_FORCE_ID
189 memcpy(rx_tx_addr, (uint8_t *)"\x73\xd3\x31\x30\x11", PROPEL_ADDRESS_LENGTH); //TX1: 73 d3 31 30 11
191 memcpy(rx_tx_addr, (uint8_t *)"\x94\xc5\x31\x30\x11", PROPEL_ADDRESS_LENGTH); //TX2: 94 c5 31 30 11
192 rf_ch_num = 0x03; //TX1
193 memcpy(hopping_frequency,(uint8_t *)"\x39\x2A\x18\x23",PROPEL_RF_NUM_CHANNELS); //TX1: 57,42,24,35
194 rf_ch_num = 0x00; //TX2
195 memcpy(hopping_frequency,(uint8_t *)"\x47\x36\x27\x23",PROPEL_RF_NUM_CHANNELS); //TX2: 71,54,39,35
196 rf_ch_num = 0x01; // Manual search
197 memcpy(hopping_frequency,(uint8_t *)"\x44\x33\x0D\x23",PROPEL_RF_NUM_CHANNELS); //Manual: 68,51,13,35
198 rf_ch_num = 0x02; // Manual search
199 memcpy(hopping_frequency,(uint8_t *)"\x3C\x2E\x1B\x23",PROPEL_RF_NUM_CHANNELS); //Manual: 60,46,27,35
203 uint16_t PROPEL_callback()
210 PROPEL_bind_packet(false); //rx_id unknown
212 return PROPEL_BIND_PERIOD;
215 status=NRF24L01_ReadReg(NRF24L01_07_STATUS);
216 if (status & _BV(NRF24L01_07_MAX_RT))
217 {// Max retry (6) reached
218 phase = PROPEL_BIND1;
219 return PROPEL_BIND_PERIOD;
221 if (!(_BV(NRF24L01_07_RX_DR) & status))
222 return PROPEL_BIND_PERIOD; // nothing received
223 // received frame, got rx_id, save it
224 NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE);
225 memcpy(rx_id, &packet_in[1], 4);
226 PROPEL_bind_packet(true); //send bind packet with rx_id
231 if (_BV(NRF24L01_07_RX_DR) & NRF24L01_ReadReg(NRF24L01_07_STATUS))
233 NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE);
234 if (packet_in[0] == 0xa3 && memcmp(&packet_in[1],rx_id,4)==0)
235 {//confirmation from the model
236 phase++; //PROPEL_DATA1
237 bind_phase=PROPEL_DEFAULT_PERIOD;
243 NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, PROPEL_ADDRESS_LENGTH);
244 NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, PROPEL_ADDRESS_LENGTH);
245 PROPEL_bind_packet(true); //send bind packet with rx_id
249 #ifdef PROPEL_HUB_TELEMETRY
250 if (_BV(NRF24L01_07_RX_DR) & NRF24L01_ReadReg(NRF24L01_07_STATUS))
251 {// data received from the model
252 NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE);
254 for(uint8_t i=0; i<PROPEL_PACKET_SIZE; i++)
255 debug("%02X ",packet_in[i]);
258 if (packet_in[0] == 0xa3 && memcmp(&packet_in[1],rx_id,3)==0)
260 telemetry_counter++; //LQI
261 v_lipo1=packet_in[5]; //number of life left?
262 v_lipo2=packet_in[4]; //bit mask: 0x80=flying, 0x08=taking off, 0x04=landing, 0x00=landed/crashed
263 if(telemetry_lost==0)
268 if(packet_count>=100)
271 TX_LQI=telemetry_counter;
272 RX_RSSI=telemetry_counter;
273 telemetry_counter = 0;
277 PROPEL_data_packet();
280 return PROPEL_PACKET_PERIOD;
285 BIND_IN_PROGRESS; // autobind protocol
286 PROPEL_initialize_txid();
288 hopping_frequency_no = 0;
293 // equations for checksum check byte from truth table
300 // || !a && b && c && g
301 // || !a && b && c && d && h
302 // || !a && b && d && g && h;
304 // (2) y = !b && !c && !d
310 // || b && c && d && h
311 // || b && d && g && h;
313 // (3) x = !c && !d && g
317 // || c && d && g && h
318 // || !c && d && !g && h;