Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QR Beam

https://qrbeam-web.vercel.app — la app y las instrucciones están ahí.

Mandar archivos chicos a un teléfono sin internet, por la pantalla de la compu. La compu dibuja el archivo como una animación de códigos QR, el teléfono la filma y lo rearma. No hay wifi, ni bluetooth, ni cable, ni cuenta, ni nada que salga de las dos máquinas.

Dos piezas:

Qué Dónde Cómo se usa
Emisor https://qrbeam-web.vercel.app — o qrbeam-sender.html local Doble click al archivo local: es uno solo, anda sin servidor y sin internet.
Receptor QRBeam.apk — 801 KB, también en https://qrbeam-web.vercel.app/QRBeam.apk Se instala una sola vez en el Galaxy A51. Después nunca más necesita red.

Cómo se usa

  1. En el teléfono, instalar QRBeam.apk (hay que permitir "instalar apps de origen desconocido"; está firmado con una clave propia, no viene de Play).
  2. En la compu, abrir https://qrbeam-web.vercel.app (o el qrbeam-sender.html local, que es lo mismo y no necesita red) y soltar el archivo. Empieza a parpadear el QR. Conviene darle Pantalla completa.
  3. Abrir QR Beam en el teléfono y apuntar a la pantalla, a unos 20–30 cm, con el QR entero adentro del marco.
  4. Cuando llega al 100% suena, vibra y el archivo queda en Descargas/QRBeam.

No hace falta atrapar todos los QR: los que se pierden se recuperan solos en la vuelta siguiente. Podés tapar la cámara un rato y seguir después.

Si no engancha: bajar la velocidad, achicar el QR, subir el brillo de la pantalla.

Cuánto tarda

Con los valores por defecto (QR de 57×57, corrección baja, 5 QR por segundo) van 255 bytes por QR: 1,25 KB/s teóricos y más o menos 1 KB/s real contando los que se pierden. Es la configuración conservadora, la que engancha más fácil. Subiendo el tamaño del QR y la velocidad va bastante más rápido.

Archivo Por defecto (57×57 a 5/s) 73×73 a 10/s 97×97 a 15/s
20 KB (un PDF de una carilla) ~20 s ~8 s ~2 s
100 KB ~1,5 min ~35 s ~10 s
1 MB ~15 min ~6 min ~1,7 min

Arriba de unos pocos MB deja de tener sentido: es lento por naturaleza, es un canal óptico.

Cómo funciona

El archivo se parte en bloques (255 bytes con los valores por defecto) y se manda con un fountain code (código LT). El emisor primero manda cada bloque una vez, y después manda para siempre combinaciones XOR de bloques al azar. El receptor junta lo que puede agarrar, en cualquier orden, y cuando tiene información suficiente despeja el archivo entero. Perder frames cuesta tiempo, nunca datos: no hay ningún frame que sea imprescindible, y no hace falta canal de vuelta para pedir retransmisión.

Sin pérdidas cierra en 1,05 veces la cantidad de bloques. Con la mitad de los frames perdidos, en 2,5 veces.

El formato de frame está documentado arriba de sender/src/qrb-core.js y de Qrb.kt, que son la misma especificación escrita dos veces.

Tres decisiones que no son obvias

El grado del droplet viaja dentro del frame. El emisor elige cuántos bloques combina con una distribución robust soliton, que usa log y sqrt. Si el receptor tuviera que reproducir esa cuenta, una diferencia de un ulp entre V8 y la JVM rompería la transferencia. Mandando el grado, lo único que tiene que coincidir entre los dos lenguajes es aritmética entera: fmix32, xorshift32 y un módulo. Hay un test que compara los dos, valor por valor.

El payload va blanqueado (XOR contra un chorro pseudoaleatorio derivado del header, que viaja en claro). No es cifrado ni pretende serlo. Es que un QR cuyo contenido tiene rachas largas del mismo byte sale con manchones grandes y el detector de ZXing no lo encuentra. Medido con un archivo con zonas de ceros —o sea, casi cualquier archivo real: documentos, ejecutables, imágenes con relleno—: 23% de frames ilegibles sin blanqueo, 3% con blanqueo.

