androidarduinoadk

Arduino Mega ADK strange acc.write() behavior (huge delay) NAK issue seemd to be confirmed. Android generates NAK's as a result of acc.write


I'm trying to communicate between Arduino Mega Adk (ADK 2011) and android device. Something goes ok, but something goes completely wrong. Transfer data from Android to Arduino via acc.read from Arduino side works fine. But when i try to send some bytes from Arduino to Android - something strange happens. First of all here is Arduino sketch:

    #include <Max3421e_constants.h>
    #include <Max3421e.h>
    #include <Usb.h>
    #include <AndroidAccessory.h>

    #define COMMAND_LED 0x2
    #define TARGET_PIN_18 0x12
    #define TARGET_PIN_19 0x13
    #define V_ON 0x1
    #define V_OFF 0x0

    #define PIN_18 18
    #define PIN_19 19
    #define INPUT_PIN 30


    AndroidAccessory acc("Google, Inc.",
                 "DemoKit",
                 "Ololo device board",
                 "1.0",
                 "http://www.android.com",
                 "0000000012345678");

    byte rcvmsg[3];
    byte sndmsg[3];
    int buttonState = 0;

    void setup();
    void loop();

    void led_setup(){
      pinMode(PIN_18, OUTPUT);     
      pinMode(PIN_19, OUTPUT);
      pinMode(INPUT_PIN, INPUT);
    }
    void setup()
    {
      Serial.begin(115200);
      Serial.print("\r\nStart");
      led_setup();
      acc.powerOn();
    }
    void loop()
    {
      if (acc.isConnected()) {
     buttonState = digitalRead(INPUT_PIN);
      if (buttonState == 1){
       sndmsg[0] = 0x2;
       sndmsg[1] = 0x1;
       sndmsg[2] = 0x1;
       int len = acc.write(sndmsg, 3);
       digitalWrite(PIN_19, HIGH);
      }
      else {
       //Nothing here for test
      }  
    }
  //usefull test for button  
     buttonState = digitalRead(INPUT_PIN);
     if (buttonState == 1){
       digitalWrite(PIN_19, HIGH);
      }
      else {
        digitalWrite(PIN_19, LOW);
      }
    }

Ok. When acc.write() is executed it takes up to ~1 second to transfer data to android. And this time doesn't depend on number of bytes in sndmsg. Only if i execute acc.write(sndmsg,0) (sending 0 bytes) - everything goes fast. That is a little bit disturbing. I've tried to change board to another one but have got the same result. Any advices? may be that is a common bug, but there is no such much information in web.

UPD: Wrote some very simple code, that only sends 3 bytes via acc.write. here it is:

#include <Max3421e_constants.h>
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>

AndroidAccessory acc("Google, Inc.",
             "DemoKit",
             "Demokit board",
             "1.0",
             "http://www.android.com",
             "0000000012345678");


byte msg[3];
unsigned long time;
void setup();
void loop();
void led_setup(){
}
void setup()
{
  Serial.begin(115200);
  Serial.print("\r\nStart");
  acc.powerOn();
}
void loop()
{
  if (acc.isConnected()) {
   Serial.print("\r\nis Connected");
   msg[0] = 0x1;
   msg[1] = 0x1;
   msg[2] = 0x1;
   //Serial.print("\r\nSending");
   time = millis();
   Serial.print ("\r\nBefore write\r\n");
   Serial.print (time);
   acc.write(msg, 3);
   time = millis();
   Serial.print("\r\nAfter write: \r\n");
   Serial.print (time);
   //delay(500);
  }
}

And it's debug output is:

is Connected
Before write
6983
After write: 
10958
is Connected
Before write
10958
After write: 
14491
is Connected

and so on. So on some reasons acc.write takes a lot of time and there is no data in the android app.

New UPD (19.01.2015):

Today i've performed some experiments. Here are results. First, i've looked into AndroidAccessory.cpp and found write function:

 int AndroidAccessory::write(void *buff, int len)
    {
        usb.outTransfer(1, out, len, (char *)buff);
        return len;
    }

Ok, then i looked into usb host shield library and found there usb.cpp with outTransfer fucntion, that returns error code if ouccured and 0x00 if everything is ok. So i modified write function to return an error code instead of lenght, like this:

int AndroidAccessory::write(void *buff, int len)
{
    byte rcode;
    rcode =  usb.outTransfer(1, out, len, (char *)buff);
    return int(rcode);
}

and recived "4" as result. According to MAX3421Econstants.h it is hrNAK (0x04) error code. Any ideas? Looks like accessory does not recive NAKs from Android and write fails as a result.

Situation update: Did some research. There is a hell of NAK's when accessory is connected. Here is dump from usb connector: enter image description here


Solution

  • i found the solution. And it is very simple - i didn't setup communication with accessory correctly. This is not an Arduino problem. Arduino works fine. It is just how android interacts with android accessory. So, results:

    It is a little bit strange for me, because it was really hard to found a problem: i have an application, written according to this manual: http://developer.android.com/guide/topics/connectivity/usb/accessory.html But, because i'm not very familiar with android, it seems that i've done some mistakes and receive strange behavior:

    So i decided to rewrite program in more simple way, just with one function and tried to do it in right way. And after some debugging it finally started to work as i want. So thank everyone, who spend time for reading this. Bonus: dump of normal packet, ended with EOP not NAK enter image description here

    UPD 26.01.2015: I found problem and it was in my android code. here is explanation:

    Android developer's manual said that function, which set up communication with accessory must start it's own thread in which all communications with input and output streams are held. Smth like this:

    private void openAccessory() {
        Log.d(TAG, "openAccessory: " + accessory);
        mFileDescriptor = mUsbManager.openAccessory(mAccessory);
        if (mFileDescriptor != null) {
            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
            mInputStream = new FileInputStream(fd);
            mOutputStream = new FileOutputStream(fd);
            Thread thread = new Thread(null, this, "AccessoryThread");
            thread.start();
        }
    }
    

    I really messed this thing up. Forgot about creating new thread in openAccessory function and tried to do it in different place. And recieve a hell of NAK's. Then i've changed my code and add some debug-like run() function like this:

    public void run() {
            final byte[] buffer = new byte[16384];
            int ret = 0;
            int i = 0;
            while (i<50) {
                try {
                    ret = mInputStream.read(buffer);
                    i++;
                    Thread.sleep(500);
                } catch (IOException e) {
                    break;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    

    And until this run method exists (while i < 50) android reads arduino correctly. And when thread ends up (i > 50) - Arduino starts to readout Error Code 4 (NAK) from Android.

    That's all folks.