Using GPIO with root access

Post Reply
User avatar
dhelio
Posts: 14
Joined: Thu Feb 20, 2020 5:15 pm
languages_spoken: english, italian, neapolitan
ODROIDs: U3, XU-4, N2, H2
Has thanked: 3 times
Been thanked: 6 times
Contact:

Using GPIO with root access

Unread post by dhelio » Sat Feb 29, 2020 1:17 am

Hello,

following the example available on the wiki at https://wiki.odroid.com/odroid-c2/appli ... on_android I'm trying to make the GPIO work with a simple program made in Android Studio.

So basically the idea is to activate a led by pressing a button on the Android interface. I followed religiously the tutorials, but I keep getting the error "unable to open dev/mem/ : permission denied". I've already rooted the device with Magisk. The code of the main activity is very very simple:

Code: Select all

public class MainActivity extends Activity {

    static {
        System.loadLibrary("wpi_android"); //Load the C library
    }
	//pin modes
    private final static int INPUT = 0;
    private final static int OUTPUT = 1;

	//probably useless
    private boolean is_LedOn = false;

	//C functions
    public native int wiringPiSetup();
    public native void digitalWrite(int pin, int stato);
    public native void pinMode(int pin, int modalita);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        wiringPiSetup();

        pinMode(4, OUTPUT); //Set the pin 4 of the N2 GPIO to OUTPUT. It will activate the LED when high.

        Button btnLed = (Button) findViewById(R.id.btn_led); //Simple GUI button

	//When the button is pressed, change the state of the LED
        btnLed.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                is_LedOn = !is_LedOn;
                int stato = (is_LedOn) ? 1 : 0;
                digitalWrite(4,  stato);
            }
        });
    }
}

I guess that I have to give permissions to my app, but how?

User avatar
tobetter
Posts: 4671
Joined: Mon Feb 25, 2013 10:55 am
languages_spoken: Korean, English
ODROIDs: X, X2, U2, U3, XU3, C1
Location: Paju, South Korea
Has thanked: 88 times
Been thanked: 338 times
Contact:

Re: Using GPIO with root access

Unread post by tobetter » Sat Feb 29, 2020 7:44 am

Instead of following the example, please visit the link and try to use AndroidThings compatible features. If you can run the AndroidThings API after installing the libraries to Android Studio and can test like a generic Android app.
viewtopic.php?f=178&t=37101&p=275070&hi ... gs#p275071
These users thanked the author tobetter for the post:
dhelio (Tue Mar 17, 2020 11:34 pm)

Luke.go
Posts: 447
Joined: Mon May 30, 2016 1:55 pm
languages_spoken: english
Has thanked: 26 times
Been thanked: 36 times
Contact:

Re: Using GPIO with root access

Unread post by Luke.go » Mon Mar 02, 2020 6:45 pm

Please check the example. :)
https://github.com/xiane/thingsGpioExample

And another example code belows.
It just same work what you given code, just pin is changed to the pin #7.

Code: Select all

package odroid.hardkernel.com.androidthingsexample;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.google.android.things.pio.Gpio;
import com.google.android.things.pio.PeripheralManager;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private boolean is_LedOn = false;
    private Gpio led;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        PeripheralManager peripheralManager = PeripheralManager.getInstance();

        try {
            led = peripheralManager.openGpio("7");
            led.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
        }catch (Exception e) {
            e.printStackTrace();
        }

        Button LED = findViewById(R.id.btn_led);

        LED.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                is_LedOn = !is_LedOn;
                try {
                    led.setValue(is_LedOn);
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
    }
}

User avatar
dhelio
Posts: 14
Joined: Thu Feb 20, 2020 5:15 pm
languages_spoken: english, italian, neapolitan
ODROIDs: U3, XU-4, N2, H2
Has thanked: 3 times
Been thanked: 6 times
Contact:

Re: Using GPIO with root access

Unread post by dhelio » Mon Mar 02, 2020 7:35 pm

Thank you guys! I managed to understand how to interact with the GPIO with Android Things

I have to say, Things is much easier to understand than the wiringPi lib. Next, I'll try to wrap the code in C# to use it in Unity. Wish me luck!

User avatar
tobetter
Posts: 4671
Joined: Mon Feb 25, 2013 10:55 am
languages_spoken: Korean, English
ODROIDs: X, X2, U2, U3, XU3, C1
Location: Paju, South Korea
Has thanked: 88 times
Been thanked: 338 times
Contact:

