Nano 33 BLE Sense + ESP32 (smart glove)

Hello everybody! I found project of smart glove on instructables made by Matlek.
But I want to receive gesture "Stop" on ESP32 by getting high signal on pin D7 on Nano 33 BLE Sense.
As I understand value of object "ledCharacteristic" is transmitting to ESP32.
I added lines to the code, but it doesn't work so far. I commented out the added lines this way // ***** ADDED string/strings of code ****
I don't even see my added values in the com port monitor. Where am I going wrong?
This is changed code for Nano 33 BLE Sense:

/*
  Smartglove for cyclists: https://www.instructables.com/member/Matlek/
  This code is made for an Arduino Nano 33 BLE Sense board:
  it detects left hand gestures, and sends the gestures information through BLE (to another microcontroller with an LED matrix);
  It is a mix of the following codes, after a few modifications:
    -"LED" example from the "ArduinoBLE" library (Peripheral>LED).
    -"IMU_Classifier" found here: https://github.com/arduino/ArduinoTensorFlowLiteTutorials/blob/master/GestureToEmoji/ArduinoSketches/IMU_Classifier/IMU_Classifier.ino.
*/

#include <ArduinoBLE.h>
#include <Arduino_LSM9DS1.h>
#include <TensorFlowLite.h>
#include <tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h>
#include <tensorflow/lite/experimental/micro/micro_error_reporter.h>
#include <tensorflow/lite/experimental/micro/micro_interpreter.h>
#include <tensorflow/lite/schema/schema_generated.h>
#include <tensorflow/lite/version.h>
#include "model.h"
const float gyroscopeThreshold = 300;
const int numSamples = 64;
int samplesRead = numSamples;
// global variables used for TensorFlow Lite (Micro)
tflite::MicroErrorReporter tflErrorReporter;
// pull in all the TFLM ops, you can remove this line and
// only pull in the TFLM ops you need, if would like to reduce
// the compiled size of the sketch.
tflite::ops::micro::AllOpsResolver tflOpsResolver;
const tflite::Model* tflModel = nullptr;
tflite::MicroInterpreter* tflInterpreter = nullptr;
TfLiteTensor* tflInputTensor = nullptr;
TfLiteTensor* tflOutputTensor = nullptr;
// Create a static memory buffer for TFLM, the size may need to
// be adjusted based on the model you are using
constexpr int tensorArenaSize = 8 * 1024;
byte tensorArena[tensorArenaSize];
// array to map gesture index to a name
const char* GESTURES[] = {
  "Left arm",
  "Brake",
  "Hand front rotation",
  "Hand back rotation",
  "Hand left",
  "Hand right"
};
#define NUM_GESTURES (sizeof(GESTURES) / sizeof(GESTURES[0]))
int nbGesture = 0;
int oldNbGesture = 0;
int gestureTriggered;

//******************** ADDED strings **************************
// variables for button 
const int buttonPin = 7; 
int oldButtonState = LOW;
//******************** ADDED strings **************************


void setup() {
  Serial.begin(115200);
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
  // print out the samples rates of the IMUs
  Serial.print("Accelerometer sample rate = ");
  Serial.print(IMU.accelerationSampleRate());
  Serial.println(" Hz");
  Serial.print("Gyroscope sample rate = ");
  Serial.print(IMU.gyroscopeSampleRate());
  Serial.println(" Hz");
  Serial.println();
  // get the TFL representation of the model byte array
  tflModel = tflite::GetModel(model);
  if (tflModel->version() != TFLITE_SCHEMA_VERSION) {
    Serial.println("Model schema mismatch!");
    while (1);
  }
  // Create an interpreter to run the model
  tflInterpreter = new tflite::MicroInterpreter(tflModel, tflOpsResolver, tensorArena, tensorArenaSize, &tflErrorReporter);
  // Allocate memory for the model's input and output tensors
  tflInterpreter->AllocateTensors();
  // Get pointers for the model's input and output tensors
  tflInputTensor = tflInterpreter->input(0);
  tflOutputTensor = tflInterpreter->output(0);

  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT);
  digitalWrite(22, HIGH);
  digitalWrite(23, HIGH);
  digitalWrite(24, HIGH);

//******************** ADDED strings **************************
  // configure the button pin as input
  pinMode(buttonPin, INPUT);
