Integracija ESP8266 modula i mikrofona

Izvor: SIS Wiki
Skoči na: orijentacija, traži

Izradio: Marko Flajšek


Tehnološki zamah u područjima računala, interneta i gotovo svih elektroničkih aparata u posljednjih nekoliko godina zaprepastio je one stare, a i mlađe generacije. Smartphone-i, tableti, prijenosna računala i sve slične tehnologije postali su sveprisutne, a njihov nedostatak je nezamisliv. No, kod svakog sljedećeg izuma koji "zaprepasti" svijet postavlja se pitanje: Gdje je granica? Tehnologija koja se u prethodnih nekoliko godina nadmetnula kao vrlo atraktivnom i primamljivom entuzijastima i iskustvenjacima u razvoju tehnologije je Internet of Things (IoT).

Kratica IoT, kako to navodi kolumnist Forbes-a, Jacob Morgan, je koncept povezivanja bilo kojeg uređaja za on/off prekidačem na internet, a i međusobno. To uključuje mobilne uređaje, aparate za kavu, perilice rublja, slušalice, lapme, nosive uređaje i gotovo sve ostalo što Vam može pasti na pamet. To se odnosi i na komponente strojeva, primjerice mlazni motor aviona ili bušilica na naftnoj platformi. Analitička tvrtka Gartner tvrdi da će se do 2020 godine broj povezanih uređaja iznositi 26 milijarde. IoT je ogrmona mreža povezanih "stvari" (uključujući i ljude), a veze koje se ostvaruju su između međusobno ljudi, međusobno stvari i između ljudi i stvari [1].


Zadatak ovog projekta bila je integracija mikrokontrolera ESP8266, koji pripada u porodicu IoT uređaja te SparkFun Sound Detector-a koji omogućuje snimanje zvuka, u smislu da se snimljeni zvuk u realnom vremenu odašilje i sprema putem internetske veze.

Sadržaj


Što je NodeMCU ESP8266?

Mikrokontroler je kompaktno mikroračunalo dizajnirano da upravlja operacijama ugrađenih sustava u motornim vozilima, robotima, uredskim aparatima, kompleksnim medicinskim uređajima, mobilnim radio stanicama, automatima, kućanskim aparatima i raznim drugim uređajima. Tipični mikrokontroler sadrži procesor, memoriju i ostale periferne dodatke [2].

Slika 1. Izgled NodeMCU-a (Izvor: [1])

NodeMCU, prikazan na slici 1., je open-source IoT platforma. Uključuje firmware koji radi na ESP8266 Wi-Fi SoC (System on a Chip), proizveden od Espressiv Systems te hardware koji je baziran na ESP-12 modulu. Temin "NodeMCU" se po defaultu odnosi na firmware, a ne na dev kit. Na prvi pogled bi običan korisnik pomislio da ovaj mikrokontroler nije nešto posebno, pošto se na samoj stranici proizvoda (http://www.nodemcu.com/index_en.html) ne nalazi gotovo ništa informacija o njemu, već samo nekoliko obilježja i primjera koda.

Najbitnije značajke ovog mikrokontrolera razumljive svim korisnicima su [4]:

Tehničke specifikacije [3]:


ESP-ov ugrađeni Wi-Fi može poslužiti kao samostalna Wi-Fi točka za spajanje drugih uređaja ili kao tehnologija za spajanje na bilo koju bežičnu mrežu. Protokol koji ovaj mikrokontroler podržava je 802.11 b/g/n [7]. Kao što je već u uvodu spomenuto, ESP8266 je kompatibilan sa velikim brojem perifernih uređaja kao što su uređaji za GPS, kamere, senzori, a i mikrofoni, od kojih je jedan upotrijebljen i u ovom projektu, no više o tome u nastavku. Sva ova komunikacija ostvaruje se spajanjem putem Pin-ova na ESP-u čiji je dijagram prikazan na slici 2. Za potrebe ovog projekta korišteni su pinovi:


Slika 2. Dijagram Pin-ova na ESP8266 (Izvor: [2])


Primjer testnih programa

U nastavku je prikazan kod za program paljenja i gašenja LED lampice ESP-a pomoću Web servera pokrenutog na ESP-u.

Prije pokretanja programa je potrebno ESP8266 spojiti putem USB kabla tipa Micro-B Male To Male, a zatim pokrenuti Arduino IDE program i promijeniti SSID i password za Wi-Fi pristup nekoj vlastitoj mreži. Nakon što pokrenete program, u pregledniku se upisom IP adrese ESP-a otvara Web stranica sa kontrolama za paljenje i gašenje LED lampice. Cijeli kod je kreiran po uzoru na primjer dostupan na ovoj stranici gdje se nalazi i cijeli proces postavljanja Arduino IDE-a i povezivanja sa NodeMCU ESP-om: http://www.instructables.com/id/Quick-Start-to-Nodemcu-ESP8266-on-Arduino-IDE/

#include <ESP8266WiFi.h>
 
const char* ssid = "Hotspot";
const char* password = "xxxxxxxx";
 
int ledPin = 2;
WiFiServer server(80);

void setup() {
  Serial.begin(115200);
  delay(10);
 
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
 
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
 
  // Start the server
  server.begin();
  Serial.println("Server started");
 
  // Print the IP address
  Serial.print("Use this URL to connect: ");
  Serial.print("http://");
  Serial.print(WiFi.localIP());
  Serial.println("/");
}

void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
 
  // Wait until the client sends some data
  Serial.println("new client");
  while(!client.available()){
    delay(1);
  }
 
  // Read the first line of the request
  String request = client.readStringUntil('\r');
  Serial.println(request);
  client.flush();
 
  // Match the request
 
  int value = LOW;
  if (request.indexOf("/LED=ON") != -1)  {
    digitalWrite(ledPin, LOW);
    value = LOW;
  }
  if (request.indexOf("/LED=OFF") != -1)  {
    digitalWrite(ledPin, HIGH);
    value = HIGH;
  }
 
  // Return the response
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
 
  client.print("Led pin is now: ");
 
  if(value == LOW) {
    client.print("On");
  } else {
    client.print("Off");
  }
  client.println("<br><br>");
  client.println("<a href=\"/LED=OFF\"\"><button>Turn Off </button></a>");
  client.println("<a href=\"/LED=ON\"\"><button>Turn On </button></a><br />");  
  client.println("</html>");
 
  delay(1);
  Serial.println("Client disonnected");
  Serial.println("");
}

Druga metoda pisanja i pokretanja programa na ESP-u je pomoću MicroPythona - open source interpretera Python programskog jezika. Jednostavnost Python-a čini MicroPython odličnim izborom za početnike koji nisu još upoznati za programiranjem i ovim hardware-om. Sadrži i nekoliko sljedećih unikatnih svojstava koji ga odvajaju od ostalih ugrađenih sustava[10]:

Kako bi MicroPython mogli koristiti na ESP-u potrebno ga je prije toga flash-ati firmware sa kompajliranim MicroPython-om koji je dostupan na ovom linku: http://micropython.org/download. Detaljne upute se nalaze na https://learn.adafruit.com/building-and-running-micropython-on-the-esp8266?view=all#overview.

Važno je napomenuti da, za razliku od Arduino-a, MicroPhython po defaultu zahtijeva dvije datoteke - boot.py i main.py. Boot.py datoteka je prva datoteka koja se izvršava pri paljenju ili resetiranju, a sadrži low-level kod koji postavlja mikrokontroler prije završetka bootanja. Main.py datoteka, koja se pokreće sljedeća po redu, bi trebala sadržavati sve glavne skripte koje bi željeli pokretati na ESP-u [11].

Slijedi primjer koda napisan u MicroPythonu koji, ovisno o veličini buke koja se stvara na Sound Detector-u, pali i gasi LED lampicu.

