Direct Bluetooth connection from App(No Particle Cloud)


#42

Hi @eric

Sorry, busy weekend.

Yes. I have a USB to serial converter. We actually manufacture one for XBee interface modules here:

It uses an FT232 FTDI USB to Serial converter which is the same chip on your adapter. I will give that a try and let you know. If it works do you think there would be a demand for one of these adapted so the Bluz module could plug in? If so we could through one together pretty easily. This would allow customers to flash firmware over USB/Serial rather than OTA easily.


#43

I definitely think there could be some interest. When we started this, before we really figured out what the final product would look like, we ran a survey to determine what features were necessary. One of the questions directly asked if people would like to program locally, either through an adapter like this or by having a USB connector on-board but with the understanding that it would raise the price, or if they would only like to program over the air. Over 2/3 of people, and it was actually closer to 75%, said they didn’t need to program locally. However, there are still at least 25% of users out there that would like it, so that is a substantial number.

As for what this board would look like, I think it would be beneficial to have more than just the adapter on board. The Particle programmer shield now uses an STMicro chip and OpenOCD so you can program to the bare metal. That may be a good thing to add. Also, it would be nice to have the adapter control the pins so it could force bluz into bootloader setup mode so the user doesn’t have to worry about pressing buttons.

And finally, my gut tells me that the RFDuino form factor would work better here. So instead of a female micro-USB, have a male type-A USB connecter so that it can plug directly into your PC’s USB port. So it would look more like this: http://www.rfduino.com/product/rfd22121-usb-shield-for-rfduino/index.html

That way people can power it easily and also don’t have to worry about carrying more cables around.

Anyway, that is my thoughts on the subject. Would love to hear others!


#44

Why in the heck did I never think to get out my Particle Programmer Shield DOH! Works great now!

Sometimes I just need a good kick in the head haha. Guess there is no since in re-inventing the wheel when this is already available.


#45

Ah, yes, that will work just fine for the serial portion!

However, it won’t work for the OpenOCD or JTAG portion. The pinouts for the Photon for JTAG programming are different than bluz since we use SWD. Still, it should solve your problem with programming bluz over serial.


#46

Hey @eric,

Well being able to flash firmware has certainly sped up my development :slight_smile: Also Serial1.println working helps as well.

For some reason though when my ios app connects to the module the user app on the module hangs for a while and then starts executing again. Also I am sending data from my ios app but am getting nothing on the module side. Is it normal for the user app in the module to hang when a connection is established?


#47

Yes, the user app will certainly be interrupted when connecting. All the system firmware for bluz is interrupt driven, and then the code gets called in the main context. So when the handshake starts, data gets handed down and the system firmware takes over to process it. Then the user app loop() function may get called a few times before more data gets handed down for processing and the system firmware takes over again.

So the user app will definitely be very intermittent when the connection is happening.

When you say you are sending data on the iOS side and not getting anything on the bluz side, how are you sending the data? And what is it trying to do on the bluz side? Could you share the code in the DataCallback function and also how you are sending data on the iOS side? You can DM it to me if you don’t want to publish it.


#48

Here are all files:

custom_data_service.h:

#ifndef _CUSTOM_DATA_SERVICE_H
#define	_CUSTOM_DATA_SERVICE_H

#ifdef __cplusplus

#include <stdint.h>
#include <stdlib.h>
#include "data_service.h"

class CustomDataService : public DataService
{
public:
    static CustomDataService* instance();

    //DataService functions
    virtual int32_t getServiceID();
    virtual int32_t DataCallback(uint8_t *data, int16_t length);
    int32_t availableBytes;

private:
    //this is a singleton class, so these all need to be private so they can't be called
    CustomDataService(){
    	availableBytes = 0;
    };
    CustomDataService(CustomDataService const&){
    	availableBytes = 0;
    };
    CustomDataService& operator=(CustomDataService const&);
    static CustomDataService* m_pInstance;
};

#endif
#endif	/* _CUSTOM_DATA_SERVICE_H */

