Jak wyświetlić na tablecie z systemem android przewodowo USB obraz z arduino GIGA, podaj przykładowy program?
SerialUSB w postaci ustalonego protokołu (nagłówek + dane + stopka/CRC). usb‑serial‑for‑android, odbiera bajty, rekonstruuje bitmapę i wyświetla ją. • Arduino GIGA R1 WiFi (USB‑C; wsparcie USB Device i USB Host)
• Moduł kamery – np. ArduCAM OV5642 lub prosty moduł SPI (OV2640/OV7670)
• Tablet Android z obsługą USB OTG + przejściówka OTG (USB‑C↔USB‑A)
Poniższy szkic przechwytuje pojedynczą klatkę JPEG z ArduCAM OV5642 (QVGA 320×240), pakuje w nagłówek i wysyła do hosta.
#include <ArduCAM.h>
#include <SPI.h>
#define CS_PIN 7 // Chip Select kamery
#define BAUD 2000000 // 2 Mb/s – realnie ok. 180 kB/s
ArduCAM cam(OV5642, CS_PIN);
void setup() {
Serial.begin(BAUD);
while (!Serial) {} // czekaj na enumerację USB
SPI.begin();
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
// Szybki test połączenia
cam.write_reg(ARDUCHIP_TEST1, 0x55);
if (cam.read_reg(ARDUCHIP_TEST1) != 0x55) {
Serial.println(F("Kamera nieodpowiada"));
while (1);
}
cam.set_format(JPEG);
cam.InitCAM();
cam.OV5642_set_JPEG_size(OV5642_320x240);
delay(1000);
Serial.println(F("READY"));
}
void loop() {
if (Serial.available() && Serial.read() == 'C') { // command 'C' = capture
sendFrame();
}
}
void sendFrame() {
cam.flush_fifo();
cam.clear_fifo_flag();
cam.start_capture();
while (!cam.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));
uint32_t len = cam.read_fifo_length();
if (len == 0 || len > 250*1024) return; // sanity check
// Nagłówek: 0xAB 0xCD + 3‑bajtowa długość
uint8_t hdr[5] = {0xAB, 0xCD, (uint8_t)(len>>16), (uint8_t)(len>>8), (uint8_t)len};
Serial.write(hdr, 5);
cam.CS_LOW();
cam.set_fifo_burst();
for (uint32_t i = 0; i < len; i++) {
Serial.write(SPI.transfer(0x00));
if (i % 1024 == 0) Serial.flush(); // opróżnij bufor co 1 kB
}
cam.CS_HIGH();
uint16_t crc = 0xFFFF; // proste CRC‑16
Serial.write((uint8_t*)&crc, 2); // stopka
}
• Komenda 'C' z tabletu uruchamia wykonanie zdjęcia.
• Nagłówek i stopka pozwalają aplikacji Android sprawdzić kompletność klatki.
Biblioteka: implementation("com.github.mik3y:usb-serial-for-android:3.5.2")
class MainActivity : AppCompatActivity() {
private lateinit var usbManager: UsbManager
private var serialPort: UsbSerialPort? = null
private val ioExecutor = Executors.newSingleThreadExecutor()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
usbManager = getSystemService(USB_SERVICE) as UsbManager
openSerial()
findViewById<Button>(R.id.btnCapture).setOnClickListener { sendCommand('C'.code) }
}
private fun openSerial() {
val driver = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager).firstOrNull() ?: return
val conn = usbManager.openDevice(driver.device) ?: return
serialPort = driver.ports[0].apply {
open(conn)
setParameters(2_000_000, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
}
val manager = SerialInputOutputManager(serialPort, listener)
ioExecutor.submit(manager)
}
private val listener = object : SerialInputOutputManager.Listener {
private val buffer = ByteArrayOutputStream()
private var expectedLen = -1
override fun onNewData(data: ByteArray) {
for (b in data) processByte(b.toInt() and 0xFF)
}
private fun processByte(b: Int) {
buffer.write(b)
val arr = buffer.toByteArray()
if (expectedLen == -1 && arr.size >= 5 && arr[0]==0xAB.toByte() && arr[1]==0xCD.toByte()) {
expectedLen = (arr[2].toUByte().toInt() shl 16) or
(arr[3].toUByte().toInt() shl 8) or arr[4].toUByte().toInt()
buffer.reset() // usuń nagłówek
} else if (expectedLen > -1 && buffer.size() >= expectedLen + 2) { // +CRC
val jpeg = buffer.toByteArray().copyOfRange(0, expectedLen)
runOnUiThread {
val bmp = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.size)
findViewById<ImageView>(R.id.imageView).setImageBitmap(bmp)
}
buffer.reset(); expectedLen = -1
}
}
override fun onRunError(e: Exception) { e.printStackTrace() }
}
private fun sendCommand(c: Int) = serialPort?.write(byteArrayOf(c.toByte()), 10)
}
• Przycisk „Capture” wysyła 'C', aplikacja czeka na nagłówek, odczytuje długość, kolekcjonuje JPEG i wyświetla.
• Do manifestu dodaj <uses-feature android:name="android.hardware.usb.host" android:required="true"/> oraz filtr device_filter.xml z VID/PID Arduino.
usb-serial-for-android jest de‑facto standardem (ostatnia stabilna: 3.5.2 – luty 2024). Serial.flush() po 1–2 kB ogranicza przepełnienie bufora USB. UsbSerialPort (metoda .purgeHwBuffers()) co kilka sekund. Arduino GIGA nie wyśle wideo do Androida jak klasyczna kamera, ale przy wykorzystaniu USB CDC można sprawnie przesłać skompresowany JPEG lub ramkę RGB. W praktyce:
SerialUSB (przykład powyżej). usb‑serial‑for‑android), zweryfikuj długość/CRC i wyświetl jako Bitmap.