To further explain, the following code in the serial-transport.js and command-definitions explains what is going on when packets are received from the bean via serial (see the last two lines):
-
During the serial transport service setup, the code registers a callback that is called when data is received:
setup(setupCallback) {
logger.info('Setting up SERIAL notifications')
this._setupNotification(UUID_CHAR_SERIAL_TRANSPORT, (err) => {
setupCallback(err)
this.registerForNotifications(UUID_CHAR_SERIAL_TRANSPORT, (data) => {
this._packetReceived(data)
})
})
}
Function that is called when data is received
This function parses the LightBlue Serial Transport Message for you and then passes the data and command ID and other info to another function.
_packetReceived(buf) {
let packet = LightBluePacket.fromBuffer(buf)
this._incomingPackets.push(packet)
logger.debug(`PACKET <<<: ${packet.toString()}`)
if (packet.finalPacket()) {
let packetPayloads = []
for (let p of this._incomingPackets) {
packetPayloads.push(p.getPayload())
}
let commandPayload = util.concatBuffers(packetPayloads)
let incomingMessageId = commandPayload.readUInt16BE(2) & ~commands.MESSAGE_RESPONSE_BIT
let incomingCommandDefn = commands.definitions().incoming[incomingMessageId]
let outgoingCommandDefn = commands.definitions().outgoing[incomingMessageId]
if (incomingCommandDefn) {
this._handleIncomingCommand(commandPayload, incomingCommandDefn)
} else if (outgoingCommandDefn) {
this._handleIncomingResponse(commandPayload, outgoingCommandDefn)
} else {
logger.warn(`Couldn't find definition for command: ${incomingMessageId}`)
}
this._incomingPackets = [] // Clear incoming packets
}
}
-
Function that is called for incoming commands/messages
_handleIncomingCommand(buf, defn) {
let command = commands.Command.fromBuffer(buf, defn)
if (command.getMessageId() === commands.commandIds.LB_PROTOCOL_ERROR) {
// Handle protocol errors here, nobody else should need to register for this error
let err = command.asObject()
logger.error(LB PROTOCOL ERROR: expected ${err.expected_header} got ${err.received_header}
)
} else {
// Notify any registered callbacks of received command
let commandCallback = this._commandCallbacks[command.getMessageId()]
if (commandCallback) {
commandCallback(command.asObject(command.getDefinition().arguments))
}
}
}
Note the last 3 lines above. If you have registered a callback for the SERIAL_DATA command, your callback will be triggered here.
-
API you use to register a callback in your code (you need to get the serialTransportService object and register the callback - my sample code shows an example):
registerForCommandNotification(commandId, callback) {
this._commandCallbacks[commandId] = callback
}
Example:
bean.lookupServices((err)=> {
// The bean is now ready to be used, you can either call the methods available
// on the `LightBlueDevice` class, or grab the individual services objects which
// provide their own API, for example: bean.getDeviceInformationService().
if (err) {
console.log(`Service lookup FAILED: ${err}`)
} else {
serialTransportService = bean.getSerialTransportService()
serialTransportService.registerForCommandNotification(serialTransport.commandIds.SERIAL_DATA, (commandObj)=> {
receivedBuffer = commandObj.data
logger.info(receivedBuffer)
receivedString = receivedBuffer.toString()
logger.info("Received string: " + receivedString + "; Received string length = " + receivedString.length)
})
- List of command IDs
The command IDs are listed in src/command-definitions.js
const commandIds = {
SERIAL_DATA: 0x0000,
LB_PROTOCOL_ERROR: 0x0001,
BT_SET_ADV: 0x0500,
BT_SET_CONN: 0x0502,
BT_SET_LOCAL_NAME: 0x0504,
BT_SET_PIN: 0x0506,
BT_SET_TX_PWR: 0x0508,
BT_GET_CONFIG: 0x0510,
BT_SET_CONFIG: 0x0511,
BT_SET_CONFIG_NOSAVE: 0x0540,
BT_END_GATE: 0x0550,
BT_ADV_ONOFF: 0x0512,
BT_SET_SCRATCH: 0x0514,
BT_GET_SCRATCH: 0x0515,
BT_RESTART: 0x0520,
BL_CMD_START: 0x1000,
BL_FW_BLOCK: 0x1001,
BL_STATUS: 0x1002,
BL_GET_META: 0x1003,
CC_LED_WRITE: 0x2000,
CC_LED_WRITE_ALL: 0x2001,
CC_LED_READ_ALL: 0x2002,
CC_ACCEL_READ: 0x2010,
CC_TEMP_READ: 0x2011,
CC_BATT_READ: 0x2015,
CC_POWER_ARDUINO: 0x2020,
CC_GET_AR_POWER: 0x2021,
CC_ACCEL_GET_RANGE: 0x2030,
CC_ACCEL_SET_RANGE: 0x2035,
AR_SLEEP: 0x3000,
ERROR_CC: 0x4000,
DB_LOOPBACK: 0xFE00,
DB_COUNTER: 0xFE01,
DB_E2E_LOOPBACK: 0xFE02,
DB_PTM: 0xFE03
}
For receiving serial data you need to use the SERIAL_DATA command ID
-
The serial-transport.js exports the command ids for you
module.exports = {
SerialTransportService: SerialTransportService,
UUID: UUID_SERVICE_SERIAL_TRANSPORT,
characteristics: {
UUID_CHAR_SERIAL_TRANSPORT: UUID_CHAR_SERIAL_TRANSPORT
},
commandIds: commands.commandIds,
}