TinyCircuits Forum

TinyCircuits Products => TinyDuino Processors & TinyShields => Topic started by: Fatwhale on January 29, 2014, 08:48:07 PM

Title: Bluetooth Tutorial
Post by: Fatwhale on January 29, 2014, 08:48:07 PM
Hey guys.
I could seriously need a tutorial on how to send data from the tinyduino to a processing sketch via the ble module.
I tried copying the code to my board and my BT4.0 dongle could find the device. I connected to it, but that's it. Sometimes the board led blinks twice, meaning it's connected, sometimes not. I have not found a way of definitely connecting it.

Troubleshooting is pretty hard for me, because I do not understand the code. I tried looking up some documentation, but I cannot see how this is working. I took a look at the Bluegiga API documentation but that didn't help.

My problem for starters is the boot response function:

Code: [Select]
void my_ble_evt_system_boot(const ble_msg_system_boot_evt_t *msg) {
    #ifdef DEBUG
        Serial.print("###\tsystem_boot: { ");
        Serial.print("major: "); Serial.print(msg -> major, HEX);
        Serial.print(", minor: "); Serial.print(msg -> minor, HEX);
        Serial.print(", patch: "); Serial.print(msg -> patch, HEX);
        Serial.print(", build: "); Serial.print(msg -> build, HEX);
        Serial.print(", ll_version: "); Serial.print(msg -> ll_version, HEX);
        Serial.print(", protocol_version: "); Serial.print(msg -> protocol_version, HEX);
        Serial.print(", hw: "); Serial.print(msg -> hw, HEX);
        Serial.println(" }");
    #endif

    // set advertisement interval to 200-300ms, use all advertisement channels
    // (note min/max parameters are in units of 625 uSec)
    ble112.ble_cmd_gap_set_adv_parameters(320, 480, 7);
    while (ble112.checkActivity(1000));

    // USE THE FOLLOWING TO LET THE BLE STACK HANDLE YOUR ADVERTISEMENT PACKETS
    // ========================================================================
    // start advertising general discoverable / undirected connectable
    //ble112.ble_cmd_gap_set_mode(BGLIB_GAP_GENERAL_DISCOVERABLE, BGLIB_GAP_UNDIRECTED_CONNECTABLE);
    //while (ble112.checkActivity(1000));

    // USE THE FOLLOWING TO HANDLE YOUR OWN CUSTOM ADVERTISEMENT PACKETS
    // =================================================================
#if 1
    // build custom advertisement data
    // default BLE stack value: 0201061107 e4ba94c3c9b7cdb09b487a438ae55a19
    uint8 adv_data[] = {
        0x02, // field length
        BGLIB_GAP_AD_TYPE_FLAGS, // field type (0x01)
        BGLIB_GAP_AD_FLAG_GENERAL_DISCOVERABLE | BGLIB_GAP_AD_FLAG_BREDR_NOT_SUPPORTED, // data (0x02 | 0x04 = 0x06)
        0x11, // field length
        BGLIB_GAP_AD_TYPE_SERVICES_128BIT_ALL, // field type (0x07)
        0xe4, 0xba, 0x94, 0xc3, 0xc9, 0xb7, 0xcd, 0xb0, 0x9b, 0x48, 0x7a, 0x43, 0x8a, 0xe5, 0x5a, 0x19
    };

    // set custom advertisement data
    ble112.ble_cmd_gap_set_adv_data(0, 0x15, adv_data);
    while (ble112.checkActivity(1000));

    // build custom scan response data (i.e. the Device Name value)
    // default BLE stack value: 140942474c69622055314131502033382e344e4657
    uint8 sr_data[] = {
        0x14, // field length
        BGLIB_GAP_AD_TYPE_LOCALNAME_COMPLETE, // field type
        'M', 'y', ' ', 'A', 'r', 'd', 'u', 'i', 'n', 'o', ' ', '0', '0', ':', '0', '0', ':', '0', '0'
    };

    // get BLE MAC address
    ble112.ble_cmd_system_address_get();
    while (ble112.checkActivity(1000));
    BGAPI_GET_RESPONSE(r0, ble_msg_system_address_get_rsp_t);

    // assign last three bytes of MAC address to ad packet friendly name (instead of 00:00:00 above)
    sr_data[13] = (r0 -> address.addr[2] / 0x10) + 48 + ((r0 -> address.addr[2] / 0x10) / 10 * 7); // MAC byte 4 10's digit
    sr_data[14] = (r0 -> address.addr[2] & 0xF)  + 48 + ((r0 -> address.addr[2] & 0xF ) / 10 * 7); // MAC byte 4 1's digit
    sr_data[16] = (r0 -> address.addr[1] / 0x10) + 48 + ((r0 -> address.addr[1] / 0x10) / 10 * 7); // MAC byte 5 10's digit
    sr_data[17] = (r0 -> address.addr[1] & 0xF)  + 48 + ((r0 -> address.addr[1] & 0xF ) / 10 * 7); // MAC byte 5 1's digit
    sr_data[19] = (r0 -> address.addr[0] / 0x10) + 48 + ((r0 -> address.addr[0] / 0x10) / 10 * 7); // MAC byte 6 10's digit
    sr_data[20] = (r0 -> address.addr[0] & 0xF)  + 48 + ((r0 -> address.addr[0] & 0xF ) / 10 * 7); // MAC byte 6 1's digit

    // set custom scan response data (i.e. the Device Name value)
    ble112.ble_cmd_gap_set_adv_data(1, 0x15, sr_data);
    while (ble112.checkActivity(1000));

    // put module into discoverable/connectable mode (with user-defined advertisement data)
    ble112.ble_cmd_gap_set_mode(BGLIB_GAP_USER_DATA, BGLIB_GAP_UNDIRECTED_CONNECTABLE);
    while (ble112.checkActivity(1000));
#endif

    // set state to ADVERTISING
    ble_state = BLE_STATE_ADVERTISING;
}

If I uncomment the "// USE THE FOLLOWING TO LET THE BLE STACK HANDLE YOUR ADVERTISEMENT PACKETS" section and instead comment the preprocessor if, the whole sketch is not working. So I tried understanding what is happening inside the condition. The adv_data array is a complete mystery to me. The sr_data array is a bit clearer but I don't get why the field length attribute is 0x14. And the biggest mystery is still, why the sketch works after this.

I added a system hello response and it works. If I let the ble stack handle my advertisement packets this won't work. I get a timeout.

The next thing I can't figure out is the simplest thing. IF I am connected, how do I send data to my pc?

I am really desperate because I need this for a project at university and I didn't think this would this complicated. I am willing to learn, but I need some guidance since I can't find any on the internet. Please help me figure this out.

edit:
Oh and by the way: I have revision 2 of the bluetooth board.
Title: Re: Bluetooth Tutorial
Post by: Fatwhale on February 03, 2014, 01:48:40 PM
Ok, what I've got so far is this:

Code: [Select]
#include <SoftwareSerial.h>
#include "BGLib.h"

// BLE state machine definitions
#define BLE_STATE_STANDBY           0
#define BLE_STATE_SCANNING          1
#define BLE_STATE_ADVERTISING       2
#define BLE_STATE_CONNECTING        3
#define BLE_STATE_CONNECTED_MASTER  4
#define BLE_STATE_CONNECTED_SLAVE   5

#define GATT_HANDLE_C_RX_DATA   17
#define GATT_HANDLE_C_TX_DATA   20

uint8_t ble_state = BLE_STATE_STANDBY;
uint8_t connection = 0;

SoftwareSerial bleSerialPort(3, 4);
BGLib bt((HardwareSerial *)&bleSerialPort, 0, 1);

void setup() { 
  bt.onTimeout = onTimeout;
  bt.ble_rsp_attclient_attribute_write = bt_rsp_attclient_attribute_write;
  bt.ble_rsp_system_hello = bt_rsp_system_hello;
  bt.ble_evt_system_boot = bt_evt_system_boot;
  bt.ble_evt_connection_status = bt_evt_connection_status;
  bt.ble_rsp_gap_connect_direct = bt_rsp_gap_connect_direct;
  bt.ble_evt_connection_disconnected = bt_evt_connection_disconnect;
 
  Serial.begin(38400);
  bleSerialPort.begin(38400);
 
  bt_wait();
  bt_set_advertising_parameters();
  bt_set_advertising();
  ble_state = BLE_STATE_ADVERTISING;
}

void loop() {
  Serial.println("Operations Menu:");
  Serial.println("0) Reset BLE112 module");
  Serial.println("1) Say hello to the BLE112 and wait for response");
  Serial.println("Command?");
 
  uint8_t count = 0;
  uint8_t data[] = {0xff, 0x45};
  while(1) {
    bt.checkActivity(); 
   
    if (Serial.available()) {
      uint8_t ch = Serial.read();
      if (ch == '0') {
          Serial.println("Reboote system.");
          bt_restart();
      }
      if (ch == '1') {
          Serial.println("Hallo?");
          bt.ble_cmd_system_hello();
          bt_wait();
      }
    }
   
    if(ble_state == BLE_STATE_CONNECTED_SLAVE) {
      count++;
      if(count > 200) {
        count = 0;
        uint8 bytes = bleSerialPort.write(data, 2);
        Serial.print("Data send! Number of bytes: ");
        Serial.println(bytes);
        //bt.ble_cmd_attclient_attribute_write(connection, GATT_HANDLE_C_TX_DATA, 0x02, data);
        //bt_wait();
      }
    }
    delay(1);
  }
}

void bt_rsp_attclient_attribute_write(const struct ble_msg_attclient_attribute_write_rsp_t * msg) {
  Serial.print("###\tattribute written: { ");
  Serial.print("connection: "); Serial.print(msg -> connection, HEX);
  Serial.print(", result: "); Serial.print(msg -> result, HEX);
  Serial.println("}");
}

void bt_wait() {
  while (bt.checkActivity(1000));
}

void bt_restart() {
  bt.ble_cmd_gap_set_adv_parameters(320, 480, 7);
  bt_wait();
}

void bt_set_advertising() {
  bt.ble_cmd_gap_set_mode(BGLIB_GAP_GENERAL_DISCOVERABLE, BGLIB_GAP_UNDIRECTED_CONNECTABLE);
  bt_wait();
}

void bt_set_advertising_parameters() {
  bt.ble_cmd_gap_set_adv_parameters(320, 480, 7);
  bt_wait();
}

void bt_evt_system_boot(const ble_msg_system_boot_evt_t *msg) {
  Serial.print("###\tsystem_boot: { ");
  Serial.print("major: "); Serial.print(msg -> major, HEX);
  Serial.print(", minor: "); Serial.print(msg -> minor, HEX);
  Serial.print(", patch: "); Serial.print(msg -> patch, HEX);
  Serial.print(", build: "); Serial.print(msg -> build, HEX);
  Serial.print(", ll_version: "); Serial.print(msg -> ll_version, HEX);
  Serial.print(", protocol_version: "); Serial.print(msg -> protocol_version, HEX);
  Serial.print(", hw: "); Serial.print(msg -> hw, HEX);
  Serial.println(" }");
}

void bt_evt_connection_status(const ble_msg_connection_status_evt_t *msg) {
  Serial.print("###\tconnection_status: { ");
  Serial.print("connection: "); Serial.print(msg -> connection, HEX);
  Serial.print(", flags: "); Serial.print(msg -> flags, HEX);
  Serial.print(", address: ");
  // this is a "bd_addr" data type, which is a 6-byte uint8_t array
  for (uint8_t i = 0; i < 6; i++) {
      if (msg -> address.addr[i] < 16) Serial.write('0');
      Serial.print(msg -> address.addr[i], HEX);
  }
  Serial.print(", address_type: "); Serial.print(msg -> address_type, HEX);
  Serial.print(", conn_interval: "); Serial.print(msg -> conn_interval, HEX);
  Serial.print(", timeout: "); Serial.print(msg -> timeout, HEX);
  Serial.print(", latency: "); Serial.print(msg -> latency, HEX);
  Serial.print(", bonding: "); Serial.print(msg -> bonding, HEX);
  Serial.println(" }");
 
  if((msg->flags & 0x05) == 0x05) {
    connection = msg->connection;   
    if(ble_state == BLE_STATE_ADVERTISING) {
      ble_state = BLE_STATE_CONNECTED_SLAVE;
      Serial.println("Du bist jetzt verbunden!");
    }
  }
}

void bt_rsp_gap_connect_direct(const struct ble_msg_gap_connect_direct_rsp_t * msg) {
  Serial.print("###\tconnection_direct: { ");
  Serial.print("result: "); Serial.print(msg -> result, HEX);
  Serial.print(", connection: "); Serial.print(msg -> connection_handle, HEX);
  Serial.println("}");
 
  connection = msg->connection_handle;
}

void bt_evt_connection_disconnect(const struct ble_msg_connection_disconnected_evt_t *msg) {
  Serial.print("###\tconnection_disconnect: { ");
  Serial.print("connection: "); Serial.print(msg -> connection, HEX);
  Serial.print(", reason: "); Serial.print(msg -> reason, HEX);
  Serial.println(" }");
 
  bt_set_advertising();
  bt_wait();
 
  ble_state = BLE_STATE_ADVERTISING;
}

void bt_rsp_system_hello(const ble_msg_system_hello_rsp_t *msg) {
  Serial.println("Ja bitte?"); 
}

//####################################################
// Internal BGLib Class Callback Functions
//####################################################

void onTimeout() {
  Serial.println("Timeout! Starte system neu.");
  bt_restart();
}

But I still can't read anything in my processing sketch. In the loop section I get messages for sending data (also the number of send bytes is 2) so the connection seems to be established. In my windows bluetooth settings it also says that the device is connected.
BUT: I can not for the love of god figure out what the right way of sending data even is. At first I thought it's sending an attribute. But I don't really get what that even is. And it didn't work. (see the commented lines in the loop section)
So I thought, I'd just use the software serial port (bleSerialPort), but of course also nothing and I don't even know if I just use the wrong function or if there's another problem. In the ble_cmd_attclient_attribute_write function response it at least said, that everything went fine. (errorcode was 0)

The serial port I use on my windows machine was created in the bluetooth settings (see attachement. edit: actually no. Upload won't work. Well, I created a new "incoming" serial port. It's called COM 9). And my processing code looks like this (just a modified example code):
Code: [Select]
import processing.serial.*;

Serial myPort;
int val;

void setup()
{
  size(200, 200);
  String portName = Serial.list()[7]; //COM 9 is here, see above. Created in BT settings
  myPort = new Serial(this, portName, 38400);
}

void draw()
{
  if ( myPort.available() > 0) {
    val = myPort.read();
  }
  background(255);   
  if (val == 0) {             
    fill(0);                   
  }
  else {                     
    fill(204);                 
  }
  rect(50, 50, 100, 100);
}

I really need some help!
Title: Re: Bluetooth Tutorial
Post by: TinyCircuits on February 03, 2014, 09:29:10 PM
HI Fatwhale,

Sorry for the delay in getting back with you.  In order to communicate with the BLE device, you need an app that is also capable of communicating with the BLE module, this isn't the standard COM port that is used in Windows.  I haven't yet done this in Windows, however there are several examples how to do this with iPhones. 

Are you sure this is a BLE TinyShield and not just a standard Bluetooth TinyShield, which would explain why it was discovered with a COM Port number?  If you just need it to communicate with a Windows PC, I'd highly recommend just using the standard Bluetooth module, if needed we can swap this out for you.

Thanks,

Ken
TinyCircuits
Title: Re: Bluetooth Tutorial
Post by: Fatwhale on February 04, 2014, 11:57:02 AM
It is the BLE shield. I bought it because it's smaller. But thank you very much for the tipp about the extra program I probably need. I created the COM Port manually. That finally explains why it wasn't detected automatically. I guess this is a lead to the solution of my problem. Hopefully I'll get the up and running. If anybody happens to know about additional help I'd of course be very glad to see what you got.
Title: Re: Bluetooth Tutorial
Post by: Ariel on January 30, 2015, 07:57:03 PM
Hello ,
Try using the nRF Master Control app on Google Play to discover your device & talk to it. The BLE BGLib doesn't need any encryption/ pairing to send and receive data. I am using the GATT_HANDLE_C_TX_DATA handle  and the function call to command attribute write.
the following are the parameters to this function.
/* Function */
void ble_cmd_attclient_write_command(
 uint8 connection,
 uint16 atthandle,
 uint8 data_len,
 const uint8* data_data
);
my_data is just an array of size 10. Use the following in void loop()

uint8_t result = ble112.ble_cmd_attributes_write(GATT_HANDLE_C_TX_DATA,0, 10,my_data);
Hope this helps,
Regards,