🔊

SoundFi

I first saw this discussion and it turned out sound was a good choice to transmit data one way>How to transmit data underwater over 40m So, I wanted to make a little project that demonstrates that possibility.
 
notion image
 

The Idea

The idea was simple, to transmit data using sound waves. But, not only simple text I wanted to create a device that can demonstrate how we can use sound to take readings in water bodies (oceans) and send it to surface stations.
What I wanted to do was simple. Collect data from BME 280 sensor(measures temperature, humidity and air pressure), send it via sound to a receiver, The receiver takes the data, splits it and sends it to Cloud. I am using Blynk service. It is a very easy to use service to send data and has a simple to use app.

Procedure

The Scrapped Method

I originally thought of using ultrasonic sensors I had laying around. I thought of sending pulses long and short depending on 0 and 1 on the string.
On the receiver end I planned to measure the pulse and determine whether it was a 0 or a 1.
So, I created some test code to send 0 and 1 as pulses of sound and other for receiver that would measure the duration of the pulse.
For the hardware I wired an Arduino Leonardo to transmitting side and an Arduino Uno to receiver ultrasonic sensor. I leaned that the normal ultrasonic sensors don't work at 3.3v so 5v Arduino.s were my only option.
String data = "11011010";
#define oPin D3
void setup() {
  pinMode(oPin, OUTPUT);
}

void loop() {
  for (int x = 1; x < 500; x++) {
    for (int i = 0; i < data.length(); i++) { 
      int n = data[i];
      if (n == 1) {
        digitalWrite(oPin, 1);
        delay(0.5);
        digitalWrite(oPin, 0);
        delay(1);
      }
      if (n == 0) {
        digitalWrite(oPin, 1);
        delay(1);
        digitalWrite(oPin, 0);
        delay(0.5);
      }
    }
    delay(1000);
  }
}
#define pIn 8

int cumulative_time = 0;

int MAX_DURATION = 500 * 1000;
byte timeD[5000];

String signalR = "";
void setup() {
  pinMode(pIn, INPUT);
  Serial.begin(9600);
}

void loop() {
  long beginning_time = micros() / 1000;
  for ( int i = 0; cumulative_time < MAX_DURATION; i++) {
    int time_delta = micros() / 1000 - beginning_time;
    timeD[i] = (time_delta);
    signalR += digitalRead(pIn);
    cumulative_time = time_delta;
  }

  for(int i=0;i<signalR.length();i++){
        Serial.println(timeD[i],signalR[i]);
        
  }
}
 
I soon found out that it didn't work. The reason being that ultrasonic sensors work in a very different way. Basically, the echo pin remains high for time from when the pulse was started (when trig pin was hit) to when it received back the pulse. The pulseIn() function in arduino basically thus measures time for which the pin was kept high. Since we cannot remotely trigger a pin without sending, the method cannot work.

New idea

notion image
Then I thought to just use transducers of the ultrasonic sensors. I soldered one ultrasonic sensors transmitter pins and other ultrasonic sensor's receiver to jumper cables. The reason was that after the project I could de solder the wires and use it for something else.
 
Then I planned to use that as baseline to work on. But soon found out this great way how to do it on Hackaday. https://hackaday.com/2018/06/01/dead-simple-ultrasonic-data-communication/ . This tells us how important research is!
Basically what is happening is data is being transmitted by pulses of 2ms and 4ms for 0 and 1 respectively. On receiver side it is being measured as 0 or 1 and being reverses back to ASCII to character and printing it.

Part Required

  • Arduino Uno // or any other
  • Wemos D1 mini // or any other Wi-Fi enabled uC
  • 2 ultrasonic sensors // or just transducers
  • LM386 //to amplify sound waves
  • LM 393 // as a comparator
  • 10K potentiometer //for fine-tuning
  • 100nF capacitor //to remove noise
  • 10K resistor
  • BME 280 // temperature, humidity, air pressure sensor
 

Assembling

The schematic for the same is on the Hackaday post. I am just using wemos instead of an Arduino so that the data received can also be sent to the cloud. The only change is supply to comparator which will be 3.3v instead of 5v.

Schematic for receiver. Credit : the hackaday post

 
notion image
 
I did not connect the LED as found no practical use to it.

Schematic for Transmitter is pretty simple:

  1. Connect BME 280 sensor via I2C/SPI. I used I2C because it requires only 2 wires. Also connect VCC/Vin and GND pins of sensor to your development board.
  1. Connect the transducer's one pin to pin 3 and other to GND
 
Transmitter
Transmitter
 
Receiver
Receiver
 
 

Working

Here is how it works:
  1. Data us read from BME280 sensor on the transmitter and a string is made in this format : <temperature>,<pressure>,<humidity>,#
  1. The string is then broken into characters and each character into 8 bits ASCII Value.
  1. If it is 1 then the ultrasound pulse of 2ms and if 0 then of 4ms is formed. The "tone()" function of Arduino creates a very high vibrating frequency .
  1. On the receiver side data is received by measuring the time duration of which the pulse is their. Then it is converted to either 1 or 0 based on the pulse duration.
  1. Then the bits are converted to ASCII to characters and finally a string is formed.
  1. This string is then used to separate the temperature, air pressure and humidity values.
  1. The data is then pushed to Blynk cloud on pins V1, V2, V3 respectively.

Code

/*
  Developed by:  Eduardo Zola - Zola Lab 2018 - www.zolalab.com.br - egzola@gmail.com
  Ultrasonic Data Communication
  Edited by Vimarsh - vimarsh.info
  To send weather data using BME 280 sensor
*/