import machine
import time
pin = machine.Pin(2, machine.Pin.OUT)
adc = machine.ADC(0)
while True:
    print('ADC: ' + str(adc.read()))
    volts = (adc.read()*3.3)/1024;

    if(volts >= 1.0):
        pin.low()
    else:
        pin.high()
        
    time.sleep(0.25)


O SparkFun Sound Detector-u

Sound Detector, proizveden od tvrtke SparkFun je mala pločica koja kombinira mikrofon i nekoliko sklopova za obradu. Osim audio outputa, pruža i binarnu indikaciju prisutnosti zvuka i analognu reprezentaciju amplitude. Zanimljiva komponenta je i crvena LED lampica koja svijetli pri prisutnosti nekog zvuka. Na slici 3. je prikazana ova komponenta [5].

Slika 3. SparkFun Sound Detector (Izvor: [3])

Kako bi započeli koristiti Sound Detector potrebno ga je spojiti na neko napajanje (između 3.5 i 5.5V), po mogućnosti na ono od 5 Volti. Na sljedećem grafu su prikazana tri moguća outputa koje pruža ova pločica. Tamnozeleni trag na grafu je audio output Sound Detectora, tj. audio napon. Svijetlozeleni trag označava tzv. envelope output, tj. amplitudu zvuka koja se povećava što je zvuk kojeg mikrofon očitava glasniji. Posljednji, crveni trag, označava gate output. Ovaj output je nizak u slučaju tišine, a visok kada detektira neki zvuk [5].


Slika 4. Pimjer grafa outputa Sound Detector-a (Izvor: [4])


U srcu Sound Detector-a nalazi se trajno magnetizirana kapsula bez koje ne bi bila moguća pretvorba akustične u električnu energiju. Unutar kapsule se nalazi dijafragma koja je zapravo ploča od malog kondenzatora. Kondenzator kreira otporni djelitelj (eng. voltage divider) te se miče kao reakcija na zvuk gdje se kapacitet mijenja što se ploče približavaju ili udaljavaju. Sound detector je analogni sktop te je to kao takav osjetljiviji na buku na izvoru napajanja nego većina drugih digitalnih sklopova. Podešen je na umjerenu osjetljivost pa se kod direktnog pričanja u mikrofon ili pljeskanjem ruku gate output okida [5]

Primjer demo koda u kojem su prikazane dvije različite metode korištenja Sound Detector-a (envelope i gate output) je prikazan u nastavku [5].

/******************************************************************************
 * sound_detector_demo.ino
 * Sound detector sample sketch
 * Byron Jacquot @ SparkFun Electronics
 * February 19, 2014
 * https://github.com/sparkfun/Sound_Detector
 * 
 * The gate output (a binary indication that is high when sound
 * is present, and low when conditions are quiet) is used to fire a pin-change 
 * ISR, which lights an LED when the sound is present.  The envelope output 
 * (an analog voltage to rises to indicate the amplitude of the sound) is 
 * sampled in the loop(), and it prints an indication of the level to the 
 * serial terminal. 
 *
 * For more details about the Sound Detector, please check the hookup guide.
 *
 * Connections:
 * The Sound Detector is connected to the Adrduino as follows:
 * (Sound Detector -> Arduino pin)
 * GND → GND
 * VCC → 5V
 * Gate → Pin 2
 * Envelope → A0
 * 
 * Resources:
 * Additional library requirements: none
 * 
 * Development environment specifics:
 * Using Arduino IDe 1.0.5
 * Tested on Redboard, 3.3v/8MHz and 5v/16MHz ProMini hardware.
 * 
 * This code is beerware; if you see me (or any other SparkFun employee) at the
 * local, and you've found our code helpful, please buy us a round!
 * 
 * Distributed as-is; no warranty is given.
 ******************************************************************************/

// Define hardware connections
#define PIN_GATE_IN 2
#define IRQ_GATE_IN  0
#define PIN_LED_OUT 13
#define PIN_ANALOG_IN A0

// soundISR()
// This function is installed as an interrupt service routine for the pin
// change interrupt.  When digital input 2 changes state, this routine
// is called.
// It queries the state of that pin, and sets the onboard LED to reflect that 
// pin's state.
void soundISR()
{
  int pin_val;

  pin_val = digitalRead(PIN_GATE_IN);
  digitalWrite(PIN_LED_OUT, pin_val);   
}

void setup()
{
  Serial.begin(9600);

  //  Configure LED pin as output
  pinMode(PIN_LED_OUT, OUTPUT);

  // configure input to interrupt
  pinMode(PIN_GATE_IN, INPUT);
  attachInterrupt(IRQ_GATE_IN, soundISR, CHANGE);

  // Display status
  Serial.println("Initialized");
}

void loop()
{
  int value;

  // Check the envelope input
  value = analogRead(PIN_ANALOG_IN);

  // Convert envelope value into a message
  Serial.print("Status: ");
  if(value <= 10)
  {
    Serial.println("Quiet.");
  }
  else if( (value > 10) && ( value <= 30) )
  {
    Serial.println("Moderate.");
  }
  else if(value > 30)
  {
    Serial.println("Loud.");
  }

  // pause for 1 second
  delay(1000);
}


Streamanje zvuka

Pojam streamanja koristimo kod multimedijskog sadržaja koji se konstantno dostavlja i prezentira krajnjem korisniku od strane pošiljatelja (eng. provider). Klijent pri tome može koristiti neki media player kako bi reproducirao te podatke, primjerice digitalnu datoteku, video ili pjesmu). Od 2016. godine se streamanje generalno shvaća kao slučaj gdje korisnik putem interneta gleda video ili sluša audio sadržaj na nekom ekranu (PC-a, smartphone-a itd.) i preko zvučnika. Da bi mogao reproducirati tu datoteku, korisnik ju prethodno ne mora cijelu preuzeti. Postoji i nekoliko izazova kod streamanja putem interneta kao što je brzina veze koja može uzrokovati stanke pri kontinuiranom dohvaćanju podataka ili neprikladan software na računalu korisnika [8].

Live streaming se odnosi na Internetsko streamanje pri čemu se sadržaj dohvaća i prikazuje u realnom vremenu kao što je to slučaj primjerice kod izravnog prijenosa na televiziji. Na internetu veliki broj platformi za pregledavanje slika i video sadržaja podržava ovakvu tehnologiju. Neki od primjera su YouTube, Twitch, Instagram itd.

WAV format

Za WAV format možemo reći da je jedan od najosnovnijih audio formata. Razvijen je od strane Microsoft-a i IBM-a te je relativno labavo što daje za posljedicu da postoji veliki broj trenutnih WAV datoteka koje teoretski ne bi trebale raditi, ali nekako rade.

WAV datoteke se uobičajeno spremaju bez kompresije, što znači da mogu biti poprilično velike, ali ne mogu premašiti 4GB. Razlog je što je zaglavlje datoteke integer od 32 bita. Spremaju se u binarnom obliku, a sastoje se od komadića (eng. chunk) pri čemu svaki komadić daje neku informaciju o podacima u datoteci. Svaki komadić ima svoju svrhu i specifično je strukturiran pri čemu je i poredak bitan. Važno je napomenuti da svaki komadić, uključujući i zaglavlje datoteke, započinje za četiri znaka (byte) koji definiraju sGroupID. Na sljedećoj slici je ilustrirana struktura WAV datoteke koja će u nastavku biti detaljnije objašnjena [9].


Slika 5. Struktura WAV formata (Izvor: [5])


Zaglavlje

Iako se prema WAV specifikaciji zaglavlje ne tretira kao komadić, ono ipak dolazi prvo u svakoj WAV datoteci. Izgleda na sljedeći način[9]:

Naziv Vrsta podatka Vrijednost Opis
sGroupID (ChunkID) char[4] "RIFF" Za WAV datoteke je ova vrijednost uvijek WAVE
dwFileLength (ChunkSize) uint varijabilna Ukupna veličina datoteke, umanjena za 8 (za ignoriranje RIFF i WAVE teksta u zaglavlju)
sRiffType (Format) char[4] "WAVE" Za WAV datoteke je ova vrijednost uvijek WAVE


