Radio control: use the Jeti protocol to communicate between a DIY sensor and a Jeti receiver (using 9 bits one wire serial)

Dependencies:   mbed

JetiC.cpp

Committer:
robertspil
Date:
2013-10-29
Revision:
0:32193823db59

File content as of revision 0:32193823db59:

/*======== communication between a sensor (processor  32 bits) and a  Jeti Duplex receiver==============
_____Jeti Data communication protocol______________________
The communication from the RX module to the JetiBox
1. It is 1-wire serial communication (9600 Bauds 9bits+parity + 2 stops))
2. 1 message sent  consists of 34 byte, 2 control character (start=0xfe finish=0xff) and 32 bytes text between these control characters.
4. 1 Byte consists of 13 bits in the format: T-D-D-D-D-D-D-D-D-I-P-S-S    (LSB first= T-0-1-2-3-4-5-6-7-I-P-S-S)
        where: T = startbit, D =  8 data bits, I control characters or text ( 1=text, 0=control), P = parity based odd , S = stop bit (two).
5. The JetiBox answers each message (34 byte)  with one byte  ACK message (with I=0: control byte)
    0xF0 =11110000 if no button (four bits with value 1 if the button is NOT pushed
    bit 4 =Right , Bit 5 = Up (or Back) , Bit 6 = Down or Select und Bit 7 Left
6.a short delay  is needed between the received message and the ACK message
     from the end of 0xff to begin of ACK :3,9 msec (mesured with Saleae)
         -TU module waits 21 msec and  reply with a new 34 bytes message => total length = 79 msec

    If there is no ACK, the TU module waits 25 msec and resend the message

____hardware_________________________
- a Mbed processor 
- one pin of the processor provides the signal (Tx and Rx) to the receiver or to a JetiBox
- I use a safety resistor 1k to 4.7k between the Pin and the Rx

___tested with 
1) a JetiBox directly connected to the Mbed processor and to a 4.8V battery
2) a a JeTi receiver , a transmitter with a Jeti TU module

The processor:  MBED  lpc1768 or LPC11u34 (The same program exists with a Ardiono Pro Mini 3.3V - with native 9 bits)

___communication between the sensor programs and the Jeti interface________________
- start the Jeti interface with a instance of the class JetiC, - afterwards the interface is running automatically  with interrupts
- process the answer from the JetiBox
   isAnswer() returns True when a answer message is received
   getAnswer() returns the answer code (look at main.cpp)


*/
/*============================================================================
 Copyright (C) 2013 Robert Spilleboudt

 This program 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 2 of the License, or
 (at your option) any later version.

 This program 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 this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
============================================================================*/

#include "core.h"

#define UART_PIN  p29     //pin for tx or rx , connect to the RX module through a 4.7k resitance
#define UART_W1 96       /* this is the pulse width , the theoretical value  is 104(microsec) for 9600 bauds but there are some bugs...
   Online compiler mbed.org
     with mbed 1768 use the theoretical value 104 
	 with mbed 11u24     use 96
	Offline compiler code sourcery (2012/03)
	  mbed 1768 use 96
	  for the 11u24: not tested because  the .bin size is too great
*/
extern  instr_data rc; //common data  for all the programs
/*-------------data for the interrupts are here---------------------------*/
volatile uint8_t UART_pos;
/*Normal text message
* UART_pos = 0 -> start sending data with this interrupt enabled, start byte with 9.bit=0
* UART_pos = 1-32 -> send display data with 9.bit=1
* UART_pos = 33 -> send end byte with 9.bit=0
* UART_pos = 34 -> set sendpos=0 and disable this interrupt
Special message when returning to the expander :\x7E\x01\x31
* UART_pos =100  start sending data with this interrupt enabled, start byte with 9.bit=0 , value \x7E
* UART_pos =101  send \x01 with 9.bit=1
* UART_pos =102  send \x31 with 9.bit=1
ending with 34 to disble the interrupt
*/
volatile unsigned 	char JETI_buffer[33]; // the message from the sensot to the JetiBox
volatile  unsigned char   UART_data ; //character transmitted or received, being filled bit / bit
volatile bool UART_isanswer; 
DigitalInOut UART_pin(UART_PIN); // pin connected to the signal from Rx module
InterruptIn  UART_startBit(UART_PIN) ; //interrupt to detect the start bit (RX)
Timeout      UART_nextBit; // pulse duration
Timeout      UART_nextTx; //next start of transmission
Ticker         UART_ticker; //ticker period= 1 msec
volatile int	      UART_Tx_start;//msec counter to the next start of  a tx message

