🛠 Premier pont AOA : écho série avec Android
Objectives
Establish the first Android Open Accessory (AOA) bridge between a phone and the Mega ADK: the phone connects to the card's USB-A port — the MAX3421E acts as the USB host, and the phone acts as the USB device, which is the reverse of a classic OTG setup. The Android application detects the accessory, opens two channels in point-to-point mode, and the card echoes back any received line. Protocol reminders (AOA 1.0, source.android.com): in accessory mode, the phone enumerates with Google VID 0x18D1, PID 0x2D00 (AOAv1), 0x2D01 (+adb), 0x2D02 to 0x2D05 (AOAv2 variants); the handshake is performed via vendor control requests on endpoint 0 — req 51 GET_PROTOCOL, req 52 SET_STRING (6 strings: manufacturer, model, description, version, URI, serial number), req 53 START_ACCESSORY. Always send the version string: its absence may cause Android phones ≤ 10 to reboot during enumeration. Accessory mode has existed since Android 3.1, backported to 2.3.4.
Hardware
- 1 × Arduino Mega ADK
- 1 × Android phone (3.1 minimum)
- 1 × USB-A cable (card side) → micro-USB or USB-C (phone side), preferably short and of good quality
- External power supply 7–12 V (recommended), at least 1.5 A: with USB power only, the card and phone share a total of 500 mA
- Arduino Library: USB Host Shield 2.0 (felis/USB_Host_Shield_2.0) — on cores ≥ 1.5.5, the MAX3421E pins for the ADK are selected automatically (BOARD_MEGA_ADK); on older cores, enable USE_UHS_MEGA_ADK in settings.h
Complete Code
Card side (Arduino):
// mega-adk-pont-aoa-serie.ino — Board: "Arduino Mega ADK"
#include <Usb.h>
#include <usbhub.h>
#include <adk.h>
USB Usb;
USBHub hub0(&Usb);
ADK adk(&Usb, "AtelierADK", "PontSerie", "Pont AOA serie",
"1.0", "http://example.org", "SN000001"); // version obligatoire !
void setup {
Serial.begin(115200);
if (Usb.Init == -1) { // MAX3421E muet
Serial.println(F("OSCOKIRQ failed to assert"));
while (1);
}
Serial.println(F("Attente du telephone en mode accessoire..."));
}
void loop {
Usb.Task; // piloter l'énumération, souvent !
if (!adk.isReady) return;
uint8_t buf[64]; uint16_t len = sizeof(buf);
if (adk.RcvData(&len, buf) == 0 && len > 0) {
adk.SndData(len, buf); // écho vers l'application
Serial.write(buf, len); // miroir sur la console
}
while (Serial.available) { // console -> téléphone
uint8_t b = (uint8_t)Serial.read;
adk.SndData(1, &b);
}
delay(10);
}
Android side (Java, minimal activity with permission request):
// PontActivity.java — déclare dans le manifeste un intent-filter
// android.hardware.usb.action.USB_ACCESSORY_ATTACHED
public class PontActivity extends Activity {
private static final String ACTION_PERM = "com.atelier.adk.USB_PERMISSION";
private FileInputStream in; private FileOutputStream out;
private TextView flux; private EditText champ;
@Override protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_pont);
flux = findViewById(R.id.flux); champ = findViewById(R.id.champ);
registerReceiver(permReceiver, new IntentFilter(ACTION_PERM));
handleIntent(getIntent);
}
private void handleIntent(Intent intent) {
if (!UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction)) return;
UsbAccessory acc = UsbManager.getAccessory(intent);
UsbManager mgr = (UsbManager) getSystemService(Context.USB_SERVICE);
if (!mgr.hasPermission(acc))
mgr.requestPermission(acc, PendingIntent.getBroadcast(
this, 0, new Intent(ACTION_PERM), 0));
try {
ParcelFileDescriptor pfd = acc.open(mgr, mgr.hasPermission(acc));
in = new FileInputStream(pfd.getFileDescriptor);
out = new FileOutputStream(pfd.getFileDescriptor);
new Thread(this::boucleLecture).start;
} catch (IOException e) { e.printStackTrace; }
}
private void boucleLecture {
byte[] buf = new byte[512]; int n;
try { while ((n = in.read(buf)) > 0) {
final String s = new String(buf, 0, n);
runOnUiThread( -> flux.append("Carte : " + s));
} } catch (IOException ignored) { } // débranchement
}
public void envoyer(View v) throws IOException {
out.write((champ.getText.toString + "\n").getBytes);
}
}
Expected Verification
When plugging the card's USB-A cable into the phone, the application launches automatically via the USB_ACCESSORY_ATTACHED intent, a popup requests USB permission, then each line typed into the computer's serial console is echoed back in the application, and each line sent by the application appears on the console. The phone must re-enumerate with VID 0x18D1 (visible in USB debugging) after START_ACCESSORY.
Common Errors
- Forgetting Usb.Task in loop: enumeration never progresses, adk.isReady remains false indefinitely.
- Empty or missing version string: risk of phone reboot (Android ≤ 10); manufacturer strings different from those in accessory_filter prevent the application from launching automatically.
- AOA and MTP simultaneously: incompatible — if the phone is in "file transfer" mode, unplug/replug in charge-only mode.
- Card powered only by the computer: 500 mA budget shared with phone charging, brownouts and phantom disconnections; use an external 7–12 V ≥ 1.5 A power supply.
- Looking for Google's old ADK 2011 package: the official ecosystem is dead (wiki 404, docs redirected); use the maintained USB Host Shield 2.0 library and its ADK class.
Other tutorials for this board
Code examples for this board
clignotante.inoArduinobouton-led.inoArduinoscan-hote-usb.inoArduinoadk-chat-android.inoArduinoadk-telemetrie.inoArduinohote-clavier-usb.inoArduino