BLE Interference with digitalWrite

Hi All,

First post after reading the forum rules but please let me know if I have missed anything!

My current project is using a Nano 33 BLE as a peripheral, which monitors changes to two characteristics and uses them to calculate 6 outputs to three motors. Each motor requires a PWM input for speed and a digital (High or Low) input for direction. Since the Nano is 3.3v only and the motors require a 5v input I have used a logic level shifter to allow communication. 12v is also provided to the motors to allow them to run.

When I tested the motors with a simple sketch running three motors one direction from 0 to 255 and back to 0 then changing direction and repeating I had no issues. However when I then built on this code to add the BLE elements, see full code at bottom of the post, I was unable to have the motors turn when the central connects to the peripheral. I measured the outputs on my scope and the code does what it is meant to be doing i.e. as the characteristic values are changed the PWM and digital outputs alter. What I did notice though is there is a regular spike on the digitalWrite pins, this spike is high when the pin is pulled low and is low when the pin is pulled high, the spike is also always either to ground or to vcc.

As soon as the peripheral is disconnected from the central, the spikes go away, I also noticed removing the logic level converter removes the spikes when measuring the Nano pins. I also tried to use analogWrite instead sending a max or min value but this just crashes the OS on connection to the peripheral.

Taking a closer look on the scope, see pictures attached, there are two spikes roughly 3.8us each in duration with a 2.4us gap between them which are repeated with a frequency of roughly 3kHz.

I am unsure if this is the logic level conversion or the BLE and am hoping someone with more knowledge could possibly suggest some additional tests I can conduct or suggest something else to try.

Component details are below;

Motors = FIT0441 Brushless DC Motor
Logic level shifters = SparkFun Bidirectional Shifter part number 12009

/*
 * Title - motor test peripheral
 * Author - O Smith
 * Date - 08/02/2020
 * Version 1.0
 * Description - Test script for the wheel, sets up peripheral device with one service and two characteristics, one for wheel direction
 * the other for wheel speed. The program then calculates the required output to three motors and sends the signals to the appropraite 
 * pins for the motors.
 * 
 */

// Libraries
#include <ArduinoBLE.h>; // BLE library
#include <BasicLinearAlgebra.h> // matrix library

// Variables
float motor1Matrix = 0; // matrix output for motor 1
float motor2Matrix = 0; // matrix output for motor 2
float motor3Matrix = 0; // matrix output for motor 3
int motor1PWM = 0; // pwm inpupt for Motor 1
int motor2PWM = 0; // pwm inpupt for Motor 2
int motor3PWM = 0; // pwm inpupt for Motor 3
byte Ax = 100; // input value from controller for x
byte Ay = 100; // input value for controller for y

// Matricies
BLA::Matrix<3,3> wheelMatrix = {-0.333, 0.577,  0.333,//generate matrix for motor offsets
                                -0.333, -0.577, 0.333,
                                0.666,  0,      0.333};


// BLE Setup
BLEService wheel("5c15fae4-28e5-11ea-978f-2e728ce88125"); // set up wheel service
BLEByteCharacteristic wheelx ("5c15fe68-28e5-11ea-978f-2e728ce88125", BLERead|BLEWrite); // set up wheel speed characteristic
BLEByteCharacteristic wheely ("5c15fd1e-28e5-11ea-978f-2e728ce88125", BLERead|BLEWrite); // set up wheel direction characteristic

void motorControl(byte xaxis, byte yaxis){ // motor control function, takes wheel vectors and translates to motor speed and direction
  Serial.println("x and y values are");
  Serial.print(xaxis);
  Serial.print(" ");
  Serial.println(yaxis);
  double x = (double)xaxis; // convert to double for matrix calcs
  double y = (double)yaxis; // convert to double for matrix calcs
  Serial.println("x and y values after conversion are");
  Serial.print(x);
  Serial.print(" ");
  Serial.println(y);
  x = x - 100; // removing the 100 added for data transfer
  y = y - 100; // removing the 100 added for data transfer
  Serial.println("x and y - 100 values are");
  Serial.print(x);
  Serial.print(" ");
  Serial.println(y);
  BLA::Matrix<3,1> dirMatrix = {x, y, 0}; // generate matrix with values of x and y
  BLA::Matrix<3,1> motorMatrix = wheelMatrix*dirMatrix; // multiply matrix
  motor1Matrix = motorMatrix(0); // set matrix output for motor 1
  motor2Matrix = motorMatrix(1); // set matrix output for motor 2
  motor3Matrix = motorMatrix(2); // set matrix output for motor 3

  if (motor1Matrix >= 0) { // if the value is greater than or equal to 0 i.e not negative
    digitalWrite(9, HIGH);
    Serial.println("motor 1 is high");  
  }
  else {
    digitalWrite(9, LOW);
    Serial.println("motor 1 is low");
  }
  if (motor2Matrix >= 0) { // if the value is greater than or equal to 0 i.e not negative
    digitalWrite(3, HIGH);
    Serial.println("motor 2 is high");  
  }
  else {
    digitalWrite(3, LOW);
    Serial.println("motor 2 is low");
  }
  if (motor3Matrix >= 0) { // if the value is greater than or equal to 0 i.e not negative
    digitalWrite(6, HIGH);
    Serial.println("motor 3 is high");  
  }
  else {
    digitalWrite(6, LOW);
    Serial.println("motor 3 is low");
  }
  motor1PWM = map(motor1Matrix, -100, 100, 255, 0); 
  motor2PWM = map(motor2Matrix, -100, 100, 255, 0); 
  motor3PWM = map(motor3Matrix, -100, 100, 255, 0);
  Serial.print("motor 1 speed is ");
  Serial.println(motor1PWM);
  Serial.print("motor 2 speed is ");
  Serial.println(motor2PWM);
  Serial.print("motor 3 speed is ");
  Serial.println(motor3PWM);
  analogWrite(10, motor1PWM);
  analogWrite(4, motor2PWM);
  analogWrite(5, motor3PWM);
}