//---------busy statistic----------------------------------------------
//char processing
volatile int   UART_counter;//within a char : next bit position value 0..8 (9 bits)+ 9=parity  10,11=stop  FOR  RX or TX
bool textchar ; //true if text, false if control
bool UART_errorchar ;// true if error (parity, stop bit)
uint8_t    UART_parity;
void ISRstartBit() ;
void ISRdataBitRx();
void ISRdataBitTx();
//----interrupt routines ---------------------------------------------------------------------------------------
void UART_tickerAdd(){ //call every msec
    UART_Tx_start--;
	if( UART_Tx_start<=0){//start the Tx message
		UART_pos = 0;
		UART_counter= -1;
		UART_nextTx.attach_us(&ISRdataBitTx , 1000); 
		UART_Tx_start=10000; //the  next start  is not yet scheduled
		UART_startBit.fall(NULL); // no RX start bit interrupt
		UART_pin.output();
	    UART_pin=1;
		UART_isanswer=false;
	}
}

void ISRstartBit() { //Rx detect the start bit, start timeout to read the first data bit
	UART_nextBit.attach_us(&ISRdataBitRx , UART_W1);
	UART_counter= -1;//preparing for the first data bit
	UART_data=0 ;//received data
	UART_errorchar = false;
	UART_parity=0;
	UART_startBit.fall(NULL);  // detach the  ISRstartBit
	UART_Tx_start =10000; // no not start Tx during the Rx receive
}
void ISRdataBitRx() { //nextbit timeout in Rx mode
	UART_counter++;
	if(UART_counter< 12) { //this is not the last bit of a character     - immediatly start the next timer interrupt
		UART_nextBit.attach_us(&ISRdataBitRx , UART_W1);  // next bit
	}
	int pinread = UART_pin.read();
	if(UART_counter<8) {    // process  the first 8 databits
		UART_data = (UART_data >> 1);   //Right shift RX_data so the new bit can be masked into the Rx_data byte.
		if(pinread==1) {
			UART_data |= 0x80;               //Set MSB of RX data if received bit == 1.
			UART_parity++;
		}
		return;
	}
	if(UART_counter==8)    {    // control bit
		textchar = pinread;
		UART_parity +=pinread;
		return;
	}
	if(UART_counter==9)    { //parity
		UART_parity +=pinread;
		if(UART_parity%2 ==0) { //UART_errorchar=true;
			UART_errorchar=true;
		}
		return;
	}
	if(UART_counter==10 || UART_counter==11)   {  //stop bits
		if(pinread==0) {
			UART_errorchar=true;
		}
		return;
	}
	//the bits of one UART character are received 
	UART_Tx_start =25 ; //this start the Tx within 25 msec//restart the TX message after 25 msec

	if(UART_errorchar || textchar) {
		//rc.jetiptr->stat.UART_err ++;
		//rc.jetiptr->status=0;
		return; //do not process a incorrect answer
	}
	UART_isanswer=true;
	rc.jetiptr->buttons= UART_data;
}


