🛠 Premier pont AOA : écho série avec Android
Objetivos
Establecer el primer puente Android Open Accessory (AOA) entre un teléfono y la Mega ADK: el teléfono se conecta al puerto USB-A de la placa — el MAX3421E es el host USB, el teléfono es el dispositivo USB, lo contrario de una configuración OTG clásica. La aplicación de Android detecta el accesorio, abre dos canales en modo punto a punto, y la placa devuelve (eco) cualquier línea recibida. Recordatorios del protocolo (AOA 1.0, source.android.com): en modo accesorio, el teléfono se enumera con el VID de Google 0x18D1, PID 0x2D00 (AOAv1), 0x2D01 (+adb), 0x2D02 a 0x2D05 (variantes AOAv2); el handshake se realiza mediante solicitudes de control de proveedor en el endpoint 0 — req 51 GET_PROTOCOL, req 52 SET_STRING (6 cadenas: fabricante, modelo, descripción, versión, URI, número de serie), req 53 START_ACCESSORY. Envíe siempre la cadena de versión: su ausencia puede provocar el reinicio de teléfonos Android ≤ 10 durante la enumeración. El modo accesorio existe desde Android 3.1, retroportado a la versión 2.3.4.
Material
- 1 × Arduino Mega ADK
- 1 × teléfono Android (3.1 mínimo)
- 1 × cable USB-A (lado de la placa) → micro-USB o USB-C (lado del teléfono), preferiblemente corto y de buena calidad
- Fuente de alimentación externa de 7–12 V (recomendado), al menos 1,5 A: con alimentación solo por USB, la placa y el teléfono comparten 500 mA en total
- Biblioteca de Arduino: USB Host Shield 2.0 (felis/USB_Host_Shield_2.0) — en los cores ≥ 1.5.5, los pines del MAX3421E de la ADK se seleccionan automáticamente (BOARD_MEGA_ADK); en los cores antiguos, active USE_UHS_MEGA_ADK en settings.h
Código completo
Lado de la placa (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);
}
Lado de Android (Java, actividad mínima con solicitud de permiso):
// 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);
}
}
Verificación esperada
Al conectar el cable USB-A de la placa al teléfono, la aplicación se inicia automáticamente mediante la intent USB_ACCESSORY_ATTACHED, aparece una ventana emergente solicitando el permiso USB, luego cada línea escrita en la consola serie del ordenador vuelve como eco en la aplicación, y cada línea enviada por la aplicación se muestra en la consola. El teléfono debe volver a enumerarse con el VID 0x18D1 (visible en la depuración USB) después del START_ACCESSORY.
Errores frecuentes
- Olvidar Usb.Task en loop: la enumeración nunca avanza, adk.isReady permanece falso indefinidamente.
- Cadena de versión vacía o ausente: riesgo de reinicio del teléfono (Android ≤ 10); las cadenas de fabricante diferentes a las del filtro accessory_filter impiden el inicio automático de la aplicación.
- AOA y MTP simultáneos: incompatibles — si el teléfono está en modo «transferencia de archivos», desconecte y vuelva a conectar en modo solo carga.
- Placa alimentada únicamente por el ordenador: presupuesto de 500 mA compartido con la carga del teléfono, caídas de tensión y desconexiones fantasma; utilice una fuente de alimentación externa de 7–12 V ≥ 1,5 A.
- Buscar el antiguo paquete ADK 2011 de Google: el ecosistema oficial ha muerto (wiki 404, documentación redirigida); utilice la biblioteca mantenida USB Host Shield 2.0 y su clase ADK.
Otros tutoriales para esta placa
Ejemplos de código para esta placa
clignotante.inoArduinobouton-led.inoArduinoscan-hote-usb.inoArduinoadk-chat-android.inoArduinoadk-telemetrie.inoArduinohote-clavier-usb.inoArduino