Re: Using GPIO with root access

Unread post by tobetter » Mon Mar 02, 2020 7:36 pm

dhelio wrote:
Mon Mar 02, 2020 7:35 pm
Thank you guys! I managed to understand how to interact with the GPIO with Android Things

I have to say, Things is much easier to understand than the wiringPi lib. Next, I'll try to wrap the code in C# to use it in Unity. Wish me luck!
That sounds cool, would be better if you share the thing what you are trying to do if possible. :) This would be good success case using Andorid Things on ODROID.

User avatar
dhelio
Posts: 14
Joined: Thu Feb 20, 2020 5:15 pm
languages_spoken: english, italian, neapolitan
ODROIDs: U3, XU-4, N2, H2
Has thanked: 3 times
Been thanked: 6 times
Contact:

Re: Using GPIO with root access

Unread post by dhelio » Mon Mar 02, 2020 8:44 pm

Basically I'd like to wrap some functions of Android Things into a Jar that I may later call via Unity in a C# script. I don't really know if it is feasible or not, but I'll give it a go. I was able to do a similar thing with Arduino, but entirely in C# via the Serial.Port library, so my guess is to make something like that but with much less headaches related to the Arduino volatility in terms of noise and encumberance.

In the long run I'd like to develop a little arcade cabinet, that uses a small interface board with resistors and condensers that goes into GPIO and can run several custom-made or other games entirely in Android.

That's pretty much it. :D I'll share progress

Luke.go
Posts: 447
Joined: Mon May 30, 2016 1:55 pm
languages_spoken: english
Has thanked: 26 times
Been thanked: 36 times
Contact:

Re: Using GPIO with root access

Unread post by Luke.go » Tue Mar 03, 2020 2:34 pm


User avatar
dhelio
Posts: 14
Joined: Thu Feb 20, 2020 5:15 pm
languages_spoken: english, italian, neapolitan
ODROIDs: U3, XU-4, N2, H2
Has thanked: 3 times
Been thanked: 6 times
Contact:

Re: Using GPIO with root access

Unread post by dhelio » Wed Mar 11, 2020 1:06 am

Alright, so a little update on the matter.