El generador de QR es el de Nayuki y no qrcode-generator. Este último implementa la regla N1 de penalización de máscara de una manera propia, no la de la norma, y cada tanto elige una máscara que genera patrones de búsqueda falsos. Con ese generador, el frame que lleva el nombre del archivo —que siempre tiene el mismo contenido— salía ilegible siempre, y el archivo llegaba sin nombre.

Qué está probado y qué no

cd android && ./gradlew test — 16 tests:

  • Contrato entre lenguajes. node tools/gen_test_frames.js genera QR de verdad con el emisor JS; el test los lee con el mismo ZXing y el mismo decoder que corre en el teléfono, y rearma el archivo. Si el PRNG o el formato de frame se desincronizan, se pone rojo.
  • Circuito completo desde el navegador. BrowserCanvasTest usa QR que pintó Chrome de verdad corriendo qrbeam-sender.html, capturados del canvas. Los lee y reconstruye el archivo con el CRC y el nombre correctos.
  • Degradación tipo cámara: desenfoque, contraste comprimido (negro que llega a 45, blanco a 218) y ruido de sensor.
  • Pérdida de 1 de cada 2, 1 de cada 3 y 1 de cada 4 frames.
  • Archivos de 1 byte, bordes exactos del bloque, archivos con zonas de ceros, nombres con acentos, cambio de archivo a mitad de camino, frames corruptos, y que el CRC detecte corrupción.

En el teléfono, el botón "?" → "Probar sin cámara" corre el pipeline entero —generar, dibujar como QR, leer, tirar uno de cada cuatro, rearmar, verificar el CRC y guardar en Descargas— sin necesidad de la compu. Corrido en el emulador sobre el APK release ya minificado: pasa todo, 44 de 46 QR leídos.

Lo único que no está probado en automático es la óptica: cámara real apuntando a una pantalla real. Todo lo de arriba de la lente sí.

Un dato para no asustarse: al detector de ZXing se le escapa un 2–4% de los QR densos aunque la imagen esté impecable. Es una limitación conocida con símbolos de mucha data. El fountain code está justamente para eso.

Volver a compilar

Necesita JDK 17 (quedó instalado en ~/Library/Java/JavaVirtualMachines/temurin17) y el SDK de Android que ya tenías.

cd android && JAVA_HOME=~/Library/Java/JavaVirtualMachines/temurin17/Contents/Home ./gradlew assembleRelease

El APK sale en android/app/build/outputs/apk/release/app-release.apk.

Si tocás algo del emisor:

node tools/build_sender.js       # rearma el HTML de un solo archivo
node tools/selftest.js           # round-trip del núcleo JS
node tools/gen_test_frames.js    # regenera los frames de referencia para los tests

La app está firmada con android/keystore/qrbeam.jks (contraseña en android/keystore.properties). Es una clave casera para instalar a mano, no sirve para Play Store. Guardala si querés poder actualizar la app encima de la instalada.

Republicar el sitio

../qrbeam-web/ es la copia publicada: index.html (el emisor armado) más el APK para poder bajarlo al teléfono desde ahí mismo.

cp qrbeam-sender.html ../qrbeam-web/index.html && cp QRBeam.apk ../qrbeam-web/
cd .. && vercel deploy --prod --yes --scope tasatasa --cwd qrbeam-web

Ojo: el alias es qrbeam-web.vercel.app. qrbeam.vercel.app es de otra persona.

Detalles de la app

  • 801 KB, Android 8 en adelante, arm64 y arm32.
  • No pide permiso de internet. No puede mandar nada a ningún lado ni aunque quisiera; se puede verificar con aapt dump permissions QRBeam.apk.
  • Pide cámara, y vibración para avisar cuando termina.
  • Kotlin + CameraX + ZXing, sin Compose ni AppCompat, para que sea liviana.
  • Pellizcar hace zoom y tocar enfoca (viene de CameraX).
  • Guarda con MediaStore, así que no necesita permiso de almacenamiento.

Licencia

MIT, ver LICENSE.

Licencias de terceros

About

Mandá archivos a un teléfono sin internet, por la pantalla: QR animado con fountain code. Emisor web + app Android.

Resources

Stars

18 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages