/* 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 . */ //************************** // Telemetry serial code * //************************** #if defined TELEMETRY uint8_t RetrySequence ; #if ( defined(MULTI_TELEMETRY) || defined(MULTI_STATUS) ) #define MULTI_TIME 500 //in ms #define INPUT_SYNC_TIME 100 //in ms #define INPUT_ADDITIONAL_DELAY 100 // in 10µs, 100 => 1000 µs uint32_t lastMulti = 0; #endif // MULTI_TELEMETRY/MULTI_STATUS #if defined SPORT_TELEMETRY #define SPORT_TIME 12000 //12ms #define FRSKY_SPORT_PACKET_SIZE 8 #define FX_BUFFERS 4 uint32_t last = 0; uint8_t sport_counter=0; uint8_t RxBt = 0; uint8_t sport = 0; uint8_t pktx1[FRSKY_SPORT_PACKET_SIZE*FX_BUFFERS]; // Store for out of sequence packet uint8_t FrSkyX_RX_ValidSeq ; struct t_FrSkyX_RX_Frame { boolean valid; uint8_t count; uint8_t payload[6]; } ; // Store for FrskyX telemetry struct t_FrSkyX_RX_Frame FrSkyX_RX_Frames[4] ; uint8_t FrSkyX_RX_NextFrame=0; #endif // SPORT_TELEMETRY #if defined HUB_TELEMETRY #define USER_MAX_BYTES 6 uint8_t prev_index; #endif // HUB_TELEMETRY #define START_STOP 0x7e #define BYTESTUFF 0x7d #define STUFF_MASK 0x20 #define MAX_PKTX 10 uint8_t pktx[MAX_PKTX]; uint8_t indx; uint8_t frame[18]; #if ( defined(MULTI_TELEMETRY) || defined(MULTI_STATUS) ) static void multi_send_header(uint8_t type, uint8_t len) { Serial_write('M'); #ifdef MULTI_TELEMETRY Serial_write('P'); Serial_write(type); #else (void)type; #endif Serial_write(len); } static void multi_send_status() { multi_send_header(MULTI_TELEMETRY_STATUS, 5); // Build flags uint8_t flags=0; if (IS_INPUT_SIGNAL_on) flags |= 0x01; if (mode_select==MODE_SERIAL) flags |= 0x02; if (remote_callback != 0) { flags |= 0x04; if (IS_WAIT_BIND_on) flags |= 0x10; else if (IS_BIND_IN_PROGRESS) flags |= 0x08; #ifdef FAILSAFE_ENABLE //Is failsafe supported? switch (protocol) { case PROTO_HISKY: if(sub_protocol!=HK310) break; case PROTO_AFHDS2A: case PROTO_DEVO: case PROTO_SFHSS: case PROTO_WK2x01: case PROTO_FRSKYX: flags |= 0x20; //Yes default: break; } #endif } Serial_write(flags); // Version number example: 1.1.6.1 Serial_write(VERSION_MAJOR); Serial_write(VERSION_MINOR); Serial_write(VERSION_REVISION); Serial_write(VERSION_PATCH_LEVEL); } #endif #ifdef DSM_TELEMETRY #ifdef MULTI_TELEMETRY void DSM_frame() { if (packet_in[0] == 0x80) { multi_send_header(MULTI_TELEMETRY_DSMBIND, 10); for (uint8_t i = 1; i < 11; i++) // 10 bytes of DSM bind response Serial_write(packet_in[i]); } else { multi_send_header(MULTI_TELEMETRY_DSM, 17); for (uint8_t i = 0; i < 17; i++) // RSSI value followed by 16 bytes of telemetry data Serial_write(packet_in[i]); } } #else void DSM_frame() { Serial_write(0xAA); // Telemetry packet for (uint8_t i = 0; i < 17; i++) // RSSI value followed by 16 bytes of telemetry data Serial_write(packet_in[i]); } #endif #endif #ifdef SCANNER_TELEMETRY void spectrum_scanner_frame() { #if defined MULTI_TELEMETRY multi_send_header(MULTI_TELEMETRY_SCANNER, SCAN_CHANS_PER_PACKET + 1); #else Serial_write(0xAA); // Telemetry packet #endif Serial_write(packet_in[0]); // start channel for(uint8_t ch = 0; ch < SCAN_CHANS_PER_PACKET; ch++) Serial_write(packet_in[ch+1]); // RSSI power levels } #endif #ifdef FRSKYX_RX_TELEMETRY void frskyx_rx_channels_frame() { #if defined MULTI_TELEMETRY multi_send_header(MULTI_TELEMETRY_RX_CHANNELS, 26); #else Serial_write(0xAA); // Telemetry packet #endif for (uint8_t i = 0; i < 26; i++) Serial_write(packet_in[i]); // pps, rssi, ch start, ch count, 16x ch data } #endif #ifdef AFHDS2A_FW_TELEMETRY void AFHDSA_short_frame() { #if defined MULTI_TELEMETRY multi_send_header(packet_in[29]==0xAA?MULTI_TELEMETRY_AFHDS2A:MULTI_TELEMETRY_AFHDS2A_AC, 29); #else Serial_write(packet_in[29]); // Telemetry packet 0xAA or 0xAC #endif for (uint8_t i = 0; i < 29; i++) // RSSI value followed by 4*7 bytes of telemetry data Serial_write(packet_in[i]); } #endif #ifdef HITEC_FW_TELEMETRY void HITEC_short_frame() { #if defined MULTI_TELEMETRY multi_send_header(MULTI_TELEMETRY_HITEC, 8); #else Serial_write(0xAA); // Telemetry packet #endif for (uint8_t i = 0; i < 8; i++) // TX RSSI and TX LQI values followed by frame number and 5 bytes of telemetry data Serial_write(packet_in[i]); } #endif #ifdef MULTI_TELEMETRY static void multi_send_frskyhub() { multi_send_header(MULTI_TELEMETRY_HUB, 9); for (uint8_t i = 0; i < 9; i++) Serial_write(frame[i]); } #endif void frskySendStuffed() { Serial_write(START_STOP); for (uint8_t i = 0; i < 9; i++) { if ((frame[i] == START_STOP) || (frame[i] == BYTESTUFF)) { Serial_write(BYTESTUFF); frame[i] ^= STUFF_MASK; } Serial_write(frame[i]); } Serial_write(START_STOP); } void frsky_check_telemetry(uint8_t *packet_in,uint8_t len) { if(packet_in[1] != rx_tx_addr[3] || packet_in[2] != rx_tx_addr[2] || len != packet_in[0] + 3 ) return; // Bad address or length... telemetry_link|=1; // Telemetry data is available // RSSI and LQI are the 2 last bytes TX_RSSI = packet_in[len-2]; if(TX_RSSI >=128) TX_RSSI -= 128; else TX_RSSI += 128; TX_LQI = packet_in[len-1]&0x7F; #if defined FRSKYD_CC2500_INO if (protocol==PROTO_FRSKYD) { //Save current buffer for (uint8_t i=3;i0 && telemetry_in_buffer[6]<=10) { //Telemetry length ok if ( ( telemetry_in_buffer[7] & 0x1F ) == (telemetry_counter & 0x1F) ) {//Sequence is ok uint8_t topBit = 0 ; if ( telemetry_counter & 0x80 ) if ( ( telemetry_counter & 0x1F ) != RetrySequence ) topBit = 0x80 ; telemetry_counter = ( (telemetry_counter+1)%32 ) | topBit ; // Request next telemetry frame } else {//Incorrect sequence RetrySequence = telemetry_in_buffer[7] & 0x1F ; telemetry_counter |= 0x80 ; telemetry_in_buffer[6]=0 ; // Discard current packet and wait for retransmit } } else telemetry_in_buffer[6]=0; // Discard packet } #endif #if defined SPORT_TELEMETRY && defined FRSKYX_CC2500_INO if (protocol==PROTO_FRSKYX) { /*Telemetry frames(RF) SPORT info 15 bytes payload SPORT frame valid 6+3 bytes [00] PKLEN 0E 0E 0E 0E [01] TXID1 DD DD DD DD [02] TXID2 6D 6D 6D 6D [03] CONST 02 02 02 02 [04] RS/RB 2C D0 2C CE //D0;CE=2*RSSI;....2C = RX battery voltage(5V from Bec) [05] HD-SK 03 10 21 32 //TX/RX telemetry hand-shake bytes [06] NO.BT 00 00 06 03 //No.of valid SPORT frame bytes in the frame [07] STRM1 00 00 7E 00 [08] STRM2 00 00 1A 00 [09] STRM3 00 00 10 00 [10] STRM4 03 03 03 03 [11] STRM5 F1 F1 F1 F1 [12] STRM6 D1 D1 D0 D0 [13] CHKSUM1 --|2 CRC bytes sent by RX (calculated on RX side crc16/table) [14] CHKSUM2 --|*/ telemetry_lost=0; uint16_t lcrc = FrSkyX_crc(&packet_in[3], len-7 ) ; if ( ( (lcrc >> 8) != packet_in[len-4]) || ( (lcrc & 0x00FF ) != packet_in[len-3]) ) return; // Bad CRC if(packet_in[4] & 0x80) RX_RSSI=packet_in[4] & 0x7F ; else RxBt = (packet_in[4]<<1) + 1 ; //Save outgoing telemetry sequence FrSkyX_TX_IN_Seq=packet_in[5] >> 4; //Check incoming telemetry sequence uint8_t packet_seq=packet_in[5] & 0x03; if ( packet_in[5] & 0x08 ) {//Request init FrSkyX_RX_Seq = 0x08 ; FrSkyX_RX_NextFrame = 0x00 ; FrSkyX_RX_Frames[0].valid = false ; FrSkyX_RX_Frames[1].valid = false ; FrSkyX_RX_Frames[2].valid = false ; FrSkyX_RX_Frames[3].valid = false ; } else if ( packet_seq == (FrSkyX_RX_Seq & 0x03 ) ) {//In sequence struct t_FrSkyX_RX_Frame *p ; uint8_t count ; // packet_in[4] RSSI // packet_in[5] sequence control // packet_in[6] payload count // packet_in[7-12] payload p = &FrSkyX_RX_Frames[packet_seq] ; count = packet_in[6]; // Payload length if ( count <= 6 ) {//Store payload p->count = count ; for ( uint8_t i = 0 ; i < count ; i++ ) p->payload[i] = packet_in[i+7] ; } else p->count = 0 ; // Discard p->valid = true ; FrSkyX_RX_Seq = ( FrSkyX_RX_Seq + 1 ) & 0x03 ; // Move to next sequence if ( FrSkyX_RX_ValidSeq & 0x80 ) { FrSkyX_RX_Seq = ( FrSkyX_RX_ValidSeq + 1 ) & 3 ; FrSkyX_RX_ValidSeq &= 0x7F ; } } else {//Not in sequence debugln("NS"); struct t_FrSkyX_RX_Frame *q ; uint8_t count ; // packet_in[4] RSSI // packet_in[5] sequence control // packet_in[6] payload count // packet_in[7-12] payload if ( packet_seq == ( ( FrSkyX_RX_Seq +1 ) & 3 ) ) {//Received next sequence -> save it q = &FrSkyX_RX_Frames[packet_seq] ; count = packet_in[6]; // Payload length if ( count <= 6 ) {//Store payload q->count = count ; for ( uint8_t i = 0 ; i < count ; i++ ) q->payload[i] = packet_in[i+7] ; } else q->count = 0 ; q->valid = true ; FrSkyX_RX_ValidSeq = 0x80 | packet_seq ; } FrSkyX_RX_Seq = ( FrSkyX_RX_Seq & 0x03 ) | 0x04 ; // Request re-transmission of original sequence } } #endif } void init_frskyd_link_telemetry() { telemetry_link=0; telemetry_counter=0; v_lipo1=0; v_lipo2=0; RX_RSSI=0; TX_RSSI=0; RX_LQI=0; TX_LQI=0; } void frsky_link_frame() { frame[0] = 0xFE; // Link frame if (protocol==PROTO_FRSKYD) { frame[1] = telemetry_in_buffer[3]; // A1 frame[2] = telemetry_in_buffer[4]; // A2 frame[3] = telemetry_in_buffer[5]; // RX_RSSI telemetry_link &= ~1 ; // Sent telemetry_link |= 2 ; // Send hub if available } else if (protocol==PROTO_HUBSAN||protocol==PROTO_AFHDS2A||protocol==PROTO_BAYANG||protocol==PROTO_NCC1701||protocol==PROTO_CABELL||protocol==PROTO_HITEC||protocol==PROTO_BUGS||protocol==PROTO_BUGSMINI) { frame[1] = v_lipo1; frame[2] = v_lipo2; frame[3] = RX_RSSI; telemetry_link=0; } frame[4] = TX_RSSI; frame[5] = RX_LQI; frame[6] = TX_LQI; frame[7] = frame[8] = 0; #if defined MULTI_TELEMETRY multi_send_frskyhub(); #else frskySendStuffed(); #endif } #if defined HUB_TELEMETRY void frsky_user_frame() { if(telemetry_in_buffer[6]) {//only send valid hub frames frame[0] = 0xFD; // user frame if(telemetry_in_buffer[6]>USER_MAX_BYTES) { frame[1]=USER_MAX_BYTES; // packet size telemetry_in_buffer[6]-=USER_MAX_BYTES; telemetry_link |= 2 ; // 2 packets need to be sent } else { frame[1]=telemetry_in_buffer[6]; // packet size telemetry_link=0; // only 1 packet or processing second packet } frame[2] = telemetry_in_buffer[7]; for(uint8_t i=0;i> 8; //0-100 crc_s &= 0x00ff; } Serial_write(0xff - crc_s); } #else void sportSend(uint8_t *p) { uint16_t crc_s = 0; Serial_write(START_STOP);//+9 Serial_write(p[0]) ; for (uint8_t i = 1; i < 9; i++) { if (i == 8) p[i] = 0xff - crc_s; if ((p[i] == START_STOP) || (p[i] == BYTESTUFF)) { Serial_write(BYTESTUFF);//stuff again Serial_write(STUFF_MASK ^ p[i]); } else Serial_write(p[i]); crc_s += p[i]; //0-1FF crc_s += crc_s >> 8; //0-100 crc_s &= 0x00ff; } } #endif void sportIdle() { #if !defined MULTI_TELEMETRY Serial_write(START_STOP); #endif } void sportSendFrame() { uint8_t i; sport_counter = (sport_counter + 1) %36; if(telemetry_lost) { sportIdle(); return; } if(sport_counter<6) { frame[0] = 0x98; frame[1] = 0x10; for (i=5;i<8;i++) frame[i]=0; } switch (sport_counter) { case 0: frame[2] = 0x05; frame[3] = 0xf1; frame[4] = 0x02 ;//dummy values if swr 20230f00 frame[5] = 0x23; frame[6] = 0x0F; break; case 2: // RSSI frame[2] = 0x01; frame[3] = 0xf1; frame[4] = RX_RSSI; frame[5] = TX_RSSI; frame[6] = RX_LQI; frame[7] = TX_LQI; break; case 4: //BATT frame[2] = 0x04; frame[3] = 0xf1; frame[4] = RxBt;//a1; break; default: if(sport) { for (i=0;i= FRSKY_SPORT_PACKET_SIZE) {//8 bytes no crc if ( sport < FX_BUFFERS ) { uint8_t dest = sport * FRSKY_SPORT_PACKET_SIZE ; uint8_t i ; for ( i = 0 ; i < FRSKY_SPORT_PACKET_SIZE ; i++ ) pktx1[dest++] = pktx[i] ; // Triple buffer sport += 1 ;//ok to send } // else // { // // Overrun // } pass = 0;//reset } } #endif void TelemetryUpdate() { // check for space in tx buffer #ifdef BASH_SERIAL uint8_t h ; uint8_t t ; h = SerialControl.head ; t = SerialControl.tail ; if ( h >= t ) t += TXBUFFER_SIZE - h ; else t -= h ; if ( t < 64 ) { return ; } #else uint8_t h ; uint8_t t ; h = tx_head ; t = tx_tail ; if ( h >= t ) t += TXBUFFER_SIZE - h ; else t -= h ; if ( t < 32 ) { return ; } #endif #if ( defined(MULTI_TELEMETRY) || defined(MULTI_STATUS) ) { uint32_t now = millis(); if ((now - lastMulti) > MULTI_TIME) { multi_send_status(); lastMulti = now; return; } } #endif #if defined SPORT_TELEMETRY if (protocol==PROTO_FRSKYX) { // FrSkyX for(;;) { //Empty buffer struct t_FrSkyX_RX_Frame *p ; uint8_t count ; p = &FrSkyX_RX_Frames[FrSkyX_RX_NextFrame] ; if ( p->valid ) { count = p->count ; for (uint8_t i=0; i < count ; i++) proces_sport_data(p->payload[i]) ; p->valid = false ; // Sent FrSkyX_RX_NextFrame = ( FrSkyX_RX_NextFrame + 1 ) & 3 ; } else break ; } telemetry_link=0; sportSendFrame(); } #endif // SPORT_TELEMETRY #if defined DSM_TELEMETRY if(telemetry_link && protocol == PROTO_DSM) { // DSM DSM_frame(); telemetry_link=0; return; } #endif #if defined AFHDS2A_FW_TELEMETRY if(telemetry_link == 2 && protocol == PROTO_AFHDS2A) { AFHDSA_short_frame(); telemetry_link=0; return; } #endif #if defined HITEC_FW_TELEMETRY if(telemetry_link == 2 && protocol == PROTO_HITEC) { HITEC_short_frame(); telemetry_link=0; return; } #endif #if defined SCANNER_TELEMETRY if (telemetry_link && protocol == PROTO_SCANNER) { spectrum_scanner_frame(); telemetry_link = 0; return; } #endif #if defined FRSKYX_RX_TELEMETRY if (telemetry_link && protocol == PROTO_FRSKYX_RX) { frskyx_rx_channels_frame(); telemetry_link = 0; return; } #endif if((telemetry_link & 1 )&& protocol != PROTO_FRSKYX) { // FrSkyD + Hubsan + AFHDS2A + Bayang + Cabell + Hitec + Bugs + BugsMini + NCC1701 frsky_link_frame(); return; } #if defined HUB_TELEMETRY if((telemetry_link & 2) && protocol == PROTO_FRSKYD) { // FrSkyD frsky_user_frame(); return; } #endif } /**************************/ /**************************/ /** Serial TX routines **/ /**************************/ /**************************/ #ifndef BASH_SERIAL // Routines for normal serial output void Serial_write(uint8_t data) { uint8_t nextHead ; nextHead = tx_head + 1 ; if ( nextHead >= TXBUFFER_SIZE ) nextHead = 0 ; tx_buff[nextHead]=data; tx_head = nextHead ; tx_resume(); } void initTXSerial( uint8_t speed) { #ifdef ENABLE_PPM if(speed==SPEED_9600) { // 9600 #ifdef ORANGE_TX USARTC0.BAUDCTRLA = 207 ; USARTC0.BAUDCTRLB = 0 ; USARTC0.CTRLB = 0x18 ; USARTC0.CTRLA = (USARTC0.CTRLA & 0xCF) | 0x10 ; USARTC0.CTRLC = 0x03 ; #else #ifdef STM32_BOARD usart3_begin(9600,SERIAL_8N1); //USART3 USART3_BASE->CR1 &= ~ USART_CR1_RE; //disable RX leave TX enabled #else UBRR0H = 0x00; UBRR0L = 0x67; UCSR0A = 0 ; // Clear X2 bit //Set frame format to 8 data bits, none, 1 stop bit UCSR0C = (1<CR1 &= ~ USART_CR1_RE; //disable RX leave TX enabled #else UBRR0H = 0x00; UBRR0L = 0x22; UCSR0A = 0x02 ; // Set X2 bit //Set frame format to 8 data bits, none, 1 stop bit UCSR0C = (1<CR1 &= ~ USART_CR1_RE; //disable RX leave TX enabled #else UBRR0H = 0x00; UBRR0L = 0x07; UCSR0A = 0x00 ; // Clear X2 bit //Set frame format to 8 data bits, none, 1 stop bit UCSR0C = (1<SR & USART_SR_TXE) { #endif if(tx_head!=tx_tail) { if(++tx_tail>=TXBUFFER_SIZE)//head tx_tail=0; #ifdef STM32_BOARD USART3_BASE->DR=tx_buff[tx_tail];//clears TXE bit #else UDR0=tx_buff[tx_tail]; #endif } if (tx_tail == tx_head) { tx_pause(); // Check if all data is transmitted . if yes disable transmitter UDRE interrupt } #ifdef STM32_BOARD } #endif } #else //BASH_SERIAL // Routines for bit-bashed serial output // Speed is 0 for 100K and 1 for 9600 void initTXSerial( uint8_t speed) { TIMSK0 = 0 ; // Stop all timer 0 interrupts #ifdef INVERT_SERIAL SERIAL_TX_off; #else SERIAL_TX_on; #endif UCSR0B &= ~(1<>= 7 ; // Top bit if ( SerialControl.speed == SPEED_100K ) { #ifdef INVERT_SERIAL byteLo |= 0x02 ; // Parity bit #else byteLo |= 0xFC ; // Stop bits #endif // calc parity temp = byte ; temp >>= 4 ; temp = byte ^ temp ; temp1 = temp ; temp1 >>= 2 ; temp = temp ^ temp1 ; temp1 = temp ; temp1 <<= 1 ; temp ^= temp1 ; temp &= 0x02 ; #ifdef INVERT_SERIAL byteLo ^= temp ; #else byteLo |= temp ; #endif } else { byteLo |= 0xFE ; // Stop bit } byte <<= 1 ; #ifdef INVERT_SERIAL byte |= 1 ; // Start bit #endif uint8_t next = SerialControl.head + 2; if(next>=TXBUFFER_SIZE) next=0; if ( next != SerialControl.tail ) { SerialControl.data[SerialControl.head] = byte ; SerialControl.data[SerialControl.head+1] = byteLo ; SerialControl.head = next ; } if(!IS_TX_PAUSE_on) tx_resume(); } void resumeBashSerial() { cli() ; if ( SerialControl.busy == 0 ) { sei() ; // Start the transmission here #ifdef INVERT_SERIAL GPIOR2 = 0 ; #else GPIOR2 = 0x01 ; #endif if ( SerialControl.speed == SPEED_100K ) { GPIOR1 = 1 ; OCR0B = TCNT0 + 40 ; OCR0A = OCR0B + 210 ; TIFR0 = (1<>= 1 GPIOR0 = byte ; if ( --GPIOR1 == 0 ) { TIMSK0 &= ~(1<>= 1 GPIOR2 = byte ; if ( --GPIOR1 == 0 ) { if ( IS_TX_PAUSE_on ) { SerialControl.busy = 0 ; TIMSK0 &= ~(1<head != ptr->tail ) { GPIOR0 = ptr->data[ptr->tail] ; GPIOR2 = ptr->data[ptr->tail+1] ; uint8_t nextTail = ptr->tail + 2 ; if ( nextTail >= TXBUFFER_SIZE ) nextTail = 0 ; ptr->tail = nextTail ; GPIOR1 = 8 ; OCR0A = OCR0B + 40 ; OCR0B = OCR0A + 8 * 20 ; TIMSK0 |= (1< 2 ) byte = GPIOR0 ; else byte = GPIOR2 ; if ( byte & 0x01 ) SERIAL_TX_on; else SERIAL_TX_off; byte /= 2 ; // Generates shorter code than byte >>= 1 if ( GPIOR1 > 2 ) GPIOR0 = byte ; else GPIOR2 = byte ; if ( --GPIOR1 == 0 ) { // prepare next byte volatile struct t_serial_bash *ptr = &SerialControl ; if ( ptr->head != ptr->tail ) { GPIOR0 = ptr->data[ptr->tail] ; GPIOR2 = ptr->data[ptr->tail+1] ; uint8_t nextTail = ptr->tail + 2 ; if ( nextTail >= TXBUFFER_SIZE ) nextTail = 0 ; ptr->tail = nextTail ; GPIOR1 = 10 ; } else { SerialControl.busy = 0 ; TIMSK0 &= ~(1<