Pairing with BM78 module

This post continues the BM78 series (Initializing Microchip’s BM78 module, …) with the different paring modes and an example implementation.

Pairing modes

So first lets look at the different pairing modes. There are four of them:

typedef enum {
    BM78_PAIRING_PIN = 0x00,
    BM78_PAIRING_JUST_WORK = 0x01,
    BM78_PAIRING_PASSKEY = 0x02,
    BM78_PAIRING_USER_CONFIRM = 0x03
} BM78_PairingMode_t;

PIN Pairing Mode

Is a very well know, simple, somewhat secure pairing mode with a static PIN. A pin needs to be between 4-5 digits which makes it not particularly save. But for simple applications where interaction is not possible this method can be used.

This mode can be setup using the following command: BM78_CMD_WRITE_PAIRING_MODE_SETTING (0x0B):

SINC|LENH|LENL|COM |STOR|MODE|CHKS
0xAA 0x00 0x03 0x0B 0x01 0x00 0xYY - will be stored in EEPROM
0xAA 0x00 0x03 0x0B 0x00 0x00 0xYY - will not be store in EEPROM

The PIN number can be changed using BM78_CMD_WRITE_PIN_CODE (0x51) command:

SINC|LENH|LENL|COM |STOR|DIG1|DIG2|DIG3|DIG4|CHKS
0xAA 0x00 0x06 0x51 0x01 0x31 0x32 0x33 0x34 0xYY - pin: 1234

SINC|LENH|LENL|COM |STOR|DIG1|DIG2|DIG3|DIG4|DIG5|DIG6|CHKS
0xAA 0x00 0x08 0x51 0x01 0x31 0x32 0x33 0x34 0x35 0x36 0xYY - pin: 123456

If both commands are executed successfully you BM78 can be now paired using the desired PIN. To put the module into pairing mode you need to put it into BM78_STATUS_STANDBY_MODE (0x03) using BM78_STANDBY_MODE_ENTER (0x01). Assuming you are in the BM78_STATUS_IDLE_MODE (0x09):

SINC|LENH|LENL|COM |MODE|CHKS
0xAA 0x00 0x02 0x1C 0x01 0xYY

If you have a device paired already and don’t want to allow other devices to pair themselves but be connectable to already paired devices you need to put the BM78 module into BM78_STATUS_STANDBY_MODE (0x03) using BM78_STANDBY_MODE_ENTER_ONLY_TRUSTED (0x02):

SINC|LENH|LENL|COM |MODE|CHKS
0xAA 0x00 0x02 0x1C 0x02 0xYY

Just Works Pairing Method

This is the most unsecure but simple pairing mode. Whenever the BM78 module is put into BM78_STATUS_STANDBY_MODE (0x03) using BM78_STANDBY_MODE_ENTER (0x01) other bluetooth devices can be paired with it without any pin, passkey or user interaction. Use this mode with caution.

Passkey Pairing Method

This is the most secure available pairing method. It displays a random passkey on the connecting device and requires the user to repeat that passkey. The BM78 uses the following command to do that:

BM78_CMD_PASSKEY_ENTRY_RES (0x40)

SINC|LENH|LENL|COM |ACT |KEY |CHKS
0xAA 0x00 0x02 0x40 0x01 0x31 0xYY - enter digit '1'
0xAA 0x00 0x02 0x40 0x02 0x00 0xYY - last digit erased
0xAA 0x00 0x02 0x40 0x03 0x00 0xYY - passkey cleared
0xAA 0x00 0x02 0x40 0x04 0x00 0xYY - entry completed

User Confirm Pairing Method

Pairing request on a mobile phone

The user confirmation method is a very simple method relying of user’s feedback. It tries to make sure that at the time of the pairing the user has both devices under physical control. The pairing request will exchange a random passkey and display it on both devices. The user should check that the passkey matches on both devices and confirm or reject with a simple “yes” or “no” answer.

BM78’s pairing request

Implementation

The implementation can be found at https://github.com/kubovy/mclib/blob/master/components/bm78_pairing.c. Basically, the first two methods do not need any extra implementation. The last two, the passkey method and the user confirm method, have sample implementation realized in two function:

inline void BMP_bm78PairingEventHandler(BM78_Response_t response, uint8_t *data) {
    uint8_t i;
    switch (response.op_code) {
        case BM78_EVENT_PASSKEY_ENTRY_REQ:
            LCD_clear();
            LCD_setString("New Pairing Request ", 0, true);
            LCD_setString("    Code:           ", 1, true);
            LCD_setString("D) Delete   Clear (B", 2, true);
            LCD_setString("C) Confirm  Abort (A", 3, true);
            BMP_cancel();
            BMP_passkeyCodeIndex = 0;
            break;
        case BM78_EVENT_PAIRING_COMPLETE:
            LCD_clear();
            switch(response.PairingComplete_0x61.result) {
                case BM78_PAIRING_RESULT_COMPLETE:
                    LCD_setString(" New Device Paired  ", 1, true);
                    break;
                case BM78_PAIRING_RESULT_FAIL:
                    LCD_setString("  Pairing Failed!   ", 1, true);
                    break;
                case BM78_PAIRING_RESULT_TIMEOUT:
                    LCD_setString(" Pairing Timed Out! ", 1, true);
                    break;
                default:
                    break;
            }
            BMP_cancel();
            break;
        case BM78_EVENT_PASSKEY_DISPLAY_YES_NO_REQ:
            LCD_clear();
            LCD_setString("New Pairing Request ", 0, true);
            LCD_setString("    Code:           ", 1, false);
            for (i = 0; i < 6; i++) {
                LCD_replaceChar(response.PasskeyDisplayYesNoReq_0x62.passkey[i],
10 + i, 1, false); } LCD_displayLine(1); LCD_setString("C) Confirm Abort (A", 3, true); BMP_cancel(); BMP_waitingForPasskeyConfirmation = true; break; case BM78_EVENT_DISCONNECTION_COMPLETE: BMP_cancel(); break; default: break; } } void BMP_processKey(uint8_t key) { if (BMP_passkeyCodeIndex < 0xFF) { // Pairing Passkey if (key >= '0' && key <= '9') { // Digit BM78_execute(BM78_CMD_PASSKEY_ENTRY_RES, 2,
BM78_PAIRING_PASSKEY_DIGIT_ENTERED, key); LCD_replaceChar(key, (BMP_passkeyCodeIndex++) + 10, 1, true); } else if (key == 'B') { // Clear BM78_execute(BM78_CMD_PASSKEY_ENTRY_RES, 2,
BM78_PAIRING_PASSKEY_CLEARED, 0x00); LCD_replaceString(" ", 10, 1, true); BMP_passkeyCodeIndex = 0; } else if (key == 'C') { // Confirm BM78_execute(BM78_CMD_PASSKEY_ENTRY_RES, 2,
BM78_PAIRING_PASSKEY_ENTRY_COMPLETED, 0x00); LCD_clear(); BMP_passkeyCodeIndex = 0xFF; } else if (key == 'D') { // Delete BM78_execute(BM78_CMD_PASSKEY_ENTRY_RES, 2,
BM78_PAIRING_PASSKEY_DIGIT_ERASED, 0x00); LCD_replaceChar(' ', (--BMP_passkeyCodeIndex) + 10, 1, true); } } else if (BMP_waitingForPasskeyConfirmation) { switch(key) { // Pairing Confirmation case 'C': // Confirm (YES) LCD_clear(); LCD_setString(" Pairing Confirmed ", 1, true); BM78_execute(BM78_CMD_USER_CONFIRM_RES, 1,
BM78_PAIRING_USER_CONFIRM_YES); BMP_waitingForPasskeyConfirmation = false; break; case 'A': // Abort (NO) LCD_clear(); LCD_setString(" Pairing Aborted ", 1, true); BM78_execute(BM78_CMD_USER_CONFIRM_RES, 1,
BM78_PAIRING_USER_CONFIRM_NO); BMP_waitingForPasskeyConfirmation = false; break; } } }

Conclusion

I never got the Passkey Pairing method working properly. I guess there is something wrong with my BM78 module. But anyway it requires the user to be actually able to enter the digits, which is not the case in most of my projects. If the device you are building has a display the User Confirm method is ok. In smaller devices without the option of user interaction one of, Just Works method or PIN method, needs to be used. I will use mostly the PIN method.

I would recommend to allow pairing by default when no devices are paired. After that the MCU should disable it. Manual enablement of pairing additional devices or complete memory clear will be then needed before pairing another or different device.

Leave a Reply

Your email address will not be published. Required fields are marked *