#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

unsigned long delayTime;



void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);

  unsigned status;

  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  status = bme.begin();
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
    Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(), 16);
    Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
    Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
    Serial.print("        ID of 0x60 represents a BME 280.\n");
    Serial.print("        ID of 0x61 represents a BME 680.\n");
    while (1);
  }
}

void loop()
{
  send(Values());
  //send("Ultrasonic communication by Zola Lab\n");
  delay(1000);
  //send("Hello World\n\n");
}

void send(String msg)
{
  byte ch;
  unsigned int pos = 0;
  unsigned int sz = msg.length();
  while (pos < sz)
  {
    ch = msg.charAt(pos);
    Serial.print((char)ch);
    tone(3, 40000);
    delay(10);
    noTone(3);
    for (int i = 0; i < 8; i++)
    {
      boolean b;
      b = bitRead(ch, 7 - i);
      if (b)
      {
        tone(3, 40000);
        delay(2);
      }
      else
      {
        tone(3, 40000);
        delay(4);
      }
      noTone(3);
      delay(11);
    }
    pos++;
  }
}


String Values() {
  String s="";
  s = String(bme.readTemperature()) + "," + String(bme.readPressure() / 100.0F) + "," + String(bme.readHumidity()) + ",#";
  
  return s;
}
 
/*
  Developed by:  Eduardo Zola - Zola Lab 2018 - www.zolalab.com.br - egzola@gmail.com
  Ultrasonic Data Communication
  
  Modified by Vimarsh - vimarsh.info
  To send recieved weather data to IOT app sevice Bly
*/


int pos = 0;
unsigned char CH = 0;
unsigned int bits1 = 0;
boolean capture = false;

#define BLYNK_PRINT Serial


#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YourAuthToken";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "ssid";
char pass[] = "psk";

void setup()
{
  Serial.begin(115200);
  pinMode(5, INPUT_PULLUP);

  Blynk.begin(auth, ssid, pass);
}

void loop()
{
  Blynk.run();
  if (digitalRead(5))
  {
    bits1 = 0;
    unsigned long deltaT = millis();
    while (millis() - deltaT <= 10) if (digitalRead(5)) bits1 ++;
    //Serial.println(bits1);
    String s;
    if (capture)
    {
      boolean b = 0;
      if (bits1 > 290 && bits1 < 600) b = 0;
      if (bits1 > 20 && bits1 < 290) b = 1;
      if (b) bitSet(CH, 7 - pos); else bitClear(CH, 7 - pos);
      //Serial.print(b);
      pos++;
      if (pos == 8)
      {
        if ((char)CH == '#')
          send_data(s);
        s += (char)CH;
        //Serial.print((char)CH);
        pos = 0;
        capture = false;
      }
    }
    if (bits1 > 600)
    {
      capture = true;
      pos = 0;
    }
  }
}

void send_data(String s) {
  String buff = ""; int k = 1;
  String temp, humid, pressure;
  for (int i = 0; i < s.length(); i++) {
     if (s[i] != ',')
      buff += s[i];
    if (s[i] == ',') {
      //buff.resize(buff.size() - 1); This works with std:string not String :-(
      if (k == 1)
        temp = buff;
      else if (k == 2)
        pressure = buff;
      else if (k == 3)
        humid = buff;
      k++;
      buff = "";
    }
  }
  Blynk.virtualWrite(V1, temp);
  Blynk.virtualWrite(V2, pressure);
  Blynk.virtualWrite(V1, humid);
}
 

Setting up Blynk Application - Cloud connectivity

Blynk is the easiest way to connect your Arduino/Raspberry Pi project to the cloud.
Blynk doesn't offer a webpage from where you can control/monitor your devices. All must happen through the smartphone app.
First step is to download the Blynk app from the Play Store/ App Store.
Then follow these steps to complete setting it up.
See the setup images from the original guide / post linked at top of this page
Also, because you are using a cloud service. You can download the app on any other phone and also see that data!
 

Results

You can listen to sensor sending data 3 times with one second delay in between: Listen here
 
I found that the connection link was not stable for esp8266 to Blynk server perhaps because of code in-between which Blynk says no as because of delays the server session may time out.
Also, the distance between sensors and sensitivity may have to be tuned in a better way. Just if I had an oscilloscope!
Data being received>
 
notion image
 

Speed of Transmission

I edited the code of receiver to find time it takes to received the full string. I turned out to be around 2600 ms.
long initial_time=micros();  
...  
//existing code  
...  
long final_time = micros();  
long transmit_time= (final_time-initial_time)/1000;  
Serial.println(transmit_time);
Here is output with that:
 
notion image
 
To calculate speed I measured the Bytes being sent and divided it by time it takes.
It is 21 bytes:
 
notion image
 
Dividing it by 2592 milli seconds

Speed = 8.077 bytes / second

 
notion image
 
The speed seems quite less and it is compared to WiFi and other RF. But that is still reasonable if we want to send some specific readings.

Read This

If you want to see the data on your smartphone which my device is transmitting. Then see the steps>
I will keep it on for some days probably and hopefully it will be transmitting. I live in Gujarat, India. So those are my indoor weather conditions. Also, I have added a button to control the onboard LED on the Wemos D1
steps presentation on the original post (simple blynk procedure and stuff)
 

Conclusion

This was a fun experiment for it. It was easy and a great insight on how to transmit data using sound.
Later I wish to add some sort of encryption and try for two way communication without interference.