void ISRdataBitTx() { //nextbit timeout in Tx mode
	int txpin =0;
	if(UART_counter== -1) { //start a new char UART_data with the correct textchar
		switch(UART_pos) { // UART_data = next char to transmit
		case 0:// start TX message
			UART_startBit.fall(NULL);
			UART_pin.output();
			UART_pin=0;  //begin the start bit
			UART_data = 0xFE;
			textchar = false;
			UART_pos++;
			break;
		case 33: // send end byte with 9.bit=0
			UART_data = 0xFF;
			textchar = false;
			UART_pos++;
			break;
		case 34: // all data sent
			// listen to RX
			UART_pin.input();
			UART_pin.mode(PullUp);
			//UART_pin.mode(PullNone);
			UART_startBit.fall(& ISRstartBit);//detect the next received start bit
			UART_Tx_start =25 ; //this start the Tx within 25 msec//restart the TX message after 25 msec
			return;
		case 100:
			UART_data = 0x7E;
			textchar = false;
			UART_pos++;
			break;
		case 101:
			UART_data = 0x01;
			textchar = true;
			UART_pos++;
			break;
		case 102:
			UART_data = 0x31;
			textchar = true;
			UART_pos=34;
			break;

		default: // set 9.bit=1 text message
			textchar = true;
			UART_data = JETI_buffer[UART_pos-1]; // send byte from LCD buffer
			UART_pos++;     // increment to next byte
		}
		//the start bit
		txpin=0;
		UART_parity=0;
	}
	// write each bit, beginning with LSB
	if(UART_counter >=0 && UART_counter<8) {   //data bits 0..7
		int out = UART_data & 0x01;
		txpin=out;
		UART_parity += out;
		UART_data = (UART_data >>1);
	}
	if(UART_counter==8) {   //control bit
		if(textchar)
			txpin =1;
		else
			txpin=0;
		UART_parity += txpin;

	}
	if(UART_counter==9) {   //parity bit
		UART_parity++;
		txpin= UART_parity%2;
	}
	if(UART_counter<10) {
		UART_nextBit.attach_us(&ISRdataBitTx , UART_W1);  //next tx bit
		UART_counter++;
	} else { //stop  bits
		UART_counter=-1; //to start the next char
		txpin=1; // stop bits
		UART_nextBit.attach_us(&ISRdataBitTx , 2*UART_W1); //2 stop bits
	}
	UART_pin.write(txpin);
}
//----communication
JetiC::JetiC() { //start the interface with the Jeti transmitter module
	for(int i=0; i<32; i++) {
		JETI_buffer[i]=0x20   ;
	}
	JETI_buffer[0] ='S';
	JETI_buffer[1] ='E';
	JETI_buffer[2] ='N';
	JETI_buffer[3] ='S';
	JETI_buffer[4] ='O';
	JETI_buffer[5] ='R';

	JETI_buffer[32]='\0';
	
	UART_ticker.attach_us(&UART_tickerAdd, 1000); 
	UART_Tx_start =0; //this start the Tx
}
bool JetiC::isAnswer() {
	if(!UART_isanswer)
		return false;
	//message received
	UART_isanswer = false;
	UART_Tx_start =25 ; //this start the Tx within 25 msec
	return true; //return true only one time
}
//----//various utility functions to fill the buffer  without the big "printf", too big for the memeory-----------------------------
void JetiC::clear() { //fill with spaces
	pos=0;
	for(int i=0; i<32; i++)
		JETI_buffer[i]=' ';
}
void JetiC::setPos(int x) {
	if((x>=0) && (x<32))
		pos=x;
}

void JetiC::print(char c) {
	JETI_buffer[pos] = c;
	if(pos< 31)
		pos++;
}
void JetiC::print(long n , uint8_t len) {
	printNumber(n, 0,len);
}
void JetiC::printf(float value, uint8_t p  , uint8_t len) { //value ,decimal point , length
	int valint= value * pow(10.0 , p) +0.5 ;//convert the float in fixed point
	printNumber((int) valint, p, len); //print the numbers
}

void JetiC::printNumber(int valint, uint8_t decimal,uint8_t len) {  //decimal ==0 => no decimal point decimal ==2 => string as 123.45
	int i=0;
	int sign = 1;
	if(valint<0) {
		sign=0;
		valint=-valint;
	}
	while(valint > 0 || (decimal>0 &&i<decimal+2) ||(decimal==0 && i<1)) {
		if(i>0 && i==decimal) { //insert adecimal point
			JETI_buffer[pos +len-i -1] = '.';
		} else {
			JETI_buffer[pos +len-i -1] = (char)(((int)'0')+(valint % 10));
			valint /= 10;
		}
		i++;
		if(i>len-2)
			break;
	}
	if(sign==0)
		JETI_buffer[pos +len-i -1] = '-';
	else
		JETI_buffer[pos +len-i -1] = '+';

	pos+= len;
}
void  JetiC::print_p(const char *s) {
	char c;
	while((c = (*s)) !='\0') {
		s++;
		print(c);
	}
}
//--------debug the jeti protocol
void JetiC::debug_display() {
    clear();
	print_p("  JetiDebug"); //line1
	setPos(16);
	if(buttons== B_RIGHT)
		print_p("  B_RIGHT");
	if(buttons== B_DOWN)
		print_p("  B_DOWN");
	if(buttons== B_UP)
		print_p("  B_UP");
	if(buttons== B_LEFT)
		print_p("  B_LEFT");
	return;

}