xseignard / cordovarduino

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

serial.registerReadCallback() receives ArrayBuffer with 0 values #127

Open alaeddinezaghdoud opened 3 years ago

alaeddinezaghdoud commented 3 years ago

Hi, I am using the serial plugin for serial communication in ionic : https://ionicframework.com/docs/v3/native/serial/

I could open a connection between the app and my embedded device. But when I try to read incoming values, the ReadCallback() is succesfull but I receive an ArrayBuffer with 0 values.

Here is my code :

this.serial.requestPermission({ vid: '2323', pid: '1111', driver: 'CdcAcmSerialDriver' } ).then(() => { this.serial.open({ baudRate: 9600, dataBits: 8, stopBits: 1, parity: 0, dtr: true, rts: true, sleepOnPause: true }) .then((x) => { this.apiService.showToast('Serial connection opened'); this.serial.registerReadCallback().subscribe((data) => { const view = new Uint8Array(data); if (view.length >= 1) { for (let i = 0; i < view.length; i++) { // if we received a \n, the message is complete, display it if (view[i] === 23) { const value = parseInt(str, 2); str = ''; this.apiService.showToast('value' + value); } // if not, concatenate with the begening of the message else { const tempstr = String.fromCharCode(view[i]); const stresc = escape(tempstr); str += unescape(stresc); } } } }); }).catch((error: any) => { this.apiService.showToast('[Serial] open error' + error); }); }).catch((err) => { this.apiService.showToast(JSON.stringify(err)); });

I have succesfully established the connection using Putty(terminal) and it works fine : the embedded device is always sending its status multiple times per second and every message starts with #.

Can anyone help me?

mkopa commented 3 years ago

I spent all day on this problem - to no avail. From my POV this plugin is broken and needs to be fixed.

Every time, no matter if I use "read" function or registering callback in registerReadCallback I get empty object (no data).

Example code:

const serialConnect = async () => {
   return serial
       .requestPermission()
       .then(() => {
         setData({ ...data, status: "connecting" });

         return (
           serial
             .open({ baudRate: 1200, dataBits: 8, stopBits: 1, parity: 0, dtr: true, rts: true, sleepOnPause: true })
             .then(async () => {
               const observable = serial.registerReadCallback();

               observable.subscribe({
                 next(x) {
                   if (x instanceof ArrayBuffer) {
                     const typedArray = new Uint8Array(x);
                     const array = Array.from(typedArray);
                     alert("arraybuffer " + JSON.stringify(array)); // empty data
                   } else {
                     alert("object: " + JSON.stringify(x));  // Callback registered successfully
                   }
                 },
                 error(err) {
                   setData({ ...data, status: "disconnected" });
                 },
                 complete() {
                   setData({ ...data, status: "disconnected" });
                 },
               });

               await serial
                 .writeHex(toHexString([0x11, 0x22, 0x33]))
                 .catch((e) => {
                   setData({ ...data, status: "disconnected" });
                 });

               setData({ ...data, status: "connected" });

    //           serial.read().then((data) => {
    //             // data also is empty
    //           });

               return "ok";
             })
             .catch((error: any) => {
               setData({ ...data, status: "disconnected" });
               return error;
             })
         );
       })
       .catch((e) => e);
};

Today I used cordova-plugin-serial@0.0.3 https://www.npmjs.com/package/cordova-plugin-serial

I wrote a simple wrapper:

interface PortOptions {
  baudRate?: number; // default 9600
  dataBits?: number; // default 8
  stopBits?: number; // default 1
  parity?: number; // default 0
  dtr?: boolean; // default true
  rts?: boolean; // default true
  sleepOnPause?: boolean; // default false
}

type ReadCallback = (data: ArrayBuffer) => void;

const serial = (window as any).serial;

const SerialPort = {
  requestPermission: async () => {
    return new Promise((resolve: any, reject: any) => {
      serial.requestPermission(
        function success(successMessage: string) {
          resolve(successMessage);
        },
        function error(error: string) {
          reject(error);
        },
      );
    });
  },
  open: async (portOptions: PortOptions) => {
    const _portOptions: PortOptions = {
      ...{ baudRate: 9600, dataBits: 8, stopBits: 1, parity: 0, dtr: true, rts: true, sleepOnPause: false },
      ...portOptions,
    };

    return new Promise((resolve: any, reject: any) => {
      serial.open(
        _portOptions,
        function (successMessage: string) {
          resolve(successMessage);
        },
        function (openError: string) {
          if (openError === "Already open") { // ugly code, but it solves the problem if the user just pulls out the USB cable.
            serial.close(
              () => {
                serial.open(
                  _portOptions,
                  function (successMessage: string) {
                    resolve(successMessage);
                  },
                  function (openError: string) {
                    reject(openError);
                  },
                );
              },
              () => {
                serial.open(
                  { baudRate: 1200 },
                  function (successMessage: string) {
                    resolve(successMessage);
                  },
                  function (openError: string) {
                    reject(openError);
                  },
                );
              },
            );
          } else {
            reject(openError);
          }
        },
      );
    });
  },

  write: async (data: string) => {
    return new Promise((resolve: any, reject: any) => {
      serial.write(
        data,
        function (successMessage: string) {
          resolve(successMessage);
        },
        function (writeError: string) {
          reject(writeError);
        },
      );
    });
  },

  writeHex: async (data: string) => {
    return new Promise((resolve: any, reject: any) => {
      serial.writeHex(
        data,
        function (successMessage: string) {
          resolve(successMessage);
        },
        function (writeError: string) {
          reject(writeError);
        },
      );
    });
  },

  read: async (): Promise<ArrayBuffer> => {
    return new Promise((resolve: any, reject: any) => {
      serial.read(
        function (buffer: ArrayBuffer) {
          resolve(buffer);
        },
        function (readError: string) {
          reject(readError);
        },
      );
    });
  },

  close: async (): Promise<void> => {
    return new Promise((resolve: any, reject: any) => {
      serial.close(
        function () {
          resolve();
        },
        function (closeError: string) {
          reject(closeError);
        },
      );
    });
  },

  registerReadCallback: async (callbackForRegister: ReadCallback): Promise<void> => {
    return serial.registerReadCallback(callbackForRegister, function (registerCallbackError: string) {
      throw new Error(registerCallbackError);
    });
  },
};

export { SerialPort };

and this is the example code:

const serialConnect = async () => {
    try {
      await SerialPort.requestPermission();
      await SerialPort.open({ baudRate: 1200 });
      await SerialPort.registerReadCallback((data: ArrayBuffer) => {
        const view = new Uint8Array(data);
        alert("Incomming data: " + JSON.stringify(view));
      });
      await SerialPort.writeHex(toHexString([0x11, 0x22, 0x33]));
      setData({ ...data, status: "connected" });
      return "ok";
    } catch (error) {
      setData({ ...data, status: "disconnected" });
      return error;
    }
  };

It seems to work fine. All the data is incoming correctly!

As far as I know, cordovarduino is based on cordova-plugin-serial but why doesn't it work?

alaeddinezaghdoud commented 3 years ago

Thank you for your reply.

I am trying to use your solution. But I need your help.

I have installed cordova-plugin-serial@0.0.3 in my ionic project using npm i cordova-plugin-serial.

I tried to use your wrapper but I cannot find the correct declaration for serial in this line : const serial = (window as any).serial;

Can you please explain how to properly install the plugin in my ionic project in order to use the wrapper?

mkopa commented 3 years ago

@alaeddinezaghdoud

I am using capacitor.js to build an android application. I removed cordovarduino from package.json dependencies.

Then:

$ rm -rf node_modules
$ npm i
$ npm i cordova-plugin-serial
$ ionic cap sync
$ ionic cap run android -l --external

And that's it. I don't know if there is a .d.ts file for this library, but it is available in the global "window" object. So I just cast it to type "any" and used it according to the examples in JavaScript

alaeddinezaghdoud commented 3 years ago

@mkopa

These are the steps I have done.

I installed cordova-plugin-serial in a new blank ionic project.

Then I tried your code. I think my problem is that i still dont get the correct declaration of serial.

The plugin is installed and I can see it under plugins.

But I have a problem to find the declaration in this line(eq serial has multiple declarations and none of them is referring to : cordova-plugin-serial) : const serial = (window as any).serial;

Can you help?

mkopa commented 3 years ago

@alaeddinezaghdoud

That's why I wrote the wrapper. I don't create applications in Ionic on a daily basis, but I needed to write an application to support some device.

I didn't spend too much time looking for the best solution and mine is definitely not it.

cordovaduino did not work for me according to the documentation, so I made this hack - maybe someone will find it useful as a temporary solution to the problem that you and I had.

alaeddinezaghdoud commented 3 years ago

Thank you for your help.

I have tried your solution but I still have the same problem : when I read the incoming data all I get is an array full with 0.

Do you have any idea?

Cricrikris commented 3 years ago

Hi, I am using the serial plugin for serial communication in ionic : https://ionicframework.com/docs/v3/native/serial/

I could open a connection between the app and my embedded device. But when I try to read incoming values, the ReadCallback() is succesfull but I receive an ArrayBuffer with 0 values.

Here is my code : I have succesfully established the connection using Putty(terminal) and it works fine : the embedded device is always sending its status multiple times per second and every message starts with #.

Can anyone help me?

@alaeddinezaghdoud Hi, My skill aren't really advance, so maybe i'm not doing it in the right way. Nevertheless, i've tested this plugin successfully in a ionic/vue project like this :

Serial.registerReadCallback(
                    data => {
                        console.log("incoming data : " + data.byteLength) // in order to debug if there is incoming data
                        var view = new Uint8Array(data);
                        if(view.length >= 1) {
                           [....]
                        }
                    },
                    () => {
                        new Error("Failed to register read callback");
                    }).subscribe();

So in my code, the "reading" process isn't in the "subscribe" method but directly in the success callback of "registerReadCallback" (once again, maybe i'm not doing it in the right way). One thing that really help me is debugging with android studio and adb wifi. In this way you can track serial I/O between the 2 devices. I hope this could help you

Christophe

mkopa commented 3 years ago

I strongly do not recommend using the Cordovarduino library for the following reasons:

  1. for incomprehensible reasons the author uses a statically linked library "usb-serial-for-android" (instead of adding it as a dependency in plugin.xml)
  2. restriction to use only one serial port.

Because of the restriction no. 1 we are deprived of updates in the project https://github.com/mik3y/usb-serial-for-android on which the Cordovarduino library is based. Because of this, adapters based on Qinheng CH340, CH341A chips do not support baud rate 1200 (the patch made by me contains the update and baud rate 1200 works correctly: https://github.com/mkopa/cordovarduino ). There are many more updates in the last 5 years.

xseignard commented 3 years ago

@Cricrikris that's exactly what you need to do

@mkopa your comment is exactly why I don't care anymore about this lib. There's always a random dude willing to say shit about the open source work that I did and never contribute back the few lines he wrote. The open source community owe you a big thanks!

LOL NOPE

mkopa commented 3 years ago

@xseignard please don't get so upset. This library is listed as official on the Ionic framework project website https://ionicframework.com/docs/native/serial). The libraries that the author ignores are really detrimental to the Ionic project.

Many people find it annoying that a simple library needs so much effort to work, and in the end it turns out to be outdated and not working as it should.

Thank you very much for finally writing that you are NO LONGER DEVELOPING this library. Be good enough and mark it in the README.md main file.

tarmanda commented 3 years ago

I strongly do not recommend using the Cordovarduino library for the following reasons:

  1. for incomprehensible reasons the author uses a statically linked library "usb-serial-for-android" (instead of adding it as a dependency in plugin.xml)
  2. restriction to use only one serial port.

Because of the restriction no. 1 we are deprived of updates in the project https://github.com/mik3y/usb-serial-for-android on which the Cordovarduino library is based. Because of this, adapters based on Qinheng CH340, CH341A chips do not support baud rate 1200 (the patch made by me contains the update and baud rate 1200 works correctly: https://github.com/mkopa/cordovarduino ). There are many more updates in the last 5 years.

@mkopa , I wasn't expecting working with ionic serial would be so painful; I was testing usb serial connectivity with raspberry pi zero with CDC driver and getting always 0 as size. Your solution made my day; btw, is there any plan of making the version 0.0.11 of cordovarduino available at npm?

filipppp commented 3 years ago

@mkopa I'm a beginner and I'm using ionic with capacitor, in the ionic native docs they just install the cordova package with npm install, now instead of writing cordovarduino i installed the npm package from ur github repo, but now I get an

Could not find com.github.mik3y:usb-serial-for-android:3.3.3.

error, do you know by chance how to fix this or how to install the package properly?

mkopa commented 3 years ago

Hi @filipppp it should work identical to the documentation, with a small change if you want to use my fork:

$ npm install mkopa/cordovarduino
$ npm install @ionic-native/serial
$ ionic cap sync

You can also try doing a full reinstall of node_modules:

$ npm ci

edit: Is possible that you just need to rebuild the project in Android Studio

filipppp commented 3 years ago

okay now it works, dont know what I changed since I installed it like that, but I guess the reinstall "npm ci" did it, but sadly I still cant get the Serial Connection to work since the registerReadCallback doesn't return anything :/

thanks for the help tho!!

filipppp commented 3 years ago

Moreover, this library doesnt let you pick a USB Port, what if I have multiple USB Devices on my Phone?

mkopa commented 3 years ago

Unfortunately, the first recognized device will be selected. https://github.com/xseignard/cordovarduino/blob/2bbdc45e339d676b7a3267f75ed9ac43d9fcfbef/src/android/fr/drangies/cordova/serial/Serial.java#L230

You can try to use "Serial.java" file as a template and write your code to support multiple USB devices. The library that this project uses is very good quality! https://github.com/mik3y/usb-serial-for-android Good luck!

filipppp commented 3 years ago

That could be it, since I've done lsusb on my mobile phone in the shell and getting all kinds of busses and devices, will try editing the Serial.java file

filipppp commented 3 years ago

Well, I'm gonna need more time since I cant debug anything since I cant find a way to print stuff, or get autocompletion or anything out of that plugin, that codeline should work for my specific case, but I don't know how to apply this:


     List<UsbSerialPort> ports = driver.getPorts();
          for (UsbSerialPort p : ports) {
               if (p..getDriver().getDevice().getVendorId() == 0x1A86) {
                port = p;
                break;
               }
          }

EDIT: got it working now, editing the java file from the node package and ionic cap sync didn't work for me, there are suddenly two Serial.java, one is "ty.cordova.serial" and one is "fr.drangies.cordova.serial", it seems the second one is the real one and I edited the file directly and verified it with logcat. Thing is now I for sure know that the arduino serial port is targetted, but it still doesn't give me any Serial data from it, so yeah

filipppp commented 3 years ago

@mkopa Sorry for bothering you for the last time, but I get this error when using your fork:

Execution failed for task ':app:mergeDebugResources'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
   > Could not find com.github.mik3y:usb-serial-for-android:3.3.3.
     Searched in the following locations:
       - https://repo.maven.apache.org/maven2/com/github/mik3y/usb-serial-for-android/3.3.3/usb-serial-for-android-3.3.3.pom
       - https://dl.google.com/dl/android/maven2/com/github/mik3y/usb-serial-for-android/3.3.3/usb-serial-for-android-3.3.3.pom
       - https://jcenter.bintray.com/com/github/mik3y/usb-serial-for-android/3.3.3/usb-serial-for-android-3.3.3.pom
     Required by:
         project :app

Any ideas?

EDIT: Silly me, just read the README from the mik3y/usb-serial-for-android repo, now my Serial Connection works with ur fork @mkopa ! Big thanks to u!!

devendranegi121 commented 1 year ago

I am still fetch same issue serial.registerReadCallback() receives ArrayBuffer with 0 values using cordova angular

Can you please help me on this