Decoding ASCII binary over serial

I've built an arduino with a piezo-electric speaker connected to the TX pin, 1. The sketch is meant to broadcast the ASCII character 'A' over TX at 275baud. On the receiving end is a processing sketch that decodes the audio sample into a binary string:

Here is the arduino sketch:

void setup()
{
  Serial.begin(275);
}

void loop()
{
  Serial.write('A');
  delay(370);
  delayMicroseconds(895);
}

The problem is, the received bits dont match the ASCII binary code for 'A' at all, 01000001. I tried checking for a pattern or a mistake in the receiving (inverted the numbers, read it backwards, looked for omitted bits) but nothing seems to work.

Every character is unique, and each one seems to be 10-bits long. Does anyone have any idea what is behind how arduino encodes ASCII over serial?

Better yet, is there a way to have the TX pin simply bit-bang a binary string at a given baud rate?

Better yet, is there a way to have the TX pin simply bit-bang a binary string at a given baud rate?

You could bit-bang output on any other digital pin (except RX). There are certain supported baud rates. 275 is not one of them.

The sketch is meant to broadcast the ASCII character 'A' over TX at 275baud.

What makes you think that will work?
What is your circuit for the receiver?
The serial data is an NRZ signal (none return to zero) and is not suitable to be fed to an audio transducer. You are just transmitting edges.
You also have the problem of a piezo ringing when you hit it with an edge.

You need to turn both ones and zeros into separate tones and then have the receiving end demodulate them.

I've brought it up to 300baud with no problem. I still get the same pattern of pulses.

However, I wonder what makes you so sure, Grumpy_Mike that transmitting via serial pin like this is a bad idea? Earlier in this project I have used a fast-fourier transform to accomplish the same task, but the power needed is just too much and too slow. Below is a new image, at 300baud.

Transmitting 'h'

Can anyone really look at that and say I'm not getting a binary signal?

The audio is being captured at 44100hz. At 300baud, a bit comes out to about 147 samples. Each one of the bit above the spikes in the audio is the result of the amplitude of the signal passing a threshold (the horizontal line through the middle)

I'm not sure I understand about only seeing edges either. The serial pin seems to be outputting using PWM like all the others. I'm not sure of the frequency, but it's in the audible range. Each bit consists of a multitude of wavepeaks.

So, I conclude that I must be the failwhale of the day. However, I can't seem to see what's wrong with all of this.

It's not kosher, this being an Arduino forum :slight_smile: but here is the code for Processing if anyone is curious to try it out. It requires the minim audio library.

import ddf.minim.analysis.*;
import ddf.minim.*;
//import ddf.minim.effects.*;


Minim minim;
AudioInput in;
AudioRecorder recorder;
//FFT fft;

int langth = 0;
int[] Sample = new int[300];
int showBar = 0;
int sampleSize = 16384; //16384
float[] samples;
float thresh = 48;
int crossed = 0;
float sampleRate;
int [] rxString = new int[0];

//at 44111, 300baud on the arduino should be every 147 samples but actually seems to read best at about 98% speed, 144.06.
//300baud = 144.06
//400baud = 111
//275baud = 158.9
//1200baud = 36.75

void setup()
{
  size(1110, 256);
  
  minim = new Minim(this);
  
  in = minim.getLineIn(Minim.STEREO, sampleSize);
  sampleRate = in.sampleRate();

  
  // create an FFT object that has a time-domain buffer 
  // the same size as jingle's sample buffer
  // note that this needs to be a power of two 
  // and that it means the size of the spectrum
  // will be 512. see the online tutorial for more info.
  //  fft = new FFT(in.bufferSize(), sampleRate);
  //  lpf = new LowPassFS(500 , in.sampleRate());
  //in.addEffect(lpf);
  //in.linAverages(128);

}

void draw()
{
  int readRX = 0;
  background(51);
  noFill();
  beginShape();
  //draw line to show where thresh is
  stroke(255);
  line(0, height - thresh*4, width, height - thresh*4);
  // perform a forward FFT on the samples in jingle's left buffer
  // note that if jingle were a MONO file, 
  // this would be the same as using jingle.right or jingle.left
  //  fft.forward(in.mix);
    // draw the line for frequency band i, scaling it by 300 so we can see it a bit better
    //15 = 5600hz
    //8 = 3000hz
    //line(0, height, 0, height - fft.getBand(15)*300);     

  samples = in.left.toArray();  //figure out where the first pulse is and read from there.
  for(int i = 0; i < samples.length; i++){
    if(samples[i]*50 > thresh){
    readRX = i-5;
    break;
    }
  }

  float lineNumber = (sampleSize/144.06-readRX/144.06);
  int mapped = int(map(readRX, 0, sampleSize, 0, width));
  line(mapped, 0, mapped, height);

  for(float p=readRX; p < samples.length;){
    float x = 0;
    for(float d = p+6; d < p + 144.06 && d < samples.length; d++){ 
      x = map(d, 0, in.bufferSize(), 0, width); 
      stroke(#14CAFA);
      vertex(x, height - samples[int(d)]*200);
  
      //Draw a line at each bit interval. At 300baud, about every 111 samples.
      //THIS SLOWS DOWN EVERYTHING! DEBUGGING ONLY
//      stroke(#9B4949);
//      for(int e = 0; e < lineNumber; e++){
//        float interval = mapped+width/(sampleSize/157.16)*e;
 //       line(interval, 0, interval, height); 
 //     }

      if(samples[int(d)-1]*50 < samples[int(d)]*50 && samples[int(d)-1]*50 < thresh && samples[int(d)]*50 >= thresh){
        crossed++;
      }
    }
      
    if(crossed >= 1){
      text(0, x-10, 50);
      rxString = append(rxString, 1);
    }
    else{
      text(1, x-10, 50);
      rxString = append(rxString, 0);
    }
        

        

                crossed = 0;
//println(rxString);
      p = p+144.06;
    }
    endShape();
        //String joinedrxString = join(nf(rxString, 0), "");
        //println(unbinary(joinedrxString));
        //println(joinedrxString);
}

void stop()
{
  // always close Minim audio classes when you finish with them
  in.close();
  minim.stop();
  
  super.stop();
}

PaulS, I've searched all day for a way to bitbang at a given baud rate, but short of getting dirty myself, I can't find one. I'd prefer not to write dozens more lines of code just because my serial pin seems to be wonky...

Anyways, you said various bitrates are supported, do you know of code examples for something like this? Unfortunately, there doesn't seem to be a Serial.xxx() command to bit bang a binary string.

The serial pin seems to be outputting using PWM like all the others.

Quite simply no it is not, look at the signal on a scope with nothing else connected. The signal stays at the appropriate level for the whole of the baud time.

I wonder what makes you so sure, Grumpy_Mike that transmitting via serial pin like this is a bad idea?

I did say that asynchronous serial code is a NRZ code. A piezo will deform when it receives a voltage and flex back when that voltage is removed. Added to that it rings like a bell when it makes a change. Is it that that is fooling you into thinking it is a PWM signal. So a sequence of zeros are identical to a sequence of ones.

Can anyone really look at that and say I'm not getting a binary signal?

Sure you are getting a binary signal because you are hitting the device with binary signals but it doesn't contain the information you think it contains.

Quite simply this is why you can't get it to work, it can't work.

I've searched all day for a way to bitbang at a given baud rate, but short of getting dirty myself, I can't find one.

NewSoftSerial uses any two digital pins to send/receive serial data using bit-banging. The source code is freely distributed.

Far better than using a speaker to try to send data via audio.

You do know that serial data is transmitted with a start bit and ends with a stop bit, so each character is 10 bits long.

Got it!

Yes. Turns out I was just looking at the edges of when the parity changes from 1 to 0 and vice versa. So what I did is, instead of assigning a bit to each spike; each spike, itself was a toggle to switch from 1 to 0 and back. This works very well. I have transmitted HelloWorld, and found out that 44100/300=147 is really the perfect size for correct timing.

Anyways, I'm pretty excited for now. Here is updated Processing code: (I'm a horrible programmer. still crashed half the time, but is a good proof of concept.)

import ddf.minim.analysis.*;
import ddf.minim.*;
//import ddf.minim.effects.*;


Minim minim;
AudioInput in;
AudioRecorder recorder;
//FFT fft;

int langth = 0;
int[] Sample = new int[300];
int showBar = 0;
int sampleSize = 16384; //16384
float[] samples;
float thresh = 48;
int crossed = 0;
float sampleRate;
int [] rxString = new int[0];
int bitDur = 147;

//at 44111, 300baud on the arduino should be every 147 samples but actually seems to read best at about 98% speed, 144.06.
//300baud = 144.06
//400baud = 111
//275baud = 158.9
//1200baud = 36.75

void setup()
{
  size(1110, 256);
  
  minim = new Minim(this);
  
  in = minim.getLineIn(Minim.STEREO, sampleSize);
  sampleRate = in.sampleRate();

  
  // create an FFT object that has a time-domain buffer 
  // the same size as jingle's sample buffer
  // note that this needs to be a power of two 
  // and that it means the size of the spectrum
  // will be 512. see the online tutorial for more info.
  //  fft = new FFT(in.bufferSize(), sampleRate);
  //  lpf = new LowPassFS(500 , in.sampleRate());
  //in.addEffect(lpf);
  //in.linAverages(128);

}

void draw()
{
  int readRX = 0;
     int isOne = 1;
     String [] binString = new String[0];
     String joinedBin = "00000000";
          int separatedBin;
String [] splitBin = new String[0];


  background(51);
  noFill();
  beginShape();
  //draw line to show where thresh is
  stroke(255);
  line(0, height - thresh*4, width, height - thresh*4);
  // perform a forward FFT on the samples in jingle's left buffer
  // note that if jingle were a MONO file, 
  // this would be the same as using jingle.right or jingle.left
  //  fft.forward(in.mix);
    // draw the line for frequency band i, scaling it by 300 so we can see it a bit better
    //15 = 5600hz
    //8 = 3000hz
    //line(0, height, 0, height - fft.getBand(15)*300);     

  samples = in.left.toArray();  //figure out where the first pulse is and read from there.
  for(int i = 0; i < samples.length; i++){
    if(samples[i]*50 > thresh){
    readRX = i-5;
    break;
    }
  }

  float lineNumber = (sampleSize/bitDur-readRX/bitDur);
  int mapped = int(map(readRX, 0, sampleSize, 0, width));
  line(mapped, 0, mapped, height);

  for(int p=readRX; p < samples.length;){
    float x = 0;
    for(int d = p+6; d < p + bitDur && d < samples.length; d++){ 
      x = map(d, 0, in.bufferSize(), 0, width); 
      stroke(#14CAFA);
      vertex(x, height - samples[int(d)]*200);
  
      //Draw a line at each bit interval. At 300baud, about every 111 samples.
      //THIS SLOWS DOWN EVERYTHING! DEBUGGING ONLY
//      stroke(#9B4949);
//      for(int e = 0; e < lineNumber; e++){
//        float interval = mapped+width/(sampleSize/157.16)*e;
 //       line(interval, 0, interval, height); 
 //     }

      if(samples[int(d)-1]*50 < samples[int(d)]*50 && samples[int(d)-1]*50 < thresh && samples[int(d)]*50 >= thresh){
        crossed++;
      }
    }

    if(crossed >= 1 && isOne == 1){
      isOne = 0;
      text(isOne, x-10, 50);
      binString = append(binString, "0");;
      
    }
    else if(crossed >= 1 && isOne == 0){
      isOne = 1;
      text(isOne, x-10, 50);
      binString = append(binString, "1");

    }
    else{
      if(isOne == 1){
         text(isOne, x-10, 50);
         binString = append(binString, "1");
      }
      if(isOne == 0){
        text(isOne, x-10, 50);
        binString = append(binString, "0");
      }

    }
      crossed = 0; //reset crossed for the next bitspace
      p = p+bitDur; //jump to the next bitspace
    }
    endShape();

    for(int f = 1; f < 71;){  //split, format and print binary string
      splitBin = subset(binString, f, 8);
      joinedBin = join(reverse(splitBin), "");
      println((char)unbinary(joinedBin));  
      f = f+10; //jump 10 bits to the next character
    }
              
             

}

void stop()
{
  // always close Minim audio classes when you finish with them
  in.close();
  minim.stop();
  
  super.stop();
}

This code couldn't even compile. It uses an undefined variable and it has unmatched braces.

Pete

Sorry, works on mine. Make sure you have minim installed, of course.

Does minim automatically correct syntax errors?

Pete

doubt it. if it helps you, please send me the debugging errors. thanks.