Arduino BLE- connected, no service

This script starts correctly. I see it in BLE scanners. I can connect.
The service UUID is reported, but the service is a “Hello World” which isn’t available.

LightBlue scanner says there’s one service, but only the UUID is shown.
nRF gives the UUID.

The serial print at line 19 does not happen. The only serial print is at line 39.

    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
    Ported to Arduino ESP32 by Evandro Copercini
    updates by chegewara
*/
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

void setup() {
  Serial.begin(9600);
  Serial.println("Starting BLE work!");

  BLEDevice::init("Monster Laboratories");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setValue("Hello World says Neil");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}

I assume something wasn’t declared properly.

I ultimately want to report sensor data, which can be seen at the basic connection level.

Next, I’ll make a central device and marry it to the sensor pod. I think that’s going to make a bulletin board/web server.
And then I’ll add more pods.

But this is starting out as a beacon.

1 Like

I can only comment on

The serial print at line 19 does not happen. The only serial print is at line 39.

as I havn’t done any Bluetooth/BLE stuff, but will be following your posts with interest as I do want to do some stuff with Arduino ESP32 & BLE in the near future, so if you can work all the kinks out for me that would be great! :laughing:

I suspect it is simply a matter of the ESP32 rebooting faster than the serial monitor connecting… if you added a delay(1000); or delay(2000); before Serial.being(9600); you’ll probably catch it. Or simply reset the ESP32 while the serial monitor is active.

I tend to start from the examples and work backwards… have you tried https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/examples/BLE_iBeacon/BLE_iBeacon.ino to see if it works? If so, you should be compare it to what you have, and figure out what is needed.

Edit: Ok, I see you are basing it from the examples - i.e. BLE_server.ino? Perhaps this is all it is supposed to do - show the difference between client and server BLE code? For beacon type stuff, maybe you do need to look at BLE_iBeacon.ino?

Awesome. That right there is the kind of stuff I’m looking for in particular.
I’ve been in the laboratory forever, so I know all the secrets there. This feels weird, being an amateur again. It would be cool but for the frustration. I’ve gotten really upset during this transition from Arduino, and I don’t like that at all. But it works now, and it didn’t.

This would be the price of jumping in without suffering the tedium of typing it out every day for four years in college.

I’m going to write a ‘how I did it’ for BLE telemetry.

The BLE Beacon script has some parts I’ve been looking for.

1 Like

But there’s an issue with it, and it’s something I’ve been trying hard to get my hands around. I don’t really understand “class”.

This is the error message:
‘class BLEAdvertising’ has no member named ‘setAdvertisementType’

I’m looking at where these things are in the text, and I can’t understand how they’re related or how one would be a member of another. I can probably figure out the membership thing.

class defines a template for how an object will be created, by the constructor, later in the code.

class flintstone {
...
};

Later you can declare variables of this class type:

flintstone Fred, Wilma, BamBam;

The class can declare a member function, for example, scream() and make it public:

class flintstone {
public:
    void scream() {
        // do something here...
    };
};

You can call it as follows:

flintstone Fred;

Fred.scream();

From the error you are getting, there is a class named BLEAdvertising which appears not ti have a member function called setAdvertisementType, however, that member function is being called in the code, somewhere.

HTH

Cheers,
Norm.

How do you add member functions?

And I’ve seen/read that stuff before. It just has no meaning for me.

Ok, lets take an Arduino pin. Bear in mind I’m typing this on a tablet, in bed! This is not tested but hopefully helps.

You would create the definition of a digitalPin class in, for example, digitalPin.h:

class digitalPin {
public:

// Constructor.
pin(uint8_t arduinoPin);

// Destructor.
~pin() {
    // Does nothing, not really required here.
};

void setMode(uint8_t mode);

void high();

void low():

void toggle():

private:
    volatile char *port;   // PORTB, C or D.
    volatile char *ddr;    // DDRB, C or D.
    uint8_t pinBit;
    // etc
};

So far so good, we have a “template” or blueprint for our pin object(s). There are private stuff, only accessible from inside the class, the public stuff is visible outside the class and can be called, accessed etc.

Take a look at the functions setMode(), high(), low() and toggle(), they are member functions. They are functions which belong to a class, the digitalPin class in this example.

The header file defines a digitalPin class, but there’s no code. How does high() for example work? The object’s implementation is usually in a separate file from the header. For example digitalPin.cpp might have:

   ...
// Turns an output pin high.
void digitalPin::high() {
    // We need the PORTx register and the 
    // correct bit in it.
    port |= (1 << pinBit);
}
...

The constructor would have taken an Arduino pin number, 13 for example, and converted it to a DDR, a PORT, a bit number and, for input pins, a PIN register. These would be saved in private member variables. For pin 13, those would be:

  • Port = PORTB;
  • Ddr = DDRB;
  • Pin = PINB;
  • Bit number = 5.

