@Dracon @tandisbh
Hello,
For a project of mine I am trying to send video and audio from an ESP32-CAM to a Webserver. Video was no problem thanks to many tutorials but as you i am struggling with audio transmission.
I found this:
https://www.youtube.com/watch?v=NWdemILPp3Y which has a different goal but was helpful in so far that I am now able to send a constant frequency to my Webserver. The mic i ordered is still on the way but using its I2S interface i hope i will be able to send life audio from it.
In the video he uses PCM from the Wave Format and streams it using the HTML5 audio feature. I used a lot of his code and implemented it in my code for the video server. With that i can now send a constant frequency but technically any integer value should work. So in theory you should just be able to replace the sin-function with e.g an I2S read-function (some format converting may be required).
The one problem i encountered so far is the conflict between my audio an video stream. I don't know enough about HTML to explain it but I think it do to the while(true) loop in the stream_handler. Maybe u can switch between them but I will test it in the future.
Anyways here iss the code (without the video stream, just audio).
Code: Select all
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // disable brownout problems
#include "soc/rtc_cntl_reg.h" // disable brownout problems
#include "esp_http_server.h"
#include <math.h>
// Replace with your network credentials
const char* ssid = "xxxxxxxxxxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
//Wifi Reset Counter
int counter = 0;
httpd_handle_t camera_httpd = NULL;
httpd_handle_t audio_stream_httpd = NULL;
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
<head>
<title>ESP32-CAM Robot</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px; background-color:#222222}
td { padding: 8 px; }
</style>
</head>
<body>
<h1 style="color: white">ESP32-CAM Doorbell Audio</h1>
<audio controls src="" type="audio/x-wav" id="audio-wav">
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("audio-wav").src = window.location.href.slice(0, -1) + ":81/audio_stream";
</script>
</body>
</html>
)rawliteral";
//loads Webpage itself
static esp_err_t index_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}
//################################################
#define SCRATCH_BUFSIZE 8192
typedef struct {
uint32_t chunk_id;
uint32_t chunk_size;
uint32_t format;
}chunk_riff_t;
typedef struct {
uint32_t chunk_id;
uint32_t chunk_size;
uint16_t audio_format;
uint16_t num_of_channels;
uint32_t samplerate;
uint32_t byterate;
uint16_t block_align;
uint16_t bits_per_sample;
}chunk_fmt_t;
typedef struct {
uint32_t chunk_id;
uint32_t chunk_size;
}chunk_data_t;
typedef struct {
chunk_riff_t riff;
chunk_fmt_t fmt;
chunk_data_t data;
}wav_header_t;
struct streaming_wav_t{
wav_header_t hdr;
int16_t *buf;
int buf_size;
int cnt;
} ;
void streaming_wav_destroy( struct streaming_wav_t* wav ) {
free(wav->buf);
}
int streaming_wav_factor( struct streaming_wav_t* wav ) {
return (
wav->hdr.fmt.num_of_channels *
wav->hdr.fmt.bits_per_sample / 8 );
}
void streaming_wav_init( struct streaming_wav_t* wav, int buffer_size ) {
int num_channels = 1;
int bits_per_sample = 16;
int sample_rate = 8000;
wav->cnt = 0;
streaming_wav_header( wav, num_channels, bits_per_sample, sample_rate );
wav->buf = (int16_t*)malloc( buffer_size );
wav->buf_size = buffer_size / streaming_wav_factor( wav );
}
void streaming_wav_header( struct streaming_wav_t* wav, int num_channels, int bits_per_sample, int sample_rate )
{
wav_header_t* w = (wav_header_t*) &(wav->hdr);
int len = 0xFFFFFFFF;
w->riff.chunk_id = 0X46464952; // "RIFF"
w->riff.format = 0X45564157; // "WAVE"
w->riff.chunk_size = len;
w->fmt.chunk_id = 0X20746D66; // "fmt "
w->fmt.audio_format = 1;
w->fmt.bits_per_sample = bits_per_sample;
w->fmt.block_align = num_channels * bits_per_sample/8;
w->fmt.byterate = sample_rate * num_channels * bits_per_sample/8;
w->fmt.chunk_size = 16;
w->fmt.num_of_channels = num_channels;
w->fmt.samplerate = sample_rate;
w->data.chunk_id = 0X61746164;
w->data.chunk_size = len;
}
void streaming_wav_play( struct streaming_wav_t* wav, float frequency ) {
int sample_rate = wav->hdr.fmt.samplerate;
int Loop = sample_rate / frequency * 1000;
for( int j = 0 ; j < wav->buf_size ; j++ )
{
int i = wav->cnt++;
if ( wav->cnt > Loop )
wav->cnt = 0;
double sin_float = 15000 * sinf( 2 * i * M_PI / ( sample_rate / frequency ) ); //15k for Amplitude
int16_t lval = sin_float;
int16_t rval = sin_float;
if ( wav->hdr.fmt.num_of_channels == 2 ) {
wav->buf[j*2] = lval;
wav->buf[j*2+1] = rval;
} else {
wav->buf[j] = lval;
}
}
}
static esp_err_t audio_stream_handler(httpd_req_t *req)
{
Serial.println("in audio_stream_handler");
httpd_resp_set_type(req, "audio/x-wav");
streaming_wav_t wav;
Serial.println("sending header");
streaming_wav_init( &wav, SCRATCH_BUFSIZE );
httpd_resp_send_chunk(req, (const char*)&(wav.hdr), sizeof(wav.hdr));
size_t chunksize = SCRATCH_BUFSIZE;
float frequency = 600;
int cnt = 0;
while(true) { //Basically while(true)
streaming_wav_play( &wav, frequency );
if (httpd_resp_send_chunk(req, (const char*)wav.buf, chunksize) != ESP_OK) { //Sending Audio Buffer
Serial.println("File sending failed / Windows Closed");
httpd_resp_sendstr_chunk(req, NULL);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
streaming_wav_destroy( &wav );
return ESP_FAIL;
}
}
streaming_wav_destroy( &wav );
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}
//#########################################################################
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t audio_stream_uri = {
.uri = "/audio_stream",
.method = HTTP_GET,
.handler = audio_stream_handler,
.user_ctx = NULL
};
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(camera_httpd, &index_uri);
}
config.server_port += 1;
config.ctrl_port += 1;
if (httpd_start(&audio_stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(audio_stream_httpd, &audio_stream_uri);
}
}
//######################################################################
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
//Enable Serial Debug
Serial.begin(115200);
Serial.setDebugOutput(false);
// Wi-Fi connection
Serial.println("");
Serial.print("Connecting to Wifi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
counter++;
if (counter == 10){
Serial.println("");
Serial.println("Could not Connect to Wifi - Restarting...");
delay(1000);
ESP.restart();
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.println(WiFi.localIP());
// Start streaming web server
startCameraServer();
}
//###################################################################
void loop() {
}
There is probably some stuff in there thats useless, but I'm still figuring out a lot of stuff myself.
Hope i could be of any help.