marclee44 / me

1 stars 0 forks source link

Android超声波数据传输初探 #18

Open marclee44 opened 2 years ago

marclee44 commented 2 years ago

我们经常会遇到需要隔空进行验证的情况,诸如特定wifi、蓝牙、NFC等等,都可实现。今天我们来了解下超声波在这方面的应用可能。

为什么是超声波

采用的第三方库

经调查哦,我们选择了libquietquiet-lwip这个基础库。 这是一套C++的库,可直接开发成桌面应用。同时,他还提供了其他平台的封装: Javascript: quiet-js iOS: QuietModemKit Android: org.quietmodem.Quiet 本文主要以Android版本为例。

开发准备

CMake和Android NDK必须提前安装,这是构建Quiet中包含的jni包的必备条件。 安装并不复杂,Android Studio中,File菜单点击Setting,在弹出的设置窗口中,找到Android SDK image 在SDK Tools中,勾选任意版本的NDK和CMake,点击Apply,等待完成即可。 这里我选择了22.1.7171670版本的NDK和3.18.1版本的CMake。

下载org.quietmodem.Quiet的完整zip包,将其中的quiet目录单独取出备用。

创建项目

正常创建项目,在local.properties文件中,加入NDK的路径,指向NDK的安装目录

sdk.dir=D\:\\Android\\android-sdk
ndk.dir=D\:\\Android\\android-sdk\\ndk\\22.1.7171670

将之前的quiet目录复制到项目根目录后,修改settings.gradle文件,添加quiet的导入

rootProject.name = "test"
include ':app'
include ':quiet'

修改Module的build.gradle文件,引用quiet

    implementation project(path: ':quiet')

同步后,编译已可通过。嗯,之后的编译因为每次都要重新构建jni包,会变得很慢,先忍一下,以后想办法。

权限

接收超声波需要使用录音功能,因此必须声明录音的相应权限

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

收发代码

首先是设置Transmitter

    private void setupTransmitter() {
        FrameTransmitterConfig transmitterConfig;
        try {
            transmitterConfig = new FrameTransmitterConfig(
                    this,getProfile());
            transmitter = new FrameTransmitter(transmitterConfig);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ModemException e) {
            throw new RuntimeException(e);
        }
    }

接着在发送时只需要调用Transmitter.send即可

    private void send(String payload) {
        try {
            transmitter.send(payload.getBytes());
        } catch (IOException e) {
            // our message might be too long or the transmit queue full
        }
    }
    private static final int TIEMOUT = 30;
    private static final int BUFFER_SIZE = 1024;
    private Subscription frameSubscription = Subscriptions.empty();

    private void subscribeToFrames() {
        frameSubscription.unsubscribe();
        frameSubscription = frameReceiverCreate(this, getProfile()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(buf -> {
            Log.i("ReceivedContent", new String(buf, Charset.forName("UTF-8")));
            Long time = System.currentTimeMillis() / 1000;
            String timestamp = time.toString();
            Log.i("ReceiveStatus", "Received " + buf.length + " @" + timestamp);
        }, error-> {
            Log.i("ReceiveStatus", "error " + error.toString());
        });
    }

    private Observable<byte[]> frameReceiverCreate(Context context, String profile) {
        return Observable.create(subscriber -> {
            try {
                FrameReceiverConfig receiverConfig = new FrameReceiverConfig(context, profile);
                FrameReceiver frameReceiver = new FrameReceiver(receiverConfig);
                frameReceiver.setBlocking(TIEMOUT, 0);
                final byte[] buf = new byte[BUFFER_SIZE];

                subscriber.add(new Subscription() {
                    public boolean isUnsubscribed;

                    @Override
                    public void unsubscribe() {
                        frameReceiver.close();
                        isUnsubscribed = true;
                    }

                    @Override
                    public boolean isUnsubscribed() {
                        return isUnsubscribed;
                    }
                });

                while(!subscriber.isUnsubscribed()) {
                    long recvLen = 0;
                    try {
                        recvLen = frameReceiver.receive(buf);
                        byte[] immutableBuf = java.util.Arrays.copyOf(buf, (int)recvLen);
                        subscriber.onNext(immutableBuf);
                    } catch (SocketTimeoutException e ) {
                        // ignore timeouts - attempt new receive
                    }
                }

            } catch (Exception e) {
                subscriber.onError(e);
            }

        });
    }

测试效果

经测试,在标准10人会议室内,收发超声波数据没有什么问题。作为一种验证手段,基本没有问题