To turn a digital pin high, you set the correct bit in its Port register to 1. To make it low, you clear the bit. To make a pin toggle, you set the bit in its Pin register.

An input pin has its bit in the Ddr register clear, an output pin has it set, an input_pullup clears the bit and writes a 1 to the Port register. :grin:

So, a member function is usually defined in the class definition, in the header file; is implemented, usually, in the cpp file.

Given a proper working version of the digitalPin class, the blink sketch would look similar to this:

#include "digitalPin.h"

digitalPin blinky(LED_BUILTIN);

void setup(} {
    blinky.setMode(OUTPUT);
    blinky.high();
}

void loop() {
    delay(1000);
    blinky.toggle();
}

Even without the above, blink can be reduced to:

// Blink the builtin led.

void setup() {
    // pinMode(LED_BUILTIN, OUTPUT) ...
    DDRB |= (1 << DDB5);
}

void loop() {
    // Toggle LED_BUILTIN every second.
    PINB |= (1 << PINB5);
    delay(1000);
}

HTH

Cheers,
Norm.

You asked, yesterday:

I responded overnight with some stuff off the top of my head. Well, today I actually built the AVRPin class I talked about, so here is a small class and some explanations to hopefully make things a bit clearer.

Enjoy.

Here’s a proper working version of using member functions in a class. Hopefully I will explain matters clearly enough for you to understand - but ask away if not.

Here are the steps I went through to generate a small class to mimic an Arduino Pin.

# Create a directory:
mkdir -p ~/SourceCode/temp/PIO_AVRPin
cd ~/SourceCode/temp/PIO_AVRPin
pio init --board uno
cd include
vi AVRPin.h

The following code should be typed/copied and pasted into include/AVRPin.h:

#ifndef AVRPIN_H
#define AVRPIN_H

#ifndef ARDUINO
#define LOW 0
#define HIGH 1
#endif

#include <stdint.h>
#include <avr/io.h>

class AVRPin {
private:
    // Which is our pin's PORTx register?
    volatile uint8_t *portRegister;

    // Which is our pin's DDRx register?
    volatile uint8_t *ddrRegister;

    // Which is our pin's PINx register?
    volatile uint8_t *pinRegister;

    // Which bit in all registers is for our pin?
    uint8_t pinBit;
    
public:

    // To refer to a pin, we do something like 
    // AVRpin LED(AVRpin::AAVRPIN_D13, AVRpin::OUTPUT_PIN);
    typedef enum AVRpins : uint8_t {
        AVRPIN_D0 = 0,
        AVRPIN_D1,
        AVRPIN_D2,
        AVRPIN_D3,
        AVRPIN_D4,
        AVRPIN_D5,
        AVRPIN_D6,
        AVRPIN_D7,
        AVRPIN_D8,
        AVRPIN_D9,
        AVRPIN_D10,
        AVRPIN_D11,
        AVRPIN_D12,
        AVRPIN_D13,
        AVRPIN_D14,
        AVRPIN_D15,
        AVRPIN_D16,
        AVRPIN_D17,
        AVRPIN_D18,
        AVRPIN_D19,
        AVRPIN_A0 = AVRPIN_D14,
        AVRPIN_A1,
        AVRPIN_A2,
        AVRPIN_A3,
        AVRPIN_A4,
        AVRPIN_A5
    } AVRpin_t;

    // Pin modes.
    typedef enum pinModes : uint8_t {
        INPUT_PIN = 0,
        INPUT_PULLUP_PIN,
        OUTPUT_PIN
    } pinMode_t;

    // Constructor.
    AVRPin(const AVRpin_t pin, pinMode_t mode);

    // No destructor required.

    
    // Turn pin HIGH.
    void setHigh();

    // Turn pin LOW.
    void setLow();

    // Toggle pin.
    void toggle();

    // Get pin state. HIGH or LOW.
    uint8_t getState();
};


#endif // AVRPIN_H

The class definition in AVRPin.h defines the “blueprint” for our “object” - which is an Arduino digital pin. The class name can be used as a new variable type in a source program. The same as int, long, String (which is a class) etc.

We can see in the public section that the pin can be constructed to mimic a specific Arduino pin and be given a mode to operate in, all done by the constructor. Once an AVRPin object exists, it can be driven HIGH or LOW and can be toggled and the current state of the pin can be read at any time. These public functions can be used to make the pin do something.

Those functions which do this are the member functions you were asking about. There’s nothing special, they are functions, which are members of a class.

The private section holds a few variables which we don’t want to be accessed from outside our class. These would relate to static variables in a C++ source file, those are only visible inside functions defined in that source file. Private variables (and functions) can only be called or used from within the class.

