1

我是一名学生,正在做一个 Android Things 项目,该项目是关于将 Rasperi pi 与加速度计传感器连接以获取加速度数据,我在 Android Studio 中完成了编码部分并且传感器工作正常,然后我尝试将我的项目连接到按照本教程进行云计算:http: //blog.blundellapps.co.uk/tut-google-cloud-iot-core-mqtt-on-android/

我想我必须通过在 MainActivity 类中调用下面的这个方法将加速度计数据发布到云端,我这样做了但仍然有错误,请参阅我在这个问题末尾所做的更新。

@Override
    public void onSensorChanged(SensorEvent event) {
        Log.d(TAG, "Accel X " + event.values[0]);
        Log.d(TAG, "Accel Y " + event.values[1]);
        Log.d(TAG, "Accel Z " + event.values[2]);
    }

另外,我是否必须在云平台中做一些事情才能接收数据?我已经设置了与我的 Google IoT Core 详细信息的通信,如下面的代码所示:

 communicator = new IotCoreCommunicator.Builder()
                .withContext(this)
                .withCloudRegion("us-central1") 
                .withProjectId("my-first-project-198704")  
                .withRegistryId("vibration") 
                .withDeviceId("my-device") 
                .withPrivateKeyRawFileId(R.raw.rsa_private)
                .build();

这是我在云中看到的:在此处输入图像描述

这是我在 Android Studio 中的加速数据: 在此处输入图像描述

我的项目由两个模块组成,但在这里我将只附加第二个模块,因为问题仅限于 3000 个字符,而我认为第二个模块是我需要编辑的。

示例模块:

AccelerometerActivity 类:

package com.cacaosd.sample;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;

import com.cacaosd.adxl345.ADXL345SensorDriver;
import com.cacaosd.sample.BoardDefaults;

import java.io.IOException;

public class AccelerometerActivity extends Activity implements SensorEventListener {

    private static final String TAG = AccelerometerActivity.class.getSimpleName();

    private ADXL345SensorDriver mSensorDriver;
    private SensorManager mSensorManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mSensorManager.registerDynamicSensorCallback(new SensorManager.DynamicSensorCallback() {
            @Override
            public void onDynamicSensorConnected(Sensor sensor) {
                if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                    mSensorManager.registerListener(AccelerometerActivity.this,
                            sensor, SensorManager.SENSOR_DELAY_NORMAL);
                }
            }


        });

        try {
            mSensorDriver = new ADXL345SensorDriver(BoardDefaults.getI2CPort());
            mSensorDriver.registerAccelerometerSensor();
        } catch (IOException e) {
            Log.e(TAG, "Error configuring sensor", e);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "Closing sensor");
        if (mSensorDriver != null) {
            mSensorManager.unregisterListener(this);
            try {
                mSensorDriver.close();
            } catch (IOException e) {
                Log.e(TAG, "Error closing sensor", e);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mSensorDriver = null;
            }
        }
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        Log.d(TAG, "Accel X " + event.values[0]);
        Log.d(TAG, "Accel Y " + event.values[1]);
        Log.d(TAG, "Accel Z " + event.values[2]);
    }



    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        Log.i(TAG, "sensor accuracy changed: " + accuracy);

    }
}

BoardDefaults 类:

    package com.cacaosd.sample;

import android.os.Build;

import com.google.android.things.pio.PeripheralManagerService;

import java.util.List;

/**
 * Created by cagdas on 20.12.2016.
 */

@SuppressWarnings("WeakerAccess")
public class BoardDefaults {
    private static final String DEVICE_EDISON_ARDUINO = "edison_arduino";
    private static final String DEVICE_EDISON = "edison";
    private static final String DEVICE_RPI3 = "rpi3";
    private static final String DEVICE_NXP = "imx6ul";
    private static String sBoardVariant = "";

    /**
     * Return the preferred I2C port for each board.
     */
    public static String getI2CPort() {
        switch (getBoardVariant()) {
            case DEVICE_EDISON_ARDUINO:
                return "I2C6";
            case DEVICE_EDISON:
                return "I2C1";
            case DEVICE_RPI3:
                return "I2C1";
            case DEVICE_NXP:
                return "I2C2";
            default:
                throw new IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE);
        }
    }

    private static String getBoardVariant() {
        if (!sBoardVariant.isEmpty()) {
            return sBoardVariant;
        }
        sBoardVariant = Build.DEVICE;
        // For the edison check the pin prefix
        // to always return Edison Breakout pin name when applicable.
        if (sBoardVariant.equals(DEVICE_EDISON)) {
            PeripheralManagerService pioService = new PeripheralManagerService();
            List<String> gpioList = pioService.getGpioList();
            if (gpioList.size() != 0) {
                String pin = gpioList.get(0);
                if (pin.startsWith("IO")) {
                    sBoardVariant = DEVICE_EDISON_ARDUINO;
                }
            }
        }
        return sBoardVariant;
    }
}