//******************** ADDED strings **************************

  // initialize the BLE hardware
  BLE.begin();
  Serial.println("BLE Central - LED control");

  // start scanning for peripherals
  //BLE.scanForUuid("e4297ee0-8c88-11ea-bc55-0242ac130003");
  BLE.scan();
}

void loop() {
  BLEDevice peripheral = BLE.available();
  if (peripheral) {
    // discovered a peripheral, print out address, local name, and advertised service
    Serial.print("Found ");
    Serial.print(peripheral.address());
    Serial.print(" '");
    Serial.print(peripheral.localName());
    Serial.print("' ");
    Serial.print(peripheral.advertisedServiceUuid());
    Serial.println();
    if (peripheral.localName() != "MySmartglove") {
      return;
    }
    BLE.stopScan();
    // connect to the peripheral
    Serial.println("Connecting ...");

    if (peripheral.connect()) {
      Serial.println("Connected");
    } else {
      Serial.println("Failed to connect!");
      return;
    }

    // discover peripheral attributes
    Serial.println("Discovering attributes ...");
    if (peripheral.discoverAttributes()) {
      Serial.println("Attributes discovered");
    } else {
      Serial.println("Attribute discovery failed!");
      peripheral.disconnect();
      return;
    }

    // retrieve the LED characteristic
    BLECharacteristic ledCharacteristic = peripheral.characteristic("e4297ee1-8c88-11ea-bc55-0242ac130003");

    if (!ledCharacteristic) {
      Serial.println("Peripheral does not have LED characteristic!");
      peripheral.disconnect();
      return;
    } else if (!ledCharacteristic.canWrite()) {
      Serial.println("Peripheral does not have a writable LED characteristic!");
      peripheral.disconnect();
      return;
    }

    while (peripheral.connected()) {
      float aX, aY, aZ, gX, gY, gZ;
      // wait for significant motion
      while (samplesRead == numSamples) {
        if (IMU.gyroscopeAvailable()) {
          IMU.readGyroscope(gX, gY, gZ);
          // sum up the absolutes
          float gSum = fabs(gX) + fabs(gY) + fabs(gZ);
          // check if it's above the threshold
          if (gSum >= gyroscopeThreshold) {
            // reset the sample read count
            samplesRead = 0;
            break;
          }
        }
      }

            
      // check if the all the required samples have been read since
      // the last time the significant motion was detected
      oldNbGesture = nbGesture;
      while (samplesRead < numSamples) {
        // check if new acceleration AND gyroscope data is available
        if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
          // read the acceleration and gyroscope data
          IMU.readAcceleration(aX, aY, aZ);
          IMU.readGyroscope(gX, gY, gZ);
          // normalize the IMU data between 0 to 1 and store in the model's
          // input tensor
          tflInputTensor->data.f[samplesRead * 6 + 0] = (aX + 4.0) / 8.0;
          tflInputTensor->data.f[samplesRead * 6 + 1] = (aY + 4.0) / 8.0;
          tflInputTensor->data.f[samplesRead * 6 + 2] = (aZ + 4.0) / 8.0;
          tflInputTensor->data.f[samplesRead * 6 + 3] = (gX + 2000.0) / 4000.0;
          tflInputTensor->data.f[samplesRead * 6 + 4] = (gY + 2000.0) / 4000.0;
          tflInputTensor->data.f[samplesRead * 6 + 5] = (gZ + 2000.0) / 4000.0;
          samplesRead++;
          if (samplesRead == numSamples) {
            // Run inferencing
            TfLiteStatus invokeStatus = tflInterpreter->Invoke();
            if (invokeStatus != kTfLiteOk) {
              while (1);
              return;
            }
            // Loop through the output tensor values from the model
            for (int i = 0; i < NUM_GESTURES; i++) {
              Serial.print(GESTURES[i]);
              Serial.print(": ");
              Serial.println(tflOutputTensor->data.f[i], 6);
              if ((tflOutputTensor->data.f[i]) > 0.6) {
                gestureTriggered = i;
                nbGesture++;
              }
            }
          }
        }
      }
      Serial.print("The gesture is :");
      Serial.println(GESTURES[gestureTriggered]);
      if (oldNbGesture != nbGesture) {
        if (gestureTriggered == 0) {
          ledCharacteristic.writeValue((byte)0x00);
          colors(0);
        }
        //        if (gestureTriggered == 1) {
        //          ledCharacteristic.writeValue((byte)0x01);
        //          colors(1);
        //        }
        if (gestureTriggered == 2) {
          ledCharacteristic.writeValue((byte)0x02);
          colors(2);
        }
        //********* ADDED string *************
        //comment out these lines of code
        /*
        if (gestureTriggered == 3) {
          ledCharacteristic.writeValue((byte)0x03);
          colors(3);
        }
        */
        if (gestureTriggered == 4) {
          ledCharacteristic.writeValue((byte)0x04);
          colors(4);
        }
        if (gestureTriggered == 5) {
          ledCharacteristic.writeValue((byte)0x05);
          colors(5);
        }
      }

      //******************** ADDED strings **************************
      // read the button pin
      int buttonState = digitalRead(buttonPin);

       if (oldButtonState != buttonState) {
      // button changed
      oldButtonState = buttonState;

      if (buttonState) {
        Serial.println("button pressed");

        // button is pressed, write 0x03 to turn the LED on
         ledCharacteristic.writeValue((byte)0x03);
      } else {
        Serial.println("button released");        
      }
    }
    //******************** ADDED strings **************************

      
    }
    // peripheral disconnected, start scanning again
    //BLE.scanForUuid("e4297ee0-8c88-11ea-bc55-0242ac130003");
    BLE.scan();
  }
}

int colors (int i) {
  if (i == 0) {
    for (int it1 = 0; it1 <= 1; it1++) {
      digitalWrite(22, LOW);
      digitalWrite(23, LOW);
      delay(500);
      digitalWrite(22, HIGH);
      digitalWrite(23, HIGH);
      delay(500);
    }
  }
  if (i == 1) {
    for (int it1 = 0; it1 <= 1; it1++) {
      digitalWrite(22, LOW);
      delay(500);
      digitalWrite(22, HIGH);
      delay(500);
    }
  }
  if (i == 2) {
    digitalWrite(23, LOW);
    delay(500);
    digitalWrite(23, HIGH);
    delay(500);
  }
  if (i == 3) {
    digitalWrite(22, LOW);
    delay(500);
    digitalWrite(22, HIGH);
  }
  if (i == 4) {
    digitalWrite(22, LOW);
    delay(500);
    digitalWrite(23, LOW);
    delay(500);
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH);
  }
  if (i == 5) {
    digitalWrite(23, LOW);
    delay(500);
    digitalWrite(22, LOW);
    delay(500);
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH);
  }
}

This is original (unmodified) code for ESP32:

/*
    Smartglove for cyclists: https://www.instructables.com/member/Matlek/
    This code is made for an ESP32 microcontroller, to control an LED matrix through BLE.
    It is a mix of the following codes, after a few modifications:
    -"BLE_Write" example from the "BLE ESP32 ARDUINO" library.
    -"MatrixGFXDemo64" example from the "FastLED NeoMatrix" library.
*/

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
String gestureValue = "0";
int gestureNb = 0;
int old_gestureNb = 0;
BLEServer *pServer = NULL;
bool deviceConnected = false;
unsigned long previousMillis = 0;
const long interval = 1000;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID        "e4297ee0-8c88-11ea-bc55-0242ac130003"
#define CHARACTERISTIC_UUID "e4297ee1-8c88-11ea-bc55-0242ac130003"
#define DISABLE_WHITE
#include <Adafruit_GFX.h>
#include <FastLED_NeoMatrix.h>
#include <FastLED.h>
// Allow temporaly dithering, does not work with ESP32 right now
#ifndef ESP32
#define delay FastLED.delay
#endif
#define PIN 33
#define BRIGHTNESS 64
#define mw 20
#define mh 8
#define NUMMATRIX (mw*mh)
CRGB leds[NUMMATRIX];
// Define matrix width and height.
FastLED_NeoMatrix *matrix = new FastLED_NeoMatrix(leds, mw, mh,
    NEO_MATRIX_BOTTOM     + NEO_MATRIX_RIGHT +
    NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG);

void matrix_show() {
  matrix->show();
}

// This could also be defined as matrix->color(255,0,0) but those defines
// are meant to work for adafruit_gfx backends that are lacking color()
#define LED_BLACK    0

#define LED_RED_VERYLOW   (3 <<  11)
#define LED_RED_LOW     (7 <<  11)
#define LED_RED_MEDIUM    (15 << 11)
#define LED_RED_HIGH    (31 << 11)