Format chunk

Format komadić je zapravo metadata komadić. Specificira većinu stvari kao što je sample rate, bit depth, broj kanala itd. Prije nego prijeđemo na strukturu, važno je razjasniti nekoliko pojmova[9]:

Naziv Vrsta podataka Vrijednost
sGroupID char[4] "fmt"
dwChunkSize uint varijabilna
wFormatTag ushort 1
wChannels ushort varijabilna
dwSamplesPerSec uint varijabilna
dwAvgBytesPerSec uint sampleRate * blockAlign
wBlockAlign ushort Channels * (dwBitsPerSample / 8)
dwBitsPerSample uint varijabilna


Data chunk

Data komadić je relativno jednostavan. Sadrži sGroupID, duljinu podataka i same podatke. Ovisno o odabranoj vrijednosti bit depth-a, razlikovat će se i tip polja [9].

Naziv Vrsta podatka Vrijednost
sGroupID char[4] "data"
dwChunkSize uint varijabilna
sampleData byte[]/short[]/float[] sample data

Za sample data je važno napomenuti da svaki element polja podataka može biti pozitivan, negativan ili 0. Također je bitan i opseg svakog od pojedinih elemenata. Pošto svaki sample predstavlja aplitudu, moramo uzeti u obzir minimalnu i maksimalnu aplitudu temeljem tipa podatka kojeg smo odabrali. Tipovi su: byte[] za 8-bitni audio, short[] za 16-bitni, a float[] za 32-bitni audio [9].

Rasponi vrijednosti za svaki od tipova podataka su [9]:

Kreiranje wave datoteke izgleda poput sastavljanja zaglavlja, Format chunk-a i Data chunk-a i njihovog zapisivanja u binarnom obliku u tom istom poretku [9].

Na slici 6. nalazi se primjer sturkture prvih 72 bajta WAVE datoteke pri čemu su bajtovi prikazani kao heksadekadski brojevi:


Slika 6. Primjer WAV datoteke (Izvor: [6])


Rješenje projekta

U ovom poglavlju biti će objašnjen način na koji je riješen zadani problem, s tehničkog i teoretskog pogleda.

Tehnička implementacija

Za realizaciju ovog projekta korištene su ove komponente:

Za spajanje ESP-a i Sound Detector-a nužno je bilo povezani odgovarajuće pinove na obje komponente. Temeljem dokumentacije komponenti povezane su na sljedeći način (sa lijeve strane se nalaze oznake pinova ESP-a, a sa desne Sound Detector-a):

Kao što je u prethodnim poglavljima objašnjeno, GND komponenta definira uzemljenje, VCC je napon koji treba dovesti do Sound Detector-a - u ovom slučaju je to napon od 5V. Kroz razvoj projekta je testiran rad i sa opcijom napajanja od 3.3V, u kojem je slučaju kvaliteta izlazne datoteke je bila vidljivo slabija (prisutno je više šumova).

A0 je jedini 10-bitni analogno-digitalni pretvornik na korištenom ESP-u te je to bila jedina opcija pristupa, pošto je analogan format zvuka pri snimanju, no kako bi sa njime mogli manipulirati i u ovom slučaju pretvoriti ga u odgovarajući audio format, potrebno ga je pretvoriti u digitalni oblik. U slučaju tišine je vrijednost ADC-a iznosila polovicu skale - 512 bitova.

Na sljedećoj slici je prikazan objašnjeni način spajanja.


Slika 7. Način spajanja Sound Detectora sa ESP8266 mikrokontrolerom


Objašnjenje rješenja

Rješenje projekta možemo podijeliti na nekoliko komponenti. Prvi dio cijele slagalice je ESP koji ima ulogu odašiljača zvuka koji se dohvaća na Sound Detector-u. Prilikom spajanja ESP-a na računalo, upali se LED lampica na obadvije komponente, a mikrokontroler se spaja na odgovarajuću Wi-Fi mrežu za koju su postavljeni parametri u main datoteci te čeka spajanje klijenta koji želi dohvatiti audio podatke. Nakon što se poveže, ispisuje se IP adresa mikrokontrolera. Na slici 8. je prikazan opisani proces na Serial Monitor-u u Arduino IDE-u.


Slika 8. Prikaz spajanja u Serial Monitor-u


U drugom dijelu dolazimo do klijentske aplikacije napisane u node.js-u koja se spaja na spomenutu IP adresu i socket (u ovo primjeru su to adresa 192.168.1.19 i socket 3443). Kako bi bili u mogućnosti pokrenuti tu aplikaciju potrebno je prethodno instalirati node.js na računalo te u command prompt-u ući u direktorij u kojem je pohranjena datoteka datoteka application.js. Pokretanje node.js aplikacije izvršava se naredbom node application.js. Na ekranu bi se trebao pojaviti tekst prikazan na slici 9. koji prikazuje dohvaćenje komadiće u heksadekadskom obliku.


Slika 9. Dohvaćanje podataka u Node.js-u


Nakon što su dohvaćeni svi podaci u prethodno zadanom vremenskom intervalu dohvaćanja, kreira se .wav datoteka koju je moguće reproducirati u nekoj aplikaciji za audio ili video reprodukciju poput VLC playera. Primjer gotove wav datoteke u koju je spremljen rezultat snimanja ovog projekta nalazi se na sljedećem linku: https://www.dropbox.com/s/zlf2txqlq6jewqi/test.wav?dl=0


Programski kod

Programski kod je napisan u programskim jezicima C i JavaScript. Modificiran na temelju primjera sličnog projekta, ali na Particle Photon mikrokontroleru [12].

main.ino program je skecth napisan koristeći tekstualni editor u Arduino IDE-u. Sastoji se od nekoliko funkcija čije će zadaće biti objašnjene u nastavku[13][14]:

Firmware - main.ino

#include <ESP8266WiFi.h>
#include <WiFiClient.h>

#define AUDIO_BUFFER_MAX 8192

int audioStartIdx = 0, audioEndIdx = 0;
uint16_t audioBuffer[AUDIO_BUFFER_MAX];
uint16_t txBuffer[AUDIO_BUFFER_MAX];

// version without timers
unsigned long lastRead = micros();

// WiFi paremeters
char* ssid = "Hotspot";
char* password = "xxxxxxxx";

// LED Pin on the board
int ledPin = 2;

WiFiClient checkClient;
WiFiClient audioClient;

// Start a TCP Server on port 3443
WiFiServer server(3443);

void setup() {
    Serial.begin(115200);
    delay(10);
  
    pinMode(A0, INPUT);
    pinMode(ledPin, OUTPUT);
   
    // Connect to WiFi network
    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);
   
    WiFi.begin(ssid, password);
   
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
  
    lastRead = micros();
    server.begin();
    Serial.println("Server started");
  
    // Print the IP address
    Serial.print("Use this URL to connect: ");
    Serial.print("http://");
    Serial.print(WiFi.localIP());
    Serial.println("/");

}

void loop() {
    checkClient = server.available();

    if (!checkClient) {
      return;
    }

    if (checkClient.connected()) {
        audioClient = checkClient; 
        Serial.println("Client connected");
    }

    //listen for 1000ms, taking a sample every 125us,
    //and then send that chunk over the network.
    listenAndSend(1000);

}

void listenAndSend(int delay) {
    unsigned long startedListening = millis();
    
    while ((millis() - startedListening) < delay) {
        unsigned long time = micros();
        
        if (lastRead > time) {
            // time wrapped?
            //lets just skip a beat for now, whatever.
            lastRead = time;
        }
        
        //125 microseconds is 1/8000th of a second
        if ((time - lastRead) > 125) {
            lastRead = time;
            readMic();
        }
    }
    sendAudio();
}


// Callback for Timer 1
void readMic(void) {
    uint16_t value = analogRead(A0);
    if (audioEndIdx >= AUDIO_BUFFER_MAX) {
        audioEndIdx = 0;
    }
    audioBuffer[audioEndIdx++] = value;
}

void copyAudio(uint16_t *bufferPtr) {
    //if end is after start, read from start->end
    //if end is before start, then we wrapped, read from start->max, 0->end
    
    int endSnapshotIdx = audioEndIdx;
    bool wrapped = endSnapshotIdx < audioStartIdx;
    int endIdx = (wrapped) ? AUDIO_BUFFER_MAX : endSnapshotIdx;
    int c = 0;
    
    for(int i=audioStartIdx;i<endIdx;i++) {
        // do a thing
        bufferPtr[c++] = audioBuffer[i];
    }
    
    if (wrapped) {
        //we have extra
        for(int i=0;i<endSnapshotIdx;i++) {
            // do more of a thing.
            bufferPtr[c++] = audioBuffer[i];
        }
    }
    
    //and we're done.
    audioStartIdx = audioEndIdx;
    
    if (c < AUDIO_BUFFER_MAX) {
        bufferPtr[c] = -1;
    }
}

// Callback for Timer 1
void sendAudio(void) {
    copyAudio(txBuffer);
    
    int i=0;
    uint16_t val = 0;
        
    if (audioClient.connected()) {
       write_socket(audioClient, txBuffer);
    }
    else {
        while( (val = txBuffer[i++]) < 65535 ) {
            Serial.print(val);
            Serial.print(',');
        }
        Serial.println("DONE");
    }
}

// an audio sample is 16bit, we need to convert it to bytes for sending over the network
void write_socket(WiFiClient socket, uint16_t *buffer) {
    int i=0;
    uint16_t val = 0;
    
    int tcpIdx = 0;
    uint8_t tcpBuffer[1024];
    
    while( (val = buffer[i++]) < 65535 ) {
        if ((tcpIdx+1) >= 1024) {
            socket.write((uint8_t*)tcpBuffer, tcpIdx);
            tcpIdx = 0;
        }
        
        tcpBuffer[tcpIdx] = val & 0xff;
        tcpBuffer[tcpIdx+1] = (val >> 8);
        tcpIdx += 2;
    }
    
    // any leftovers?
    if (tcpIdx > 0) {
        socket.write((uint8_t*)tcpBuffer, tcpIdx);
    }
}

Datoteka server.js se pokreće na klijentu, a zadatak joj je osluškivanje podataka na definiranom portu i IP adresi i kreiranje WAV datoteke prema prethodno objašnjenim kriterijima.

Server - server.js


// ESP8266 IP address

var settings = {
	ip: "192.168.1.6",
	port: 3440
};


/**
 * Created by middleca on 7/18/15.
 */


//based on a sample from here
//http://stackoverflow.com/questions/19548755/nodejs-write-binary-data-into-writablestream-with-buffer


var fs = require("fs");

var samplesLength = 1000;
var sampleRate = 8000;

var outStream = fs.createWriteStream("test.wav");

var writeHeader = function() {

	var b = new Buffer(1024);

	b.write('RIFF', 0);

	/* file length */
	b.writeUInt32LE(32 + samplesLength * 2, 4);
	//b.writeUint32LE(0, 4);

	b.write('WAVE', 8);
	
	/* format chunk identifier */
	b.write('fmt ', 12);
	
	/* format chunk length */
	b.writeUInt32LE(16, 16);

	/* sample format (raw) */
	b.writeUInt16LE(1, 20);

	/* channel count */
	b.writeUInt16LE(1, 22);

	/* sample rate */
	b.writeUInt32LE(sampleRate, 24);

	/* byte rate (sample rate * block align) */
	b.writeUInt32LE(sampleRate * 2, 28);

	/* block align (channel count * bytes per sample) */
	b.writeUInt16LE(2, 32);

	/* bits per sample */
	b.writeUInt16LE(16, 34);

	/* data chunk identifier */
	b.write('data', 36);

	/* data chunk length */
	//b.writeUInt32LE(40, samplesLength * 2);
	b.writeUInt32LE(0, 40);

	outStream.write(b.slice(0, 50));
};

writeHeader(outStream);


var net = require('net');
console.log("connecting...");

client = net.connect(settings.port, settings.ip, function () {

    client.setNoDelay(true);

    client.on("data", function (data) {

        try {
		console.log("GOT DATA");
		outStream.write(data);
		//outStream.flush();
		console.log("got chunk of " + data.length + " bytes ");
		console.log("got chunk of " + data.toString('hex'));
	}

        catch (ex) {
		console.error("Er!" + ex);
        }
    });
});


setTimeout(function() {
	console.log('recorded for 3 seconds');
	client.end();
	outStream.end();
	process.exit(0);
}, 3 * 1000);


Zaključak

Premda rješenje projekta nije u cijelosti realizirano, početna ideja projekta bila je koliko zanimljiva, toliko i izazovna. Kao autoru ovog projekta, drago mi je da sam imao prilike isprobati primjer IoT tehnologije i "zagrepsti površinu" kroz jednu od mogućnosti koje ove tehnologije omogućavaju. Na projektu bi se, kao dodatak, mogao implementirati i niz drugih funkcionalnosti poput kontinuiranog streamanja, prepoznavanja glasa, direktnog outputa putem drugog ESP-a na neki zvučnik itd.

Smatram da ove male kartice poput ESP-a, koje su tek zaživjele u prethodnih nekoliko godina te koje su "pod haubom" zapravo daleko veće, imaju veliki potencijal u sadašnjosti, a i u budućnosti u različitim poljima života.


Literatura

  1. Morgan J., A Simple Explanation Of 'The Internet Of Things', http://www.forbes.com/sites/jacobmorgan/2014/05/13/simple-explanation-internet-things-that-anyone-can-understand/#13f314456828 dostupno 21.1.2017.
  2. http://internetofthingsagenda.techtarget.com/definition/microcontroller, dostupno 21.1.2017.
  3. https://en.wikipedia.org/wiki/NodeMCU#cite_note-nodemcu_firmware-4, dostupno 21.1.2017.
  4. https://www.seeedstudio.com/NodeMCU-v2-Lua-based-ESP8266-development-kit-p-2415.html# dostupno, 21.1.2017.
  5. Byron J., Sound Detector Hookup Guide, https://learn.sparkfun.com/tutorials/sound-detector-hookup-guide, dostupno 21.1.2017.
  6. Jayakumar M., Quick Start to Nodemcu (ESP8266) on Arduino IDE, http://www.instructables.com/id/Quick-Start-to-Nodemcu-ESP8266-on-Arduino-IDE/, dostupno 21.1.2017.
  7. ESP8266 WiFi Module Quick Start Guide, http://rancidbacon.com/files/kiwicon8/ESP8266_WiFi_Module_Quick_Start_Guide_v_1.0.4.pdf, dostupno 21.1.2017.
  8. https://en.wikipedia.org/wiki/Streaming_media, dostupno 24.2.2017.
  9. dawate, Intro to Audio Programming, Part 2: Demystifying the WAV Format, https://blogs.msdn.microsoft.com/dawate/2009/06/23/intro-to-audio-programming-part-2-demystifying-the-wav-format/, dostupno 24.1.2017.
  10. MicroPython Basics: What is MicroPython?, https://learn.adafruit.com/micropython-basics-what-is-micropython/overview, dostupno 24.1.2017.
  11. MicroPython Basics: Load Files & Run Code, https://learn.adafruit.com/micropython-basics-load-files-and-run-code/boot-scripts, dostupno 24.1.2017.
  12. Middlecamp D., Sending sound over the internet!, https://www.hackster.io/middleca/sending-sound-over-the-internet-f097b4, dostupno 24.1.2017.
  13. https://www.arduino.cc/en/Reference/Setup, dostupno 25.1.2017.
  14. https://www.arduino.cc/en/Reference/Loop, dostupno 25.1.2017.
Osobni alati
Imenski prostori
Inačice
Radnje
Orijentacija
Traka s alatima