IotCoreCommunicator 类:

    package com.cacaosd.sample1;

import android.content.Context;
import android.util.Log;

import java.util.concurrent.TimeUnit;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

public class IotCoreCommunicator {

    private static final String SERVER_URI = "ssl://mqtt.googleapis.com:8883";

    public static class Builder {

        private Context context;
        private String projectId;
        private String cloudRegion;
        private String registryId;
        private String deviceId;
        private int privateKeyRawFileId;

        public Builder withContext(Context context) {
            this.context = context;
            return this;
        }

        public Builder withProjectId(String projectId) {
            this.projectId = projectId;
            return this;
        }

        public Builder withCloudRegion(String cloudRegion) {
            this.cloudRegion = cloudRegion;
            return this;
        }

        public Builder withRegistryId(String registryId) {
            this.registryId = registryId;
            return this;
        }

        public Builder withDeviceId(String deviceId) {
            this.deviceId = deviceId;
            return this;
        }

        public Builder withPrivateKeyRawFileId(int privateKeyRawFileId) {
            this.privateKeyRawFileId = privateKeyRawFileId;
            return this;
        }

        public IotCoreCommunicator build() {
            if (context == null) {
                throw new IllegalStateException("context must not be null");
            }

            if (projectId == null) {
                throw new IllegalStateException("projectId must not be null");
            }
            if (cloudRegion == null) {
                throw new IllegalStateException("cloudRegion must not be null");
            }
            if (registryId == null) {
                throw new IllegalStateException("registryId must not be null");
            }
            if (deviceId == null) {
                throw new IllegalStateException("deviceId must not be null");
            }
            String clientId = "projects/" + projectId + "/locations/" + cloudRegion + "/registries/" + registryId + "/devices/" + deviceId;

            if (privateKeyRawFileId == 0) {
                throw new IllegalStateException("privateKeyRawFileId must not be 0");
            }
            MqttAndroidClient client = new MqttAndroidClient(context, SERVER_URI, clientId);
            IotCorePasswordGenerator passwordGenerator = new IotCorePasswordGenerator(projectId, context.getResources(), privateKeyRawFileId);
            return new IotCoreCommunicator(client, deviceId, passwordGenerator);
        }

    }

    private final MqttAndroidClient client;
    private final String deviceId;
    private final IotCorePasswordGenerator passwordGenerator;

    IotCoreCommunicator(MqttAndroidClient client, String deviceId, IotCorePasswordGenerator passwordGenerator) {
        this.client = client;
        this.deviceId = deviceId;
        this.passwordGenerator = passwordGenerator;
    }

    public void connect() {
        monitorConnection();
        clientConnect();
        subscribeToConfigChanges();
    }

    private void monitorConnection() {
        client.setCallback(new MqttCallback() {
            @Override
            public void connectionLost(Throwable cause) {
                Log.e("TUT", "connection lost", cause);
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                Log.d("TUT", "message arrived " + topic + " MSG " + message);
                // You need to do something with messages when they arrive
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                Log.d("TUT", "delivery complete " + token);
            }
        });
    }

