Problem with reading data from Arduino in Unity - getting malformed strings

Hi all, this is my first post and I’m relatively new to both C# and Arduino, so I’m hoping I’ve just made a silly error in how I’m implementing something.

I have an arduino hooked up to a rotary encoder which I am using to send information to Unity, with the intention of having these data control the player movement.

However, I am having a problem where Unity will incorrectly read the string I am sending from the Arduino. I have tried a few variations of code on the arduino side, but they all end up with the same problem.

At the moment, the Arduino counts the increment on the rotary encoder, returning a number between 0 and 1023. Unity polls the Arduino to read the latest value each time Update() is called.

I have copied both codes I am using below. I am expecting to print a number between 0-1023 to the Unity console. This generally works, but occasionally the printed number is not the same number that the arduino is sending. For example, the following numbers should be read by Unity when it polls the Arduino:
[500, 512, 520, 530, 541]
But instead Unity will read something like:
[500, 512, 2, 0, 530].

I have also tried things like having the Arduino accumulate from 0, then whenever Unity polls the Arduino it will also send a signal telling the Arduino to reset it’s counter back to 0, as I was hoping that dealing with smaller integers/string lengths might solve my problem, however I still had the same issue.

I am assuming that this is an issue of the data sent from the Arduino becoming malformed when Unity attempts to read it, as the serial monitor for the Arduino always shows the expected output. However, after playing around with it for a while and trying to google some similar problems I have been unable to come up with a solution. I was wondering if anyone has experienced any similar issues, or has any idea about how I might fix this?

To provide some additional information, the “malformed” readouts on the Unity side always seem to be single integers (so a string with length of 1), it will never randomly spit out an unexpected 3-digit number. Also, oddly this problem only seems to occur when the rotary encoder is moving quickly. So it is not an issue of the string length sent by the Arduino itself, but might be related to the frequency at which Unity is reading information from the Arduino. The way the code is set up, Unity will only receive something from the Arduino if the rotary encoder has moved between Update() calls. I have tried playing around with the baud rate, however this did not solve my problem either.

Aduino code:

#define encoderBlack 3 
#define encoderOrange 4
#define encoderWhite 5
int pinA = LOW;
// int pinZ = LOW; // signals full rotation, unnecessary
int pinB = LOW;

int lastPinA = LOW;

int encoderPos = 0; 
int posMax = 1024;

void setup() {
  pinMode(pinA, INPUT);
  // pinMode(encoderOrange, INPUT);
  pinMode(pinB, INPUT);
  Serial.begin(250000);
}

void loop() {
  pinA = digitalRead(encoderBlack);
  // pinZ = digitalRead(encoderOrange);
  pinB = digitalRead(encoderWhite);

  if(pinA != lastPinA) {
    if (pinB == pinA) {

      encoderPos--; // Counter-CW
      if (encoderPos < 0) {
        encoderPos = posMax - 1;
      }
      Serial.print(encoderPos); 
      Serial.println();
    }
    else {
      encoderPos++; // CW 
      if (encoderPos >= posMax) { 
        encoderPos = 0; 
      }
      Serial.print(encoderPos); 
      Serial.println();
    }
  }
  lastPinA = pinA;
}

Unity code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System.Linq;

public class RotaryEncoderScript : MonoBehaviour
{
    SerialPort sp3; 

    public float x_cm = 0f;
    public float y_cm = 0f;
    string moveInputAxis;
    public float moveSpeed = 5000; 


    private Rigidbody rb;

    private void Start()
    {
        rb = GetComponent<Rigidbody>(); 

        // assigns and opens serial port
        sp3 = new SerialPort("COM3", 250000); 
        sp3.DtrEnable = false; 
        sp3.ReadTimeout = 1; 
        sp3.WriteTimeout = 1; 
        sp3.Open();
    }


    void Update()
    { 
        moveInputAxis = CheckForRecievedData();
        if (Input.GetKeyDown(KeyCode.Escape) && sp3.IsOpen)
            sp3.Close(); 

        if (moveInputAxis != string.Empty)
        {
            print(moveInputAxis);
            float moveAxis = float.Parse(moveInputAxis);
            print(moveAxis); 
            ApplyInput(moveAxis); 
        }
    }

    private void ApplyInput(float moveInput)
    {
        Move(moveInput);

    }

    private void Move(float input)
    {
        rb.AddForce(transform.right * input * moveSpeed, ForceMode.Force);
    }


    // Method to check rotary encoder for sent data
    public string CheckForRecievedData()
    {
        try 
        {
            string inData = sp3.ReadLine(); 

            sp3.WriteLine("1");
            sp3.BaseStream.Flush();
            sp3.DiscardInBuffer();
            return inData; 
        }
        catch { return string.Empty; }
    }

}

I had the idea that if I used Serial.println(encoderPos) at a set interval (~15ms) instead of in every loop this might solve my problem, however I don’t know how to do this so I’m in the process of trying to figure this out, but I will update this post if I manage to test that before anyone replies.

edit: I should also add, I realize this is an Arduino forum and a lot of people won’t have experience with Unity, even if they do with C#. Unfortunately what I am doing is a pretty niche use of Unity to begin with, and barely anyone on the Unity forums has any experience with this type of thing, so it was suggested I post here instead.

Can’t figure out how to delete the topic so I guess I’ll answer my own question and leave it here.

It seems like the problem I had was due to the frequency at which the Arduino was printing data, where if the frequency was particularly high I had malformed strings on the Unity end. Maybe this is due to Update() polling the Arduino while it was in the middle of writing data, or that the Arduino would write new data to the top of the queue before Unity had finished reading the previous string.

Either way, simply limiting the number of times the Arduino was trying to print anything using millis() and a % operation solved my problem - now everything is being read correctly. For greater precision, especially since millis() is not completely precise, I think that polling the arduino from a separate thread might also work, although it’s hard to say whether or not I’d still experience the same problem without testing it.