Menu

Node MCU with Unity

Using Unity and NodeMCU we are going to create a Wi-Fi connected gun that has been combined with AR Core.

Lets being with the introduction to Transport Layer ie, TCP/ UPD.
Transport Layer manages message delivery between points and provides reliability if necessary. Layers above the Transport layer (Session, Presentation, and Application) are highly specialized and cannot help us organize real-time multiplayer. Therefore, we have to stop at the Transport Layer and use its TCP and UDP protocols for optimal data exchange between players.

TCP is a connection-oriented protocol, which means that communication occurs between two devices that establish a connection to exchange data.This protocol ensures reliability because it guarantees that all transmitted data will reach its destination in the correct order. If any data is lost during transmission, TCP will automatically retry the request until all data is successfully transmitted.

Establishing a connection involves the following steps -

  • The client and server create sockets for exchanging data over TCP.
  • The client sends a SYN (synchronization) segment to the server with a specified destination port.
  • The server accepts the SYN segment, creates its own socket, and sends a SYN-ACK (synchronization-acknowledgment) segment to the client.
  • The client accepts the SYN-ACK segment and sends an ACK (acknowledgment) segment to the server to complete the connection establishment process. A reliable two-way connection is now established.
  • The example below shows a basic implementation of a TCP client and can be extended to work with specific data and game logic. The code connects to a server with a specified IP address and port, and then sends and receives data through the connection. A network stream is used for asynchronous data reception from the server.

    public static void StartConnection()
    {
       _udpClient = new UdpClient(Port);
       _isReceiving = true;
       _receiveMessageThread = new Thread(ReceiveData)
       {
           IsBackground = true
       };
       _receiveMessageThread.Start();
    
       // Broadcast a discovery message to find ESP8266
       byte[] discoveryBytes = Encoding.UTF8.GetBytes("ESP8266BoardInfo");
    #if UNITY_ANDROID
       _udpClient.EnableBroadcast = true;
       _udpClient.Send(discoveryBytes, discoveryBytes.Length, new IPEndPoint(IPAddress.Broadcast, Port));
    #elif UNITY_IPHONE
       IPEndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Parse(espAccessPointIPRange), espPort);
       _udpClient.Send(discoveryBytes, discoveryBytes.Length, broadcastEndPoint);
    #endif
    }
                        

    The Method StartConnection() establishes a connection to the server at IP address and Port and carried out to the method ReceiveData

    private static void ReceiveData()
    {
       while (_isReceiving)
       {
           IPEndPoint sourceEndPoint = new IPEndPoint(IPAddress.Any, Port);
           byte[] receivedBytes = _udpClient.Receive(ref sourceEndPoint);
           string messagePayload = Encoding.UTF8.GetString(receivedBytes);
           _unityMainThreadDispatcherMain.Enqueue(()=>onReceiveMessage?.Invoke(messagePayload));
       }
    }
                        

    The ReceiveData method is responsible for receiving the payload from the Port and returns the payload message back to unityMainThread since ReceiveData is currently running over new thread.

    public static void SendData(string messagePayload)
    {
        if (_udpClient == null || !_udpClient.Client.EnableBroadcast) return;
        byte[] bytePayload = Encoding.UTF8.GetBytes(messagePayload);
        _udpClient.EnableBroadcast = true;
        _udpClient.Send(bytePayload, bytePayload.Length, new IPEndPoint(IPAddress.Broadcast, Port));
    }
                        

    The SendData method is responsible for broadcasting the payload data over the UPD and it broadcasts the message over the specific port.

    public static void CloseConnection()
    {
        _isReceiving = false;
        _receiveMessageThread?.Abort();
        if(_udpClient != null && !_udpClient.Client.Connected) _udpClient.Close();
    }
                        

    Finally, check for the UPD is there any connection over the port in case of any connection close the UPD.

    Now, lets work on the Arduino side. For components lets use with Node-MCU ESP 8266 12E Wifi module. NodeMCU is an open source development board and firmware based in the widely used ESP8266 -12E WiFi module. It allows you to program the ESP8266 WiFi module with the simple and powerful LUA programming language or Arduino IDE. With just a few lines of code you can establish a WiFi connection and define input/output pins according to your needs exactly like arduino, turning your ESP8266 into a web server and a lot more. It is the WiFi equivalent of ethernet module. Now you have internet of things (iot) real tool.

    Lets setup the Arduino IDE to configure for ESP8266 WiFi module, there are many tutorials out there. Import the ESP8266WiFi.h, WiFiUDP.h modules and create a SSID, Port and Password predefined.

    void setUp()
    {
        WiFi.softAP(ssid, password,6,false,1); // Create the access point
        IPAddress apIP = WiFi.softAPIP();
        udp.begin(espPort);
    }
                        

    The above setup method creates a WiFi Access point and starts UPD session over a specific port. Now you can send and receive payloads over the UPD Port.

    void SendMessage(char* message)
    {
        // Send a response back to Unity
        unsigned int remotePort = udp.remotePort();
        int status = udp.beginPacket(remoteIP, remotePort);
        Serial.println(status);
        Serial.println(message);
        udp.write(message);
        udp.endPacket();
    }
                        
    // Check if there is data available from Unity
      int packetSize = udp.parsePacket();
      if (packetSize) {
        char buffer[255];
        int bytesRead = udp.read(buffer, sizeof(buffer) - 1);
        if (bytesRead > 0) {
          buffer[bytesRead] = '\0';
          remoteIP = udp.remoteIP();
          remotePort = udp.remotePort();
          ReceivedMessage(buffer);
          Serial.println(remoteIP.toString());
          Serial.println(remotePort);
        }
      }
                        

    The SendMessage method is responsible for sending the message over the UPD and the below methods runs over loop to check if there are any messages received from Unity Side.

    Now, lets look at the internal components how the gun was made. Since NodeMCU ESP8266 operates at 5V & 3.3V, we have low space we have connected 3 AA batteries in series which gives approx 4.5 volts. Added a Trigger switch at the place of a battery near to the trigger of the gun and added a support to hold it. Soldered other components and added a motor to simulate a bullet fired.