    private void clientConnect() {
        try {
            MqttConnectOptions connectOptions = new MqttConnectOptions();
            // Note that the the Google Cloud IoT Core only supports MQTT 3.1.1, and Paho requires that we explicitly set this.
            // If you don't, the server will immediately close its connection to your device.
            connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);

            // With Google Cloud IoT Core, the username field is ignored, however it must be set for the
            // Paho client library to send the password field. The password field is used to transmit a JWT to authorize the device.
            connectOptions.setUserName("unused-but-necessary");
            connectOptions.setPassword(passwordGenerator.createJwtRsaPassword());

            IMqttToken iMqttToken = client.connect(connectOptions);
            iMqttToken.setActionCallback(new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    Log.d("TUT", "success, connected");
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Log.e("TUT", "failure, not connected", exception);
                }
            });
            iMqttToken.waitForCompletion(TimeUnit.SECONDS.toMillis(30));
            Log.d("TUT", "IoT Core connection established.");
        } catch (MqttException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Configuration is managed and sent from the IoT Core Platform
     */
    private void subscribeToConfigChanges() {
        try {
            client.subscribe("/devices/" + deviceId + "/config", 1);
        } catch (MqttException e) {
            throw new IllegalStateException(e);
        }
    }

    public void publishMessage(String subtopic, String message) {
        String topic = "/devices/" + deviceId + "/" + subtopic;
        String payload = "{msg:\"" + message + "\"}";
        MqttMessage mqttMessage = new MqttMessage(payload.getBytes());
        mqttMessage.setQos(1);
        try {
            client.publish(topic, mqttMessage);
            Log.d("TUT", "IoT Core message published. To topic: " + topic);
        } catch (MqttException e) {
            throw new IllegalStateException(e);
        }
    }

    public void disconnect() {
        try {
            Log.d("TUT", "IoT Core connection disconnected.");
            client.disconnect();
        } catch (MqttException e) {
            throw new IllegalStateException(e);
        }
    }

}

IotCorePasswordGenerator 类:

    package com.cacaosd.sample1;

import android.content.res.Resources;
import android.util.Base64;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

class IotCorePasswordGenerator {

    private final String projectId;
    private final Resources resources;
    private final int privateKeyRawFileId;

    IotCorePasswordGenerator(String projectId, Resources resources, int privateKeyRawFileId) {
        this.projectId = projectId;
        this.resources = resources;
        this.privateKeyRawFileId = privateKeyRawFileId;
    }

    char[] createJwtRsaPassword() {
        try {
            byte[] privateKeyBytes = decodePrivateKey(resources, privateKeyRawFileId);
            return createJwtRsaPassword(projectId, privateKeyBytes).toCharArray();
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("Algorithm not supported. (developer error)", e);
        } catch (InvalidKeySpecException e) {
            throw new IllegalStateException("Invalid Key spec. (developer error)", e);
        } catch (IOException e) {
            throw new IllegalStateException("Cannot read private key file.", e);
        }
    }

    private static byte[] decodePrivateKey(Resources resources, int privateKeyRawFileId) throws IOException {
        try(InputStream inStream = resources.openRawResource(privateKeyRawFileId)) {
            return Base64.decode(inputToString(inStream), Base64.DEFAULT);
        }
    }

    private static String inputToString(InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }

    private static String createJwtRsaPassword(String projectId, byte[] privateKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        return createPassword(projectId, privateKeyBytes, "RSA", SignatureAlgorithm.RS256);
    }

    private static String createPassword(String projectId, byte[] privateKeyBytes, String algorithmName, SignatureAlgorithm signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Instant now = Instant.now();
        // Create a JWT to authenticate this device. The device will be disconnected after the token
        // expires, and will have to reconnect with a new token. The audience field should always be set
        // to the GCP project id.
        JwtBuilder jwtBuilder =
                Jwts.builder()
                        .setIssuedAt(Date.from(now))
                        .setExpiration(Date.from(now.plus(Duration.ofMinutes(20))))
                        .setAudience(projectId);

        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory kf = KeyFactory.getInstance(algorithmName);

        return jwtBuilder.signWith(signatureAlgorithm, kf.generatePrivate(spec)).compact();
    }

}


MainActivity class:

    package com.cacaosd.sample1;

import android.app.Activity;
import android.hardware.SensorEvent;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;


import com.cacaosd.sample.AccelerometerActivity;
import com.cacaosd.sample.R;
import com.cacaosd.sample1.IotCoreCommunicator;


import com.google.android.things.pio.Gpio;


import java.io.IOException;
import java.util.concurrent.TimeUnit;


public class MainActivity extends Activity {

    AccelerometerActivity mAccelerometerActivity = new AccelerometerActivity();

    private IotCoreCommunicator communicator;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Setup the communication with your Google IoT Core details
        communicator = new IotCoreCommunicator.Builder()
                .withContext(this)
                .withCloudRegion("us-central1") // ex: europe-west1
                .withProjectId("my-first-project-198704")   // ex: supercoolproject23236
                .withRegistryId("vibration") // ex: my-devices
                .withDeviceId("my-device") // ex: my-test-raspberry-pi
                .withPrivateKeyRawFileId(R.raw.rsa_private)
                .build();

