Les 2 bibliothèques sont terminées et fonctionnelles, elles sont téléchargeables ici.
Le premier test étant concluant, nous allons porter la bibliothèque IRRemote pour RFduino. Nous allons aussi coder une bibliothèque permettant l'encodage et l'envoi de signal. Pour différencier les deux, nous les nommerons IRTagRecv et IRTagSend. Afin d’alléger la bibliothèque, nous allons commencer par définir un protocole.
Nous voulons que chaque paquet contienne deux informations : l'identifiant du lanceur et l'identifiant de l'action.
Le paquet est donc composé de 16 bits.
Je commence par la bibliothèque de réception. Elle se compose de 2 fichiers : IRTagRecv.h et IRTagRecv.cpp et d'un fichier démo : IRRecvDemo.ino. Pour coder cette librairie, je me suis fortement inspiré de IRRemote d'arduino et de codes trouvés sur le forum de rfduino.
#ifndef IR_RECV_H #define IR_RECV_H #include "Arduino.h" #define IR_USEC_START 2000 #define IR_USEC_MARK_ONE 1500 #define IR_USEC_MARK_ZERO 600 #define IR_USEC_SPACE 600 #define IR_MARK LOW #define IR_SPACE HIGH #define IR_DECODED 1 #define IR_ERROR 0 #define IR_STATE_IDLE 0 #define IR_STATE_MARK 1 #define IR_STATE_SPACE 2 #define IR_STATE_STOP 3 #define IR_TOLERANCE 25 #define IR_LTOL (1.0 - IR_TOLERANCE/100.) #define IR_UTOL (1.0 + IR_TOLERANCE/100.) #define IR_USEC_PER_TICK 50 #define IR_GAP_TICKS (5000/IR_USEC_PER_TICK) #define IR_RAW_BUFFER_LENGTH 100 #define IR_TICKS_LOW(us) (int) (us*IR_LTOL/IR_USEC_PER_TICK) #define IR_TICKS_HIGH(us) (int) (us*IR_UTOL/IR_USEC_PER_TICK + 1) #define IR_MARK_EXCESS 100 typedef struct { int state; int timer; int recvPin; unsigned int rawBuffer[IR_RAW_BUFFER_LENGTH]; int rawBufferLength; NRF_TIMER_Type* nrf_timer; IRQn_Type irqn_type; } irparams_t; class DecodeResult { public: int tagPlayer; int tagAction; unsigned long value; int bits; unsigned int *rawBuffer; int rawBufferLength; }; class IRTagRecv { public: IRTagRecv(int pin, NRF_TIMER_Type* nrf_timer, IRQn_Type irqn); void begin(); int decode(DecodeResult *results); void resume(); private: int decodeTag(DecodeResult *results); }; #endif
#include "IRTagRecv.h" irparams_t irparams; IRTagRecv::IRTagRecv(int pin, NRF_TIMER_Type* nrf_timer, IRQn_Type irqn) { irparams.recvPin = pin; irparams.nrf_timer = nrf_timer; irparams.irqn_type = irqn; } // Toutes les 50usec void IR_TIMER_INTERUPT(void) { if (irparams.nrf_timer->EVENTS_COMPARE[0] != 0) { int irdata = digitalRead(irparams.recvPin); irparams.timer++; if (irparams.rawBufferLength >= IR_RAW_BUFFER_LENGTH) { irparams.state = IR_STATE_STOP; // Buffer overflow } if (irparams.state == IR_STATE_IDLE) { if (irdata == IR_MARK) { if (irparams.timer < IR_GAP_TICKS) { irparams.timer = 0; } else { irparams.rawBufferLength = 0; irparams.rawBuffer[irparams.rawBufferLength++] = irparams.timer; irparams.timer = 0; irparams.state = IR_STATE_MARK; } } } else if (irparams.state == IR_STATE_MARK) { if (irdata == IR_SPACE) { irparams.rawBuffer[irparams.rawBufferLength++] = irparams.timer; irparams.timer = 0; irparams.state = IR_STATE_SPACE; } } else if (irparams.state == IR_STATE_SPACE) { if (irdata == IR_MARK) { irparams.rawBuffer[irparams.rawBufferLength++] = irparams.timer; irparams.timer = 0; irparams.state = IR_STATE_MARK; } else if (irparams.timer > IR_GAP_TICKS) { irparams.state = IR_STATE_STOP; } } else if (irparams.state == IR_STATE_STOP) { if (irdata == IR_MARK) { irparams.timer = 0; } } irparams.nrf_timer->EVENTS_COMPARE[0] = 0; } } void IRTagRecv::begin() { // Config Timer irparams.nrf_timer->TASKS_STOP = 1; // Arrete le timer irparams.nrf_timer->MODE = TIMER_MODE_MODE_Timer; irparams.nrf_timer->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos); irparams.nrf_timer->PRESCALER = 4; // résolution de 1 usec irparams.nrf_timer->TASKS_CLEAR = 1; irparams.nrf_timer->CC[0] = IR_USEC_PER_TICK; irparams.nrf_timer->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos; irparams.nrf_timer->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos); attachInterrupt(irparams.irqn_type, IR_TIMER_INTERUPT); irparams.nrf_timer->TASKS_START = 1; // Redemarre le timer // Init irparams.state = IR_STATE_IDLE; irparams.rawBufferLength = 0; pinMode(irparams.recvPin, INPUT); } int IRTagRecv::decode(DecodeResult *results) { results->rawBuffer = irparams.rawBuffer; results->rawBufferLength = irparams.rawBufferLength; if (irparams.state != IR_STATE_STOP) { return IR_ERROR; } if (decodeTag(results)) { return IR_DECODED; } resume(); return IR_ERROR; } int IR_MATCH(int measured, int desired) { return (measured >= IR_TICKS_LOW(desired)) && (measured <= IR_TICKS_HIGH(desired)); }; int IR_MATCH_MARK(int measured, int desired) { return IR_MATCH(measured, desired + IR_MARK_EXCESS); }; int IR_MATCH_SPACE(int measured, int desired) { return IR_MATCH(measured, desired - IR_MARK_EXCESS); }; int IRTagRecv::decodeTag(DecodeResult *results) { if (results->rawBufferLength < 2 * 16 + 2) { return IR_ERROR; } // TODO: repeat detection // Start detection if (!IR_MATCH_MARK(results->rawBuffer[1], IR_USEC_START)) { return IR_ERROR; } int offset = 2; long data = 0; for (int i=0 ; i<16 ; i++) { if (!IR_MATCH_SPACE(results->rawBuffer[offset], IR_USEC_SPACE)) { return IR_ERROR; } offset++; if (IR_MATCH_MARK(results->rawBuffer[offset], IR_USEC_MARK_ONE)) { data = (data << 1) | 1; } else if (IR_MATCH_MARK(results->rawBuffer[offset], IR_USEC_MARK_ZERO)) { data <<= 1; } else { return IR_ERROR; } offset++; } results->value = data; // Parse data results->tagPlayer = (data >> 8) & 0xFF; results->tagAction = data & 0xFF; return IR_DECODED; } void IRTagRecv::resume() { irparams.state = IR_STATE_IDLE; irparams.rawBufferLength = 0; }
#include "IRTagRecv.h" // IRTagRecv a besoin d'un numero de pin et d'un timer non utilisé IRTagRecv recv(2, NRF_TIMER1, TIMER1_IRQn); DecodeResult result; void setup() { Serial.begin(9600); recv.begin(); } void printRaw() { for (int i=0 ; i<result.rawBufferLength ; i+=2) { Serial.print(result.rawBuffer[i] * 50, DEC); Serial.print(" usec, "); Serial.print(result.rawBuffer[i+1] * 50, DEC); Serial.println(" usec"); } Serial.println("---"); } void loop() { if (recv.decode(&result)) { //printRaw(); Serial.print(result.tagPlayer, DEC); Serial.print(" : "); Serial.println(result.tagAction, DEC); recv.resume(); } }
Voici le début de la librairie d'envoi et un fichier démo.
#ifndef IR_SEND_H #define IR_SEND_H #include "Arduino.h" #define IR_DATA_MASK 0x80000000 #define IR_USEC_START 2000 #define IR_USEC_MARK_ONE 1500 #define IR_USEC_MARK_ZERO 600 #define IR_USEC_SPACE 600 typedef struct { int sendPin; } irsend_params_t; class IRTagSend { public: IRTagSend(int sendPin); void begin(); void sendData(unsigned long data, int len); private: void pulseIR(int usec); }; #endif
#include "IRTagSend.h" irsend_params_t irsend_params; IRTagSend::IRTagSend(int sendPin) { irsend_params.sendPin = sendPin; } void IRTagSend::begin() { pinMode(irsend_params.sendPin, OUTPUT); } void IRTagSend::sendData(unsigned long data, int len) { data <<= (32-len); pulseIR(IR_USEC_START); delayMicroseconds(IR_USEC_SPACE); for (int i=0 ; i<len ; i++) { if (data & IR_DATA_MASK) { pulseIR(IR_USEC_MARK_ONE); } else { pulseIR(IR_USEC_MARK_ZERO); } delayMicroseconds(IR_USEC_SPACE); data <<= 1; } } // Envoi un pwm à 38KHz ~= 26usec void IRTagSend::pulseIR(int usec) { while (usec > 0) { digitalWrite(irsend_params.sendPin, HIGH); delayMicroseconds(8); digitalWrite(irsend_params.sendPin, LOW); delayMicroseconds(8); usec -= 26; } }
#include "IRTagSend.h" IRTagSend irsend(3); byte playerID = 42; void setup() { Serial.begin(9600); irsend.begin(); } void loop() { if (Serial.read() != -1) { int action = 31; int data = (playerID<<8) | action; irsend.sendData(data, 16); } }