It is entirely possible to make an .aar module with Android Studio and using the resulting library in Unity (in fact, it works!), but performance-wise it is FAR from being acceptable (for example, calling a static function from the aar in Unity's Update function works inconsistently).

I'm looking for ways to optimize this right now, but it looks like a path that nobody has travelled yet. Using Xamarin's Android Things library is out of the question, as it uses the Mono.Android that differs from the Unity's Mono, so...

I'll keep you updated. Might take me a while, we're all in the red zone due to coronavirus :?
These users thanked the author dhelio for the post:
Luke.go (Wed Mar 11, 2020 10:27 am)

User avatar
dhelio
Posts: 14
Joined: Thu Feb 20, 2020 5:15 pm
languages_spoken: english, italian, neapolitan
ODROIDs: U3, XU-4, N2, H2
Has thanked: 3 times
Been thanked: 6 times
Contact:

Re: Using GPIO with root access

Unread post by dhelio » Tue Mar 17, 2020 8:02 pm

Final update:

it works!

So I've tried several methods to make the GPIO work in Unity, but the best one thus far was making the aar module with non-static functions, importing it into Unity and calling said functions from a Thread using Unity's AndroidJavaObject wrapper for JNI.

it looks something like this (it's just an example):

Java code:

Code: Select all

public class GPIOComms {

    private final String tag = "GPIOCommsLib";

    private PeripheralManager pManager;
    private List<String> pinList;
    private List<String> pwmList;

    private Gpio[] pins;
    private Pwm[] pwms;

    public void Initialize () {
        pManager = PeripheralManager.getInstance();
        pinList = pManager.getGpioList();
        pwmList = pManager.getPwmList();

        //Debug GPIO
        String gpioPins = "";
        for (int i=0; i<pinList.size()-1; i++) {
            gpioPins+=pinList.get(i)+", ";
        }
        gpioPins+=pinList.get(pinList.size()-1);

        //Debug PWMs
        String pwmPins = "";
        for (int i=0; i<pwmList.size()-1; i++)
            pwmPins+=pwmList.get(i) + ", ";
        pwmPins+=pwmList.get(pwmList.size()-1);

        pins = new Gpio[pinList.size()];
        pwms = new Pwm[pwmList.size()];
    }
}
Then from android studio I can use Build->Make Module to build the aar library, place it anywhere inside my Unity project and from Unity:

Code: Select all

private void Start() {
        new System.Threading.Thread(() => {
            AndroidJNI.AttachCurrentThread();
            AndroidJavaObject gpio = new AndroidJavaObject("my.library.name.classname");
            v = gpio.Call("Initialize");
        }).Start();
    }
It works flawlessly, and it's very very fast and efficient.

Obviously I'll keep working on it, and will make an extensive library for all my GPIO needs. :D thank you everyone!

EDIT: I want to add that this is the best solution for me specifically, but interprocess communications are possible in a variety of ways. I've tried also by making and Android Service that produces data and that communicates it via named pipes or localhost udp sockets to the Unity app, and it worked too, but it was too slow for me specifically, and kind of an hassle to set the proper communication protocol between the two apps.

I've also tried 0MQhttps://zeromq.org/ library for faster sockets communications, but wasn't what I needed.

This to say that the N2 (and more generally all Odroids GPIOs) can work with other applications written in different languages, even on different devices!
These users thanked the author dhelio for the post (total 2):
Luke.go (Wed Mar 18, 2020 9:14 am) • odroid (Wed Mar 18, 2020 9:36 am)

Luke.go
Posts: 447
Joined: Mon May 30, 2016 1:55 pm
languages_spoken: english
Has thanked: 26 times
Been thanked: 36 times
Contact:

Re: Using GPIO with root access

Unread post by Luke.go » Wed Mar 18, 2020 9:17 am

dhelio wrote:
Tue Mar 17, 2020 8:02 pm
Final update:

it works!

So I've tried several methods to make the GPIO work in Unity, but the best one thus far was making the aar module with non-static functions, importing it into Unity and calling said functions from a Thread using Unity's AndroidJavaObject wrapper for JNI.

it looks something like this (it's just an example):

Java code:

Code: Select all

public class GPIOComms {

    private final String tag = "GPIOCommsLib";

    private PeripheralManager pManager;
    private List<String> pinList;
    private List<String> pwmList;

    private Gpio[] pins;
    private Pwm[] pwms;

    public void Initialize () {
        pManager = PeripheralManager.getInstance();
        pinList = pManager.getGpioList();
        pwmList = pManager.getPwmList();

        //Debug GPIO
        String gpioPins = "";
        for (int i=0; i<pinList.size()-1; i++) {
            gpioPins+=pinList.get(i)+", ";
        }
        gpioPins+=pinList.get(pinList.size()-1);

        //Debug PWMs
        String pwmPins = "";
        for (int i=0; i<pwmList.size()-1; i++)
            pwmPins+=pwmList.get(i) + ", ";
        pwmPins+=pwmList.get(pwmList.size()-1);

        pins = new Gpio[pinList.size()];
        pwms = new Pwm[pwmList.size()];
    }
}
Then from android studio I can use Build->Make Module to build the aar library, place it anywhere inside my Unity project and from Unity:

Code: Select all

private void Start() {
        new System.Threading.Thread(() => {
            AndroidJNI.AttachCurrentThread();
            AndroidJavaObject gpio = new AndroidJavaObject("my.library.name.classname");
            v = gpio.Call("Initialize");
        }).Start();
    }
It works flawlessly, and it's very very fast and efficient.

Obviously I'll keep working on it, and will make an extensive library for all my GPIO needs. :D thank you everyone!

EDIT: I want to add that this is the best solution for me specifically, but interprocess communications are possible in a variety of ways. I've tried also by making and Android Service that produces data and that communicates it via named pipes or localhost udp sockets to the Unity app, and it worked too, but it was too slow for me specifically, and kind of an hassle to set the proper communication protocol between the two apps.

I've also tried 0MQhttps://zeromq.org/ library for faster sockets communications, but wasn't what I needed.

This to say that the N2 (and more generally all Odroids GPIOs) can work with other applications written in different languages, even on different devices!
What a nice work! :)
So We can use it with unity or difference languages??

User avatar
dhelio
Posts: 14
Joined: Thu Feb 20, 2020 5:15 pm
languages_spoken: english, italian, neapolitan
ODROIDs: U3, XU-4, N2, H2
Has thanked: 3 times
Been thanked: 6 times
Contact:

Re: Using GPIO with root access

Unread post by dhelio » Wed Mar 18, 2020 6:03 pm

So We can use it with unity or difference languages??
The specific library I am making is for Unity exclusively, but it's also possibile to communicate values to Unity using sockets (so ideally you may send values to a PC on local network running its version of Unity. I've tested it on localhost only tho), although this method is much slower, but it has the undeniable advantage of being platform-independent (since every programming language has sockets in a way or another), so with the due changes it may be used for communications between Java2C, Java2Ruby, Java2Python, etc...

As example I'll add the experimentation code I wrote for Java and Unity. Comments are in italian, couldn't bother to translate them. Sorry!

Java:

Code: Select all

package my.app.appname;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import com.google.android.things.pio.Gpio;
import com.google.android.things.pio.PeripheralManager;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;

public class GPIOSocket extends Service {

    //---------------------------------- VARIABILI PRIVATE

    private final String tag = "JavaGPIOSocket";

    //Variabili per il GPIO
    private PeripheralManager pManager;
    private List<String> pinList;
    private Gpio[] pins;

    //Variabili per la connessione
    private Socket socket;
    private ServerSocket serverSocket;
    private Thread serverThread;
    private OutputStream outboundStream;
    private InputStream inboundStream;

    private final int port = 50000;
    private final int backlog = 50;

    //---------------------------------- VARIABILI PUBBLICHE

    //---------------------------------- FUNZIONI PRIVATE

    private void InitializeGPIO () {
        Log.d(tag,"Inizializzazione GPIO in corso...");
        pManager = PeripheralManager.getInstance();
        pinList = pManager.getGpioList();

        String tmp = "";
        for (int i=0; i<pinList.size()-1; i++)
            tmp+=pinList.get(i)+", ";
        tmp+=pinList.get(pinList.size()-1);
        Log.d(tag,"Pin disponibili: ["+tmp+"]");

        pins = new Gpio[pinList.size()];
        for (int i=0; i<pins.length; i++) {
            try {
                pins[i] = pManager.openGpio(pinList.get(i));
                pins[i].setDirection(Gpio.ACTIVE_HIGH);
            } catch (IOException e) {
                Log.e(tag,"Errore durante l'inizializzazione del pin indice ["+i+"] -> pin reale ["+pinList.get(i)+"]");
            }
        }
        Log.d(tag,"GPIO Inizializzato!");
    }

    private String ReadGPIO () {
        StringBuilder s = new StringBuilder();
        s.append("|");
        for (int i=0; i<pins.length; i++) {
            try {
                if (pins[i].getValue()) {
                    s.append("1");
                } else {
                    s.append("0");
                }
                s.append("|");
            } catch (IOException e) {
                Log.e(tag,"Errore durante la lettura del pin ["+pinList.get(i)+"] : ["+e+"]");
            }
        }
        return  s.toString();
    }

    private void Connect () {
        InetAddress address = null;
        try {
            address = InetAddress.getLocalHost();
            serverSocket = new ServerSocket(port,backlog, address);
            socket = serverSocket.accept();
            outboundStream = socket.getOutputStream();
            inboundStream = socket.getInputStream();
        } catch (UnknownHostException uhe) {
            Log.e(tag,"Errore durante la creazione della socket su indirizzo ["+address+"]");
        } catch (IOException e) {
            Log.e(tag,"Errore durante la creazione della socket ["+e+"]");
        }
    }

    private void Disconnect () {
        try {
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            Log.e(tag,"Errore durante la disconnessione del server ["+e+"]");
        }
    }

    private void Send(String Value) {
        try {
            int stringLength = Value.length();
            byte[] stringLegthBytes = new byte[4]; //1 int è 4 byte
            stringLegthBytes[0] = (byte)((stringLength) & 0xff); //0xff sostituisce i bit a che sono ff con 00. Questo perché byte è un tipo signed, e mi tornerebbe un valore che non c'entra altrimenti
            stringLegthBytes[1] = (byte)((stringLength >> 8) & 0xff);
            stringLegthBytes[2] = (byte)((stringLength >> 16) & 0xff);
            stringLegthBytes[3] = (byte)((stringLength >> 24) & 0xff);
            Log.d(tag,"Tento invio di ["+Value+"], della dimensione di ["+Value.length()+"]");
            outboundStream.write(stringLegthBytes); //Invio prima la lunghezza della stringa e poi la stringa
            Log.d(tag,"Inviata dimensione");
            outboundStream.write(Value.getBytes());
            Log.d(tag,"Inviata stringa");
        } catch (IOException e) {
            Log.e(tag,"Errore durante l'invio di ["+Value+"] a Unity: ["+e+"]");
        }
    }

    private void ThreadBehaviour () {
        Connect();
        for(;;) {
            try {
                Send(ReadGPIO());
                Thread.sleep(5);
            } catch (InterruptedException e) {
                Log.e(tag,"Sleep del thread interrotto!");
            }
        }
        //Disconnect();
    }

    //---------------------------------- FUNZIONI PUBBLICHE

    //---------------------------------- FUNZIONI DI ANDROID

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        InitializeGPIO();

        serverThread = new Thread(new Runnable() {
            @Override
            public void run() {
                ThreadBehaviour();
            }
        });
        serverThread.start();

        return super.onStartCommand(intent, flags, startId);
    }
}

