如何从Django服务器传输文件到esp32?

jgovgodb  于 2023-05-08  发布在  Go
关注(0)|答案(1)|浏览(185)

我正在做一个项目,我有一个Django服务器和一个带有SD卡的ESP32。我想通过HTTP将.wav文件传输到ESP32,但是,我没有正确的方法。这个文件对于ESP32来说有点太大了,我得到一个错误,[E][WiFiClient.cpp:517] flush(): fail on fd 48, errno: 11, "No more processes"。有人告诉我,通过JSON发送文件是一个非常糟糕的主意,我同意。我只是不知道还能怎么发送。我试过这个:

def text_to_wav(voice_name: str, text: str):
  language_code = "-".join(voice_name.split("-")[:2])
  text_input = tts.SynthesisInput(text=text)
  voice_params = tts.VoiceSelectionParams(
      language_code=language_code, name=voice_name
  )
  audio_config = tts.AudioConfig(audio_encoding=tts.AudioEncoding.LINEAR16)

  client = tts.TextToSpeechClient()
  response = client.synthesize_speech(
      input=text_input,
      voice=voice_params,
      audio_config=audio_config,
  )

  return b64encode(response.audio_content).decode("utf-8")

@api_view(["POST"])
def tts_view(request: Request):
  voice_name = request.data.get("voice_name")
  text = request.data.get("text")

  if not voice_name or not text:
    print("Missing voice_name or text")
    return JsonResponse({"error": "Missing voice_name or text"}, status=400)

  response = HttpResponse(text_to_wav(voice_name, text), content_type="audio/wav")
  response["Content-Disposition"] = "attachment; filename=%s.wav" % text

  return response

不过,我认为这是为了将文件传输到Web浏览器,而不是ESP32。
另一方面,我是这样做的请求:

void request_tts(string text, fs::FS &SD) {
  HTTPClient http;

  string server = SERVER_NAME;
  server += "tts/create-speech/";
  http.begin(server.c_str());

  string body = "{\"text\":\"" + text + "\", \"voice_name\":\"es-US-Neural2-B\"}";

  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", "Api-Key 0ZfAfvpE.y498gbIbw2QdKCCcJCIETRal1q9mjPy9");

  int response_code = http.POST(body.c_str());

  // Get payload
  String payload = http.getString();

  http.end();
}

我试着下载AsyncHTTPRequest_Generic,但它就是不能编译,说我丢失了一个头文件,STM32的东西(对不起,我不记得丢失的确切文件,但我重新下载了3次,这是很久以前的事了)。

yhxst69z

yhxst69z1#

所以解决方案是以块形式流式传输文件。我不得不修改服务器和ESP32中的请求来管理这一点。
这是服务器中的修改:

from django.http import StreamingHttpResponse

def text_to_wav(voice_name: str, text: str):
  language_code = "-".join(voice_name.split("-")[:2])
  text_input = tts.SynthesisInput(text=text)
  voice_params = tts.VoiceSelectionParams(
      language_code=language_code, name=voice_name
  )
  audio_config = tts.AudioConfig(audio_encoding=tts.AudioEncoding.LINEAR16)

  client = tts.TextToSpeechClient()
  response = client.synthesize_speech(
      input=text_input,
      voice=voice_params,
      audio_config=audio_config,
  )

  return response.audio_content # Take out the b64encoding

def wav_streaming_iterator(wav_content):
  # Define the size of each chunk to be yielded
  chunk_size = 8192
  while True:
    # Get the next chunk of data
    chunk = wav_content[:chunk_size]
    # If there's no more data, break out of the loop
    if not chunk:
      break
    # Remove the chunk from the data buffer
    wav_content = wav_content[chunk_size:]
    # Yield the chunk to the caller
    yield chunk

@api_view(["POST"])
def tts_view(request: Request):
  voice_name = request.data.get("voice_name")
  text = request.data.get("text")

  if not voice_name or not text:
    print("Missing voice_name or text")
    return JsonResponse({"error": "Missing voice_name or text"}, status=400)

  # Generate the audio content as a WAV file and create a streaming 
  response object
  response = StreamingHttpResponse(
      wav_streaming_iterator(text_to_wav(voice_name, text)),
      content_type="audio/wav",
  )
  # Set the filename of the response
  response["Content-Disposition"] = 'attachment; filename="output.wav"'

  # Return the streaming response
  return response

wav_streaming_iterator函数是一个生成器函数,它以字节字符串的形式接收WAV音频数据块,并将其以较小的块返回给调用者。这对于通过网络连接发送大型音频文件非常有用,因为它允许数据以较小的片段而不是一次发送。
tts_view函数是Django REST框架API的端点,它从请求数据中获取语音名称和文本,使用text_to_wav函数从文本中生成WAV音频文件,并返回一个流式响应对象,该对象使用wav_streaming_iterator函数迭代较小的音频数据块。设置Content-Dispositio n头以指示响应应作为名为“output.wav”的文件下载。
在ESP32中管理此内容的方法:

void request_tts(string text, fs::FS &SD) {
  HTTPClient http;

  // Set the URL of the TTS API
  string server = SERVER_NAME;
  server += "tts/create-speech/";
  http.begin(server.c_str());

  // Set the text and voice name in the request body
  string body = "{\"text\":\"" + text + "\", \"voice_name\":\"es-US-Neural2-B\"}";

  // Set the headers of the request
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", "Api-Key 0ZfAfvpE.y498gbIbw2QdKCCcJCIETRal1q9mjPy9");

  // Send the POST request and get the HTTP response code
  int http_code = http.POST(body.c_str());

  // Check if the response code is OK
  if (http_code != 200) {
    http.end();
    throw runtime_error("Error requesting TTS");
  }

  // Set the filename for the WAV file to be saved
  string filename = "/audios/" + text + ".wav";

  // Open the file for writing
  fs::File file = SD.open(filename.c_str(), FILE_WRITE);
  if (!file) {
    http.end();
    throw runtime_error("Error opening file");
  }

  // Write each chunk of the response to the file as it arrives
  Serial.println("Writing to file");
  WiFiClient *stream = http.getStreamPtr();
  const size_t buffer_size = 512;
  uint8_t buffer[buffer_size];
  size_t bytes_read;
  while (http.connected() && (bytes_read = stream->readBytes(buffer, buffer_size)) > 0) {
    file.write(buffer, bytes_read);
  }

  // Close the file and end the HTTP connection
  file.close();
  http.end();

  Serial.println("TTS request completed");

  return;
}

其神奇之处在于响应数据以512字节的块形式读取并写入文件。
就这样了

相关问题