#define LED_GREEN_VERYLOW (1 <<  5)
#define LED_GREEN_LOW     (15 << 5)
#define LED_GREEN_MEDIUM  (31 << 5)
#define LED_GREEN_HIGH    (63 << 5)

#define LED_BLUE_VERYLOW  3
#define LED_BLUE_LOW    7
#define LED_BLUE_MEDIUM   15
#define LED_BLUE_HIGH     31

#define LED_ORANGE_VERYLOW  (LED_RED_VERYLOW + LED_GREEN_VERYLOW)
#define LED_ORANGE_LOW    (LED_RED_LOW     + LED_GREEN_LOW)
#define LED_ORANGE_MEDIUM (LED_RED_MEDIUM  + LED_GREEN_MEDIUM)
#define LED_ORANGE_HIGH   (LED_RED_HIGH    + LED_GREEN_HIGH)

#define LED_PURPLE_VERYLOW  (LED_RED_VERYLOW + LED_BLUE_VERYLOW)
#define LED_PURPLE_LOW    (LED_RED_LOW     + LED_BLUE_LOW)
#define LED_PURPLE_MEDIUM (LED_RED_MEDIUM  + LED_BLUE_MEDIUM)
#define LED_PURPLE_HIGH   (LED_RED_HIGH    + LED_BLUE_HIGH)

#define LED_CYAN_VERYLOW  (LED_GREEN_VERYLOW + LED_BLUE_VERYLOW)
#define LED_CYAN_LOW    (LED_GREEN_LOW     + LED_BLUE_LOW)
#define LED_CYAN_MEDIUM   (LED_GREEN_MEDIUM  + LED_BLUE_MEDIUM)
#define LED_CYAN_HIGH   (LED_GREEN_HIGH    + LED_BLUE_HIGH)

#define LED_WHITE_VERYLOW (LED_RED_VERYLOW + LED_GREEN_VERYLOW + LED_BLUE_VERYLOW)
#define LED_WHITE_LOW   (LED_RED_LOW     + LED_GREEN_LOW     + LED_BLUE_LOW)
#define LED_WHITE_MEDIUM  (LED_RED_MEDIUM  + LED_GREEN_MEDIUM  + LED_BLUE_MEDIUM)
#define LED_WHITE_HIGH    (LED_RED_HIGH    + LED_GREEN_HIGH    + LED_BLUE_HIGH)

void matrix_clear() {
  // clear does not work properly with multiple matrices connected via parallel inputs
  memset(leds, 0, sizeof(leds));
}

void ScrollText_Left() {
  matrix_clear();
  matrix->setTextWrap(false);  // we don't wrap text so it scrolls nicely
  matrix->setTextSize(1);
  matrix->setRotation(0);
  for (int8_t x = 0; x >= -60; x--) {
    yield();
    matrix_clear();
    matrix->setCursor(x, 0);
    matrix->setTextColor(LED_ORANGE_HIGH);
    matrix->print("<<<<<<<<<<<<<<");
    matrix_show();
    delay(50);
  }
  matrix->setCursor(0, 0);
}

void ScrollText_Merci() {
  matrix_clear();
  matrix->setTextWrap(false);  // we don't wrap text so it scrolls nicely
  matrix->setTextSize(1);
  matrix->setRotation(0);
  for (int8_t x = 20; x >= -30; x--) {
    yield();
    matrix_clear();
    matrix->setCursor(x, 0);
    matrix->setTextColor(LED_PURPLE_HIGH);
    matrix->print("Merci");
    matrix_show();
    delay(70);
  }
  matrix->setCursor(0, 0);
}

void ScrollText_Hello() {
  matrix_clear();
  matrix->setTextWrap(false);  // we don't wrap text so it scrolls nicely
  matrix->setTextSize(1);
  matrix->setRotation(0);
  for (int8_t x = 20; x >= -30; x--) {
    yield();
    matrix_clear();
    matrix->setCursor(x, 0);
    matrix->setTextColor(LED_CYAN_HIGH);
    matrix->print("Hello!");
    matrix_show();
    delay(70);
  }
  matrix->setCursor(0, 0);
}

