🔍

quick balance board building with ESP32 and Unity

hardware building

the lower side of the board is packed with 4 foam blanks to stick from the ground
at each corner of the board, we place a contact touch puffered with foam

Arduino ESP32 touchcode + serial data send


#include <Arduino.h>
#include <touchy.h>


aTouch t1(12); // initalize pin13 as touch pin
aTouch t2(14); // initalize pin13 as touch pin

float hor = 0.;
float vert = 0.;


void sendDATA(){


        Serial.print(hor);
        Serial.print(";");
        Serial.print(vert);
        Serial.println();
        delay(10);


}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {

  t1.readAndProcessInput();
  t2.readAndProcessInput();


  if( t1.is_triggered ){

      hor +=.03;

    
     // Serial.println("Touch1 pressed");
  }

  if( t2.is_triggered ){

    hor -=.03;
      //Serial.println("Touch2 pressed");
  }

    
    hor *=.98;

 

    hor = constrain(hor, -1., 1.);

    Serial.println(hor);


    sendDATA();

    delay(10);

  // put your main code here, to run repeatedly:
}




// ----------------------------------
// ------- TRXYS TOUCH HELPERS ------
// ----------------------------------
// ( put this code PRIOR to your mainloop code or you will get nice errors :) )
  
  // simple lerp helper function
  
float return_lerp(float _s, int _target,int _time){
   
   _s = _s + (( float(_target) - _s)/float(_time));
   return _s;
     
}
  
class aTouch {
  private:
    bool prev_touch_state = false;
    byte pin;
    int smooth_time = 3;
    int trigger_threshold = 15;
    long ts = 0;
  public:
    int current_val = 0;
    int smoothed_val = 0;
    int diff_val = 0;
    bool is_triggered = false;
    bool on_pressed = false;
    bool on_released = false;
     bool is_holded = false;
    aTouch(byte pin) {
     this->pin = pin;
    }
      
  
    void readAndProcessInput() {
  
        // reset interaction states
        on_pressed = false;
        on_released = false;
          
         // directly read out values TWICE = BUGFIX for debouncing 
         current_val = touchRead(pin);
         delayMicroseconds(10);
         current_val = touchRead(pin);
  
        //calculate smoothed input values 
         smoothed_val = return_lerp(smoothed_val,current_val,smooth_time);
  
         // calc current differential sum of button
          diff_val =   smoothed_val - current_val;
  
          // check if there is a noticable difference input values
          if(  diff_val  > trigger_threshold){
  
              if(prev_touch_state == false){
                is_triggered = true;
                prev_touch_state = is_triggered;
                on_pressed = true;
                ts = millis(); // set timestamp for hold routine
             }
               
          }else if( diff_val < trigger_threshold*-.4){
  
              if(prev_touch_state == true){
                is_triggered = false;
                 prev_touch_state = is_triggered;
                 on_released = true;
              }
                  
          }
  
  
          // calculate timed holding function
            
          if( ts + 2500 < millis() && is_triggered){
  
                 
              is_holded = true;
          }else{
  
              is_holded = false;
           }
 
       delayMicroseconds(2);
     }
 
};

basic unity data read and gameobject rotation


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

public class balance_controller : MonoBehaviour
{
    

    public float hor = 0f;
    public float vert = 0f;
    float shor = 0f;

    SerialPort stream;

      // SELECT YOUR COM PORT AND BAUDRATE
    public string port = "COM37";
    int baudrate = 9600;
    int readTimeout = 25;

    public GameObject cam;

    
    
     void Start()
    {
        // open port. Be shure in unity edit > project settings > player is NET2.0 and not NET2.0Subset
        stream = new SerialPort("\\\\.\\" + port, baudrate);
 
        try
        {
            stream.ReadTimeout = readTimeout;
        }
        catch (System.IO.IOException ioe)
        {
            Debug.Log("IOException: " + ioe.Message);
        }
 
        stream.Open();
    }

    // Update is called once per frame
    void Update()
    {
            
             string dataString = "null received";
 
        if (stream.IsOpen)
        {
            try
            {
                dataString = stream.ReadLine();
               // Debug.Log("RCV_ : " + dataString);
            }
            catch (System.IO.IOException ioe)
            {
                Debug.Log("IOException: " + ioe.Message);
            }
 
        }
        else
            dataString = "NOT OPEN";
    // Debug.Log("RCV_ : " + dataString);
 
        if (!dataString.Equals("NOT OPEN"))
        {


            // recived string is  like  "accx;accy;accz;gyrox;gyroy;gyroz"
            char splitChar = ';';
            string[] dataRaw = dataString.Split(splitChar);

            // normalized accelerometer values
            hor = float.Parse(dataRaw[0]) ;
           // vert = float.Parse(dataRaw[1]) ;

        }

        shor = Mathf.Lerp(shor,hor,.3f);
        cam.transform.rotation = Quaternion.Euler(new Vector3(0,180f+shor*-.5f,0));

    }
}