Skip to content

How to send messages from MyGeotab to External device #5

@spehj

Description

@spehj

I'd like to send messages from MyGeotab Runner to the external Android device connected to the Geotab GO device with the USB cable.

I use this API call with the Runner:

api.call("Add", {
    "typeName": "TextMessage",
    "entity": {
        "device": {"id":"b2"}, // Replace with device ID that should receive the data
        "messageContent": {
            "contentType": "MimeContent",
            "channelNumber": 1,
            "mimeType": "text", // Can be changed to any free format text value
            "binaryDataPacketDelay": "00:00:03.0000000", // Applies a configurable delay of up to 5 seconds in between each sequenced message of a multimessage MIME payload
            "data": "SGVsbG8gV29ybGQ=" // Replace with your data encoded in base64
        },
    "isDirectionToVehicle": true,
    "messageSize": 235 // If unspecified defaults to 235. Max of 1000.
    },
}, function(result) {
    console.log("Done: ", result);
}, function(e) {
    console.error("Failed:", e);
});

I'm using your example code, where I only changed the Device ID to a number between 4200 and 4299 based on your documentation.

My ThirdParty.java code:

public class ThirdParty {
private static final int DEVICE_ID = 4208; // Used in methods sendHandshakeConfirmation and BuildHandshakeMessage

// Some other code ...

 // State machine to handle the third party protocol - See case SEND_CONFIRMATION
    private class StateMachine implements Runnable {
        private State eState = State.SEND_SYNC;
        private AtomicBoolean fRunning = new AtomicBoolean(true);

        public void run() {
            Log.i(TAG, "Third party SM started");

            while (fRunning.get()) {
                mLock.lock();        // The lock is needed for await and atomic access to flags/buffers

                try {
                    notifyStateChanged(eState);
                    switch (eState) {
                        case SEND_SYNC: {
                            byte[] abMessage = new byte[]{MESSAGE_SYNC};
                            mAccessoryControl.write(abMessage);
                            eState = State.WAIT_FOR_HANDSHAKE;
                            Log.d("THIRD_PARTY", "SEND_SYNC");
                            break;
                        }
                        case WAIT_FOR_HANDSHAKE: {
                            // Waits for the handshake message or resends sync every 1s
                            mEvent.await(1000, TimeUnit.MILLISECONDS);

                            if (mfHandshakeReceived) {
                                eState = State.SEND_CONFIRMATION;
                            } else {
                                eState = State.SEND_SYNC;
                            }
                            break;
                        }
                        case SEND_CONFIRMATION: {
                            sendHandshakeConfirmation(); // Added this (see below)
                            // Could also use this without flags:
                            // byte[] abMessage = BuildHandshakeMessage();
                            // mAccessoryControl.write(abMessage);
                            eState = State.PRE_IDLE;
                            break;
                        }
                        case PRE_IDLE: {
                            mfHandshakeReceived = false;
                            mfAckReceived = false;
                            mfMessageToSend = false;
                            eState = State.IDLE;
                            break;
                        }
                        case IDLE: {
                            // Sleep and wait for a handshake or a message to send
                            mEvent.await();

                            if (mfHandshakeReceived) {
                                eState = State.SEND_CONFIRMATION;
                            } else if (mfMessageToSend) {
                                mAccessoryControl.write(mabMessage);
                                eState = State.WAIT_FOR_ACK;
                            }
                            break;
                        }
                        case WAIT_FOR_ACK: {
                            // Wait for the ack or reset after 5s
                            mEvent.await(5000, TimeUnit.MILLISECONDS);

                            if (mfAckReceived) {
                                eState = State.PRE_IDLE;
                            } else {
                                eState = State.SEND_SYNC;
                            }
                            break;
                        }
                        default: {
                            eState = State.SEND_SYNC;
                            break;
                        }
                    }

                } catch (InterruptedException e) {
                    Log.w(TAG, "Exception during await", e);
                } finally {
                    mLock.unlock();
                }
            }
        }

        // Stop the thread
        public void close() {
            Log.i(TAG, "Shutting down third party SM");

            mLock.lock();
            try {
                fRunning.set(false);
                mfHandshakeReceived = false;
                mfAckReceived = false;
                mfMessageToSend = false;
                mEvent.signal();
            } finally {
                mLock.unlock();
            }
        }
    }

// Some other code ...


    
    // Wrote this to send a confirmation with the flags as stated in the documentation
    public void sendHandshakeConfirmation() {
        byte[] handshakeMessage = BuildHandshakeConfirmationMessage();
        Log.d(TAG, "Sending Handshake Confirmation: " + Arrays.toString(handshakeMessage));
        mAccessoryControl.write(handshakeMessage);
    }

    private byte[] BuildHandshakeConfirmationMessage() {
        byte[] abMessage = new byte[10];

        // Start of Text
        abMessage[0] = 0x02;

        // Message Type
        abMessage[1] = MESSAGE_CONFIRMATION;

        // Message Body Length
        abMessage[2] = 4;

        // External Device ID (little-endian)
        abMessage[3] = (byte) (DEVICE_ID & 0xFF);
        abMessage[4] = (byte) ((DEVICE_ID >> 8) & 0xFF);

        // Flags
        byte flags = 0x06; // Handshake ACK = 1, Binary data wrapping = 1, Self-powered = 0
        abMessage[5] = flags;

        // Checksum (Fletcher's checksum)
        byte[] abChecksum = CalcChecksum(abMessage, 6);
        abMessage[6] = abChecksum[0];
        abMessage[7] = abChecksum[1];

        // End of Text
        abMessage[8] = 0x03;

        return abMessage;
    }

// Some other code ...

// I also tried this method without the flags that uses existing BuildMessage method
// Assemble the handshake message with the device ID - could be called in 
    private byte[] BuildHandshakeMessage() {
        byte[] abDeviceId = new byte[]{
                (byte) (DEVICE_ID & 0xFF),
                (byte) ((DEVICE_ID >> 8) & 0xFF),
                0x00, 0x00 // Placeholder for any additional data
        };
        return BuildMessage(MESSAGE_CONFIRMATION, abDeviceId);
    }


}

My procedure:

  1. I press Run in the Geotab Runner, the message displayed in the console is Done:b54, Done:b55 (number increasing for each call).
  2. I watch for any raw bytes coming to my Android device inside the RxMessage method in ThirdParty.java, but nothing gets printed.

Example:

public void RxMessage(byte[] abData) {
        Log.d(TAG, "RxMessage: abData.length:" + abData.length + ", abData:" + Arrays.toString(abData));
 // Other code ....       
}

How to make this work? Am I missing something?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions