xseignard / cordovarduino

Cordova/Phonegap plugin for USB host serial communication from an Android device.
MIT License
166 stars 110 forks source link

Rs232 with USB Adapter #89

Open Giusti opened 6 years ago

Giusti commented 6 years ago

Hey guys,

I am using cordovarduino, for my rs232 Barcode Scanner with a rs232 to usb adapter.

Now it works but it takes so long. For example when I scan a very simple Barcode it takes the ReadRegisterCallback about 10-20 Seconds to respond. I am not really sure why, but I guess it is because of the Adapter.

Any idea how I can communicate with rs232 directly. I was thinking about using this application (https://github.com/cepr/android-serialport-api/tree/master/android-serialport-api/project/src/android_serialport_api/sample) and converting it to a cordova plugin but not with much luck. Having problems including the libserial_port.so library and so on maybe someone can help me.

Kind regards Chris

xseignard commented 6 years ago

Hello, Please show some code and what's your hardware (device and smartphone)

Regards

Giusti commented 6 years ago

Hello Xseingnard,

thank you for your reply.

So my code basically looks like this:

I tried it with registerReadCallback only and with this example calling read after registerReadCallback triggers.

serial.requestPermission(function success(success) {
                alert(success)
                serial.open(opts, function success(success){
                    alert(success)
                    serial.registerReadCallback(
                        function success(data) {
                            alert('Readcallback')
                            serial.read(function success(buffer){
                                var view2 = new Uint8Array(buffer);
                                if (view2.length >= 1) {
                                    for (var j = 0; j < view2.length; j++) {

                                        var temp_str2 = String.fromCharCode(view[j]);
                                        var str_esc2 = escape(temp_str2);
                                        $scope.test2 += unescape(str_esc2);

                                    }
                                }
                            }, function error() {
                                alert("Failed to register read callback");
                            });
                            // decode the received message
                            var view = new Uint8Array(data);
                            if (view.length >= 1) {
                                for (var i = 0; i < view.length; i++) {

                                        var temp_str = String.fromCharCode(view[i]);
                                        var str_esc = escape(temp_str);
                                        $scope.test += unescape(str_esc);

                                }
                            }
                        },
                        function error() {
                            alert("Failed to register read callback");
                        });

                }, function error(error) {
                    alert(error)
                });
            }, function error(error) {
                alert(error)
            });

I think it has to do with the converter I am using converting the RS232 cable to usb. It takes about 10-20 Seconds for the Data from the Barcodescanner (Intermec SR61T) to be available in the app (on a DLOG DLT-V7212 P Terminal) via registerRead Callback.

Since there is nearly nothing on the internet regarding rs232 and cordova it would really be nice to create something. Found many people on my search who were looking for something similar. I have found the App Serial Port API sample in the App store which is basically this github repo (https://github.com/cepr/android-serialport-api/tree/master/android-serialport-api/project/src/android_serialport_api/sample) Maybe its possible to extend cordovarduino to add the rs232 functionality? the App I donwloaded works great. Now I tried to create a cordova plugin out of it (I only created 1 other cordova plugin so far and not really good with java mostly a web guy). I would really appreciate any help (sorry for the long post) Kind Regards Chris

currently it looks like this:

Plugin Structure:

├───src
│   ├───com
│   │   └───serial
│   │           └───plugin
│   │               └───SerialPlugin.java
│   │               └───SerialPort.java
│   │               └───SerialPortActivity.java
│   │               └───SerialPortFinder.java
│   ├───jni
│   │    └───Android.mk
│   │    └───Application.mk
│   │    └───gen_SerialPort_h.sh
│   │    └───SerialPort.c
│   │    └───SerialPort.h
│   ├───libs
│   │   ├───armeabi
│   │       └───libserial_port.so
│   │   ├───armeabi-v7a
│   │       └───libserial_port.so
│   │   └───x86
│   │        └───libserial_port.so
│   └───ubuntu
├───www
│      └───serial.js
├───plugin.xml

Plugin.xml

<js-module src="www/serial.js" name="Serial">
        <clobbers target="window.serial" />
    </js-module>

    <!-- android -->
    <platform name="android">
        <config-file target="res/xml/config.xml" parent="widget">
            <feature name="SerialPlugin">
                <param name="android-package" value="serialport.SerialPlugin"/>
              <param name="onload" value="true" />
            </feature>
        </config-file>
        <source-file src="src/com/serial/plugin/SerialPlugin.java" target-dir="src/com/serial/plugin" />
        <source-file src="src/com/serial/plugin/SerialPort.java" target-dir="src/com/serial/plugin" />
        <source-file src="src/com/serial/plugin/SerialPortFinder.java" target-dir="src/com/serial/plugin" />
        <source-file src="src/com/serial/plugin/SerialPortActivity.java" target-dir="src/com/serial/plugin" />
        <source-file src="src/libs/x86/libserial_port.so" target-dir="libs/x86/" />
    </platform>

serial.js

var serial = {   
    getPort: function(opts, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            'SerialPlugin',
            'getPort',
            [{'opts': opts}]
        );
    }
};
module.exports = serial;

SerialPlugin.java


import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.apache.cordova.*; // Cordova 3.x
import org.apache.cordova.api.*;  // Cordova 2.9
import android.os.Parcelable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.SharedPreferences;
import android.util.Log;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.MessageFormat;

public class SerialPlugin extends CordovaPlugin {
    public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
    private SerialPort mSerialPort;
    private static final String ACTION_GET_PORT = "getPort";
    private static final String ACTION_CLOSE_PORT = "closePort";
    private static final String ACTION_READ = "readSerial";
    private static final String ACTION_WRITE = "writeSerial";
    private static final String ACTION_WRITE_HEX = "writeSerialHex";
    private static final String ACTION_CLOSE = "closeSerial";
    private static final String ACTION_READ_CALLBACK = "registerReadCallback";

    @Override
    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
        Log.d("Datakey_Serial", "Action: " + action);
        JSONObject arg_object = args.optJSONObject(0);
        //request permission
        if (ACTION_GET_PORT.equals(action)) {
            Log.d("Datakey_Serial", "Action_started: " + action);           
            JSONObject opts = arg_object.has("opts")? arg_object.getJSONObject("opts") : new JSONObject();
            Log.d("Datakey_Serial", "Params: " + opts);
            try{
                Log.d("Datakey_Serial", "Method_Started: getSerialPort()");
                getSerialPort();
            }catch(IOException e) {
                e.printStackTrace();
            }
            return true;
        }
        // open serial port
        else if (ACTION_CLOSE_PORT.equals(action)) {
            JSONObject opts = arg_object.has("opts")? arg_object.getJSONObject("opts") : new JSONObject();
            closeSerialPort();
            return true;
        }

        // the action doesn't exist
        return false;
    }

    public SerialPort getSerialPort() throws SecurityException, IOException, InvalidParameterException {
        try{    
            Log.d("Datakey_Serial", "Checking mSerialPort:"+ mSerialPort);
            if (mSerialPort == null) {
                /* Read serial port parameters */
                //SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE);
                String path = "/dev/ttyS0";
                int baudrate = 57600;

                /* Check parameters */
                if ( (path.length() == 0) || (baudrate == -1)) {
                    throw new InvalidParameterException();
                }

                /* Open the serial port */
                mSerialPort = new SerialPort(new File(path), baudrate, 0);
            }
        } catch (IOException e) {
                e.printStackTrace();
        }
        Log.d("Datakey_Serial", "See mSerialPort:"+ mSerialPort);
        String command = MessageFormat.format(javaScriptEventTemplate, "getSerialPort", mSerialPort.toString());
        this.webView.sendJavascript(command);
        return mSerialPort;
    }

    public void closeSerialPort() {     
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }
    }

    String javaScriptEventTemplate =
        "var e = document.createEvent(''Events'');\n" +
        "e.initEvent(''{0}'');\n" +
        "e.tag = {1};\n" +
        "document.dispatchEvent(e);";

}