Unity:

Code: Select all

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

using System;
using System.Net;
using System.Net.Sockets;

public class GPIOManager : MonoBehaviour
{

    private const string tag = "-C#GPIOSocket- ";

    private volatile int v = 0;
    private Socket socket;
    private System.Threading.Thread receivingThread;

    private volatile string incomingData;

    [SerializeField] private TMPro.TextMeshProUGUI dataText;

    private void Log(string log) {
        Debug.Log(tag + log);
    }

    private void ClientBehaviour() {

        for (; ; ) {
            byte[] incomingStringLengthBytes = new byte[4];
            socket.Receive(incomingStringLengthBytes);
            int incomingStringLenth = BitConverter.ToInt32(incomingStringLengthBytes,0);
            byte[] incomingString = new byte[incomingStringLenth];
            socket.Receive(incomingString);
            incomingData = System.Text.Encoding.ASCII.GetString(incomingString);
        }
        
    }

    private void Start()
    {
        Log("Avviata applicazione unity");

        IPAddress localhostAddress = Dns.GetHostEntry("localhost").AddressList[0];
        IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Loopback, 50000);
        Log("Impostazione e connessione alla socket su ["+localhostAddress+"] su porta ["+50000+"]");
        socket = new Socket(localhostAddress.AddressFamily, SocketType.Stream, ProtocolType.Unspecified);