The variable here hold the PORTx, DDRx and PINx register addresses, so that we can manipulate the pin by directly accessing the registers. This is what the Arduino Language does in functions like pinMode(), digitalWrite(), digitalRead() etc. There’s no need for these to be accessed outside of the AVRPin object(s), so they are kept private.

In this example, there are no private member functions though, all of those are public.

Next up, we need to implement the class:

cd ../src
vi AVRPin.cpp

The following code should be typed/copied and pasted into src/AVRPin.cpp:

#include "AVRPin.h"

// Constructor.
AVRPin::AVRPin(const AVRpin_t pin, pinMode_t mode) {
    // Calculate the PORT, PIN and DDR from the pin.
    // D0 through D7 = PORTD,
    // D8 through D13 = PORTB,
    // D14 (A0) through D19 (A5) = PORTC.
    if (pin <= AVRPIN_D7) {
        // We are in D registers.
        portRegister = &PORTD;
        pinRegister = &PIND;
        ddrRegister = &DDRD;

        // PinBit is the same as the pin.
        pinBit = pin;
    } else if (pin <= AVRPIN_D13) {
        // We are in B registers.
        portRegister = &PORTB;
        pinRegister = &PINB;
        ddrRegister = &DDRB;

        // PinBit is the pin - 8
        pinBit = pin - 8;
    } else {
        // We are in C registers.
        portRegister = &PORTC;
        pinRegister = &PINC;
        ddrRegister = &DDRC;

        // PinBit is the pin - 14
        pinBit = pin - 14;
    }

    // Now we have the registers, configure the pin.
    if (mode == INPUT_PIN) {
        *ddrRegister &= ~(1 << pinBit);
        *portRegister &= ~(1 << pinBit);      

    } else if (mode == INPUT_PULLUP_PIN) {
        *ddrRegister &= ~(1 << pinBit);        
        *portRegister |= (1 << pinBit);      
        
    } else {
        // OUTPUT_PIN
        *ddrRegister |= (1 << pinBit);        
    }
}



// Turn pin HIGH.
void AVRPin::setHigh() {
    *portRegister != (1 << pinBit);
}

// Turn pin LOW.
void AVRPin::setLow() {
    *portRegister &= ~(1 << pinBit);
}

// Toggle pin.
void AVRPin::toggle() {
    *pinRegister |= (1 << pinBit);
}

// Get pin state. HIGH or LOW.
uint8_t AVRPin::getState() {
    return !!(*pinRegister & (1 << pinBit));
}

The class implementation in AVRPin.cpp builds up the meat onto the bones of the blueprint. Every function defined in the class definition is prefixed with the class name - AVRPin:: the :: bit is just the scope operator. It says that, for example, the implementation of the function toggle() which follows, is a member function of the class AVRPin. Other classes can have the same function name, but they will be scoped into their own class. There’s no conflict in using the same function or variable names in different classes.

The class defines a couple of types which are used when defining the name of a new pin and the mode that pin is to be operated in. So, a new AVRPin is defined as follows:

AVRPin D13(AVRPin::AVRPIN_D13, AVRPin::OUTPUT_PIN);

Creating a pair of enums in the manner i did also scopes them to the AVRPin class. It also is a sneaky manner of getting parameter validation done “for free” by the compiler. I can only supply a pin name, or corresponding number, within the correct range, so I don’t have to do any worrying about parameter validation.

I had to give the pin names and modes different names to that used in the Arduino Language to avoid conflict.

The rest of the code just implements the various member functions using direct register access to set high, set low and toggle a particular pin.

Now we need a test file to see if the class can be used or not:

vi main.cpp

The following code should be typed/copied and pasted into src/main.cpp:

#include "Arduino.h"
#include "AVRPin.h"

AVRPin D13(AVRPin::AVRPIN_D13, AVRPin::OUTPUT_PIN);

void setup() {
    Serial.begin(9600);
}

void loop() {
    // Toggle the pin every second.    
    D13.toggle();
    Serial.print("The state of the pin is: ");
    Serial.println(D13.getState());
    delay(1000);
}

The code in main.cpp is the ubiquitous blink sketch reduced to a couple of lines. It creates a pin attached to D13, the built in LED. The constructor converts AVRPIN_D13 from a number into PORTB, DDRB and PINB registers, and also, into the value 5 for the pinBit member variables. Once we have those, we have total control over the pin.

In the loop() function, we toggle the pin and read the state of the pin and display it on the monitor.

This code compiled down to 704 bytes without the Serial stuff, and 1,806 bytes with the Serial stuff left in. The default blink sketch in the Arduino IDE compiles to 924 bytes plus 9 bytes of Static RAM. My code is smaller and, I think, easier to read and use? I hope!

I hope this small example clears up some of the worries you have about classes and member functions.

Cheers,
Norm.

1 Like