void setup() {
  Serial.begin(9600); // open serial connection
  BLE.begin(); // begin BLE device
  BLE.setLocalName("Wheel"); // set local name to "Wheel"
  BLE.setAdvertisedService(wheel); // set advertised service to the "wheel" service
  wheel.addCharacteristic(wheelx); // add thw two characteristics to the wheel service
  wheel.addCharacteristic(wheely);
  BLE.addService(wheel); // add service
  wheelx.writeValue(0x00); // set characteristic values to 0
  wheely.writeValue(0x00);
  BLE.advertise(); // begin advertising the wheel service with two characteristics 

  // set up pinmodes
  pinMode(9, OUTPUT);   // Motor 1 Direction 
  pinMode(10, OUTPUT);  // Motor 1 PWM
  pinMode(3, OUTPUT);   // Motor 2 direction
  pinMode(4, OUTPUT);   // Motor 2 pwm
  pinMode(6, OUTPUT);   // Motor 3 direction
  pinMode(5, OUTPUT);   // Motor 3 pwm
}

void loop() {
  BLEDevice controller = BLE.central(); // assign a device controller to the central node connecting
  if (controller){ //  if controller is connected
    Serial.println("controller connected");
    while (controller.connected()) { // while the controller is connected
      Serial.println("reading controller values");
      wheelx.readValue(Ax); // assign the value from the wheelx characteristic to Ax
      wheely.readValue(Ay); // assign the value from the wheely characteristic to Ay
      Serial.println("starting motor control function");
      motorControl(Ax, Ay); // run the motor control function
    }
    Serial.println("Controller disconnected");
  }
}

Are you calling digitalWrite to set the same pin state repeatedly? I have discovered the Nano 33 BLE has a problem in this usage. For example, this sketch:

void setup() {
  pinMode(2, OUTPUT);
}

void loop() {
  digitalWrite(2, HIGH);
}

results in this output on pin 2:

With a 10K pull-up resistor on pin 2, this sketch:

void setup() {
  pinMode(2, OUTPUT);
}

void loop() {
  digitalWrite(2, LOW);
}

results in this output on pin 2:

Whereas if you don't set the pin to the same state repeatedly, for example:

void setup() {
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
}

void loop() {}

you get the expected steady output level from the pin.

With any other Arduino board I've used, you can set the pin state repeatedly without disturbing the output level.

Thanks pert, this is exactly what the issue was!

My loop was resetting the pin as high or low each cycle. I added an if statement to check the previous state and only update when there was a change which has rectified it.

I will add this one to my set of notes for this board, hopefully as the documentation improves these features will be captured.

You're welcome. I'm glad to hear it's working now. Enjoy!
Per

The issue come from mbedOS, it uses pins differently than Arduino. When they tried to combine the two programming styles, they created the issue to allow Arduino programs to be used on the new platform. Every time you call digitalWrite(), mbedOS creates a pin object (mbed::DigitalInOut) which gets initialized LOW and then the pin is set. Then the object is destroyed.

You can use the mbedOS style, where they keep the object, to avoid this.

#include "mbed.h"

mbed::DigitalInOut ledPin( digitalPinToPinName( 9 ) );

void setup()
{
  ledPin.output();
}

void loop()
{
  // put your main code here, to run repeatedly:
  ledPin = HIGH;
  ledPin = LOW;
}

From my test with calling digitalWrite(2, LOW) repeatedly, it seems the pin is set to input mode momentarily.

pert:
From my test with calling digitalWrite(2, LOW) repeatedly, it seems the pin is set to input mode momentarily.

You are right. We both are. I did another experiment. Look at what happend when I used a 10k pull-up and set the pin to HIGH continuously.

The pin gets pulled LOW and floats back to HIGH twice before getting back to HIGH.

Wow, that's a strange digitalWrite!

Do you think Arduino will be able to fix this problem while still using the MbedOS API for digitalWrite()? So far, I've only directly used the MbedOS API once to help someone set the PWM frequency, and that was pretty much just a copy/paste of Arduino's analogWrite() code into the sketch, so I essentially know nothing about it.

pert:
Wow, that's a strange digitalWrite!

Do you think Arduino will be able to fix this problem while still using the MbedOS API for digitalWrite()?

They should be able to fix this. Unfortunately, there are some source files missing and I got stuck in my analysis because of this. Do you have contact to some developers at Arduino?

I do work for Arduino, but I don't feel that this gives me any special influence with the developers compared to any other member of the Arduino community. Perhaps the normal users even have a bit more influence, since after all they are the reason we do what we do.

I've been meaning to submit a bug report about this ever since I first encountered the problem on that previous thread but haven't gotten around to looking into it further. It's definitely something that should be investigated and I'm sure the developers would be glad to know about it.

I think the issue is in the following functions.

void gpio_init_out(gpio_t *gpio, PinName pin);
void gpio_init_out_ex(gpio_t *gpio, PinName pin, int value);

They are declared in gpio_api.h but I cant find the sources files for the actual code. Notepad++ search found something in the libmbed.a library file. So I guess its in this file.

Can you find the sources for these functions?

Status update on this issue: the developers are aware of it and a fix is in progress. The issue report for tracking the bug is here: