451 lines
11 KiB
Arduino
Raw Normal View History

2015-12-30 01:41:12 +01:00
/*
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 <http://www.gnu.org/licenses/>.
*/
// compatible with Hubsan H102D, H107/L/C/D and H107P/C+/D+
// Last sync with hexfet new_protocols/hubsan_a7105.c dated 2015-12-11
2015-12-30 01:41:12 +01:00
#if defined(HUBSAN_A7105_INO)
#include "iface_a7105.h"
enum{
// flags going to packet[9] (H107)
HUBSAN_FLAG_VIDEO= 0x01, // record video
HUBSAN_FLAG_FLIP = 0x08, // enable flips
HUBSAN_FLAG_LED = 0x04 // enable LEDs
2015-12-30 01:41:12 +01:00
};
enum{
// flags going to packet[9] (H107 Plus series)
HUBSAN_FLAG_HEADLESS = 0x08, // headless mode
};
enum{
// flags going to packet[9] (H301)
FLAG_H301_VIDEO = 0x01,
FLAG_H301_STAB = 0x02,
FLAG_H301_LED = 0x10,
FLAG_H301_RTH = 0x40,
};
enum{
// flags going to packet[13] (H107 Plus series)
HUBSAN_FLAG_SNAPSHOT = 0x01,
HUBSAN_FLAG_FLIP_PLUS = 0x80,
};
enum{
// flags going to packet[9] (H501S)
FLAG_H501_VIDEO = 0x01,
FLAG_H501_LED = 0x04,
FLAG_H501_RTH = 0x20,
FLAG_H501_HEADLESS1 = 0x40,
FLAG_H501_GPS_HOLD = 0x80,
};
enum{
// flags going to packet[13] (H501S)
FLAG_H501_SNAPSHOT = 0x01,
FLAG_H501_HEADLESS2 = 0x02,
FLAG_H501_ALT_HOLD = 0x08,
};
uint32_t hubsan_id_data;
2015-12-30 01:41:12 +01:00
enum {
BIND_1,
BIND_2,
BIND_3,
BIND_4,
BIND_5,
BIND_6,
BIND_7,
BIND_8,
DATA_1,
DATA_2,
DATA_3,
DATA_4,
DATA_5,
};
#define HUBSAN_WAIT_WRITE 0x80
2015-12-30 01:41:12 +01:00
static void __attribute__((unused)) hubsan_update_crc()
2015-12-30 01:41:12 +01:00
{
uint8_t sum = 0;
for(uint8_t i = 0; i < 15; i++)
sum += packet[i];
packet[15] = (256 - (sum % 256)) & 0xFF;
}
static void __attribute__((unused)) hubsan_build_bind_packet(uint8_t bind_state)
2015-12-30 01:41:12 +01:00
{
static uint8_t handshake_counter;
if(phase < BIND_7)
handshake_counter = 0;
memset(packet, 0, 16);
packet[0] = bind_state;
2015-12-30 01:41:12 +01:00
packet[1] = channel;
packet[2] = (MProtocol_id >> 24) & 0xFF;
packet[3] = (MProtocol_id >> 16) & 0xFF;
packet[4] = (MProtocol_id >> 8) & 0xFF;
packet[5] = (MProtocol_id >> 0) & 0xFF;
if(hubsan_id_data == ID_NORMAL && sub_protocol != H501)
{
packet[6] = 0x08;
packet[7] = 0xe4;
packet[8] = 0xea;
packet[9] = 0x9e;
packet[10] = 0x50;
//const uint32_t txid = 0xdb042679;
packet[11] = 0xDB;
packet[12] = 0x04;
packet[13] = 0x26;
packet[14] = 0x79;
}
else
{ //ID_PLUS
if(phase >= BIND_3)
{
packet[7] = 0x62;
packet[8] = 0x16;
}
if(phase == BIND_7)
packet[2] = handshake_counter++;
}
hubsan_update_crc();
2015-12-30 01:41:12 +01:00
}
//cc : throttle observed range: 0x00 - 0xFF (smaller is down)
//ee : rudder observed range: 0x34 - 0xcc (smaller is right)52-204-60%
//gg : elevator observed range: 0x3e - 0xbc (smaller is up)62-188 -50%
//ii : aileron observed range: 0x45 - 0xc3 (smaller is right)69-195-50%
static void __attribute__((unused)) hubsan_build_packet()
2015-12-30 01:41:12 +01:00
{
static uint8_t vtx_freq = 0, h501_packet = 0;
2015-12-30 01:41:12 +01:00
memset(packet, 0, 16);
if(vtx_freq != option || packet_count==100) // set vTX frequency (H107D)
{
vtx_freq = option;
packet[0] = 0x40; // vtx data packet
packet[1] = (vtx_freq>0xF2)?0x17:0x16;
packet[2] = vtx_freq+0x0D; // 5645 - 5900 MHz
2015-12-30 01:41:12 +01:00
packet[3] = 0x82;
packet_count++;
}
else //20 00 00 00 80 00 7d 00 84 02 64 db 04 26 79 7b
{
packet[0] = 0x20; // normal data packet
packet[2] = convert_channel_8b(THROTTLE); //Throtle
2015-12-30 01:41:12 +01:00
}
packet[4] = 0xFF - convert_channel_8b(RUDDER); //Rudder is reversed
packet[6] = 0xFF - convert_channel_8b(ELEVATOR); //Elevator is reversed
packet[8] = convert_channel_8b(AILERON); //Aileron
if(hubsan_id_data == ID_NORMAL && sub_protocol==H107)
{// H107/L/C/D, H102D
if( packet_count < 100)
{
packet[9] = 0x02 | HUBSAN_FLAG_LED | HUBSAN_FLAG_FLIP; // sends default value for the 100 first packets
packet_count++;
}
else
{
packet[9] = 0x02;
// Channel 5
if(Servo_AUX1) packet[9] |= HUBSAN_FLAG_FLIP;
// Channel 6
if(Servo_AUX2) packet[9] |= HUBSAN_FLAG_LED;
// Channel 8
if(Servo_AUX4) packet[9] |= HUBSAN_FLAG_VIDEO; // H102D
}
packet[10] = 0x64;
//const uint32_t txid = 0xdb042679;
packet[11] = 0xDB;
packet[12] = 0x04;
packet[13] = 0x26;
packet[14] = 0x79;
} else if(sub_protocol==H301)
{// H301
if( packet_count < 100)
{
packet[9] = FLAG_H301_STAB; // sends default value for the 100 first packets
packet_count++;
}
else
{
packet[9] = GET_FLAG(Servo_AUX2, FLAG_H301_LED)
| GET_FLAG(Servo_AUX3, FLAG_H301_STAB)
| GET_FLAG(Servo_AUX4, FLAG_H301_VIDEO)
| GET_FLAG(Servo_AUX1, FLAG_H301_RTH);
}
packet[10] = 0x18; // ?
packet[12] = 0x5c; // ?
packet[14] = 0xf6; // ?
2015-12-30 01:41:12 +01:00
}
else
{ //ID_PLUS && H501
packet[3] = sub_protocol==H501 ? 0x00:0x64;
packet[5] = sub_protocol==H501 ? 0x00:0x64;
packet[7] = sub_protocol==H501 ? 0x00:0x64;
if(sub_protocol==H501)
{ // H501S
packet[9] = 0x02
| GET_FLAG(Servo_AUX2, FLAG_H501_LED)
| GET_FLAG(Servo_AUX4, FLAG_H501_VIDEO)
| GET_FLAG(Servo_AUX1, FLAG_H501_RTH)
| GET_FLAG(Servo_AUX7, FLAG_H501_GPS_HOLD)
| GET_FLAG(Servo_AUX5, FLAG_H501_HEADLESS1);
//packet[10]= 0x1A;
packet[13] = GET_FLAG(Servo_AUX6, FLAG_H501_HEADLESS2)
| GET_FLAG(Servo_AUX8, FLAG_H501_ALT_HOLD)
| GET_FLAG(Servo_AUX3, FLAG_H501_SNAPSHOT);
}
else
{ // H107P/C+/D+
packet[9] = 0x06;
//FLIP|LIGHT|PICTURE|VIDEO|HEADLESS
if(Servo_AUX4) packet[9] |= HUBSAN_FLAG_VIDEO;
if(Servo_AUX5) packet[9] |= HUBSAN_FLAG_HEADLESS;
packet[10]= 0x19;
packet[12]= 0x5C; // ghost channel ?
packet[13] = 0;
if(Servo_AUX3) packet[13] = HUBSAN_FLAG_SNAPSHOT;
if(Servo_AUX1) packet[13] |= HUBSAN_FLAG_FLIP_PLUS;
packet[14]= 0x49; // ghost channel ?
}
if(packet_count < 100)
{ // set channels to neutral for first 100 packets
packet[2] = 0x80; // throttle neutral is at mid stick on plus series
packet[4] = 0x80;
packet[6] = 0x80;
packet[8] = 0x80;
packet[9] = 0x06;
packet[13]= 0x00;
packet_count++;
}
if(sub_protocol==H501)
{ // H501S
h501_packet++;
if(h501_packet == 10)
{
memset(packet, 0, 16);
packet[0] = 0xe8;
}
else if(h501_packet == 20)
{
memset(packet, 0, 16);
packet[0] = 0xe9;
}
if(h501_packet >= 20) h501_packet = 0;
}
2015-12-30 01:41:12 +01:00
}
hubsan_update_crc();
2015-12-30 01:41:12 +01:00
}
#ifdef HUBSAN_HUB_TELEMETRY
static uint8_t __attribute__((unused)) hubsan_check_integrity()
2015-12-30 01:41:12 +01:00
{
if( (packet[0]&0xFE) != 0xE0 )
return 0;
uint8_t sum = 0;
for(uint8_t i = 0; i < 15; i++)
2015-12-30 01:41:12 +01:00
sum += packet[i];
return ( packet[15] == (uint8_t)(-sum) );
2015-12-30 01:41:12 +01:00
}
#endif
2015-12-30 01:41:12 +01:00
uint16_t ReadHubsan()
{
#ifdef HUBSAN_HUB_TELEMETRY
2015-12-30 01:41:12 +01:00
static uint8_t rfMode=0;
#endif
static uint8_t txState=0;
2015-12-30 01:41:12 +01:00
uint16_t delay;
uint8_t i;
switch(phase)
{
2015-12-30 01:41:12 +01:00
case BIND_1:
bind_phase++;
if(bind_phase >= 20 && sub_protocol != H501)
{
if(hubsan_id_data == ID_NORMAL)
hubsan_id_data = ID_PLUS;
else
hubsan_id_data = ID_NORMAL;
A7105_WriteID(hubsan_id_data);
bind_phase = 0;
}
2015-12-30 01:41:12 +01:00
case BIND_3:
case BIND_5:
case BIND_7:
hubsan_build_bind_packet(phase == BIND_7 ? 9 : (phase == BIND_5 ? 1 : phase + 1 - BIND_1));
A7105_Strobe(A7105_STANDBY);
2015-12-30 01:41:12 +01:00
A7105_WriteData(16, channel);
phase |= HUBSAN_WAIT_WRITE;
2015-12-30 01:41:12 +01:00
return 3000;
case BIND_1 | HUBSAN_WAIT_WRITE:
case BIND_3 | HUBSAN_WAIT_WRITE:
case BIND_5 | HUBSAN_WAIT_WRITE:
case BIND_7 | HUBSAN_WAIT_WRITE:
2015-12-30 01:41:12 +01:00
//wait for completion
for(i = 0; i< 20; i++)
2015-12-30 01:41:12 +01:00
if(! (A7105_ReadReg(A7105_00_MODE) & 0x01))
break;
A7105_SetTxRxMode(RX_EN);
A7105_Strobe(A7105_RX);
phase &= ~HUBSAN_WAIT_WRITE;
if(hubsan_id_data == ID_PLUS)
{
if(phase == BIND_7 && packet[2] == 9)
{
phase = DATA_1;
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
BIND_DONE;
return 4500;
}
}
2015-12-30 01:41:12 +01:00
phase++;
return 4500; //7.5msec elapsed since last write
case BIND_2:
case BIND_4:
case BIND_6:
A7105_SetTxRxMode(TX_EN);
if(A7105_ReadReg(A7105_00_MODE) & 0x01) {
phase = BIND_1;
return 4500; //No signal, restart binding procedure. 12msec elapsed since last write
}
A7105_ReadData(16);
2015-12-30 01:41:12 +01:00
phase++;
if (phase == BIND_5)
A7105_WriteID(((uint32_t)packet[2] << 24) | ((uint32_t)packet[3] << 16) | ((uint32_t)packet[4] << 8) | packet[5]);
return 500; //8msec elapsed time since last write;
case BIND_8:
A7105_SetTxRxMode(TX_EN);
if(A7105_ReadReg(A7105_00_MODE) & 0x01) {
phase = BIND_7;
return 15000; //22.5msec elapsed since last write
}
A7105_ReadData(16);
if(packet[1] == 9 && hubsan_id_data == ID_NORMAL) {
2015-12-30 01:41:12 +01:00
phase = DATA_1;
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
BIND_DONE;
return 28000; //35.5msec elapsed since last write
} else {
phase = BIND_7;
return 15000; //22.5 msec elapsed since last write
}
case DATA_1:
case DATA_2:
case DATA_3:
case DATA_4:
case DATA_5:
if( txState == 0) { // send packet
#ifdef HUBSAN_HUB_TELEMETRY
2015-12-30 01:41:12 +01:00
rfMode = A7105_TX;
#endif
2015-12-30 01:41:12 +01:00
if( phase == DATA_1)
A7105_SetPower(); //Keep transmit power in sync
hubsan_build_packet();
A7105_Strobe(A7105_STANDBY);
uint8_t ch;
if((phase == DATA_5 && hubsan_id_data == ID_NORMAL) && sub_protocol == H107)
ch = channel + 0x23;
else
ch = channel;
A7105_WriteData(16, ch);
2015-12-30 01:41:12 +01:00
if (phase == DATA_5)
phase = DATA_1;
else
phase++;
delay=3000;
}
else {
#ifdef HUBSAN_HUB_TELEMETRY
if( rfMode == A7105_TX)
{// switch to rx mode 3ms after packet sent
2015-12-30 01:41:12 +01:00
for( i=0; i<10; i++)
{
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)) {// wait for tx completion
A7105_SetTxRxMode(RX_EN);
A7105_Strobe(A7105_RX);
rfMode = A7105_RX;
break;
}
}
}
if( rfMode == A7105_RX)
{ // check for telemetry frame
for( i=0; i<10; i++)
{
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01))
{ // data received
A7105_ReadData(16);
if( hubsan_check_integrity() )
{
v_lipo1=packet[13]*2;// hubsan lipo voltage 8bits the real value is h_lipo/10(0x2A=42 -> 4.2V)
2015-12-30 01:41:12 +01:00
telemetry_link=1;
}
A7105_Strobe(A7105_RX);
// Read TX RSSI
int16_t temp=256-(A7105_ReadReg(A7105_1D_RSSI_THOLD)*8)/5; // value from A7105 is between 8 for maximum signal strength to 160 or less
if(temp<0) temp=0;
else if(temp>255) temp=255;
TX_RSSI=temp;
2015-12-30 01:41:12 +01:00
break;
}
}
}
#endif
delay=1000;
}
if (++txState == 8) { // 3ms + 7*1ms
A7105_SetTxRxMode(TX_EN);
txState = 0;
}
return delay;
}
return 0;
}
uint16_t initHubsan()
{
2015-12-30 01:41:12 +01:00
const uint8_t allowed_ch[] = {0x14, 0x1e, 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a, 0x64, 0x6e, 0x78, 0x82};
A7105_Init();
2015-12-30 01:41:12 +01:00
channel = allowed_ch[MProtocol_id % sizeof(allowed_ch)];
hubsan_id_data=ID_NORMAL;
2015-12-30 01:41:12 +01:00
if(IS_BIND_IN_PROGRESS || sub_protocol==H107)
{
BIND_IN_PROGRESS; // autobind protocol
phase = BIND_1;
}
else
{
phase = DATA_1;
A7105_WriteID(MProtocol_id);
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
}
2015-12-30 01:41:12 +01:00
packet_count=0;
bind_phase=0;
#ifdef HUBSAN_HUB_TELEMETRY
init_frskyd_link_telemetry();
#endif
2015-12-30 01:41:12 +01:00
return 10000;
}
#endif