void ScrollText_Right() {
  matrix_clear();
  matrix->setTextWrap(false);  // we don't wrap text so it scrolls nicely
  matrix->setTextSize(1);
  matrix->setRotation(0);
  for (int8_t x = -60; x <= 0 ; x++) {
    yield();
    matrix_clear();
    matrix->setCursor(x, 0);
    matrix->setTextColor(LED_ORANGE_HIGH);
    matrix->print(">>>>>>>>>>>>>>");
    matrix_show();
    delay(50);
  }
  matrix->setCursor(0, 0);
}

void ScrollText_Wait() {
  matrix_clear();
  matrix->setTextWrap(false);  // we don't wrap text so it scrolls nicely
  matrix->setTextSize(1);
  matrix->setRotation(0);
  yield();
  matrix_clear();
  matrix->setCursor(2, 0);
  matrix->setTextColor(LED_ORANGE_HIGH);
  matrix->print("o");
  matrix_show();
  delay(500);
  yield();
  matrix_clear();
  matrix->setCursor(2, 0);
  matrix->setTextColor(LED_ORANGE_HIGH);
  matrix->print("oo");
  matrix_show();
  delay(500);
  yield();
  matrix_clear();
  matrix->setCursor(2, 0);
  matrix->setTextColor(LED_ORANGE_HIGH);
  matrix->print("ooo");
  matrix_show();
  delay(500);
  yield();
  matrix_clear();
  matrix->setCursor(0, 0);
  matrix->setTextColor(LED_BLACK);
  matrix_show();
  delay(500);
}

void ScrollText_Stop() {
  matrix_clear();
  matrix->setTextWrap(false);  // we don't wrap text so it scrolls nicely
  matrix->setTextSize(1);
  matrix->setRotation(0);
  yield();
  matrix_clear();
  matrix->setCursor(2, 0);
  matrix->setTextColor(LED_RED_HIGH);
  matrix->print("!!!");
  matrix_show();
  delay(1000);
  yield();
  matrix_clear();
  matrix->setCursor(0, 0);
  matrix->setTextColor(LED_BLACK);
  matrix->print("STOP");
  matrix_show();
  delay(500);
}

void ScrollText_Straight() {
  uint8_t size = max(int(mw / 8), 1);
  matrix->setRotation(3);
  matrix->setTextSize(size);
  matrix->setTextColor(LED_GREEN_HIGH);
  for (int16_t x = -10; x <= 6; x++) {
    yield();
    matrix_clear();
    matrix->setCursor(x, ((mw / 2 - size * 4) + 1));
    matrix->print(">");
    matrix_show();
    delay(100 / size);
  }
  matrix->setRotation(0);
  matrix->setCursor(0, 0);
  matrix_show();
}

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
      Serial.println("      deviceConnected = true;");
    };
    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
      Serial.println("      deviceConnected = false;");
    }
};

class MyCallbacks: public BLECharacteristicCallbacks {
    //public:
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();
      if (value.length() > 0) {
          gestureValue = int(value[0]);
        gestureNb++;
      }
    }
};

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<NEOPIXEL, PIN>(  leds, NUMMATRIX  ).setCorrection(TypicalLEDStrip);
  delay(1000);
  matrix->begin();
  matrix->setTextWrap(false);
  matrix->setBrightness(BRIGHTNESS);
#ifndef DISABLE_WHITE
  matrix->fillScreen(LED_WHITE_HIGH);
  matrix_show();
  delay(5000);
  matrix_clear();
#endif
  BLEDevice::init("MySmartglove");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );
  pCharacteristic->setCallbacks(new MyCallbacks());
  //pCharacteristic->setValue("Hello World");
  pService->start();
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->start();
}

void loop() {

  if (deviceConnected) {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      ScrollText_Straight();
    }
      Serial.print("gestureValue : ");
      Serial.println(gestureValue);
    if (gestureNb != old_gestureNb)
    {

      if (gestureValue == "0" || gestureValue == "4") {
        ScrollText_Left();
      }
      if (gestureValue == "5") {
        ScrollText_Right();
      }
      if (gestureValue == "3") {
        ScrollText_Stop();
      }
      if (gestureValue == "2") {
        ScrollText_Merci();
      }
      old_gestureNb = gestureNb;
    }
  }
  if (!deviceConnected) {
    ScrollText_Wait();
    pServer->startAdvertising(); // restart advertising
  }
}

This is photo of connection tact switch to Nano 33 BLE Sense.

To be continued

Hello petja,

