Page 1 of 1
Forum

Welcome to the Tweaking4All community forums!
When participating, please keep the Forum Rules in mind!

Topics for particular software or systems: Start your topic link with the name of the application or system.
For example “MacOS X – Your question“, or “MS Word – Your Tip or Trick“.

Please note that switching to another language when reading a post will not bring you to the same post, in Dutch, as there is no translation for that post!



Movement and storag...
 
Share:
Notifications
Clear all

[Solved] Movement and storage of servo positions via bluetooth

7 Posts
2 Users
0 Reactions
2,181 Views
(@giammarcop)
Active Member
Joined: 4 years ago
Posts: 4
Topic starter  

Hi everyone,

I am trying to program a 4DOF robotic arm (using 4 servo motors). I would like to move the servos using an Android app, by means of bluetooth (HC-05).
I developed the app, in whose interface there are 7 buttons (Up, Down, Left, Rght, Forward, Backward, Save) and a field in which you can type using the keyboard.

So far, I tried to manage only Up button (which sens 'U' via bluetooth to arduino) and Down (which sens 'D'), which would move the servo intended for vertical arm movements. That code works quite good, although the app crashes sometimes, but I still can't figure the reason out. Using the .prinln() function I can see that Arduino read in the bluetooth input the character '⸮ ' , what does it mean? I use a baud of 9600.

Then, what I would like to do is: once I have moved the servo and reached a particular position, I'd like to save the position of the motor into an array. In order to allow multiple positions saving, after Save button is pressed, I want Arduino to wait for another input from the bluetooth (so that, it would wait for another button to be clicked from the app or for something typed into the dedicated bar from keyboard) that defines the position within the array, in which I save the servo position (eg: I type '2' into the dedicated app bar and Arduino, which is waiting for an input, receiving it, saves the last servo position into the array servo01pos[2]). I wrote the "case ('S')" code that is quite long and inefficient but, at least, it works.

Thanks in advance to anyone who will help me.

Giammarco

Here the full code:

#include 

SoftwareSerial bluetooth(10, 11); //BlueTooth RX ,TX
#include  // servo library

unsigned long timestampSend = 0; // BT Write wait period
String readString;

Servo servo01; // servo name
byte pos01 = 90;
#define ANGLE_STEP 10 // 10 degree steps
int servo01pos[50];
char data;
char button;
int x = 1;

void setup() {
  Serial.begin(9600);
  bluetooth.begin(9600);
  delay(1000);
  Serial.println("Ready ...");
  Serial.println("\n");
  servo01.attach(12);
  servo01.write(pos01);

}

void loop() {
  do {
    if (bluetooth.available() > 0) {
      Serial.println(data);
      do {
        delay(500);
        data = bluetooth.read();
        Serial.println(data);
      } while (bluetooth.available() > 0);
      if (data == 'U' || data == 'D' || data == 'L' || data == 'R' || data == 'F' || data == 'B' || data == 'S' || data == 'X') {
        Serial.print("Received value: ");
        Serial.println(data);

        switch (data) {
        case ('S'): //SAVE
          Serial.println("Enter the button you desire to set...");

          do {
            data = 254;
            bluetooth.listen();
            button = bluetooth.read();
            if (button == '0' || button == '1' || button == '2') {
              data = 'S';
            }
          } while (data != 'S');

          Serial.println("SAVING POSITIONS ...");

          if (button == '0') {
            servo01pos[0] = pos01;
          } else if (button == '1') {
            servo01pos[1] = pos01;
          } else if (button == '2') {
            servo01pos[2] = pos01;
          }
          servo01.write(pos01);

          Serial.println(button);
          Serial.println(pos01);
          Serial.println(servo01pos[1]);
          Serial.println(servo01pos[2]);
          Serial.println(servo01pos[0]);
          //}
          break;

        case ('U'):
          Serial.println("UP");
          if (pos01 <= 180 - ANGLE_STEP) {
            pos01 = pos01 + ANGLE_STEP;
            servo01.write(pos01);
            Serial.println(pos01);
            delay(20);
            Serial.println(data);
          }
          break;

        case ('X'):
          Serial.println("EXIT");
          x = 0;
          break;

        }
      }

    }

  } while (x != 0);
}

   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2805
 

Hi Giammarco 😊 

I won't be able to answer all your questions, but I'll try to answer what I can.

Posted by: @giammarcop

input the character '⸮ ' , what does it mean

This character basically is an indication that the character your Arduino receives doesn't arrive correctly.
The can be caused by several things. The serial port could be an issue, but you set the speed to 9600 baud and I assume you've set the output window to 9600 baud as well.

Another reason can be the character set that is being used by the app that is sending the characters over Bluetooth.
This can be a little bit of a headache at times, for example when your Arduino expects 8 bit code, but receives for example unicode characters (which can be 1 or more bytes).
Since I don't know much about what you used to develop the app, I'd start by looking there. As a test you could try sending numbers instead of letters, maybe this makes a difference.

As an alternative you could try using the println option to print HEX or DEC numbers, and look those numbers up in ASCII tables to see if they make any sense.

For example, instead of 

Serial.println(data);

Try:

Serial.print(data, DEC); // outputs data as decimal numbers "78"
Serial.print(data, HEX); // gives "4E"

Here "D" (capital D") results in "44" (hex) or "68" (dec). I suspect you may see not only something different, but maybe even 2 instead 1 number.

Personally, I'd first make a simple sketch, without any motor support, just to test the Bluetooth part of things, so you are 100% sure the code receives the right characters,.
Once that works you can start adding the motor functions.
Too many wildcards can make it hard to debug.

 

 

As for you switch-case statement: nothing wrong with that. 👍 
I do however tend to break things up a functions, so my code becomes a little more manageable.
This also helps you put pieces of code together for a specific task, which can be tested extensively. In the end you'd have function that works 100% and you would not have to worry too much about it when working on the rest of the code.

Just as an example, make a function for saving the position, eg:

void SavePosition() {
Serial.println("Enter the button you desire to set..."); do { data = 254; bluetooth.listen(); button = bluetooth.read(); if (button == '0' || button == '1' || button == '2') { data = 'S'; } } while (data != 'S'); Serial.println("SAVING POSITIONS ..."); if (button == '0') { servo01pos[0] = pos01; } else if (button == '1') { servo01pos[1] = pos01; } else if (button == '2') { servo01pos[2] = pos01; } servo01.write(pos01); Serial.println(button); Serial.println(pos01); Serial.println(servo01pos[1]); Serial.println(servo01pos[2]); Serial.println(servo01pos[0]);
}

And modify the switch-case to:

        switch (data) {
        case ('S'): //SAVE
          SavePosition; 
          break;

        case ('U'):
          Serial.println("UP");
          if (pos01 <= 180 - ANGLE_STEP) {
            pos01 = pos01 + ANGLE_STEP;
            servo01.write(pos01);
            Serial.println(pos01);
            delay(20);
            Serial.println(data);
          }
          break;

        case ('X'):
          Serial.println("EXIT");
          x = 0;
          break;

        }
      }

As you can see; the code has become much more readable. But in the end this is just a personal preference - your code is just fine as well.

I know this doesn't give you a cookie-cut fix for the issue, but I do think it will help get the issue resolved .. or at least point you in the right direction 😊 


   
ReplyQuote
(@giammarcop)
Active Member
Joined: 4 years ago
Posts: 4
Topic starter  

@hans Thank you very much for your comprehensive reply, very gracious of you. I'll try to explain some steps a bit deeper. I used 9600 baud in both window and code. I made some trials with different apps, I used someone just downloaded from PlayStore and I found same problems (after 2 or 3 'U' messages sent to Arduino, it stops to communicate to the mobile App and it print the reversed question mark). I also tried the catlog of Android Studio (by which I developped the app), no errors were found. 

