[vc_row][vc_column width=”1/1″][vc_tour][vc_tab title=”Overview of Communication Methods on Arduino” tab_id=”1394591603-1-39″][vc_column_text]Arduino Uno provides dedicated hardwares like UART, SPI and I2C to communicate with peer MCU or host. We can use other methods to equip Arduino with more methods to communicate, such as using timer to simulate the timing sequence of an infrared remote controller to make Arduino as an universal remote controller. We can also use timer to simulate a FSK modem on Arduino. In this tutorial, we will look at how to implement FSK on Arduino.[/vc_column_text][vc_column_text][/vc_column_text][/vc_tab][vc_tab title=”Parts list and wire diagram” tab_id=”1394591603-2-43″][vc_column_text]We will need the following parts:
- 2 x Arduino Unos
- Several jumper wires
Wire diagram:
[/vc_column_text][/vc_tab][vc_tab title=”Library of SoftModem for Arduino” tab_id=”1394591890696-2-8″][vc_column_text]There are many versions of Softmodem Arduino library, you can click https://code.google.com/p/arms22/downloads/list?can=2&q=Softmodem&colspec=Filename+Summary+Uploaded+ReleaseDate+Size+DownloadCount to download.
[/vc_column_text][/vc_tab][vc_tab title=”Some problems about Softmodem.h file” tab_id=”1394591891651-3-4″][vc_column_text]I chose Softmodem-005.zip, download and put to library directory under Arduino (1.5.6).
I need to make the following modifications to make it work:
#ifndef SoftModem_h #define SoftModem_h #include <Arduino.h> //#define SOFT_MODEM_BAUD_RATE (126) //#define SOFT_MODEM_LOW_FREQ (882) //#define SOFT_MODEM_HIGH_FREQ (1764) //#define SOFT_MODEM_RX_BUF_SIZE (4) //#define SOFT_MODEM_BAUD_RATE (100) //#define SOFT_MODEM_LOW_FREQ (800) //#define SOFT_MODEM_HIGH_FREQ (1600) //#define SOFT_MODEM_RX_BUF_SIZE (4) //#define SOFT_MODEM_BAUD_RATE (315) //#define SOFT_MODEM_LOW_FREQ (1575) //#define SOFT_MODEM_HIGH_FREQ (3150) //#define SOFT_MODEM_RX_BUF_SIZE (8) //#define SOFT_MODEM_BAUD_RATE (630) //#define SOFT_MODEM_LOW_FREQ (3150) //#define SOFT_MODEM_HIGH_FREQ (6300) //#define SOFT_MODEM_RX_BUF_SIZE (16) //#define SOFT_MODEM_BAUD_RATE (600) //#define SOFT_MODEM_LOW_FREQ (2666) //#define SOFT_MODEM_HIGH_FREQ (4000) //#define SOFT_MODEM_RX_BUF_SIZE (16) //#define SOFT_MODEM_BAUD_RATE (1225) //#define SOFT_MODEM_LOW_FREQ (4900) //#define SOFT_MODEM_HIGH_FREQ (7350) //#define SOFT_MODEM_RX_BUF_SIZE (32) #define SOFT_MODEM_BAUD_RATE (1225)//Softmodem baud, 9600 baud rate is equivalent to RS232 #define SOFT_MODEM_LOW_FREQ (4900)//Softmodem means digital"0" rate #define SOFT_MODEM_HIGH_FREQ (7350)//Softmodem means digital"1" rate #define SOFT_MODEM_RX_BUF_SIZE (256)//Softmodemreceive data buffer / / After repeated tests, Arduino Communication Error Rate of this configuration is the lowest //#define SOFT_MODEM_BAUD_RATE (2450) //#define SOFT_MODEM_LOW_FREQ (7350) //#define SOFT_MODEM_HIGH_FREQ (14700) //#define SOFT_MODEM_RX_BUF_SIZE (32) #define SOFT_MODEM_DEBUG_ENABLE (0) class SoftModem //: public Stream//Because i'm not quite familiar with c++, the compiler error, Modified repeatedly found commented ": public Stream," is not an error.We need to learn c++ carefully { private: volatile uint8_t *_txPortReg; uint8_t _txPortMask; uint8_t _lastTCNT; uint8_t _lastDiff; uint8_t _recvStat; uint8_t _recvBits; uint8_t _recvBufferHead; uint8_t _recvBufferTail; uint8_t _recvBuffer[SOFT_MODEM_RX_BUF_SIZE]; uint8_t _lowCount; uint8_t _highCount; unsigned long _lastWriteTime; void modulate(uint8_t b); public: SoftModem(); ~SoftModem(); void begin(void); void end(void); virtual int available(); virtual int read(); virtual void flush(); virtual int peek(); virtual size_t write(const uint8_t *buffer, size_t size); virtual size_t write(uint8_t data); void demodulate(void); void recv(void); static SoftModem *activeObject; }; #endif
[/vc_column_text][/vc_tab][vc_tab title=”Softmodem.cpp file” tab_id=”1394591892850-4-1″][vc_column_text]
#include "SoftModem.h" #define TX_PIN (3) #define RX_PIN1 (6) // AIN0 #define RX_PIN2 (7) // AIN1 SoftModem *SoftModem::activeObject = 0; SoftModem::SoftModem() { } SoftModem::~SoftModem() { end(); } #if F_CPU == 16000000 #if SOFT_MODEM_BAUD_RATE <= 126 #define TIMER_CLOCK_SELECT (7) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(1024)) #elif SOFT_MODEM_BAUD_RATE <= 315 #define TIMER_CLOCK_SELECT (6) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(256)) #elif SOFT_MODEM_BAUD_RATE <= 630 #define TIMER_CLOCK_SELECT (5) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(128)) #elif SOFT_MODEM_BAUD_RATE <= 1225 #define TIMER_CLOCK_SELECT (4) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(64)) #else #define TIMER_CLOCK_SELECT (3) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(32)) #endif #else #if SOFT_MODEM_BAUD_RATE <= 126 #define TIMER_CLOCK_SELECT (6) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(256)) #elif SOFT_MODEM_BAUD_RATE <= 315 #define TIMER_CLOCK_SELECT (5) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(128)) #elif SOFT_MODEM_BAUD_RATE <= 630 #define TIMER_CLOCK_SELECT (4) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(64)) #else #define TIMER_CLOCK_SELECT (3) #define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(32)) #endif #endif #define BIT_PERIOD (1000000/SOFT_MODEM_BAUD_RATE) #define HIGH_FREQ_MICROS (1000000/SOFT_MODEM_HIGH_FREQ) #define LOW_FREQ_MICROS (1000000/SOFT_MODEM_LOW_FREQ) #define HIGH_FREQ_CNT (BIT_PERIOD/HIGH_FREQ_MICROS) #define LOW_FREQ_CNT (BIT_PERIOD/LOW_FREQ_MICROS) #define MAX_CARRIR_BITS (40000/BIT_PERIOD) // 40ms #define TCNT_BIT_PERIOD (BIT_PERIOD/MICROS_PER_TIMER_COUNT) #define TCNT_HIGH_FREQ (HIGH_FREQ_MICROS/MICROS_PER_TIMER_COUNT) #define TCNT_LOW_FREQ (LOW_FREQ_MICROS/MICROS_PER_TIMER_COUNT) #define TCNT_HIGH_TH_L (TCNT_HIGH_FREQ * 0.80) #define TCNT_HIGH_TH_H (TCNT_HIGH_FREQ * 1.15) #define TCNT_LOW_TH_L (TCNT_LOW_FREQ * 0.85) #define TCNT_LOW_TH_H (TCNT_LOW_FREQ * 1.20) #if SOFT_MODEM_DEBUG_ENABLE static volatile uint8_t *_portLEDReg; static uint8_t _portLEDMask; #endif enum { START_BIT = 0, DATA_BIT = 8, STOP_BIT = 9, INACTIVE = 0xff }; void SoftModem::begin(void) { pinMode(RX_PIN1, INPUT); digitalWrite(RX_PIN1, LOW); pinMode(RX_PIN2, INPUT); digitalWrite(RX_PIN2, LOW); pinMode(TX_PIN, OUTPUT); digitalWrite(TX_PIN, LOW); _txPortReg = portOutputRegister(digitalPinToPort(TX_PIN)); _txPortMask = digitalPinToBitMask(TX_PIN); #if SOFT_MODEM_DEBUG_ENABLE _portLEDReg = portOutputRegister(digitalPinToPort(13)); _portLEDMask = digitalPinToBitMask(13); pinMode(13, OUTPUT); #endif _recvStat = INACTIVE; _recvBufferHead = _recvBufferTail = 0; SoftModem::activeObject = this; _lastTCNT = TCNT2; _lastDiff = _lowCount = _highCount = 0; TCCR2A = 0; TCCR2B = TIMER_CLOCK_SELECT; ACSR = _BV(ACIE) | _BV(ACIS1); DIDR1 = _BV(AIN1D) | _BV(AIN0D); // digital port off } void SoftModem::end(void) { ACSR &= ~(_BV(ACIE)); TIMSK2 &= ~(_BV(OCIE2A)); DIDR1 &= ~(_BV(AIN1D) | _BV(AIN0D)); SoftModem::activeObject = 0; } void SoftModem::demodulate(void) { uint8_t t = TCNT2; uint8_t diff; if(TIFR2 & _BV(TOV2)){ TIFR2 |= _BV(TOV2); diff = (255 - _lastTCNT) + t + 1; } else{ diff = t - _lastTCNT; } if(diff < (uint8_t)(TCNT_HIGH_TH_L)) return; _lastTCNT = t; if(diff > (uint8_t)(TCNT_LOW_TH_H)) return; // _lastDiff = (diff >> 1) + (diff >> 2) + (_lastDiff >> 2); _lastDiff = diff; if(_lastDiff >= (uint8_t)(TCNT_LOW_TH_L)){ _lowCount += _lastDiff; if((_recvStat == INACTIVE) && (_lowCount >= (uint8_t)(TCNT_BIT_PERIOD * 0.5))){ _recvStat = START_BIT; _highCount = 0; _recvBits = 0; OCR2A = t + (uint8_t)(TCNT_BIT_PERIOD) - _lowCount; // 1 bit period after detected TIFR2 |= _BV(OCF2A); TIMSK2 |= _BV(OCIE2A); } } else if(_lastDiff <= (uint8_t)(TCNT_HIGH_TH_H)){ _highCount += _lastDiff; if((_recvStat == INACTIVE) && (_highCount >= (uint8_t)(TCNT_BIT_PERIOD))){ _lowCount = _highCount = 0; } } } ISR(ANALOG_COMP_vect) { SoftModem::activeObject->demodulate(); } void SoftModem::recv(void) { uint8_t high; if(_highCount > _lowCount){ if(_highCount >= (uint8_t)TCNT_BIT_PERIOD) _highCount -= (uint8_t)TCNT_BIT_PERIOD; else _highCount = 0; high = 0x80; } else{ if(_lowCount >= (uint8_t)TCNT_BIT_PERIOD) _lowCount -= (uint8_t)TCNT_BIT_PERIOD; else _lowCount = 0; high = 0x00; } if(_recvStat == START_BIT){ // Start bit if(!high){ _recvStat++; }else{ goto end_recv; } } else if(_recvStat <= DATA_BIT) { // Data bits _recvBits >>= 1; _recvBits |= high; _recvStat++; } else if(_recvStat == STOP_BIT){ // Stop bit uint8_t new_tail = (_recvBufferTail + 1) & (SOFT_MODEM_RX_BUF_SIZE - 1); if(new_tail != _recvBufferHead){ _recvBuffer[_recvBufferTail] = _recvBits; _recvBufferTail = new_tail; } goto end_recv; } else{ end_recv: _recvStat = INACTIVE; TIMSK2 &= ~_BV(OCIE2A); } } ISR(TIMER2_COMPA_vect) { OCR2A += (uint8_t)TCNT_BIT_PERIOD; SoftModem::activeObject->recv(); #if SOFT_MODEM_DEBUG_ENABLE *_portLEDReg ^= _portLEDMask; #endif } int SoftModem::available() { return (_recvBufferTail + SOFT_MODEM_RX_BUF_SIZE - _recvBufferHead) & (SOFT_MODEM_RX_BUF_SIZE - 1); } int SoftModem::read() { if(_recvBufferHead == _recvBufferTail) return -1; int d = _recvBuffer[_recvBufferHead]; _recvBufferHead = (_recvBufferHead + 1) & (SOFT_MODEM_RX_BUF_SIZE - 1); return d; } int SoftModem::peek() { if(_recvBufferHead == _recvBufferTail) return -1; return _recvBuffer[_recvBufferHead]; } void SoftModem::flush() { _recvBufferHead = _recvBufferTail = 0; } void SoftModem::modulate(uint8_t b) { uint8_t cnt,tcnt,tcnt2; if(b){ cnt = (uint8_t)(HIGH_FREQ_CNT); tcnt2 = (uint8_t)(TCNT_HIGH_FREQ / 2); tcnt = (uint8_t)(TCNT_HIGH_FREQ) - tcnt2; }else{ cnt = (uint8_t)(LOW_FREQ_CNT); tcnt2 = (uint8_t)(TCNT_LOW_FREQ / 2); tcnt = (uint8_t)(TCNT_LOW_FREQ) - tcnt2; } do { cnt--; { OCR2B += tcnt; TIFR2 |= _BV(OCF2B); while(!(TIFR2 & _BV(OCF2B))); } *_txPortReg ^= _txPortMask; { OCR2B += tcnt2; TIFR2 |= _BV(OCF2B); while(!(TIFR2 & _BV(OCF2B))); } *_txPortReg ^= _txPortMask; } while (cnt); } size_t SoftModem::write(const uint8_t *buffer, size_t size) { uint8_t cnt = ((micros() - _lastWriteTime) / BIT_PERIOD) + 1; if(cnt > MAX_CARRIR_BITS) cnt = MAX_CARRIR_BITS; for(uint8_t i = 0; i<cnt; i++) modulate(HIGH); size_t n = size; while (size--) { uint8_t data = *buffer++; modulate(LOW); // Start Bit for(uint8_t mask = 1; mask; mask <<= 1){ // Data Bits if(data & mask){ modulate(HIGH); } else{ modulate(LOW); } } modulate(HIGH); // Stop Bit } modulate(HIGH); // Push Bit _lastWriteTime = micros(); return n; } size_t SoftModem::write(uint8_t data) { return write(&data, 1); }
[/vc_column_text][/vc_tab][vc_tab title=”Sample code” tab_id=”1394591893819-5-7″][vc_column_text]We create a simple pair of code to transmit on one Arduino and receive on another Arduino:
Transmitter:
#include <SoftModem.h> SoftModem modem; void setup() { modem.begin(); Serial.begin(115200); } void loop() { modem.write('C'); Serial.write('C'); delay(100); }
Receiver:
#include <SoftModem.h> SoftModem modem; int dat; void setup() { modem.begin(); Serial.begin(115200); pinMode(13,OUTPUT); } void loop() { if(modem.available()) { dat = modem.read(); if(dat == 'C') { Serial.write('Y'); digitalWrite(13,HIGH); } else { Serial.write('N'); digitalWrite(13,LOW); } } }
[/vc_column_text][/vc_tab][vc_tab title=”Results” tab_id=”1394591904684-6-4″][vc_column_text]
In the above, N means error, and Y means correct communication. We can see there are errors.[/vc_column_text][/vc_tab][/vc_tour][/vc_column][/vc_row]
Leave a Reply
You must be logged in to post a comment.