!! 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).
3. Select the development board AI Thinker ESP32-CAM.
4. Choose the sample code “CameraWebServer.”
5. After the code is loaded, keep only this area.
6. Configure the Wi-Fi password and TCP address, remember that low-end development boards do not support 5G Wi-Fi.
7. Configure the image upload code.
8. After the device code configuration is complete, click this “→” to upload the code to the development board.
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.
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]()