Posted by: @hans

As a test you could try sending numbers instead of letters, maybe this makes a difference.

Posted by: @hans

As an alternative you could try using the println option to print HEX or DEC numbers, and look those numbers up in ASCII tables to see if they make any sense.

Anyway I can do as you suggest.

Posted by: @hans

I do however tend to break things up a functions, so my code becomes a little more manageable.

Great advice, I'll do it.

Posted by: @hans

I know this doesn't give you a cookie-cut fix for the issue, but I do think it will help get the issue resolved .. or at least point you in the right direction

Sure, thank you again!


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2805
 

A reason could be how you read the data from the BT module.
I see you use the read command, without checking if there is data:

data = bluetooth.read();

For example, it is quite common with serial communication (though I recall you're using the standard SoftwareSerial library) to do something like this:

  if(bluetooth.available()){
  c = bluetooth.read();
    Serial.print(c);
  }

Not sure if this is applicable for the library you're using though.
Basically saying: while characters are available: read a character and print it. (more info in these examples)

 

One other thing that came to mind, but may not be relevant, with the weird characters is the data length, parity bits and stop bits settings.
Back in the day (good old modems) this could cause garbage output, if not set right, as well.
Not sure if this can be set with Bluetooth, or is even relevant, since I do not have such a module, but back in the data 8 data bits, 1 stop bit and no parity (0) used to be quite common.

 


   
ReplyQuote
(@giammarcop)
Active Member
Joined: 4 years ago
Posts: 4
Topic starter  
Posted by: @hans

if(bluetooth.available()){
  c = bluetooth.read();
    Serial.print(c);
  }

Thank you again for the patience and your help. I tried a very short code to verify if that error could occour anyway. With that one I had not the same problem, until I add motors and functions to move the servomotors. Here the working code:

#include <SoftwareSerial.h>
SoftwareSerial bluetooth(10, 11);

void setup(){
Serial.begin(9600);
bluetooth.begin(9600);
Serial.println("The bluetooth gates are open.");
delay(1000);
}

void loop(){
while(true){
if (bluetooth.available()) {
char flag = bluetooth.read();
Serial.println(flag);
}
}

Here a easy code in which the problem occurs again:

#include <SoftwareSerial.h>
SoftwareSerial bluetooth(10, 11);

#include <Servo.h>
Servo servo01;
byte pos01 = 90;

void setup() {
// Bluetooth setup
Serial.begin(9600);
bluetooth.begin(9600);
Serial.println("The bluetooth gates are open.");
delay(1000);

// Servo setup
servo01.attach(12); //If a comment this line and the next one the code works well
servo01.write(pos01); //
}

void loop() {
while (true) {
if (bluetooth.available()) {
char flag = bluetooth.read();
Serial.println(flag);
}
}
}

Any thoughts? 

 

I also changed all my code to make it easier to read. Here the full changed code:

#define KEY_TYPE_LETTERS 1
#define KEY_TYPE_NUMBERS 2
#define ANGLE_STEP 10 // 10 degrees steps
#define ANGLE_MIN 0 // 10 degrees steps
#define ANGLE_MAX 180 // 10 degrees steps

#include <SoftwareSerial.h> // Bluetooth library
SoftwareSerial bluetooth(10, 11); // BlueTooth RX ,TX

#include <Servo.h> // Servo library
Servo servo01; // servo name
byte pos01 = 90;
int servo01pos[5];
char button;

void setup(){
// Bluetooth setup
Serial.begin(9600);
bluetooth.begin(9600);
Serial.println("The bluetooth gates are open.\n Connect to HC-05 from any other bluetooth device with 1234 as pairing key!.\nReady ...\n");
delay(1000);

// Servo setup
servo01.attach(12);
servo01.write(pos01);
}

void loop(){
while(true){
/*if (bluetooth.available()) {
String flag = bluetooth.readString();
Serial.println(flag);
//int flag2 = flag;
//Serial.println(flag2);
}
*/
char bt = readBT(KEY_TYPE_LETTERS);

switch (bt) {
case ('S'):
Serial.println("Pressed S");
pressedS();
break;
case ('U'):
Serial.println("Pressed U");
pressedU();
break;
case ('D'):
Serial.println("Pressed D");
pressedD();
break;
case ('X'):
Serial.println("Pressed X");
Serial.println("EXIT");
return;
}
}
}

/*void printArray(int[] array){
Serial.println("[");
for(int i = 0; i < array.length; i++){
if(i!=array.length-1){
Serial.println(myArray[i] + ", ");
} else {
Serial.println(myArray[i]);
}
}
Serial.println("]\n");
}*/

// Read the new value from bluetooth
// e' una funzione di nome readBT che prende in input un intero (keyType, che deve essere 1 o 2) e restituisce un carattere (in questo caso quello ricevuto dal BT)
char readBT(int keyType){

String btStr;
char bt;

while (true){
delay(500);

if (bluetooth.available()){
btStr = bluetooth.readString();
bt = btStr.charAt(btStr.length() - 1); //btStr.charAt(0)
Serial.println("readBT: Data#1 = ");
Serial.println(bt);
Serial.println("\n");

switch (keyType){
case KEY_TYPE_NUMBERS:
if(checkCharNumbers(bt)){
Serial.println("readBT: Data#2 = ");
Serial.println(bt);
Serial.println("\n");
return bt;
}
break;

case KEY_TYPE_LETTERS:
if(checkCharLetters(bt)){
Serial.println("readBT: Data#2 = ");
Serial.println(bt);
Serial.println("\n");
return bt;
}
break;

default:
return '\0';
}
}
}

}

// Queste funzioni danno vero se ricevono come input una dei char sotto
bool checkCharLetters(char bt){
return bt == 'U' || bt == 'D' || bt == 'L' || bt == 'R' || bt == 'F' || bt == 'B' || bt == 'S' || bt == 'X';
}

bool checkCharNumbers(char bt){
return bt == '0' || bt == '1' || bt == '2';
}

void pressedS(){
Serial.println("Enter the button you desire to set...");
char number = readBT(KEY_TYPE_NUMBERS);

Serial.println("SAVING POSITIONS ...");

if (number == '0') {
servo01pos[0] = pos01;
} else if (number == '1') {
servo01pos[1] = pos01;
} else if (number == '2') {
servo01pos[2] = pos01;
}
servo01.write(pos01);

Serial.println("Button:\n");
Serial.println(button + "\n");
Serial.println("pos01:\n");
Serial.println(pos01 + "\n");
Serial.println("servo01pos[0,1,2]\n");
Serial.println(servo01pos[0]);
Serial.println(servo01pos[1]);
Serial.println(servo01pos[2]);
//printArray(servo01pos);
}

void pressedU(){
pos01 = min(pos01 + ANGLE_STEP, ANGLE_MAX);
servo01.write(pos01);
Serial.println("New Pos after U:");
Serial.println(pos01);
delay(500);
}

void pressedD(){
pos01 = max(pos01 - ANGLE_STEP, ANGLE_MIN);
servo01.write(pos01);
Serial.println("New Pos after D:");
Serial.println(pos01);
delay(500);
}

Thank you again!

Giammarco


   
ReplyQuote
(@giammarcop)
Active Member
Joined: 4 years ago
Posts: 4
Topic starter  

I think I solved the problem using a power supply. It seems that the reversed question mark occurred when there was a power outage. Now it seems to work properly. Thank you very much again for your help!
Giammarco


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2805
 

Hi Giammarco!

OK, cool - good to hear you've found the culprit 😁 


   
ReplyQuote
Share: