/*
rf433.cpp - Arduino Library for RF433.
Copyright 2013-2014 - Laurent Menu-Kerforn
    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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include "Arduino.h"
#include "rf433.h"


/* ==================================================
 _                    
| |__   __ _ ___  ___ 
| '_ \ / _` / __|/ _ \
| |_) | (_| \__ \  __/
|_.__/ \__,_|___/\___|
                      
===================================================== */
rf433::rf433() {
	powerled=-1;
	powerpin=-1;
	activityled=-1;
	}

int rf433::init() {
	return reset();
	}

word rf433::micro2ticks(unsigned long us){
	unsigned int ret;
	us>>=2; //division par 4
	if(us>65535) return 0;
	ret=us;
	return ret;}

unsigned long rf433::ticks2micro(word ticks){
	unsigned long ret=(unsigned long)ticks;
	ret<<=2; //division par 4
	return ret;}

int rf433::poweron() {
	setpower(HIGH);
	setpowerled(HIGH);
	return 1;}

int rf433::poweroff(){
	setpower(LOW);
	setpowerled(LOW);
	return 1;}


int rf433::setactivityledpin(int pin) {
	activityled=pin;
	if(pin>=0) {
		pinMode(activityled,OUTPUT);
		digitalWrite(activityled,LOW);
		};
	return 1;
	}

int rf433::setpowerpin(int pin) {
	powerpin=pin;
	if(pin>=0) {
		pinMode(powerpin,OUTPUT);
		digitalWrite(powerpin,LOW);
		};
	return 1;}

int rf433::setpowerledpin(int pin) {
	powerled=pin;
	if(pin>=0) {
		pinMode(powerled,OUTPUT);
		digitalWrite(powerled,LOW);
		};
	return 1;
	}

int rf433::setpowerled(int level) {
	if(powerled>=0) digitalWrite(powerled,level);
	return 1;
	}

int rf433::setactivityled(int level) {
	if(activityled>=0) digitalWrite(activityled,level);
	return 1;
	}

int rf433::setpower(int level) {
	if(powerpin>=0) digitalWrite(powerpin,level);
	return 1;
	}


/* debug */

/*
unsigned long collect[256];
int cc=0;
*/

/* fin debug */

/* exemple utilisation timer 1
cli();
//set timer1 interrupt at 1Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 300;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
sei();
}


ISR(TIMER1_COMPA_vect){//timer1 interrupt 1Hz toggles pin 13 (LED)
//generates pulse wave of frequency 1Hz/2 = 0.5kHz (takes two cycles for full wave- toggle high then toggle low)
static int min=10;
static int max=160;
static int step=5;

static int level=min;
static int inc=step;
level+=inc;
if(level<min) {inc=step;level=min;};
if(level>255) {inc=-step;level=max;};
analogWrite(8,level);
}
*/

/* ==================================================
                   _           
 _ __ ___  ___ ___(_)_   _____ 
| '__/ _ \/ __/ _ \ \ \ / / _ \
| | |  __/ (_|  __/ |\ V /  __/
|_|  \___|\___\___|_| \_/ \___|
                               
===================================================== */
rf433r::rf433r() {
	rxindex=0;
	minsiglen=_RF_MINSIGTICKS;
	minrxtrans=_RF_MINRXTRANS;
	}

int rf433r::setminsiglen(long len) {
	minsiglen=micro2ticks(len);
	}

int rf433r::enableint() {
	cli();
	_RF_TIMSK|=(1<<_RF_ICIE); //interruption sur Capture Interrupt
	// pin 8 ICP1 
	sei();
	return 1;
	}

int rf433r::disableint() {
	cli();
	_RF_TIMSK&=~(1<<_RF_ICIE); //interruption sur Capture Interrupt
	sei();
	return 1;
	}