        Log("Socket creata. Tento connessione al server.");

        socket.Connect(serverEndPoint);

        Log("Connessione al server avvenuta. Avvio ricezione dati");

        receivingThread = new System.Threading.Thread(new System.Threading.ThreadStart(ClientBehaviour));
        receivingThread.Start();
    }

    private void Update()
    {
        dataText.text = incomingData;
    }

    private void OnApplicationQuit()
    {
        Log("Chiusura socket in corso.");
        socket.Close();
    }
}
It works on localhost (so on 2 processes on the N2), but I am fairly confident that it may work on different devices on the same network by just changing the IP address of both client and server.

EDIT: I forgot to explain what the code does :oops: basically the Java code works as a server and waits for connection from Unity. When the connection happens, the servers starts to trasmit the state of all the GPIO pins as a string to Unity. The string is formed of the values of each pin and separators (something like "|0|1|0|0|0|0|0|0|........" where 1 indicates a high pin and 0 a low pin, and | is a separator which may be used to split the values of the string. So in this case pin7 is low, while pin9 is high). The Unity code just grabs this string and shows it in a TextMeshPro text component. So if you attach a button to the pin7 and gnd you can see that the value of the first number is 0 at rest and 1 once pressed.
These users thanked the author dhelio for the post:
Luke.go (Wed Mar 18, 2020 6:20 pm)

Luke.go
Posts: 447
Joined: Mon May 30, 2016 1:55 pm
languages_spoken: english
Has thanked: 26 times
Been thanked: 36 times
Contact:

Re: Using GPIO with root access

Unread post by Luke.go » Wed Mar 18, 2020 6:24 pm

Okay! Understood :)
It looks a Android things' delay is not big deal.

Post Reply

Return to “Android”

Who is online

Users browsing this forum: No registered users and 3 guests