Serial port write (no read) causing freezing after about 30 seconds

I’m trying to use System.io.ports to communicate with arduino. This is my first time doing it without uniduino and actually writing the serial data out myself. It all works nicely until 30-100 seconds in Unity slows to about 2 fps. The lights on he aruino that show serial input and output both light up solid (should be quickly blinking while communicating) an no serial is actually getting through to the arduino.

I’m sending strings of data to control two motor controllers. Arduino code is looking for the first character L or R followed by an int followed by \n. All works fine while it actually runs.

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

public class ManualSerialControl : MonoBehaviour {

    public SerialPort serial = new SerialPort ("COM3", 19200);
    public float motorInterpSpeed = 1f;
    public float leftCurrentSpeed, rightCurrentSpeed;
    public bool controlWithArrows;

    // Use this for initialization
    void Start () {
        InvokeRepeating("SendValues", .1f, .1f);
        InvokeRepeating("PrintValues", .1f, .1f);
        // Trying 100 for write timeout seemed to help but may have been coincidence
        // It still froze after about 100 seconds
        //serial.WriteTimeout = 100;
    }
   
    // Update is called once per frame
    void Update () {

        if (serial.IsOpen == false)
            serial.Open();

        if (Input.GetButtonDown("aButton") || Input.GetKeyDown("q"))
        {
            Debug.Log ("Resetting");
            serial.Write("Reset\n");
        }

        InterpolateSpeeds();
    }

    void InterpolateSpeeds()
    {
        if (controlWithArrows) {
            if (Input.GetKey("up"))
            {
                leftCurrentSpeed = Mathf.Lerp (leftCurrentSpeed, 1f, motorInterpSpeed * Time.deltaTime * 0.25f);
                rightCurrentSpeed = Mathf.Lerp (rightCurrentSpeed, 1f, motorInterpSpeed * Time.deltaTime * 0.25f);
            } else if (Input.GetKey("down")) {
                leftCurrentSpeed = Mathf.Lerp (leftCurrentSpeed, -1f, motorInterpSpeed * Time.deltaTime * 0.25f);
                rightCurrentSpeed = Mathf.Lerp (rightCurrentSpeed, -1f, motorInterpSpeed * Time.deltaTime * 0.25f);
            }
        } else {
            leftCurrentSpeed = Mathf.Lerp (leftCurrentSpeed, Input.GetAxis ("leftVertical"), motorInterpSpeed * Time.deltaTime);
            rightCurrentSpeed = Mathf.Lerp (rightCurrentSpeed, Input.GetAxis ("rightVertical"), motorInterpSpeed * Time.deltaTime);
        }
    }

    void PrintValues()
    {
        Debug.Log ("leftCurrentSpeed: " + leftCurrentSpeed);
        Debug.Log ("rightCurrentSpeed: " + rightCurrentSpeed);
    }

    void SendValues()
    {
        int mappedValLeft = Map (leftCurrentSpeed, 1f, -1f, 0f, 6400f);
        int mappedValRight = Map (leftCurrentSpeed, 1f, -1f, 0f, 6400f);
        serial.Write ("L" + mappedValLeft + "\n");
        serial.Write ("R" + mappedValRight + "\n");
    }

    int Map(float value, float min1, float max1, float min2, float max2)
    {
        return (int)Mathf.Lerp (min2, max2, Mathf.InverseLerp(min1, max1, value));
    }
}

I doubt very much

serial.WriteTimeout = 100;

has anything to do with it lasting 100s instead of 30s, since this parameter is supposed to be in milliseconds. Also, it defaults to 500ms so setting it to 100 would likely introduce problems sooner rather than later. I could be wrong about this though – I’ve never worked with serial ports before. :face_with_spiral_eyes:

You may want to wrap your SendValues code in a try catch to see if there are some exceptions causing your InvokeRepeating to bail. I’m not sure what the Unity default behavior is for exceptions that occur in InvokeRepeating methods but I’m pretty certain it would cause them to stop executing.

Have you tried optimizing your string creation? As it stands now, you will be creating quite a few strings for the garbage collector to deal with 30 seconds in. Basically, every time you use the + operator you end up with a new string to be garbage collected.

Thanks for the tips. It turns out the Map functions were the culprit. I didn’t see any spike in memory usage in the profiler. Just the script taking up to 4k ms inside the sendValues method. Just replaced the Map lines with an int and it doesn’t freeze. Which doesn’t solve my problem but it is a start. What alternatives can I do other than Map to get the number I want?

Hmm very strange that your map function would be so slow. Seems it is just doing some linear interpolation. Maybe because you are sending your parameters in the wrong order?

void SendValues()
{
    int mappedValLeft = Map (leftCurrentSpeed, 1f, -1f, 0f, 6400f);
    int mappedValRight = Map (leftCurrentSpeed, 1f, -1f, 0f, 6400f);
    serial.Write ("L" + mappedValLeft + "\n");
    serial.Write ("R" + mappedValRight + "\n");
}

int Map(float value, float min1, float max1, float min2, float max2)
{
    return (int)Mathf.Lerp (min2, max2, Mathf.InverseLerp(min1, max1, value));
}

Most likely you want

int mappedValLeft = Map (leftCurrentSpeed, -1f, 1f, 0f, 6400f);

instead of

int mappedValLeft = Map (leftCurrentSpeed, 1f, -1f, 0f, 6400f);

Anyways, maybe you could try implementing like this to see if it’s faster (though I expect Lerp/InverseLerp to be optimized, so this is probably not a solution)

int Map(float value, float min1, float max1, float min2, float max2)
{
    var normalized = (value - min1) / (max1 - min1);
    return normalized * (max2 - min2) + min2;
}

Sorry, that’s all I’ve got…

Thanks for the thorough response. I now think it’s actually not the mapping function, sorry for the red herring. I still haven’t figured it out, but a friend came up with a solution that might not be right but works for now.

Every x number of seconds he closed the port and reopened it. Seems to do the trick.