int rf433r::dump() {
	Serial.println("======================");
	Serial.print("TCNT : "); Serial.println(_RF_TCNT);
	Serial.print("TCCRA : "); Serial.println(_RF_TCCRA);
	Serial.print("TCCRB : "); Serial.println(_RF_TCCRB);
	Serial.print("TIMSK : "); Serial.println(_RF_TIMSK);
	Serial.print("TIFR : "); Serial.println(_RF_TIFR);
	Serial.print("ACSR : "); Serial.println(_RF_ACSR);
	Serial.print("activityled : "); Serial.println(activityled);
	Serial.print("powerpin : "); Serial.println(powerpin);
	Serial.print("powerled : "); Serial.println(powerled);
	Serial.print("rxindex : "); Serial.println(rxindex);
	Serial.print("minsiglen : "); Serial.println(minsiglen);
	Serial.print("_RF_ICES : "); Serial.println(_RF_ICES);
	Serial.print("_RF_ICF : "); Serial.println(_RF_ICF);
	Serial.print("_RF_ICIE : "); Serial.println(_RF_ICIE);
	Serial.print("_RF_ACIC : "); Serial.println(_RF_ACIC);
	for(int i=0;i<rxindex;i++) {
		Serial.print(i);
		Serial.print(',');
		Serial.print(trans[i].level);
		Serial.print(',');
		Serial.println(ticks2micro(trans[i].ticks));
		};
	return 1;
	}

int rf433r::isr() {
	byte level;
	volatile static unsigned long voidsum;
	volatile static unsigned int voidcount;
	word curcount;
	int val;
	curcount=_RF_ICR;
	_RF_TCNT=0;
	//determination niveau que l'on vient de quitter
	// en fonction sens transition
	//on vient de quitter un level LOW quand la transition
	//declenchant l'interruption était montante
	level=(_RF_TCCRB & (1<<_RF_ICES))? LOW:HIGH;

	//changement sens transition
	//si on était low on vient donc de monter à HIGH et on attend la fin du signal donc transition vers LOW
	if(level==LOW) _RF_TCCRB&=~(1<<_RF_ICES);
	else _RF_TCCRB|=1<<_RF_ICES;
	//effacement interruption (car changement de front) c'est dans la doc
	_RF_TIFR|=(1<< _RF_ICF); //effacement interruption en cours	

	//on écarte l'enregistrement sur le premier front montant 
	if(!rxindex&&level==LOW) return 1;
	//on ne commence l'enregistrement que quand le premier 1 a
	//duré plus de minlen (en ticks)
	if(!rxindex&&curcount<minsiglen) return 1;	

	if(curcount<minsiglen) {
		//signal pas assez long
		voidsum+=curcount;
		voidcount++;
		//on a pas eu un nombre de transition significatif (minrxtrans)
		if(rxindex<minrxtrans) {
			rxindex=0;
			_RF_TCCRB|=(1<<_RF_ICES); // front montant 
			_RF_TIFR|=(1<< _RF_ICF); //effacement interruption en cours
			setactivityled((level==LOW)? HIGH:LOW);
			return 1;
			};
		//enregistrement dans cellule précedente si void aussi
		if(rxindex&&trans[rxindex-1].level>1) rxindex--;
		//enregistrement si dans les clous
		if(rxindex<_RF_MAXRX) {
			if(voidcount<246) trans[rxindex].level=10+voidcount;
			trans[rxindex].ticks=voidsum;
			rxindex++;
			};
		return 1;
		}
	else {
		//signal assez long
		voidsum=0;
		voidcount=0;
		//on entre sur le niveau différent
		setactivityled((level==LOW)? HIGH:LOW);
		if(rxindex<_RF_MAXRX) {
			trans[rxindex].level=level;
			trans[rxindex].ticks=curcount;
			rxindex++;
			};
		return 1;
		}
	}

int rf433r::reset() {
//	Serial.println("RESET");
	cli();
	pinMode(_RF_RXPIN,INPUT); //pas sur que ce soit nécessaire
	// digitalWrite(_RF_RXPIN,LOW);	// mode pull up, naze
	_RF_TIMSK=0; //pas d'interruption
	_RF_TCCRA=0; //mode standard
//	_RF_ACSR&=~(1<<_RF_ACIC); //sur ICP,pas analog bidule
	_RF_TCCRB=(1<<_RF_ICNC)|(1<<_RF_ICES)|(1<< _RF_CS1)|(1<<_RF_CS0); 
	//prescaler=64, intr sur front montant, noise canceller   
	// 1 tick =4 µs
	_RF_OCRA = 0; 
	sei();
	rxindex=0;
	setactivityledpin(activityled);
	setpowerpin(powerpin);
	setpowerledpin(powerled);
	setactivityled(LOW);
	return 1;
	}

int rf433r::bufferfull() {
	return (rxindex==_RF_MAXRX);
	} 

int rf433r::start() {
// Serial.println("START !");
cli();
	rxindex=0;
	cli();
	_RF_TCCRB|=(1<<_RF_ICES); // front montant 
	_RF_TIFR|=(1<< _RF_ICF); //effacement interruption en cours
	_RF_TIMSK|=(1<<_RF_ICIE); //autrisation interuption
	_RF_TCNT=0; //mise à 0 du compteur
	sei();
	}

int rf433r::stop() {
	disableint();
	setactivityled(LOW);
	}

/* ==================================================
                _ _   
  ___ _ __ ___ (_) |_ 
 / _ \ '_ ` _ \| | __|
|  __/ | | | | | | |_ 
 \___|_| |_| |_|_|\__|
                      
===================================================== */
int rf433t::setemitpin(int pin) {
	emitpin=pin;
	pinMode(emitpin,OUTPUT);
	digitalWrite(emitpin,LOW);
	return 1;}

int rf433t::slowit(){
	cli();
	_RF_TCCRB|=(1<< _RF_CS2)|(1<<_RF_CS0); //prescaler=1024   
	sei();
	}	

int rf433t::reset(){
//	Serial.println("RESET ******************************************");
// NE JAMAIS METTRE LE SETUP DES I/O DANS LE CONSTRUCTEUR !
// ELLES SONT RESETÉES AU DEFAUT AVANT LA FONCTION setup()
	cli();
	_RF_TIMSK=0;
	_RF_TCCRA=0;
	_RF_TCCRB=0;
	_RF_TCCRB|=(1<<_RF_WGM2); //CTC mode
	_RF_TCCRB|=(1<< _RF_CS1)|(1<<_RF_CS0); //prescaler=64   
	sei();
	setactivityledpin(activityled);
	setpowerpin(powerpin);
	setpowerledpin(powerled);
	setemitpin(emitpin);
	// calcul conversion µs/ticks
	// une µs =1 megaherz
	// prescale 8 : 2MHz, 2 ticks pour une µs, pb : 32768 delay maximum
	// prescale 64 : 250kHz, 4 µs pour 1 tick
	// prescale 256 : 31,250 kHz 32 µs pour 1 tick
	// prescale 20124 : 3906 Hz, 256µs pour 1 tick
	// On part sur un prescale de 64, on peut avoir le nombre de ticks en shiftant de 2 à droite ( >> 2 )
	// la précision ) 4 µs près est correcte
	// le délai max est de 65*4 µs maximum 
	resetemit();	
	return 1;
	}

int rf433t::resetemit() {
	//on commence par arrêter l'envoi en cours si il y en a un
	disableint();
	_RF_OCRA = 50000; //200000 µs 0,2 secondes
	status=0;
	setactivityled(LOW);
	digitalWrite(emitpin,LOW);
	transindex=0;
	transcount=0;
	repeatcount=0;
	trans[0].level=0;
	trans[0].ticks=0;
	return 1;
	}

rf433t::rf433t(int pin) {
	ordor[0]=0;
	ordor[1]=0;
	coeff[0]=60;
	coeff[1]=60;
	repeat=1;
	emitpin=pin;
	}

int rf433t::isrunning() {
	return status;
	}

int rf433t::recus(int level,unsigned long us) {
	int lindex;
	if(transcount+2>_RF_MAXTRANS) return 0;
	trans[transcount].level=level;
	trans[transcount].ticks=micro2ticks(us);
	transcount++;
	return 1;
	}

int rf433t::recbit(int level,int count) {
	unsigned long us;
	int levelindex;
	levelindex=(level==LOW)? 0:1;
	us=count*coeff[levelindex]+ordor[levelindex];
	return recus(level,us);
	}

int rf433t::bufferize(char *code) {
	char *p;
	int level;
	int first=1;
	int xmat=-1;
	int curlevel=-1;
	int curlevelcount=0;
	// reset();
	resetemit();
	addheader();
	for(p=code;*p;p++) {
		level=(*p=='0')? LOW:HIGH;
		if(!first && level!=curlevel) {
			//enregistrement transisiton précédente
			recbit(curlevel,curlevelcount);
			curlevelcount=0;
			};
		curlevel=level;
		curlevelcount++;
		first=0;
		};
	if(!first) recbit(curlevel,curlevelcount);
	addtailer();
	return transcount;
	}

int rf433t::enableint(){
	cli();
	_RF_TIMSK|=(1<<_RF_OCIEA); //interruption sur timer A
	sei();
	return 1;}

int rf433t::disableint(){
	cli();
	_RF_TIMSK&=~(1<<_RF_OCIEA); //pas d'interruption sur timer A
	sei();
	return 1;}

int rf433t::dump(){
	Serial.println("======================");
	Serial.print("TCCRA : "); Serial.println(_RF_TCCRA);
	Serial.print("TCCRB : "); Serial.println(_RF_TCCRB);
	Serial.print("TIMSK : "); Serial.println(_RF_TIMSK);
	Serial.print("TIFR : "); Serial.println(_RF_TIFR);
	Serial.print("status : "); Serial.println(status);
	Serial.print("repeat : "); Serial.println(repeat);
	Serial.print("repeatcount : "); Serial.println(repeatcount);
	Serial.print("transindex : "); Serial.println(transindex);
	Serial.print("transcount : "); Serial.println(transcount);
	Serial.print("emitpin : "); Serial.println(emitpin);
	Serial.print("activityled : "); Serial.println(activityled);
	Serial.print("powerpin : "); Serial.println(powerpin);
	Serial.print("powerled : "); Serial.println(powerled);
	for(int i=0;i<transcount;i++) {
		Serial.print(i);
		Serial.print(" : ");
		Serial.print(trans[i].level);
		Serial.print(" ");
		Serial.println(ticks2micro(trans[i].ticks));
		};
	return 1;}

//0=infini
int rf433t::repeatmode(int nloop){
	repeat=nloop;
	return 1;}

int rf433t::start() {
	transindex=0;
	stopit=0;
	repeatcount=0;
	status=1;
	setactivityled(trans[0].level);
	_RF_OCRA=trans[0].ticks;
/* debug */
/*
	cc=0;
//	collect[cc++]=trans[0].ticks;
	collect[cc++]=millis();
*/
/* fin debug */
	cli();
	//_RF_TIFR&=~(1<< _RF_OCFA); //on supprime l'interruption qui avait déjà été enregistrée
	_RF_TIFR|=(1<< _RF_OCFA); // METTRE A 1 POUR EFFACER LE BIT, FALLAIT Y PENSER !
	digitalWrite(emitpin,trans[0].level);
	_RF_TCNT=0; //mise à 0 du compteur
	sei();
	enableint();		
	return 1;
	}

int rf433t::stop() {
	// disableint();
	stopit=1;
	return 1;
	}

int rf433t::isr() {
	//on part du principe que l'on arrête uniquement quand tout le signal a été balancé
	//transindex pointe sur la transition qui vient de se finir
	// _RF_TCNT=0; //pas de clear on Timer count ???
	transindex++;
	if(transindex>=transcount) {
		repeatcount++;
		if(repeat && (stopit || repeatcount>=repeat)) {
			// arret
			status=0;
			_RF_TIMSK&=~(1<<_RF_OCIEA); //pas d'interruption sur timer A
			//pas d'appel de la methode disableint pour eviter de croiser sei/cli
			stopit=0;
			return 1;
			}
		transindex=0;
		};
	_RF_OCRA=trans[transindex].ticks;
/* debug */
/*
//	collect[cc++]=trans[transindex].ticks;
	//collect[cc++]=millis();
	collect[cc++]=micros();
*/
/* fin debug */
	digitalWrite(emitpin,trans[transindex].level);
	setactivityled(trans[transindex].level);
	return 1;
	}

int rf433t::addheader(){
	//utilisation de recbit(level,count) ou recus(level, microseconds)
	return 1;}

int rf433t::addtailer(){
	//utilisation de recbit(level,count) ou recus(level, microseconds)
	recus(LOW,(unsigned long)64); //retour à 0 systématique par défaut
	return 1;}

int rf433t::emit(char *string){
	if(bufferize(string)) start();
	}