With a very quick glance, I would ask:

  1. Are you sure you have wired the button correctly? You can make sure you are transmitting a signal with a multimeter.

  2. Have you tried running code that just detects the button? I would recommend using an INPUT_PULLUP mode as you only have to wire the button between D7 and GND (one less wire to worry about) although your input code will flip (high is not pressed/low is pressed).

  3. Are you getting no printed strings or are you just not getting the button strings?

Sorry, that isn't much of an answer but that is what I would do.
Matt

Hello, Matt. Thank you for answer!
I tested button on pin D7 with this simple code:

int BUTTON_PIN = 7;

int LED = 24;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(BUTTON_PIN, INPUT);
}

void loop() {
  if (digitalRead(BUTTON_PIN) == HIGH) {
    digitalWrite(LED, LOW);
  }

  else {
    digitalWrite(LED, HIGH);
  }
}

All is working good, as you can see (I have 3.3V on D7 with INPUT mode):
https://www.youtube.com/watch?v=9I63hr0RIh0
Will some of this code work with the code from the smart glove in this condition "if (oldNbGesture! = NbGesture)"?

Hello @petjat,

Good to see the button is working, can you confirm that if the button detection code is inside or outside of this if statement if (oldNbGesture != nbGesture) {

In your first code snippet, it is outside whereas in your screencap it appears to be inside. If it is inside then the button is not being checked all the time. Also in your old code, you are setting oldNbGesture = nbGesture; on every loop of the while loop instead of at the end of the if (oldNbGesture != nbGesture) { . With that configuration the statement will never "be true". If the code has changed please upload it as an attachment or post it into a response.

Thanks,
Matt

Sorry, that I answer so late, @matt_lee_smith .
Now I wrote condition transmitting value of object "ledCharacteristic" in loop while (peripheral.connected())
I added only this lines of code at all:

int BUTTON_PIN = 7;
pinMode(BUTTON_PIN, INPUT);

if (digitalRead(BUTTON_PIN) == HIGH) {
          ledCharacteristic.writeValue((byte)0x03);
          Serial.println("Stop");
        }

Here is the complete code for Nano 33 BLE Sense:

/*
 Smartglove for cyclists: https://www.instructables.com/member/Matlek/
 This code is made for an Arduino Nano 33 BLE Sense board:
 it detects left hand gestures, and sends the gestures information through BLE (to another microcontroller with an LED matrix);
 It is a mix of the following codes, after a few modifications:
   -"LED" example from the "ArduinoBLE" library (Peripheral>LED).
   -"IMU_Classifier" found here: https://github.com/arduino/ArduinoTensorFlowLiteTutorials/blob/master/GestureToEmoji/ArduinoSketches/IMU_Classifier/IMU_Classifier.ino.
*/

#include <ArduinoBLE.h>
#include <Arduino_LSM9DS1.h>
#include <TensorFlowLite.h>
#include <tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h>
#include <tensorflow/lite/experimental/micro/micro_error_reporter.h>
#include <tensorflow/lite/experimental/micro/micro_interpreter.h>
#include <tensorflow/lite/schema/schema_generated.h>
#include <tensorflow/lite/version.h>
#include "model.h"
const float gyroscopeThreshold = 300;
const int numSamples = 64;
int samplesRead = numSamples;
// global variables used for TensorFlow Lite (Micro)
tflite::MicroErrorReporter tflErrorReporter;
// pull in all the TFLM ops, you can remove this line and
// only pull in the TFLM ops you need, if would like to reduce
// the compiled size of the sketch.
tflite::ops::micro::AllOpsResolver tflOpsResolver;
const tflite::Model* tflModel = nullptr;
tflite::MicroInterpreter* tflInterpreter = nullptr;
TfLiteTensor* tflInputTensor = nullptr;
TfLiteTensor* tflOutputTensor = nullptr;
// Create a static memory buffer for TFLM, the size may need to
// be adjusted based on the model you are using
constexpr int tensorArenaSize = 8 * 1024;
byte tensorArena[tensorArenaSize];
// array to map gesture index to a name
const char* GESTURES[] = {
 "Left arm",
 "Brake",
 "Hand front rotation",
 "Hand back rotation",
 "Hand left",
 "Hand right"
};
#define NUM_GESTURES (sizeof(GESTURES) / sizeof(GESTURES[0]))
int nbGesture = 0;
int oldNbGesture = 0;
int gestureTriggered;
int BUTTON_PIN = 7;

void setup() {
 Serial.begin(115200);
 if (!IMU.begin()) {
   Serial.println("Failed to initialize IMU!");
   while (1);
 }
 // print out the samples rates of the IMUs
 Serial.print("Accelerometer sample rate = ");
 Serial.print(IMU.accelerationSampleRate());
 Serial.println(" Hz");
 Serial.print("Gyroscope sample rate = ");
 Serial.print(IMU.gyroscopeSampleRate());
 Serial.println(" Hz");
 Serial.println();
 // get the TFL representation of the model byte array
 tflModel = tflite::GetModel(model);
 if (tflModel->version() != TFLITE_SCHEMA_VERSION) {
   Serial.println("Model schema mismatch!");
   while (1);
 }
 // Create an interpreter to run the model
 tflInterpreter = new tflite::MicroInterpreter(tflModel, tflOpsResolver, tensorArena, tensorArenaSize, &tflErrorReporter);
 // Allocate memory for the model's input and output tensors
 tflInterpreter->AllocateTensors();
 // Get pointers for the model's input and output tensors
 tflInputTensor = tflInterpreter->input(0);
 tflOutputTensor = tflInterpreter->output(0);

 pinMode(22, OUTPUT);
 pinMode(23, OUTPUT);
 pinMode(24, OUTPUT);
 pinMode(BUTTON_PIN, INPUT);
 digitalWrite(22, HIGH);
 digitalWrite(23, HIGH);
 digitalWrite(24, HIGH);

 // initialize the BLE hardware
 BLE.begin();
 Serial.println("BLE Central - LED control");

 // start scanning for peripherals
 //BLE.scanForUuid("e4297ee0-8c88-11ea-bc55-0242ac130003");
 BLE.scan();
}

void loop() {
 BLEDevice peripheral = BLE.available();
 if (peripheral) {
   // discovered a peripheral, print out address, local name, and advertised service
   Serial.print("Found ");
   Serial.print(peripheral.address());
   Serial.print(" '");
   Serial.print(peripheral.localName());
   Serial.print("' ");
   Serial.print(peripheral.advertisedServiceUuid());
   Serial.println();
   if (peripheral.localName() != "MySmartglove") {
     return;
   }
   BLE.stopScan();
   // connect to the peripheral
   Serial.println("Connecting ...");

   if (peripheral.connect()) {
     Serial.println("Connected");
   } else {
     Serial.println("Failed to connect!");
     return;
   }

   // discover peripheral attributes
   Serial.println("Discovering attributes ...");
   if (peripheral.discoverAttributes()) {
     Serial.println("Attributes discovered");
   } else {
     Serial.println("Attribute discovery failed!");
     peripheral.disconnect();
     return;
   }

   // retrieve the LED characteristic
   BLECharacteristic ledCharacteristic = peripheral.characteristic("e4297ee1-8c88-11ea-bc55-0242ac130003");

   if (!ledCharacteristic) {
     Serial.println("Peripheral does not have LED characteristic!");
     peripheral.disconnect();
     return;
   } else if (!ledCharacteristic.canWrite()) {
     Serial.println("Peripheral does not have a writable LED characteristic!");
     peripheral.disconnect();
     return;
   }

   while (peripheral.connected()) {
     
   if (digitalRead(BUTTON_PIN) == HIGH) {
         ledCharacteristic.writeValue((byte)0x03);
         Serial.println("Stop");
       }


     
     float aX, aY, aZ, gX, gY, gZ;
     // wait for significant motion
     while (samplesRead == numSamples) {
       if (IMU.gyroscopeAvailable()) {
         IMU.readGyroscope(gX, gY, gZ);
         // sum up the absolutes
         float gSum = fabs(gX) + fabs(gY) + fabs(gZ);
         // check if it's above the threshold
         if (gSum >= gyroscopeThreshold) {
           // reset the sample read count
           samplesRead = 0;
           break;
         }
       }
     }
     // check if the all the required samples have been read since
     // the last time the significant motion was detected
     oldNbGesture = nbGesture;
     while (samplesRead < numSamples) {
       // check if new acceleration AND gyroscope data is available
       if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
         // read the acceleration and gyroscope data
         IMU.readAcceleration(aX, aY, aZ);
         IMU.readGyroscope(gX, gY, gZ);
         // normalize the IMU data between 0 to 1 and store in the model's
         // input tensor
         tflInputTensor->data.f[samplesRead * 6 + 0] = (aX + 4.0) / 8.0;
         tflInputTensor->data.f[samplesRead * 6 + 1] = (aY + 4.0) / 8.0;
         tflInputTensor->data.f[samplesRead * 6 + 2] = (aZ + 4.0) / 8.0;
         tflInputTensor->data.f[samplesRead * 6 + 3] = (gX + 2000.0) / 4000.0;
         tflInputTensor->data.f[samplesRead * 6 + 4] = (gY + 2000.0) / 4000.0;
         tflInputTensor->data.f[samplesRead * 6 + 5] = (gZ + 2000.0) / 4000.0;
         samplesRead++;
         if (samplesRead == numSamples) {
           // Run inferencing
           TfLiteStatus invokeStatus = tflInterpreter->Invoke();
           if (invokeStatus != kTfLiteOk) {
             while (1);
             return;
           }
           // Loop through the output tensor values from the model
           for (int i = 0; i < NUM_GESTURES; i++) {
             Serial.print(GESTURES[i]);
             Serial.print(": ");
             Serial.println(tflOutputTensor->data.f[i], 6);
             if ((tflOutputTensor->data.f[i]) > 0.6) {
               gestureTriggered = i;
               nbGesture++;
             }
           }
         }
       }
     }
     Serial.print("The gesture is :");
     Serial.println(GESTURES[gestureTriggered]);
     if (oldNbGesture != nbGesture) {
       if (gestureTriggered == 0) {
         ledCharacteristic.writeValue((byte)0x00);
         colors(0);
       }
       //        if (gestureTriggered == 1) {
       //          ledCharacteristic.writeValue((byte)0x01);
       //          colors(1);
       //        }
       if (gestureTriggered == 2) {
         ledCharacteristic.writeValue((byte)0x02);
         colors(2);
       }
       if (gestureTriggered == 3) {
         ledCharacteristic.writeValue((byte)0x03);
         colors(3);
       }
       if (gestureTriggered == 4) {
         ledCharacteristic.writeValue((byte)0x04);
         colors(4);
       }
       if (gestureTriggered == 5) {
         ledCharacteristic.writeValue((byte)0x05);
         colors(5);
       }
     }
   }
   // peripheral disconnected, start scanning again
   //BLE.scanForUuid("e4297ee0-8c88-11ea-bc55-0242ac130003");
   BLE.scan();
 }
}

int colors (int i) {
 if (i == 0) {
   for (int it1 = 0; it1 <= 1; it1++) {
     digitalWrite(22, LOW);
     digitalWrite(23, LOW);
     delay(500);
     digitalWrite(22, HIGH);
     digitalWrite(23, HIGH);
     delay(500);
   }
 }
 if (i == 1) {
   for (int it1 = 0; it1 <= 1; it1++) {
     digitalWrite(22, LOW);
     delay(500);
     digitalWrite(22, HIGH);
     delay(500);
   }
 }
 if (i == 2) {
   digitalWrite(23, LOW);
   delay(500);
   digitalWrite(23, HIGH);
   delay(500);
 }
 if (i == 3) {
   digitalWrite(22, LOW);
   delay(500);
   digitalWrite(22, HIGH);
 }
 if (i == 4) {
   digitalWrite(22, LOW);
   delay(500);
   digitalWrite(23, LOW);
   delay(500);
   digitalWrite(22, HIGH);
   digitalWrite(23, HIGH);
 }
 if (i == 5) {
   digitalWrite(23, LOW);
   delay(500);
   digitalWrite(22, LOW);
   delay(500);
   digitalWrite(22, HIGH);
   digitalWrite(23, HIGH);
 }
}

But I still didn't see data entry "Stop" in COM port and of course can't call function ScrollText_Stop() for ESP32 (code for ESP32 still is unmodified ).

Hello @petjat,

Ok, I tried to compile your code but I am missing some stuff apparently so I will have to pick it apart to see if I can get it running without the ML first.

I will try and find some time to look at it but I am pretty busy, have you tried getting this work without the ML stuff (i.e. just button detection and BLE). Basically what I would do, and knowing the button works, is build in the functionality one at a time to see when the button stops working and isolate what is going wrong. Also, you could consider using an interrupt to detect the button's change of state (you'll need to use a debounce code though because those kinds of buttons are notoriously bouncy) instead of just checking it all the time to see if that helps.

Sorry, I can't be of much help right now.
Matt

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.