Using an Arduino UNO as an fsk modem

Hey people, thanks for sharing all your knowledge,

I´ve been working on a project that searches to turn an Arduino UNO into an analog fsk modem, i´ve been trying to do it separetely (modulation and demodulation) but i don´t seem to find anyway to detect the frecuencies for the demodulation part (1,4kHz and 2,2kHz).

I am using an Arduino UNO rev 3, and i have tried this solution:

Step 3, since it is a sinusoidal signal. I tried as well, using the SoftModem library but i wasn't able to make it.

My goal is to read from a pin an analog signal input (sinusoidal) and converting it to the bits it represents. And backwards, from a bit to the waveform with the corresponding frecuency.

It will be very apreciated if you are able to help me.

Thanks in advance.

Can you post the code (in code tags please) you used to try autocorrelation? Maybe there's a bug in it. Although, I don't know how well autocorrelation works in the presence of noise - I haven't tried it.



Thanks a lot for answering, here is the code of autocorrelation.
By the way I tried with the RTTY library to make the FSK modem but it looks like it codes the fks in pulses :(. And plus the compiler had a problem on debug mode.

#include "C4.h"

// Sample Frequency in Hz
const float sample_freq = 22050;

int len = sizeof(rawData);
int i,k;
long sum, sum_old;
int thresh = 0;
float freq_per = 0;
byte pd_state = 0;
void setup() {

  sum = 0;
  pd_state = 0;
  int period = 0;
  // Autocorrelation
  for(i=0; i < len; i++)
    sum_old = sum;
    sum = 0;
    for(k=0; k < len-i; k++) sum += (rawData[k]-128)*(rawData[k+i]-128)/256;
    // Peak Detect State Machine
    if (pd_state == 2 && (sum-sum_old) <=0) 
      period = i;
      pd_state = 3;
    if (pd_state == 1 && (sum > thresh) && (sum-sum_old) > 0) pd_state = 2;
    if (!i) {
      thresh = sum * 0.5;
      pd_state = 1;
  // Frequency identified in Hz
  freq_per = sample_freq/period;

void loop() {
  // put your main code here, to run repeatedly: 

it also needed a source, in this case a pre made array was made with the data in it, i was thinking on capturing this data from an Analog input with a for routine, hoping that it will get enough data to make the sine wave array complete, but i am still not sure if the timming will be the right one, cause what i really care about is the frecuency, and the posibility that i can calculate it that way.

here is the C4.h library (which is just an array).

unsigned char rawData[970] = {
	0x73, 0x7E, 0x3E, 0x39, 0xB0, 0x4D, 0x16, 0x48, 0x7C, 0xAB, 0x95, 0x9F,
	0xA6, 0x47, 0x5E, 0x52, 0x28, 0x92, 0x9C, 0x9A, 0xDC, 0x90, 0x12, 0x7F,
	0xE5, 0x94, 0x81, 0x79, 0x90, 0x5D, 0x48, 0x9B, 0xA9, 0xB5, 0x68, 0x56,
	0x82, 0xA7, 0x5F, 0x74, 0xEB, 0x86, 0x4D, 0xB4, 0xAA, 0xAF, 0x74, 0x46,
	0x99, 0xAD, 0x7D, 0x32, 0xCA, 0x8B, 0x63, 0xBF, 0x4D, 0x76, 0x78, 0x9A,
	0xC9, 0x4D, 0xBB, 0xD9, 0x2E, 0x6E, 0x7B, 0x9B, 0xC4, 0x57, 0x79, 0x7F,
	0x0D, 0x8B, 0xC8, 0x4F, 0xB2, 0x3F, 0x6F, 0xE1, 0x2A, 0x7E, 0xA6, 0x9E,
	0x78, 0x6B, 0x64, 0x24, 0x99, 0x74, 0x1C, 0x3B, 0x6C, 0xA1, 0x9E, 0x96,
	0xAB, 0x63, 0x4F, 0x62, 0x2C, 0x72, 0xA3, 0x92, 0xCA, 0xB2, 0x2E, 0x51,
	0xD5, 0xAD, 0x81, 0x7C, 0x87, 0x75, 0x43, 0x86, 0xA5, 0xB4, 0x82, 0x54,
	0x74, 0xA0, 0x7A, 0x5E, 0xCF, 0xAF, 0x4D, 0x96, 0xB3, 0xA8, 0x8F, 0x47,
	0x82, 0xA9, 0x93, 0x3D, 0x97, 0xB4, 0x57, 0xB1, 0x70, 0x60, 0x82, 0x83,
	0xC9, 0x6F, 0x88, 0xE9, 0x59, 0x51, 0x82, 0x86, 0xC3, 0x77, 0x63, 0x8C,
	0x2B, 0x55, 0xCE, 0x68, 0x91, 0x72, 0x42, 0xDA, 0x5C, 0x56, 0xA7, 0x9C,
	0x8A, 0x66, 0x71, 0x2F, 0x73, 0x90, 0x2E, 0x31, 0x5E, 0x93, 0xA2, 0x94,
	0xA7, 0x7D, 0x4C, 0x64, 0x3B, 0x56, 0x9E, 0x94, 0xB5, 0xC3, 0x54, 0x37,
	0xB5, 0xC1, 0x87, 0x7E, 0x81, 0x82, 0x4C, 0x6F, 0xA0, 0xAF, 0x96, 0x5C,
	0x69, 0x94, 0x8C, 0x5C, 0xAC, 0xC7, 0x61, 0x7A, 0xB3, 0xA6, 0x9D, 0x57,
	0x6B, 0xA2, 0x9E, 0x55, 0x6E, 0xBF, 0x67, 0x95, 0x8F, 0x57, 0x80, 0x7C,
	0xB8, 0x92, 0x6D, 0xD9, 0x89, 0x43, 0x7D, 0x7F, 0xB4, 0x95, 0x5D, 0x88,
	0x4D, 0x37, 0xB8, 0x8B, 0x77, 0x8F, 0x3C, 0xB4, 0x8F, 0x43, 0x99, 0x9D,
	0x93, 0x6B, 0x72, 0x44, 0x55, 0x97, 0x49, 0x2B, 0x52, 0x84, 0xA1, 0x96,
	0xA0, 0x90, 0x54, 0x5F, 0x4C, 0x45, 0x8E, 0x98, 0xA5, 0xC6, 0x79, 0x32,
	0x8E, 0xC9, 0x94, 0x80, 0x7E, 0x85, 0x5C, 0x5E, 0x96, 0xA9, 0xA2, 0x6A,
	0x61, 0x88, 0x95, 0x66, 0x8C, 0xCB, 0x7E, 0x67, 0xA8, 0xA8, 0xA2, 0x6C,
	0x5D, 0x96, 0xA3, 0x6E, 0x59, 0xB3, 0x82, 0x7C, 0x9E, 0x5F, 0x75, 0x7C,
	0xA2, 0xA8, 0x6A, 0xBB, 0xAF, 0x49, 0x6E, 0x7F, 0xA2, 0xA7, 0x65, 0x7B,
	0x68, 0x31, 0x93, 0xA5, 0x6E, 0x94, 0x4E, 0x89, 0xAE, 0x4A, 0x81, 0xA0,
	0x96, 0x76, 0x6F, 0x57, 0x45, 0x8C, 0x66, 0x2E, 0x47, 0x75, 0x9C, 0x99,
	0x9A, 0x9A, 0x64, 0x59, 0x58, 0x41, 0x79, 0x9A, 0x9C, 0xBF, 0x97, 0x3F,
	0x6C, 0xC1, 0xA3, 0x82, 0x7E, 0x84, 0x6C, 0x56, 0x87, 0xA4, 0xA7, 0x7B,
	0x5F, 0x7C, 0x96, 0x74, 0x77, 0xC0, 0x99, 0x63, 0x96, 0xAB, 0xA3, 0x80,
	0x5A, 0x86, 0xA2, 0x82, 0x55, 0x99, 0x9A, 0x72, 0x9C, 0x70, 0x6A, 0x7E,
	0x92, 0xAF, 0x77, 0x9B, 0xC1, 0x60, 0x5F, 0x7E, 0x93, 0xAD, 0x76, 0x70,
	0x77, 0x3B, 0x70, 0xAE, 0x76, 0x8B, 0x67, 0x69, 0xB3, 0x63, 0x6A, 0x9D,
	0x98, 0x81, 0x6E, 0x64, 0x44, 0x78, 0x7A, 0x39, 0x3E, 0x68, 0x92, 0x9C,
	0x97, 0x9C, 0x75, 0x58, 0x5D, 0x46, 0x66, 0x95, 0x98, 0xB4, 0xAA, 0x55,
	0x55, 0xAE, 0xB0, 0x88, 0x7E, 0x82, 0x77, 0x58, 0x77, 0x9E, 0xA7, 0x8A,
	0x63, 0x72, 0x91, 0x81, 0x6F, 0xAB, 0xAD, 0x6C, 0x84, 0xA9, 0xA3, 0x8D,
	0x60, 0x76, 0x9D, 0x8F, 0x5E, 0x80, 0xA4, 0x76, 0x91, 0x82, 0x66, 0x7B,
	0x88, 0xAB, 0x88, 0x86, 0xC0, 0x7E, 0x57, 0x79, 0x89, 0xA9, 0x89, 0x6B,
	0x7B, 0x4D, 0x57, 0xA5, 0x87, 0x80, 0x7A, 0x5C, 0xA5, 0x7F, 0x5D, 0x93,
	0x9A, 0x8A, 0x71, 0x6A, 0x4C, 0x65, 0x83, 0x4C, 0x3A, 0x5C, 0x87, 0x9B,
	0x96, 0x9B, 0x84, 0x5D, 0x5E, 0x4E, 0x58, 0x8B, 0x97, 0xA9, 0xB2, 0x6F,
	0x4C, 0x95, 0xB6, 0x91, 0x80, 0x81, 0x7D, 0x60, 0x6B, 0x94, 0xA5, 0x96,
	0x6C, 0x6B, 0x89, 0x8A, 0x70, 0x96, 0xB4, 0x7C, 0x76, 0xA1, 0xA5, 0x96,
	0x6C, 0x6B, 0x94, 0x97, 0x6C, 0x6F, 0xA2, 0x82, 0x85, 0x8D, 0x69, 0x76,
	0x83, 0xA1, 0x97, 0x7D, 0xB2, 0x98, 0x5A, 0x70, 0x84, 0xA0, 0x97, 0x6F,
	0x78, 0x5F, 0x4B, 0x92, 0x95, 0x7B, 0x82, 0x5F, 0x8F, 0x94, 0x5E, 0x83,
	0x9A, 0x8F, 0x77, 0x6D, 0x56, 0x59, 0x81, 0x5F, 0x3B, 0x52, 0x7B, 0x97,
	0x97, 0x99, 0x8E, 0x66, 0x5D, 0x56, 0x51, 0x7D, 0x96, 0xA0, 0xB2, 0x86,
	0x4F, 0x7C, 0xB3, 0x9C, 0x83, 0x80, 0x80, 0x69, 0x64, 0x89, 0xA1, 0x9C,
	0x78, 0x68, 0x80, 0x8D, 0x76, 0x86, 0xB1, 0x8E, 0x71, 0x96, 0xA5, 0x9B,
	0x79, 0x66, 0x88, 0x99, 0x7A, 0x67, 0x95, 0x8F, 0x7E, 0x90, 0x72, 0x70,
	0x7F, 0x96, 0x9F, 0x80, 0xA0, 0xA7, 0x67, 0x67, 0x7F, 0x96, 0x9E, 0x78,
	0x74, 0x6C, 0x4C, 0x7C, 0x9C, 0x7E, 0x83, 0x68, 0x7B, 0x9C, 0x6A, 0x75,
	0x97, 0x93, 0x7E, 0x6F, 0x60, 0x54, 0x78, 0x6F, 0x43, 0x4A, 0x70, 0x91,
	0x98, 0x97, 0x93, 0x72, 0x5E, 0x5B, 0x51, 0x6F, 0x91, 0x9B, 0xAE, 0x97,
	0x5B, 0x69, 0xA7, 0xA5, 0x88, 0x7F, 0x81, 0x72, 0x62, 0x7D, 0x9B, 0x9F,
	0x83, 0x6A, 0x78, 0x8C, 0x7D, 0x7C, 0xA6, 0x9D, 0x74, 0x89, 0xA3, 0x9E,
	0x85, 0x68, 0x7D, 0x97, 0x85, 0x68, 0x87, 0x96, 0x7E, 0x8C, 0x7C, 0x6E,
	0x7C, 0x8E, 0xA0, 0x87, 0x91, 0xAC, 0x79, 0x62, 0x7A, 0x8E, 0x9F, 0x84,
	0x72, 0x72, 0x53, 0x69, 0x99, 0x86, 0x81, 0x73, 0x6F, 0x98, 0x7A, 0x6B,
	0x90, 0x96, 0x85, 0x73, 0x66, 0x55, 0x6D, 0x78, 0x4F, 0x46, 0x65, 0x88,
	0x97, 0x96, 0x95, 0x7D, 0x62, 0x5E, 0x54, 0x64, 0x8A, 0x98, 0xA7, 0xA2,
	0x6C, 0x5E, 0x96, 0xAA, 0x8F, 0x80, 0x80, 0x78, 0x65, 0x73, 0x94, 0x9F,
	0x8D, 0x6F, 0x72, 0x87, 0x84, 0x78, 0x99, 0xA4, 0x7D, 0x7F, 0x9D, 0xA0,
	0x8D, 0x6F, 0x74, 0x91, 0x8D, 0x6E, 0x7A, 0x96, 0x84, 0x87, 0x84, 0x6F,
	0x78, 0x87, 0x9C, 0x90, 0x89, 0xA8, 0x8B, 0x64, 0x73, 0x88, 0x9B, 0x8E,
	0x74, 0x74, 0x5E, 0x5D, 0x8E, 0x8E, 0x7F, 0x7A, 0x6C, 0x8E, 0x87, 0x6A,
	0x85, 0x96, 0x8A, 0x77, 0x6B, 0x5A, 0x64, 0x79, 0x5C, 0x46, 0x5C, 0x7F,
	0x94, 0x95, 0x95, 0x86, 0x69, 0x5F, 0x58, 0x5D, 0x80, 0x94, 0xA1, 0xA6,
	0x7D, 0x5D, 0x84, 0xA8, 0x96, 0x83, 0x80, 0x7C, 0x6A, 0x6D, 0x8B, 0x9D,
	0x94, 0x77, 0x6F, 0x81, 0x87, 0x7A, 0x8D, 0xA5, 0x89, 0x7A, 0x95, 0xA0,
	0x94, 0x77, 0x6F, 0x89, 0x91, 0x77, 0x73, 0x91, 0x8A, 0x83, 0x88, 0x74,
	0x74, 0x82, 0x96, 0x96, 0x87, 0x9F, 0x98, 0x6C, 0x6D, 0x82, 0x96, 0x94,
	0x7A, 0x74, 0x67, 0x59, 0x80, 0x92, 0x81, 0x7D, 0x6E, 0x82, 0x8F, 0x6F,
	0x7B, 0x93, 0x8E, 0x7D, 0x6F, 0x60, 0x5F, 0x76, 0x68, 0x4B, 0x54, 0x75,
	0x8F, 0x95, 0x95, 0x8C, 0x71, 0x62, 0x5C, 0x5A, 0x76, 0x90, 0x9C, 0xA6,
	0x8B, 0x62, 0x75, 0xA1, 0x9D, 0x87, 0x80, 0x7E, 0x70, 0x6A, 0x82, 0x99,
	0x98, 0x7F, 0x6F, 0x7B, 0x87, 0x7D, 0x84, 0xA1, 0x92, 0x7C

Thank you so much for replaying, hope i can work this out; this is a very important developement i must do.

Have a great day.


That example isn't really relevant to your situation. It uses a sampling rate of 22050 with a reasonably clean sample and it also has the luxury of not having to decode in real-time. I suspect that auto-correlation is too cpu intensive for what you are trying to do.

With a shift of 800Hz, I presume that the signal you are trying to decode has a fairly low baud rate. You might be able to use Goertzel's algorithm to detect the tones and use that to decode the data. I have used Goertzel's algorithm to detect DTMF tones, which requires detection of eight separate tones, but I don't know whether it can easily be applied to FSK although I suspect it should work. There would be two complications. The first would be getting the sampling and the algorithm working. The second would be in using the output of the algorithm to decode the data.


Thanks a lot for the fast reply, I will look up for that algorithm and i will try to apply it to my developement.

Again, thanks a lot, i will be giving some feedback since i think this could be very useful for the community.