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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions