Transfer data to arduino via audio

I'm trying to get an Arduino to trigger some lights from an audio signal. Not like disco lights, but controlled triggering. For example if I somehow send it "RED ON" it lights a red led, "BLUE FLASH" it pulses a blue led, etc. I have complete control over the audio so I can send it a particular frequency, or morse code, or something else, ideally not too long. Obviously this would be trivial over serial but I'm struggling to do it with an audio signal. I've built a simple filter based on the circuit below and if I play a morse code audio I can get something readable.

After a bit of tinkering with my audio source I'm getting a reasonable signal at the analogue pin. I've made my own "morse code" with more variance in the dots and dashes so this seems promising but before I go too far down this rabbit hole are there any clever ways to send/read data like this?

A couple of things I'm struggling with: How to know that a new string of data has started? How to get on with other stuff while monitoring the audio?

I'll only really need 8 potential input/outputs, but it would be nice if I could get quite a few more for a future project.

[https://europe1.discourse-cdn.com/arduino/original/4X/4/b/e/4be69acdcaf8ebdcecd3a3ce8e51a12c63f5327b.gif]

The audio is coming from a headphone socket if that makes any difference

OP’simage
audio_1

An interesting project..
You have two different challenges, a non-blocking light controller, and the more complex filter/decoder/parser to acquire the control messages.

You might try tones, or more complex messages like morse… but that’s up to you.

You might look intoPSK, and fourier filters to extract your control messages. There’s a lot of options. None of them simple.

Well, "complete" is not very precise. Can you e.g. control the duration of each half-wave separately?

The simplest solution might be a IR remote control with a IR LED at the earphone output and a IR remote receiver at the Arduino. Then you send packets of about 40 kHz tones, with a duration of about 500 or 1000 µs, similar to your morse code project. The IRremote library then decodes the packets according to a number of available protocols - chose one.

Fair point. It'll be a WAV file I will create somehow. Probably in Audacity. Play RED_ON.wav and the red led lights on the Arduino, play BLUE_FLASH.wav and the blue light flashes. Something like that

Please post the schematic. Also please provide all the other missing details of your project.

Thanks for the pointers. I've spent a few hours on this now and got something that works. Its not the most elegant but its been reliable. I created my own version of morse code. Four digits long with four different lengths of beep. I'm starting each code with a 4 long beep then using a mix of 1,2 and 3 length beeps for the actual data. That gives me 27 codes but it'll be easy to extend with more digits or more numbers.

The code is a bit hacked around as I've been tinkering but it is good to have something working. I'm a self taught, late starter coder so I'm sure there is stuff in there that will make proper coders wince but I'm open to criticism and advice on neatening it up.

I will probably tighten up the audio clips and the timing. I'm sure it could all be much faster. Each clip takes about 0.1 seconds, which is very slow for a microprocessor but its close to instant to the eye.

These are a couple of the audio clips:
4111

4221

And this is the code

#include <Adafruit_NeoPixel.h>

#define PIN 11
#define PIXNUM 32
#define ButtonLed 6

uint32_t c;
int col;

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXNUM, PIN, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.


int counter = 0;
int lastcounter = 0;
int thedata = 0;
int thebits = 0;

int inputArraySize = 5;
int inputs[5];
int biggestSoFar = 0;

int NeoOn = 0;
boolean NeoFlash = false;
int flashCounter = 0;
int flashMax = 4;//How many times to loop the flash
int flashLoop = 8;//How many LEDs per loop
unsigned long previousMillis = 0;  // will store last time LED was updated
const long flashInterval = 400;  // interval at which to flash (milliseconds)
const long blinkInterval = 300;  // interval at which to flash (milliseconds)

void setup() {
  pinMode(A5, INPUT);
  pinMode(ButtonLed, OUTPUT);
  // Start serial communication with the PC (over hardware serial)
  Serial.begin(9600);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

}

void loop() {
  int AudioSignal = analogRead(A5);//read the audio level
  AudioSignal = abs(AudioSignal - 506);//how far is it from the zero (mid) point
  biggestSoFar = AudioSignal; //set current as the biggest so far

  for (int i = 1; i < inputArraySize; i++) {
    int pos = inputArraySize - i;
    inputs[pos] = inputs[pos - 1]; //shift each signal to the next postion
    if (inputs[pos] > biggestSoFar)biggestSoFar = inputs[pos];//find the biggest in the last n inputs
  }
  inputs[0] = AudioSignal;

  //Serial.println(biggestSoFar);
  delay(1);

  if (biggestSoFar < 10 ) {
    //Serial.print("_");
    counter = 0;
  }
  if (biggestSoFar > 100 ) {
    //Serial.print("X");
    counter++;
    lastcounter = counter;
  }

  if (lastcounter != 0 and counter == 0) {
    Serial.print(lastcounter);
    Serial.print(" ");
    int number = checknumber(lastcounter);
    if (number == 4) {
      thedata = 0;
      thebits = 0;
    }
    else {
      thedata *= 10;
      thedata += number;
      thebits ++;
    }

    Serial.println(number);

    if (thebits == 3) {
      thebits = 0;

      Serial.print("***  ");
      Serial.print(thedata);
      Serial.println("  ***");
    }
    lastcounter = 0;
  }

  if (thedata > 100 && thedata < 199) {
    NeoOn = 100;
  }

  if (thedata > 200 && thedata < 299) {
    NeoOn = 200;
  }
  if (thedata >= 300)DoButtonState();

  DoLightState();
}

int DoLightState() {
  unsigned long currentMillis = millis();

  if (NeoOn == 100) { //light is off, turn it on
    Serial.println("---  BLINK  ---");
    if (thedata == 121)  c = strip.Color(180, 80, 0); //amber
    if (thedata == 111)  c = strip.Color(200, 0, 0); //red
    if (thedata == 112)  c = strip.Color(0, 200, 0); //green
    if (thedata == 113)  c = strip.Color(0, 0, 200); //blue

    for (int k = 0; k < PIXNUM; k++) {
      strip.setPixelColor(k, c);
    }
    strip.show();
    NeoOn = 101;
    previousMillis = currentMillis;
  }
  else if (NeoOn == 101) { //light is on, check how long it has been on
    if (currentMillis - previousMillis >= blinkInterval) {
      strip.clear();
      strip.show();
      NeoOn = 0;
    }
  }

  else if (NeoOn == 200) { //light is off, flash it on

    Serial.println("###  LOOP  ###");
    if (thedata == 221)  c = strip.Color(180, 80, 0); //amber
    if (thedata == 211)  c = strip.Color(200, 0, 0); //red
    if (thedata == 212)  c = strip.Color(0, 200, 0); //green
    if (thedata == 213)  c = strip.Color(0, 0, 200); //blue

    flashCounter = 0;
    NeoOn = 201;
    previousMillis = currentMillis;
  }

  else if (NeoOn == 201) { //loop the lights
    if (currentMillis - previousMillis >= flashInterval) {
      int out = flashCounter % flashLoop;
      int off = out - 2;//how many LEDs on at a time
      if (off < 0)off += flashLoop;
      strip.setPixelColor(out, c);
      strip.setPixelColor(off, 0);
      out += flashLoop;
      off += flashLoop;
      strip.setPixelColor(out, c);
      strip.setPixelColor(off, 0);
      strip.show();

      flashCounter++;
      previousMillis = currentMillis;
      if (flashCounter > flashMax * flashLoop) { //flash completed
        NeoOn = 0;
        strip.clear();
        strip.show();
      }
    }
  }

  if (thedata > 100)thedata = 0; //reset if we have complete data

}

int DoButtonState() {

  if (thedata == 311)digitalWrite(ButtonLed, HIGH); //light button LED
  if (thedata == 322)digitalWrite(ButtonLed, LOW); //turn off button LED
  thedata = 0;
}

int checknumber(int numberToTest) {

  if (numberToTest > 35)return 4;
  if (numberToTest > 27)return 3;
  if (numberToTest > 18)return 2;
  if (numberToTest > 5)return 1;
  return 0;

}

And the serial port debug

40 4
22 2
10 1
11 1
***  211  ***
###  LOOP  ###
42 4
13 1
13 1
14 1
***  111  ***
---  BLINK  ---
40 4
22 2
22 2
10 1
***  221  ***
###  LOOP  ###

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.