DEV Community

Cover image for Control rc car using raspberry pi (Part 4 : Build the controller)
medmor
medmor

Posted on • Edited on

Control rc car using raspberry pi (Part 4 : Build the controller)

The controller app UI

The UI for the app is very simple, just two buttons to run forward and backward, and a text to indicate the speed.
To turn the car, you need to turn the phone.

Controller UI components

You can find the project on github here

The controller scripts

Learn to code using Unity is very simple, all you need is to crate a monobehavior script, attach it to a gameobject and you are good to go. Make things move!.

There are three important parts in our app.

The UnityWebRequest

We can send web requests by using the UnityWebRequest class from the namespace UnityEngine.Networking.
To achieve this I created a singleton script that can be called from all other scripts that needs to send web requests. The script is like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class RequestManager : MonoBehaviour
{
    [SerializeField] private string baseUrl;

    private static RequestManager instance;

    public static RequestManager Instance
    {
        get { return instance; }
        set
        {
            if (instance == null)
            {
                instance = value;
            }
        }
    }

    private void Awake()
    {
        instance = this;
    }

    public void SendRequest(string command)
    {
        StartCoroutine(Upload(command));
    }

    IEnumerator Upload(string command)
    {
        List<IMultipartFormSection> formData = new List<IMultipartFormSection>();

        UnityWebRequest request = UnityWebRequest.Post(baseUrl + command, formData);
        yield return request.SendWebRequest();

        if (request.result != UnityWebRequest.Result.Success)
        {
            Debug.Log(request.error);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Sendig Requests to run the car forward and backward

The buttons are set to listen to the PointerDown and PointerUp events.

    public void OnPointerDown()
    {
        isPressed = true;
    }
    public void OnPointerUp()
    {
        isPressed = false;
    }
Enter fullscreen mode Exit fullscreen mode

While the pointer is down the speed will increase, and after a threshold in the difference of speed we send a Request for the server.

 void Update()
    {
        if (isPressed)
        {
            if (speed < minSpeed)
            {
                speed = minSpeed;
            }
            speed += speedIncrement * Time.deltaTime;
            if (speed > 100)
            {
                speed = 100;
            }
            if (Mathf.Abs(speed - lastSpeed) > speedDiffrenceThreshold || speed == 100)
            {
                UpdateSpeedIndicator();
                lastSpeed = speed;
                RequestManager.Instance.SendRequest(direction + "/" + (int)speed);
            }

        }
        else if (speed > 0)
        {
            speed = 0;
            lastSpeed = 0;
            UpdateSpeedIndicator();
            RequestManager.Instance.SendRequest("stop"); UpdateSpeedIndicator();
        }

    }
Enter fullscreen mode Exit fullscreen mode

The RunController script is like this:

using UnityEngine;
using UnityEngine.UI;

using TMPro;
public class RunController : MonoBehaviour
{
    enum Directions { forward, backward }
    [SerializeField] Directions direction;
    [SerializeField] private Image speedIndicator = default;
    [SerializeField] private float speedDiffrenceThreshold = 10;
    [SerializeField] private float speedIncrement = 5;
    [SerializeField] private float minSpeed = 50;
    [SerializeField] private TextMeshProUGUI speedText;

    private float speed = 0;
    private float lastSpeed = 0;
    private bool isPressed = false;

    void Update()
    {
        if (isPressed)
        {
            if (speed < minSpeed)
            {
                speed = minSpeed;
            }
            speed += speedIncrement * Time.deltaTime;
            if (speed > 100)
            {
                speed = 100;
            }
            if (Mathf.Abs(speed - lastSpeed) > speedDiffrenceThreshold || speed == 100)
            {
                UpdateSpeedIndicator();
                lastSpeed = speed;
                RequestManager.Instance.SendRequest(direction + "/" + (int)speed);
            }

        }
        else if (speed > 0)
        {
            speed = 0;
            lastSpeed = 0;
            UpdateSpeedIndicator();
            RequestManager.Instance.SendRequest("stop"); UpdateSpeedIndicator();
        }

    }
    public void OnPointerDown()
    {
        isPressed = true;
    }
    public void OnPointerUp()
    {
        isPressed = false;
    }
    private void UpdateSpeedIndicator()
    {
        speedText.text = ((int)speed).ToString();
    }
}
Enter fullscreen mode Exit fullscreen mode

Turning the car left and right

We use the smarthphne accelerometer to turn the car right and left. Unity makes this very simple, We just need to use the built in Input.acceleration.
The DirectionController script is like this:

using UnityEngine;


public class DirectionController : MonoBehaviour
{
    [SerializeField] private float directionChangeThreshold = 0.15f;
    enum Directions
    {
        center, left, right
    }
    Directions direction = Directions.center;
    Directions lastSentDirection = Directions.center;
    void FixedUpdate()
    {
        if(Input.acceleration.x > directionChangeThreshold)
        {
            direction = Directions.right;
        }
        else if(Input.acceleration.x < -directionChangeThreshold)
        {
            direction = Directions.left;
        }
        else
        {
            direction=Directions.center;
        }
        if(lastSentDirection != direction)
        {
            lastSentDirection = direction;
            RequestManager.Instance.SendRequest(direction.ToString());
        }
    }

}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)