Direct Bluetooth connection from App(No Particle Cloud)


#102

@eric, I see this when I discover the device with an app

service Uuid:871e0223-38ff-77b1-ed41-9fb3aa142db2
 characteristic Uuid:871e0224-38ff-77b1-ed41-9fb3aa142db2
   descriptor Uuid:2902
 characteristic Uuid:871e0225-38ff-77b1-ed41-9fb3aa142db2

So, to communicate with your custom_data_service.cpp I would send 0x04 data 0x03 0x04 to which characteristic?

Looking at that code it seems that the incoming data is not actually used. Did I miss something?


#103

Yup, the values are describe in our app here: https://github.com/bluzDK/iOS-app/blob/master/bluz-iOS-app/Libraries/BLEManager.swift#L13

So 871E0225-38FF-77B1-ED41-9FB3AA142DB2 is the characteristic you write to and 871E0224-38FF-77B1-ED41-9FB3AA142DB2is the characteristic you read from.

I am not sure I understand when you say the incoming data is not used?


#104

Thx for that @eric I missed that you had posted the IoS app code.
My comment about the incoming data not used referred to the code in custom_data_service.cpp. Not a problem as I need to write my own anyway.


#105

Ah, yes, that is correct. The example bluz app doesn’t use the data, but that is only because we were just showing the basic functionality.

Let me know how it goes


#106

ok fine. had some time this morning and got transmission to device working. I found I had to pad data to use whole 20 bytes before sending the 03 04. Is there a substitute for wiring’s println() to assist with debugging the device application code?

I guess that the part shield would provide allow viewing of println etc?

Unfortunately we seem to be back to where I could not flash any code to the device. Connected to gateway (IoS app). flashing white. No events seen on cli. Code flash times out.

Tried factory reset again. This gets into safe mode then says