        HandlerThread thread = new HandlerThread("MyBackgroundThread");
        thread.start();
        handler = new Handler(thread.getLooper());
        handler.post(connectOffTheMainThread); // Use whatever threading mechanism you want
    }

    private final Runnable connectOffTheMainThread = new Runnable() {
        @Override
        public void run() {
            communicator.connect();

            handler.post(sendMqttMessage);
        }
    };



    private final Runnable sendMqttMessage = new Runnable() {
        private int i;

        /**
         * We post 100 messages as an example, 1 a second
         */
        @Override
        public void run() {
            if (i == 100) {
                return;
            }

            SensorEvent event = null;

            // events is the default topic for MQTT communication
            String subtopic = "events";
            // Your message you want to send
            String message = "Hello World " + i++;
            communicator.publishMessage(subtopic, message);



            handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
        }
    };

    @Override
    protected void onDestroy() {
        communicator.disconnect();
        super.onDestroy();
    }

MainActivity 类:

package com.cacaosd.sample1;

import android.app.Activity;
import android.hardware.SensorEvent;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;


import com.cacaosd.sample.AccelerometerActivity;
import com.cacaosd.sample.R;
import com.cacaosd.sample1.IotCoreCommunicator;


import com.google.android.things.pio.Gpio;


import java.io.IOException;
import java.util.concurrent.TimeUnit;


public class MainActivity extends Activity {

    AccelerometerActivity mAccelerometerActivity = new AccelerometerActivity();

    private IotCoreCommunicator communicator;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Setup the communication with your Google IoT Core details
        communicator = new IotCoreCommunicator.Builder()
                .withContext(this)
                .withCloudRegion("us-central1") // ex: europe-west1
                .withProjectId("my-first-project-198704")   // ex: supercoolproject23236
                .withRegistryId("vibration") // ex: my-devices
                .withDeviceId("my-device") // ex: my-test-raspberry-pi
                .withPrivateKeyRawFileId(R.raw.rsa_private)
                .build();

        HandlerThread thread = new HandlerThread("MyBackgroundThread");
        thread.start();
        handler = new Handler(thread.getLooper());
        handler.post(connectOffTheMainThread); // Use whatever threading mechanism you want
    }

    private final Runnable connectOffTheMainThread = new Runnable() {
        @Override
        public void run() {
            communicator.connect();

            handler.post(sendMqttMessage);
        }
    };



    private final Runnable sendMqttMessage = new Runnable() {
        private int i;

        /**
         * We post 100 messages as an example, 1 a second
         */
        @Override
        public void run() {
            if (i == 100) {
                return;
            }

            SensorEvent event = null;

            // events is the default topic for MQTT communication
            String subtopic = "events";
            // Your message you want to send
            String message = "Hello World " + i++;
            communicator.publishMessage(subtopic, message);



            handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
        }
    };

    @Override
    protected void onDestroy() {
        communicator.disconnect();
        super.onDestroy();
    }
}

更新:

我在 IotCoreCommunicator 类中的 publishMessage() 方法上添加了第三个输入(int 加速),如下所示:

 public void publishMessage(String subtopic, String message, int acceleration) {
        String topic = "/devices/" + deviceId + "/" + subtopic;
        String payload = "{msg:\"" + message + "\"}";
        MqttMessage mqttMessage = new MqttMessage(payload.getBytes());
        mqttMessage.setQos(1);
        try {
            client.publish(topic, mqttMessage);
            Log.d("TUT", "IoT Core message published. To topic: " + topic);
        } catch (MqttException e) {
            throw new IllegalStateException(e);
        }
    }

然后我在 MainActivity 类中的 run() 方法上调用它,如下所示:

 public void run() {
            if (i == 100) {
                return;
            }

            SensorEvent event = null;

            // events is the default topic for MQTT communication
            String subtopic = "events";
            // Your message you want to send
            String message = "Hello World " + i++;
            int acceleration = mAccelerometerActivity.onSensorChanged(SensorEvent event.values);
            communicator.publishMessage(subtopic, message, acceleration);



            handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
        }
    };

但我在下面的屏幕截图中仍然有这个错误:(';' or )预期)

在此处输入图像描述

此外,我添加的第三个输入显示为从未使用过,如下面的屏幕截图所示,这有什么影响吗?

在此处输入图像描述

谢谢你。

4

0 回答 0