--- /dev/null
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+
+<head>
+ <meta charset="utf-8">
+ <title>CANVAS VIDEO</title>
+ <style>
+ #Video {
+ border: 1px solid red;
+ }
+ </style>
+</head>
+
+<body>
+ <canvas id="Video" width="120" height="160"></canvas>
+ <pre id="Stat"></pre>
+ <pre id="Msg"></pre>
+
+ <script>
+ var ws;
+ var wsURL = "ws://127.0.0.1/stream";
+ var canvas = document.getElementById('Video');
+ var ctx = canvas.getContext('2d');
+
+ var frameCount = 0;
+ var totalBytes = 0;
+ window.setInterval(function() {
+ fps = frameCount;
+ bytes = totalBytes;
+
+ frameCount = 0;
+ totalBytes = 0;
+
+ var Stat = document.getElementById('Stat');
+ Stat.innerHTML = "当前帧率: " + fps + " fps, 带宽:" + (bytes*8.0/(1024.0*1024.0)).toFixed(1) + " Mbps";
+ },1000)
+
+ function Msg(...args) {
+ msg = args.join('');
+ console.log(msg);
+
+ var Msg = document.getElementById('Msg');
+ contents = Msg.innerHTML;
+ contents += msg;
+ slice = contents.split('\n').slice(-5);
+ msg = slice.join('\n')+'\n';
+ Msg.innerHTML = msg;
+ }
+
+ function initWebsocket() {
+ ws = new WebSocket(wsURL);
+ ws.SendCmd = function(req) {
+ req = JSON.stringify(req);
+ ws.send(req);
+ }
+
+ ws.onopen = function () {
+ Msg("websocket opened");
+ };
+
+ ws.onerror = function (event) {
+ Msg("websocket error: " + event);
+ };
+
+ ws.onclose = function (event) {
+ Msg("websocket closed with code: " + event.code + " reason: " + event.reason);
+ };
+
+ ws.onmessage = function (event) {
+ imageData = event.data;
+ frameCount++;
+ totalBytes += imageData.size;
+ Msg("frame data len: ", imageData.size);
+ var image = new Image();
+ image.src = URL.createObjectURL(imageData);
+ image.onload = () => {
+ URL.revokeObjectURL(image.src);
+ ctx.save();
+ ctx.translate(60, 80);
+ ctx.rotate(90*(Math.PI/180));
+ ctx.translate(-80, -60);
+ ctx.drawImage(image, 0, 0);
+ ctx.restore();
+ }
+ };
+ }
+
+ initWebsocket();
+ </script>
+</body>
+
+</html>
--- /dev/null
+module audioweb
+
+go 1.13
+
+require github.com/gorilla/websocket v1.4.2
--- /dev/null
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
--- /dev/null
+/*
+ * ------------------------------------------------------------------------
+ * File Name: main.go
+ * Author: Zhao Yanbai
+ * 2021-01-16 23:01:01 Saturday CST
+ * Description: none
+ * ------------------------------------------------------------------------
+ */
+
+package main
+
+import (
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+var upgrader = websocket.Upgrader{
+ ReadBufferSize: 102400,
+ WriteBufferSize: 102400,
+
+ // 解决跨域问题
+ CheckOrigin: func(r *http.Request) bool {
+ return true
+ },
+}
+
+var frameChan chan []byte
+
+func init() {
+ frameChan = make(chan []byte, 8)
+}
+
+const (
+ partBOUNDARY = "123456789000000000000987654321"
+ streamContentType = "multipart/x-mixed-replace;boundary=" + partBOUNDARY
+ streamBoundary = "\r\n--" + partBOUNDARY + "\r\n"
+ streamPart = "Content-Type: image/jpeg\r\nContent-Length: %d\r\n\r\n"
+)
+
+func watchHandler(w http.ResponseWriter, r *http.Request) {
+ var err error
+ w.Header().Set("Content-Type", streamContentType)
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+
+ flusher, _ := w.(http.Flusher)
+
+ for i := 0; ; i++ {
+ var data []byte
+ select {
+ case data = <-frameChan:
+ fmt.Fprintf(w, "%v", streamBoundary)
+ fmt.Fprintf(w, streamPart, i)
+ _, err = w.Write(data)
+ flusher.Flush()
+ if err != nil {
+ break
+ }
+ default:
+ continue
+ }
+
+ }
+}
+
+func audioHandler(w http.ResponseWriter, r *http.Request) {
+ var err error
+ var ws *websocket.Conn
+
+ log.Printf("audio\n")
+ ws, err = upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ log.Printf("err: %v", err)
+ return
+ }
+
+ defer ws.Close()
+
+ for {
+ t, p, e := ws.ReadMessage()
+ log.Printf("type %v data len %v err %v", t, len(p), e)
+ if e != nil {
+ break
+ }
+
+ if len(p) < 100 {
+ continue
+ }
+
+
+ /*
+ data := make([]byte, len(p)/2, len(p)/2)
+ k := 0
+ for i :=0; i<len(p); i++ {
+ if (i / 2) % 2 == 1 {
+ continue
+ }
+
+ data[k] = p[i]
+ k++
+ }*/
+
+ select {
+ case frameChan <- p:
+ log.Printf("put frame")
+
+ err = ws.WriteMessage(websocket.BinaryMessage, []byte{'1'})
+ if err != nil {
+ log.Printf("write to arduino error: %v", err)
+ //break
+ }
+ default:
+ log.Printf("drop frame")
+ }
+
+ }
+}
+
+func streamHandler(w http.ResponseWriter, r *http.Request) {
+ var err error
+ var ws *websocket.Conn
+
+ log.Printf("stream\n")
+
+ ws, err = upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ log.Printf("err: %v", err)
+ return
+ }
+
+ defer ws.Close()
+
+ for {
+ var data []byte
+ select {
+ case data = <-frameChan:
+ //case <-time.After(1 * time.Millisecond):
+ default:
+ time.Sleep(10 * time.Millisecond)
+ continue
+ }
+
+ err = ws.WriteMessage(websocket.BinaryMessage, data)
+ if err != nil {
+ break
+ }
+ }
+}
+
+func canvasHandler(w http.ResponseWriter, r *http.Request) {
+ tmpl := template.Must(template.ParseFiles("./canvas.html"))
+ var data = map[string]interface{}{}
+ tmpl.Execute(w, data)
+}
+
+func main() {
+ defer fmt.Println("Program Exited...")
+
+ m := http.NewServeMux()
+ m.HandleFunc("/watch", watchHandler)
+ m.HandleFunc("/audio", audioHandler)
+ m.HandleFunc("/canvas", canvasHandler)
+ m.HandleFunc("/stream", streamHandler)
+ server := http.Server{
+ Addr: ":80",
+ Handler: m,
+ }
+
+ server.ListenAndServe()
+}
--- /dev/null
+#include <ArduinoWebsockets.h>
+#include <WiFi.h>
+#include <WiFiMulti.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2s.h"
+#include "esp_system.h"
+
+#define SAMPLE_RATE (8000)
+
+using namespace websockets;
+
+typedef struct {
+ const char *name;
+ const char *pwd;
+} WiFiItem_t;
+
+const char* websockets_server_host = "10.0.0.6";
+const uint16_t websockets_server_port = 80;
+
+WiFiItem_t WiFiTable[] = {
+ { "WiFiName1", "" },
+ { "WiFiName2", "12345678" },
+ { "WiFiName3", "87654321" }
+};
+
+const unsigned int WiFiCnt = sizeof(WiFiTable) / sizeof(WiFiItem_t);
+
+WebsocketsClient client;
+WiFiMulti wifiMulti;
+
+
+void setup_wifi() {
+
+ for (uint8_t t = 3; t > 0; t--) {
+ Serial.printf("[SETUP] WAIT %d...\n", t);
+ Serial.flush();
+ delay(1000);
+ }
+
+
+ for (uint8_t i = 0; i < WiFiCnt; i++) {
+ Serial.printf("Add AP %s %s\n", WiFiTable[i].name, WiFiTable[i].pwd);
+ wifiMulti.addAP(WiFiTable[i].name, WiFiTable[i].pwd);
+ }
+
+}
+
+
+
+void setup_max9841() {
+ i2s_config_t i2s_config = {
+ .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN),
+ .sample_rate = SAMPLE_RATE,
+ .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
+ .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
+ .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
+ .intr_alloc_flags = 0,
+ .dma_buf_count = 16,
+ .dma_buf_len = 60
+ };
+
+
+ Serial.println("using ADC_builtin");
+ i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
+
+ // GPIO36, VP
+ i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0);
+}
+
+const int audioBufSize = 1024;
+char audioBufA[audioBufSize];
+//char audioBufB[audioBufSize];
+char *curtAudioBuf = audioBufA;
+
+
+bool webSocketConnected = false;
+
+void capture_audio(void *args) {
+ //while(1) {
+ i2s_read_bytes(I2S_NUM_0, (char *)curtAudioBuf, audioBufSize, portMAX_DELAY);
+
+ // 只要单声道数据
+ uint16_t *buf = (uint16_t *)curtAudioBuf;
+ int to = 0;
+ for (int i = 0; i < audioBufSize / 2; i += 2) {
+ buf[to] = buf[i];
+ to++;
+ }
+ if (webSocketConnected && client.available()) {
+ Serial.println("before send");
+ client.sendBinary((const char *)curtAudioBuf, audioBufSize / 2);
+ Serial.println("read and send audio data");
+ } else {
+ if(!webSocketConnected) {
+ Serial.println("websocket not connected");
+ }
+ if(client.available()) {
+ Serial.println("client not available");
+ }
+ }
+ //}
+}
+
+
+
+void setup() {
+ Serial.begin(115200);
+
+ setup_max9841();
+
+ setup_wifi();
+}
+
+void loop() {
+
+ uint8_t wifiState = wifiMulti.run();
+ if ((wifiState != WL_CONNECTED)) {
+ Serial.printf("[WiFi] connected to wifi failed. ret %d\n", wifiState);
+ delay(1000);
+ return;
+ }
+
+ if (client.available()) {
+ capture_audio(0);
+ client.poll();
+ } else {
+ Serial.println("Connected to Wifi, Connecting to server.");
+
+ webSocketConnected = client.connect(websockets_server_host, websockets_server_port, "/audio");
+ if (webSocketConnected) {
+ Serial.println("Connected!");
+ } else {
+ Serial.println("Not Connected!");
+ }
+
+ // run callback when messages are received
+ client.onMessage([&](WebsocketsMessage message) {
+ Serial.print("Got Message: ");
+ Serial.println(message.data());
+ });
+ }
+}
const uint16_t websockets_server_port = 80;
WiFiItem_t WiFiTable[] = {
- { "AceNetGear2G", "acewifipwdis123" },
{ "WiFiName1", "" },
{ "WiFiName2", "12345678" },
{ "WiFiName3", "87654321" }