custom_data_service.cpp(length of data is appended to availableBytes variable in DataCallBack):

#include <string.h>
#include "custom_data_service.h"
#include "data_management_layer.h"
#include "registered_data_services.h"
#include "deviceid_hal.h"

CustomDataService* CustomDataService::m_pInstance = NULL;

CustomDataService* CustomDataService::instance()
{
    if (!m_pInstance){   // Only allow one instance of class to be generated.
        m_pInstance = new CustomDataService;
    }
    return m_pInstance;

}

//DataService functions
int32_t CustomDataService::getServiceID()
{
    return INFO_DATA_SERVICE;
}
int32_t CustomDataService::DataCallback(uint8_t *data, int16_t length)
{
    switch (data[0]) {
        case CUSTOM_DATA_SERVICE:
        	availableBytes += length;
            uint8_t id[12];
            HAL_device_ID(id, 12);

            uint8_t rsp[13];
            rsp[0] = CUSTOM_DATA_SERVICE & 0xFF;
            memcpy(rsp+1, id, 12);
            unsigned char message[] = "hello";
            DataManagementLayer::sendData(5, message);
            break;
    }
    return 1;
}

application.cpp:

/* Includes ------------------------------------------------------------------*/
#include "application.h"

DataService dService;

int LED = D7;

/* This function is called once at start up ----------------------------------*/
void setup()
{
	Serial1.begin(115200);
	pinMode(LED,OUTPUT);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	digitalWrite(LED, HIGH);
	Serial.println("sdkf");
	Serial1.printf("availableBytes: %i \n",dService.availableBytes);
	digitalWrite(LED, LOW);
	delay(1000);
}

include for custom_data_service added to application.h

data_service.h:

#ifndef _DATA_SERVICE_H
#define	_DATA_SERVICE_H

#ifdef __cplusplus

#include <stdint.h>

class DataService
{
public:
    DataService();
    virtual int32_t getServiceID();
    virtual int32_t DataCallback(uint8_t *data, int16_t length);
    int32_t availableBytes;
};

#endif
#endif	/* _DATA_SERVICE_H */

data_service.cpp:

#include <data_service.h>

DataService::DataService() {
	availableBytes = 0;
}

int32_t DataService::getServiceID() { return 0; };

int32_t DataService::DataCallback(uint8_t *data, int16_t length) { return 0; };

This is the iOS code that is ran from the button press. This is after the module is connected and objects have been created for the Rx and Tx characteristics:

- (IBAction)doIt:(id)sender{
    NSLog(@"Sending data");
    NSData *data = [NSData dataWithBytes:(unsigned char[]){0x03, 0x05, 0x06} length:3];
    NSData *dataEnd = [NSData dataWithBytes:(unsigned char[]){0x03, 0x04} length:2];
    [self.bluzModule writeValue:data forCharacteristic:Tx type:CBCharacteristicWriteWithoutResponse];
    [self.bluzModule writeValue:dataEnd forCharacteristic:Tx type:CBCharacteristicWriteWithoutResponse];
    NSLog(@"Done");
}

All I get from the Serial1.println on my serial log is availableBytes: 0 repeatedly even after clicking the button in the app. Not sure what I’m missing. I must be misusing DataCallback I assume.


#49

What you are doing is actually correct. The issue lies in the application.cpp file. What you are attempting to do is update the totalBytes value in a singleton class, but then you are actually creating a new DataService object in your application.cpp file. What you need to do in application.cpp is poll CustomDataService::instance().availableBytes, that will give you access to the actual object that is being handed the data in the system firmware.


#50

Changes made here:

application.cpp

/* Includes ------------------------------------------------------------------*/
#include "application.h"

//DataService dService;

int LED = D7;

/* This function is called once at start up ----------------------------------*/
void setup()
{
	Serial1.begin(115200);
	pinMode(LED,OUTPUT);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	digitalWrite(LED, HIGH);
	Serial.println("sdkf");
	Serial1.printf("availableBytes: %i \n",CustomDataService::instance()->availableBytes);
	digitalWrite(LED, LOW);
	delay(1000);
}

Nothing yet though. It almost seems like I connect from my app, it hangs for approximately 42 seconds and then it seems the module force closes the connection and resumes my user app code. 42 seconds seems like a really long time to just hang on connection, does that seem right to you? Also still getting nothing but availableBytes: 0 in the log by referencing the static instance.


#51

In this code, you are switching to the data[0] (after the first byte has been stripped away) which is 0x05 in your case. You should either change your app to send 0x03 as the second byte, or change the code in bluz to match what you are sending down.


#52

I just put it completely outside the switch statement for test and still not getting anything.

int32_t CustomDataService::DataCallback(uint8_t *data, int16_t length)
{
	availableBytes += length;

    switch (data[0]) {
        case 5:

            uint8_t id[12];
            HAL_device_ID(id, 12);

            uint8_t rsp[13];
            rsp[0] = 3 & 0xFF;
            memcpy(rsp+1, id, 12);
            unsigned char message[] = "hello";
            DataManagementLayer::sendData(5, message);
            break;
    }
    return 1;
}

Something here is just not right. I’d like to do a direct log or something in that DataCallback to let me know it is actually firing. Do you have any ideas on that? I would like to try just Serial1.print but due to scope I cannot figure out how to bring spark_wiring_usartserial.h in. Flashing an LED would be fine to. Just something to assure me it is actually getting called because I have way to many moving parts and variables here. I need to start narrowing down potential issues.


#53

You can set the D7 pin HIGH. So in your setup, just make sure to do pinMode(D7, OUTPUT); and then in your callback function, do nrf_gpio_pin_set(30);

If the callback is getting called, then the LED on D7 will light up


#54

Hi @eric

Gave that a try but the D7 LED does not light up

application.cpp:

/* Includes ------------------------------------------------------------------*/
#include "application.h"

//DataService dService;

int LED = D7;

/* This function is called once at start up ----------------------------------*/
void setup()
{
	Serial1.begin(115200);
	pinMode(LED,OUTPUT);
	digitalWrite(LED, LOW);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	Serial1.print("availableBytes: ");
	Serial1.println(CustomDataService::instance()->availableBytes);
	delay(1000);
}

custom_data_service.cpp:

#include <string.h>
#include "custom_data_service.h"
#include "data_management_layer.h"
#include "registered_data_services.h"
#include "deviceid_hal.h"

CustomDataService* CustomDataService::m_pInstance = NULL;

CustomDataService* CustomDataService::instance()
{
    if (!m_pInstance){   // Only allow one instance of class to be generated.
        m_pInstance = new CustomDataService;
    }
    return m_pInstance;

}

//DataService functions
int32_t CustomDataService::getServiceID()
{
    return INFO_DATA_SERVICE;
}
int32_t CustomDataService::DataCallback(uint8_t *data, int16_t length)
{
	nrf_gpio_pin_set(30);
	availableBytes += length;

    switch (data[0]) {
        case 5:

            uint8_t id[12];
            HAL_device_ID(id, 12);

            uint8_t rsp[13];
            rsp[0] = 3 & 0xFF;
            memcpy(rsp+1, id, 12);
            unsigned char message[] = "hello";
            DataManagementLayer::sendData(5, message);
            break;
    }
    return 1;
}

Maybe it is on my iOS app side. I just do not know. Perhaps it is at the point here where I should just wait for someone else to put out a solution for this. The strange thing is my module boots up with a slow flashing Green led, then I launch the app, module freezes for about 40 seconds or so, I click the send button in the app during this time, then the module unfreezes and starts printing to the log again and at that time the LED flashes slow magenta, I try sending data at this time as well but nothing, I believe at this point the connection is dropped by the module.

At a loss and probably in over my head I guess.


#55

Don’t give up! I think you are really close. Things look correct on the bluz side, it may be something on the iOS side. I wonder if the TX characteristic is set properly? Are you getting that value from the didDiscoverCHaracteristics function?

If you want bluz to stop dropping the connection, you can comment out this line: https://github.com/bluzDK/bluzDK-firmware/blob/develop/hal/src/bluz/core_hal.c#L59

That way you won’t have to worry about bluz disconnecting after 30-40 seconds.


#56

Hey @eric

Here is my didDiscoverCharacteristics function:

Global variables from header:
#define bluzModuleUUID @“871E0223-38FF-77B1-ED41-9FB3AA142DB2”
#define bluzCharRxUUID @“871E0224-38FF-77B1-ED41-9FB3AA142DB2”
#define bluzCharTxUUID @"871E0225-38FF-77B1-ED41-9FB3AA142DB2"
CBCharacteristic *Tx;
CBCharacteristic *Rx;

// Invoked when you discover the characteristics of a specified service.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    NSLog(@"Discovered %lu characteristics for service", sizeof(service.characteristics));
    if([service.UUID isEqual:[CBUUID UUIDWithString:bluzModuleUUID]]){
        for(CBCharacteristic *aChar in service.characteristics){
            if([aChar.UUID isEqual:[CBUUID UUIDWithString:bluzCharRxUUID]]){
                NSLog(@"Got Com Rx Characteristic");
                RxReady = true;
                Rx = aChar;
            }
            if([aChar.UUID isEqual:[CBUUID UUIDWithString:bluzCharTxUUID]]){
                NSLog(@"Got Com Tx Characteristic");
                TxReady = true;
                Tx = aChar;
            }
        }
        if(RxReady && TxReady){
            NSLog(@"Lets do the damn thing");
            //Get rid of the spinner and label
            [indicator stopAnimating];
            [indicator setHidden:true];
            [scanningLabel setText:@"Device Connected!"];
            [doItButton setHidden:false];
        }
    }
}

I actually tried swapping the characteristics earlier to make sure I didnt have them backwards.


#57

Hey @eric
I commented out the line ble_disconnect(); in core_hal.c Is that correct? Because it is still force closing the connection after 40 seconds or so.

void HAL_Handle_Cloud_Disconnect(void)
{
//    ble_disconnect();
}

#58

When you flash firmware, are you flashing only the user part or the user part and system parts? As these changes are being made in the system part, you would need to do both.


#59

I have been using the python bootloader script to flash firmware to the controller using command:

python ~/GitHub/bootloader_scripts/update_fw.py -s /dev/tty.usbserial-0020121AB

When prompted for the file I enter:

/Users/travis/GitHub/bluzDK-firmware/build/target/user-part/platform-103-m/myapp.bin

My compiler is using command:

make APP=myapp PLATFORM=bluz PARTICLE_DEVELOP=1

It is building from directory:

${workspace_loc:/Bluz_Custom}

Not sure if this is flashing user part and system part or just user part.


#60

Ah, yes, that is the problem. This would only flash the user-part.

When you make changes in application.cpp, you only need to flash the user part of the firmware down. When you make changes anywhere else, you need to flash the system part as well.

So you should run this twice, once for the system part and once for the user part (as you are doing). The user part file will be:

/Users/travis/GitHub/bluzDK-firmware/build/target/system-part1/platform-103-m/system-part1.bin

Once you flash that down, I bet you will see it start to work. It will take longer to flash, it is a much larger file.


#61

Hey @eric,

That certainly seemed to make a difference. The connection is not being forced closed as far as I can tell now. However by application only prints to the log about once every 30 seconds or so and I still an not seeing that D7 LED turn on. I almost have to assume it is on my iOS app side. But that does all seem to be fairly straight forward.

If I were just sending some raw data for a test could I send
0x03, 0x05, 0x06
Then send to end:
0x03 0x04
?
This is what I am currently doing on the iOS app side. Does 0x03 need to be a separate send?

Also I am a bit confused on how data services knows which data service to send the data to. Do all the services receive the data and only 1 of them acts on it? Where does it check to see if the first byte is 0x03 and then if it is send it to my custom data service?