{"name":"spark/safe-mode-updater/updating","data":"1","ttl":"60","published_at":"2016-05-11T10:46:30.621Z","coreid":"particle-internal"}
{"name":"spark/flash/status","data":"started ","ttl":"60","published_at":"2016-05-11T10:46:31.525Z","coreid":"b1e243bb...

I waited a while to see if anything happened but nothing did (unless I disconnected it). Led is solid magenta (not flashing). Eventually the led turns off.

After 10 mins I get

{"name":"spark/flash/status","data":"failed ","ttl":"60","published_at":"2016-05-11T11:23:26.593Z","coreid":"b1e243bb..

(Any attempts to flash code time out.)


#107

I think the example online includes the line SYSTEM_MODE(MANUAL). This would mean that bluz never connects to the cloud and hence isn’t available for firmware updates. If you wanted to use direct connection and cloud at the same time, you can remove that line, just note that you won’t be able to send data to bluz while it is attempting to connect to the cloud. Your app would also need to provide the ability to connect the cloud.

This is why we left this feature undocumented for the moment, writing a custom app without providing the cloud connection means bluz can’t connect to the cloud and hence cannot get updates.

You could leave a way to exit manual mode. For example, if a pin goes low then you could call SYSTEM_MODE(AUTOMATIC) and then it would exit manual mode and you could connect it to the normal bluz app to update code.

As for recovery, yes, a factory reset is probably the only way. If the LED was stuck in magenta, this means the update probably got stuck. I am trying to get to the bottom of this as I am hearing reports of the similar behavior. Are you using android or iOS when it get stuck?

To get out of factory reset you can get the device online and then flash an app to it. The update would start as you indicated. If it gets stuck, as it seems to for you, can you try again? The reports I have heard are intermittent, so it should work if you try again. Sorry for the issue, I am trying to dig into this and figure out the problem, but you are doing all the right things.

Let me know how it goes.


#108

Thx eric,

At the moment I’m using IoS gateway app to connect to cloud.
I tried another factory reset. This time I sent a sketch without the offending SYSTEM_MODE and we are back in business.

The method you suggest with the pin to control SYSTEM_MODE sounds sensible. Should that be called in setup() like this?

if (digitalRead(D1) == HIGH) {
  SYSTEM_MODE(AUTOMATIC);
} else {
  SYSTEM_MODE(MANUAL);
}

#109

That code looks good. You could do it in setup, yes. Then when you boot, just pull the pin HIGH and it should boot as before and you can connect to it from the bluz app and update code. Just make sure you set the pinMode with a pulldown so the pin isn’t floating when that code gets hit.

Or you could use a pull-up, meaning it would by default boot in Automatic mode and you would have to opt-in to Manual mode. Up to you


#110

Yes. I do have a pullup on D1 so it defaults OK.

However, once again, even with the original sketches, I am failing to flash them.
I go though the factory reset, get to the flashing magenta, connect to cloud and try to flash something. Seems to fail every time.


#111

Is the SET_MODE(AUTOMATIC) code working? That should eliminate the need for a factory reset.

Does the flashing magenta get stuck in LED on or off state? What is failing about it?


#112

OK. So first of all I have been getting inconsistent results, probably because of not waiting long enough after a factory reset to try the direct sketch again. Each time you load a sketch the gateway app disconnects and failure to reconnect messes things up.

I have been using the blink sketch to check that reset worked ok and that I can flash the direct sketch.

The direct sketch is currently this:

#include "application.h"
SYSTEM_MODE(AUTOMATIC);
bool sendResponse = false;
int led1 = D0;
int  modex = D1;
void dataCallbackHandler(uint8_t *data, uint16_t length) {
    if (data[1] == 0x6e)  // n: position?  encoding?
      digitalWrite(led1, HIGH);
    else
      digitalWrite(led1, LOW);
    sendResponse = true;
}

void setup() {
    pinMode(modex, INPUT);
    if (digitalRead(modex) == LOW) {
        SYSTEM_MODE(MANUAL);
    }
    pinMode(led1, OUTPUT);
    digitalWrite(led1, LOW);
    BLE.registerDataCallback(dataCallbackHandler);
}

void loop() {
    System.sleep(SLEEP_MODE_CPU);
    if (sendResponse) {
        uint8_t rsp[2];
        rsp[0] = 0xAA;
        rsp[1] = 0xBB;
        BLE.sendData(rsp, 2);
        sendResponse = false;
    }
}

When I put this in just now it seemed to be OK. and I also managed to go back and forth between the two sketches. Sigh of relief.

I have also been trying to get Serial1.printf() working so I can discover what the data looks like when received from the app I’m working on. So far I have not succeeded with that (testing it on the blink sketch); possible cable issue there, although I’ve tried 2 of them. Serial1 debug not working

If I send “on” and “off” from the app I would expect “data” in dataCallbackHandler() to contain a series of bytes so that (crudely) one could look for an ‘n’ to determine which of two commands has been received. The first way I tried did not work but I am wondering if the underlying mechanism includes the 04x byte or strips it off? Also, does the length parameter give the number of bytes received, despite the fact that the sender may have added padding?


#113

The data that goes into the dataCallbackHandler function would not have the 0x04 on the front. The header gets stripped away when the data is routed.

You mentioned padding but isn’t necessary from the bluz side. In other words, if you wanted to send the word ‘no’ in ASCII, you would first send this [0x04, 0x6e, 0x6f] and have that transmit as one data packet. Then you would transmit [0x03, 0x04] as the end of stream indicator.

Is the padding required from the app side? Does the API you are using try to merge the data together into one packet underneath?


#114

thx @eric
I added the padding to be sure that it would send 2 packets. If the data is “on” then I am expecting data[1] == 0x6e so my code is right. I guess length == 2 would also be true unless the padding is included. I’ll see if I can get a sniffer on it to find out which end is causing the problem.


#115

Yes, the code should be correct on bluz to check that. Bluz doesn’t really know about padding, so that would be included in the length. We don’t pad data in any of our gateway solutions, we simply send down the data as-is and the BLE stock just transmits, I am not sure how Windows APIs work.


#116

OK. Now I have Serial1.print() working I can see that the sketch is receiving a string of 19 bytes of zero. Now I’m returning to my app to see what could be causing that, given that I wrote it to send “on” then “off”. If I can’t see the problem there I still have the option of trying to sniff the bytes over the air.

Latest update. I can get rid of the padding and the framing still works. I’m now sending a single byte of data 0x6e then 0x6f. However, the received data byte is always zero.

here’s the code:

#include "application.h"
SYSTEM_MODE(AUTOMATIC);
bool sendResponse = false;
int led1 = D0;
int led2 = D7;
int modex = D1;
uint16_t sav_length;
uint8_t sav_data[20];
// should return the bytes received without the 0x04 header
void dataCallbackHandler(uint8_t *data, uint16_t length) {
    // cannot print here so save it
    sav_length=length;
    // for (int i=0; i<length; i++) sav_data[i] = data[i]; <<-- wrong
    for (int i=0; i<length; i++) sav_data[i] = *data++;
    sendResponse = true;
}

void setup() {
    Serial1.begin(38400);
    delay(1000);
    Serial1.print("Test-direct [1] mode: ");
    pinMode(modex, INPUT);
    if (digitalRead(modex) == LOW) {
        SYSTEM_MODE(MANUAL);
        Serial1.println("manual");
    } else
        Serial1.println("auto");
    pinMode(led1, OUTPUT);
    digitalWrite(led1, LOW);
    BLE.registerDataCallback(dataCallbackHandler);
}

void loop() {
    System.sleep(SLEEP_MODE_CPU);
    if (sendResponse) {
        uint8_t rsp[2];
        rsp[0] = 0xAA;
        rsp[1] = 0xBB;
        BLE.sendData(rsp, 2);
        Serial1.print(sav_length);
        Serial1.print(":");
        for (int i=0; i<sav_length; i++) {
            Serial1.print(sav_data[i],HEX);
            Serial1.print(' ');
        }
        Serial1.println("response sent");
        sendResponse = false;
    }
}

result:

Test-direct [1] mode: manual
1:0 response sent
1:0 response sent

Then: Connected with an app called BLE Scanner (from BluePixel Technologies). I get the same result when sending 04,6e the 03,04.

Latest: the Eureka moment: Saw a piece of bad code above and fixed it now:

1:6E response sent
1:6F response sent

Todo:

  1. Test sending data to app
  2. tidy up app and publish

#117

Great! Yes, I just tried your code above and saw the following output:

Test-direct [1] mode: auto
2:6E 6F response sent

So I must have copied it right after you got it fixed? The difference is, it looks like maybe the data went down as one packet on your side and two on yours? Not sure exactly. I did this with the Android app, I just modified it to send [0x04, 0x6e, 0x6f] 20 seconds after connection.


#118

Thx @eric,

I was sending a total of 4 packets and you sent two. In both cases it worked as intended.

Now moving on to sending data from Bluz, I want to sample a sensor at a fixed interval and send the data to the app. For sending I guess I can just use BLE.sendData(). However, to produce an interval of, say, 20 seconds I’d like to set a timer and get a callback rather than use delay() in the loop() section. That would probably disrupt incoming data and mess up the power management. Is there a proper way to do timers in the Bluz context?

And one more thing: when the app exits the bluz keeps on sending data. I thought this would be easily solved with if (Particle.connected()). Not so. In my case this always returns true. If the app exists cleanly it can send a command to stop transmission. However, the bluz should stop sending if the connection is broken in some other way.


#119

We will have Timers implemented in the next release, thanks to some awesome work by @gruvin!

In the meantime, the easiest way is to use a check. So this code will do something every two seconds:

 #define TIME_BETWEEN_SENDS 2000
 int timeLastSent = 0;
 void setup() {
 }
 void loop() {
   System.sleep(SLEEP_MODE_CPU);
   if (Particle.connected() && millis() - timeLastSent > 2000) {
     //do something
     timeLastSent = millis();
   }
 }

This will let you keep track of the time while maintaining sleep mode as much as possible.

As for the state, if you are setting mode to MANUAL, then checking if the Particle cloud is connected may not be what you want. You probably want the BLE state, which you can get here: http://docs.bluz.io/reference/ble/#getstate

So if BLE state is not BLE_CONNECTED, then stop transmitting.


#120

Thx. @eric
Duh. I had forgotten that perfectly good solution. So here’s the working example code:

#include "application.h"
SYSTEM_MODE(AUTOMATIC);
bool sendResponse = false;
bool notify = false;
int led1 = D0;  // just for demo
int led2 = D7;  // shows notifications on
int modex = D1; // high to enable flashing; low to run
int volts = A0; // connect to sensor
int rate = 10;  // send interval (seconds)
long timeLastSent;
bool debug = false;
uint16_t sav_length;
uint8_t sav_data[20];
uint8_t payload[20];
// should return the bytes received without the 0x04 header
void dataCallbackHandler(uint8_t *data, uint16_t length) {
    // cannot procass here so just save
    sav_length=length;
    for (int i=0; i<length; i++) sav_data[i] = *data++;
    sendResponse = true;
}

float readSensor() {
    int raw;
    raw = analogRead(volts);
    raw = random(1024); // simulated sensor
    float val = (float) raw/1024 * 3.3;  // assumed aref voltage
    return(val);
}

void setup() {
    Serial1.begin(38400);
    delay(1000);
    Serial1.print("Test-direct [v3] mode: ");
    pinMode(modex, INPUT);
    if (digitalRead(modex) == LOW) {
        SYSTEM_MODE(MANUAL);
        Serial1.println("manual");
    } else
        Serial1.println("auto");
    pinMode(led1, OUTPUT);
    digitalWrite(led1, LOW);
    pinMode(led2, OUTPUT);
    digitalWrite(led2, LOW);
    BLE.registerDataCallback(dataCallbackHandler);
}

void loop() {
    System.sleep(SLEEP_MODE_CPU);
    if (notify && millis() - timeLastSent > rate * 1000) {
        //if (Particle.connected()) {  // seems to be true always
            float val=readSensor();
            // convert to bytes
            int intpart=(int) val;
            int fractpart=(val-intpart)*100;
            payload[0] = intpart;
            payload[1] = fractpart;
            int len=2;
            if (debug) {
                for (int i=0; i<len; i++) {
                    Serial1.print(payload[i],HEX);
                    Serial1.print(' ');
                }
            }
            Serial1.print("sent: ");
            Serial1.println(val);
            BLE.sendData(payload, len);
        //}
        timeLastSent = millis();
    }
    if (sendResponse) {
        // acknowlege incoming message
        uint8_t rsp[2];
        rsp[0] = 0xAA;
        rsp[1] = 0xBB;
        BLE.sendData(rsp, 2);
        if (debug) {
            Serial1.print(sav_length);
            Serial1.print(":");
            for (int i=0; i<sav_length; i++) {
                Serial1.print(sav_data[i],HEX);
                Serial1.print(' ');
            }
        }
        // 1st byte of recd message is command
        switch (sav_data[0]) {
            case 0x61: // led on
                Serial1.println("led on");
                digitalWrite(led1, HIGH);
                 break;        
            case 0x60: // led off
                Serial1.println("led off");
                digitalWrite(led1, LOW);
                break;
            case 0x51: // notification on
                Serial1.println("enable notification");
                digitalWrite(led2, HIGH);
                timeLastSent = millis();
                notify = true;
                break;
            case 0x50: // notification off
                Serial1.println("disable notification");
                digitalWrite(led2, LOW);
                notify = false;
                break;
        }
        if (debug) Serial1.println("response sent");
        sendResponse = false;
    }
}

Todo: make stand-alone version of cordova app and publish (currently part of complex test app).


#121

Returning here 2 months later I think the BLE library may have changed or maybe I need an extra include. On compile I get:

test-direct.cpp:45:9: error: 'class BLEClass' has no member named 'registerDataCallback'

The doc http://docs.bluz.io/reference/ble/ still says this is the right call.

The code exactly as above. Similar error if I use the web IDE or CLI.