SerialPort.java


import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;

public class SerialPort {

    private static final String TAG = "SerialPort";

    /*
     * Do not remove or rename the field mFd: it is used by native method close();
     */
    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;

    public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
     try {
        /* Check access permission */
        if (!device.canRead() || !device.canWrite()) {
            try {
                /* Missing read/write permission, trying to chmod the file */
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                        + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                if ((su.waitFor() != 0) || !device.canRead()
                        || !device.canWrite()) {
                    throw new SecurityException();
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new SecurityException();
            }
        }
        Log.e(TAG, "trying to call open");
        mFd = open(device.getAbsolutePath(), baudrate, flags);
        if (mFd == null) {
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Getters and setters
    public InputStream getInputStream() {
        return mFileInputStream;
    }

    public OutputStream getOutputStream() {
        return mFileOutputStream;
    }

    // JNI
    private native static FileDescriptor open(String path, int baudrate, int flags);
    public native void close();
    static {
        Log.e(TAG, "loading library");
        System.loadLibrary("serial_port");
    }
}

SerialPortActivity.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.util.Log;

public abstract class SerialPortActivity extends Activity {

    protected OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;

    private class ReadThread extends Thread {

        @Override
        public void run() {
            super.run();
            while(!isInterrupted()) {
                int size;
                try {
                    byte[] buffer = new byte[64];
                    if (mInputStream == null) return;
                    size = mInputStream.read(buffer);
                    if (size > 0) {
                        onDataReceived(buffer, size);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
        }
    }

    private void DisplayError(int resourceId) {
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setTitle("Error");
        b.setMessage(resourceId);
        b.setPositiveButton("OK", new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                SerialPortActivity.this.finish();
            }
        });
        b.show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //try {
            /*mOutputStream = mSerialPort.getOutputStream();
            mInputStream = mSerialPort.getInputStream();*/

            /* Create a receiving thread */
            mReadThread = new ReadThread();
            mReadThread.start();
        /*} catch (SecurityException e) {
            Log.w("Datakey_Serial", "SecurityException:"+ e);
        } catch (IOException e) {
            Log.w("Datakey_Serial", "IOException:"+ e);
        } catch (InvalidParameterException e) {
            Log.w("Datakey_Serial", "InvalidParameterException:"+ e);
        }*/

    }

    protected abstract void onDataReceived(final byte[] buffer, final int size);

    @Override
    protected void onDestroy() {
        if (mReadThread != null)
            mReadThread.interrupt();
        //SerialPlugin.closeSerialPort();
        //mSerialPort = null;
        super.onDestroy();
    }
}

SerialPortFinder.java

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;

import android.util.Log;

public class SerialPortFinder {

    public class Driver {
        public Driver(String name, String root) {
            mDriverName = name;
            mDeviceRoot = root;
        }
        private String mDriverName;
        private String mDeviceRoot;
        Vector<File> mDevices = null;
        public Vector<File> getDevices() {
            if (mDevices == null) {
                mDevices = new Vector<File>();
                File dev = new File("/dev");
                File[] files = dev.listFiles();
                int i;
                for (i=0; i<files.length; i++) {
                    if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
                        Log.d(TAG, "Found new device: " + files[i]);
                        mDevices.add(files[i]);
                    }
                }
            }
            return mDevices;
        }
        public String getName() {
            return mDriverName;
        }
    }

    private static final String TAG = "SerialPort";

    private Vector<Driver> mDrivers = null;

    Vector<Driver> getDrivers() throws IOException {
        if (mDrivers == null) {
            mDrivers = new Vector<Driver>();
            LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers"));
            String l;
            while((l = r.readLine()) != null) {
                // Issue 3:
                // Since driver name may contain spaces, we do not extract driver name with split()
                String drivername = l.substring(0, 0x15).trim();
                String[] w = l.split(" +");
                if ((w.length >= 5) && (w[w.length-1].equals("serial"))) {
                    Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length-4]);
                    mDrivers.add(new Driver(drivername, w[w.length-4]));
                }
            }
            r.close();
        }
        return mDrivers;
    }

    public String[] getAllDevices() {
        Vector<String> devices = new Vector<String>();
        // Parse each driver
        Iterator<Driver> itdriv;
        try {
            itdriv = getDrivers().iterator();
            while(itdriv.hasNext()) {
                Driver driver = itdriv.next();
                Iterator<File> itdev = driver.getDevices().iterator();
                while(itdev.hasNext()) {
                    String device = itdev.next().getName();
                    String value = String.format("%s (%s)", device, driver.getName());
                    devices.add(value);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return devices.toArray(new String[devices.size()]);
    }

    public String[] getAllDevicesPath() {
        Vector<String> devices = new Vector<String>();
        // Parse each driver
        Iterator<Driver> itdriv;
        try {
            itdriv = getDrivers().iterator();
            while(itdriv.hasNext()) {
                Driver driver = itdriv.next();
                Iterator<File> itdev = driver.getDevices().iterator();
                while(itdev.hasNext()) {
                    String device = itdev.next().getAbsolutePath();
                    devices.add(device);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return devices.toArray(new String[devices.size()]);
    }
}
xseignard commented 6 years ago

hello @Giusti nice work!

If you have some insights on how to integrate that on this plugin it will be a great addition!

In the meantime, do you have any insights about #84?

Regards