Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

!! Note of Pitfall !!

Documenting the painful experiences of a beginner

Device

ESP32-CAM 12E (purchased very cheaply on certain platforms), be sure to buy one with a charging port and no soldering required, otherwise it can be easily damaged if you’re not familiar with it.

Development Tools

Arduino IDE 2.32, VS2022 (.Net 7)

Programming Languages

Arduino (C/C++) (I don’t understand this either, just follow the example, as long as it runs)

VS2022 (.Net 7, TCP, Signal)

Assembly Steps

1. Connect the Esp32-cam module to the computer using a data cable.

2. Open Arduino IDE, check if the ESP32 board is installed (this installation can take a day, package size 700M+, feel free to message me for a quick 10-minute installation).

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

3. Select the development board AI Thinker ESP32-CAM.

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

4. Choose the sample code “CameraWebServer.”

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

5. After the code is loaded, keep only this area.

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

6. Configure the Wi-Fi password and TCP address, remember that low-end development boards do not support 5G Wi-Fi.

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

7. Configure the image upload code.

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

8. After the device code configuration is complete, click this “→” to upload the code to the development board.

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

9. The .Net code is version 7.0, and you need to configure Signal and TCP.

10. Let’s talk about the pitfalls!!

Issue:

The .Net system runs on port 5000, Signal also works on port 5000, and TCP is used to listen on a new port. If you receive data via TCP and directly broadcast it through Signal, it will cause the Signal key class to be null (since the issue was resolved, I suspect there is a problem here, but I won’t delve into it).

Solution:

After receiving data via TCP, add client code for Signal, sending data from the client to the Signal server (port 5000), and the server will receive data and broadcast it to the front-end JS.

11. Configure Signal and TCP listening in .Net7 Startup.cs, add two items as shown in the following image.

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

12. The key method for reading TCP here includes client and server.

  public class TcpImageServerHelper   {           private readonly int _port;  // Server listening port      private readonly bool _startListening;  // Whether to start listening      private TcpListener _listener;  // TCP listener      // Constructor, initializes the port number and whether to start listening      public TcpImageServerHelper(int port, bool startListening)      {          _port = port;          _startListening = startListening;      }      // Start TCP server      public async Task StartAsync()      {           if (_startListening)          {                              _listener = new TcpListener(IPAddress.Any, _port);              _listener.Start();              //Console.WriteLine("Server started...");              while (true)              {                  TcpClient client = _listener.AcceptTcpClient();                  NetworkStream stream = client.GetStream();                  using (MemoryStream ms = new MemoryStream())                  {                      byte[] lengthBuffer = new byte[4];                      int bytesRead = stream.Read(lengthBuffer, 0, lengthBuffer.Length);                      if (bytesRead != lengthBuffer.Length)                      {                          //Console.WriteLine("Incomplete length header received");                          continue;                      }                      int length = BitConverter.ToInt32(lengthBuffer, 0);                      //Console.WriteLine($"Expected frame length: {length} bytes");                      byte[] buffer = new byte[length];                      int totalBytesRead = 0;                      while (totalBytesRead < length)                      {                          bytesRead = stream.Read(buffer, totalBytesRead, length - totalBytesRead);                          if (bytesRead <= 0)                          {                              //Console.WriteLine("Connection closed unexpectedly");                              break;                          }                          totalBytesRead += bytesRead;                      }                      if (totalBytesRead == length)                      {                          // Process a complete frame                          await new SignalClient().ReceiveImage(buffer, bytesRead);                      }                      else                      {                          //Console.WriteLine($"Incomplete frame received: {totalBytesRead} / {length} bytes");                      }                  }              }          }      }      // Stop TCP server      public async Task StopAsync()      {          _listener?.Stop();  // Stop listening          await Task.CompletedTask;  // Wait for the task to complete      }     }    public class SignalClient {            public async Task ReceiveImage(byte[] buffer, int bytesRead)    {        var connection = new HubConnectionBuilder()        .WithUrl("http://localhost:5000/chathub")        .Build();        connection.On<string, string>("ReceiveMessage", (user, message) =>        {            Console.WriteLine($"{user} says {message}");        });        await connection.StartAsync();        // Send a message to the server        await connection.InvokeAsync("SendMessage", buffer, bytesRead);    }} public class SignalService : Hub {               public override async Task OnConnectedAsync()     {         // Get the connection ID of the connected client         string connectionId = Context.ConnectionId;         // Print connection information         Console.WriteLine($"New client connected: {connectionId}");         // You can perform other logic here, such as recording the connection, sending a welcome message, etc.         await Clients.Caller.SendAsync("ReceiveMessage", "System", "Welcome to the chat!");         // Call the base class's OnConnectedAsync method         await base.OnConnectedAsync();     }     public override async Task OnDisconnectedAsync(Exception exception)     {         // Get the connection ID of the disconnected client         string connectionId = Context.ConnectionId;         // Print disconnection information         Console.WriteLine($"Client disconnected: {connectionId}");         // You can perform other logic here, such as recording disconnection, cleaning up resources, etc.         await Clients.Others.SendAsync("ReceiveMessage", "System", $"{Context.UserIdentifier} has left the chat.");         // Call the base class's OnDisconnectedAsync method         await base.OnDisconnectedAsync(exception);     }     public async Task SendMessage(byte[] buffer, int bytesRead)     {         await Clients.All.SendAsync("ReceiveImage", buffer, bytesRead);     } }

13. Finally, after connecting the front-end Signal to respond to the data sent from the back-end, simply use the img tag to display it.

@{    Layout = "~/Views/Shared/_FormWhite.cshtml";}
Live Stream

Real-time Video Streaming with ESP32-CAM, C#.NET, and JavaScript

Leave a Comment

×