stringReplace, switchCase and bluetooth serial

Hi all,

I’m using a library to control lego powerfunctions motors, which recognises the commands PWM_FWDx and PWM_REVx, where x = 1-7 for speed and FWD/REV determine motor direction. I’m reading bluetooth serial data (values 0-4) and using that data to switch cases in my program. Since I need to send a command like “PWM_FWD4”, I split this command into two values: PWMstring and PWMvalue, so that I can modify the speed and direction independently.

2 cases modify the PWMvalue, and are able to increase and decrease the motor speed accordingly so I know that works. But I’m trying to switch the PWMstring to change direction and I just can’t get it working - the PWMstring never changes its value, so the motors only turn in one direction. Here’s the code:

#include <SoftwareSerial.h>// import the serial library
#include <legopowerfunctions.h> //Power Functions library

boolean newData = false;
boolean forward = true;

SoftwareSerial bluetooth(10, 11); // RX, TX
LEGOPowerFunctions lego(3); //IR pin for power functions control
char bluetoothData; // the data given from mobile app
String PWMstring = String('PWM_FWD');
int PWMvalue=0;

void setup() {
  Serial.begin(9600);
  bluetooth.begin(9600);
  pinMode(ledpin, OUTPUT);
}

void loop() {

recvOneChar();
controlTrain();
}

void recvOneChar() {
  
 if (bluetooth.available() > 0) {
 bluetoothData = bluetooth.read();
 newData = true;
 }
}

void controlTrain() {
  if (newData == true) {
    
 Serial.print("This just in ... ");
   switch (bluetoothData) {
    
     case '2': { //BROKEN
      
      if (forward == true) {  //train is moving forward
       String PWMstring = String('PWM_REV');  //change direction
       forward = false;
      }
      else      {          // train is reversing
       PWMstring.replace('PWM_REV', 'PWM_FWD'); //change direction
       forward = true;
       }
    setTrainSpeed(); [color=blue]//modified thanks to UKHeliBob[/color] 
      }
   break;
   
     case '3': {  // decrease motor speed
      PWMvalue--;
         if (PWMvalue<1) {
            PWMvalue=0;    
           stopTrain();
        }
      Serial.print("drop speed to ");
      Serial.println(PWMvalue);
      setTrainSpeed();  
      }
   break;
   
     case '4': { //increase motor speed
      PWMvalue++;
         if (PWMvalue>7) {
            PWMvalue=7;
        }
      Serial.print("increase speed to ");
      Serial.println(PWMvalue);
      setTrainSpeed(); 
      }
   break;
   }
   
 Serial.println(bluetoothData);
 newData = false;
  }
}

void setTrainSpeed()
{
  lego.SingleOutput(0, (PWMstring, PWMvalue), RED, CH1); 
}

besides “PWMstring.replace(‘PWM_REV’, ‘PWM_FWD’);”, I’ve also tried redeclaring the variable:
String PWMstring = String(‘PWM_REV’); - but this didn’t work either. I also tried using string.replace to swap the last three characters of the string, but that also had no effect. Can anyone shed any light on what I’m doing wrong?

I also tried using Serial.print to print out the contents of PWMvalue to debug, but the value never changed and I only got digits instead of the string itself - any idea why?

I have not looked too far into your code but this
          setTrainSpeed;does not call the setTrainSpeed function

Thanks for the keen eye. I guess that crept in while I was bugfixing. Like I said, I had added serial prints at every step, so I've already confirmed that the code is switching between the if and else in Case 2, and then calling up setTrainSpeed. But when printing PWMvalue, the content never changes.

          stopTrain;I can see a theme developing here.

Does the code actually get into any of the cases ?

Yes, the program switches cases just fine (there are two other cases I've cut from this code), and the startTrain and stopTrain functions are called when required to. I checked this by adding Serial.println(bluetoothData); to the end of the controlTrain function, and Serial.println(PWMscript); to the startTrain and stopTrain functions - Serial Monitor showed they were working, and the motor on my desk span accordingly. I added serialprintln(forward); into case 2, and saw that the boolean was swapping each time that case was triggered, so I know the if and else requirements are being met.

It's simply the content of PWMscript that never changes, and I don't know why.

String PWMstring = String('PWM_FWD');

Why diddle around with Strings in the first place, and if you do, what does PWMstring end up as after the line of code above bearing in mind that single quotes are used to enclose a single character in C ? Have you tried printing it at any time ?

UKHeliBob:

String PWMstring = String('PWM_FWD');

Why diddle around with Strings in the first place, and if you do, what does PWMstring end up as after the line of code above bearing in mind that single quotes are used to enclose a single character in C ? Have you tried printing it at any time ?

What would the alternative to strings be? Just declare it as an int? I'm still a relative beginner at coding.

As I said, I've tried printing this string every time I run the startTrain function, and the serial monitor returns a line of digits instead of any text characters. These digits never change regardless of the conditions in case 2 (if or else).

    void SingleOutput(int mode, int step, int output, int channel);  //4 parameters

As you can see, the SingleOutput() function is not expecting to receive a String (uppercase S) or a string (lowercase s) so I don't expect that your

 lego.SingleOutput(0, (PWMstring, PWMvalue), RED, CH1); //how many parameters ?

will produce any sensible results and what is it with the brackets inside the function parameters in your code ?

Advice : match the parameters to what the function expects.

UKHeliBob:
I don't expect that your

 lego.SingleOutput(0, (PWMstring, PWMvalue), RED, CH1); //how many parameters ?

will produce any sensible results

And yet, cases 3 and 4 are controlling the motor speed as expected, as can be seen on my desk right now.

UKHeliBob:
and what is it with the brackets inside the function parameters in your code ?

Advice : match the parameters to what the function expects.

Can you elaborate a bit? Maybe provide an example so I can see what you mean?

cases 3 and 4 are controlling the motor speed as expected

I suspect that is happening because of pure luck.

Try printing the parameters that you are using for SingleOutput() in the setTrainSpeed() function and include the braces. What do you think that (PWMstring, PWMvalue), as used in your program, resolves to ?

Can you elaborate a bit? Maybe provide an example so I can see what you mean?

The SingleOutput() function expects 4 parameters, all of them integers.

void SingleOutput(int mode, int step, int output, int channel);

I am not familiar with the library and only have the source for the library files to go on, but one thing is certain and that is no strings or Strings are involved.

In the .cpp file the function is as follows

void LEGOPowerFunctions::SingleOutput(int mode, int step, int output, int channel) {
    int nib1, nib2, nib3, nib4;

    //set nibs
    nib1 = toggle[channel] | channel;
    nib2 = 0x4 | mode | output;
    nib3 = step;
    nib4 = 0xf ^ nib1 ^ nib2 ^ nib3;

    message_pause(channel, messagecount);
    pf_send(nib1 << 4 | nib2, nib3 << 4 | nib4);

    if (toggle[channel] == 0)
        toggle[channel] = 8;
    else
        toggle[channel] = 0;
}

but I have no idea what the exact meaning of the parameters are.

I really appreciate you taking the time to dig into the library documentation. I've looked at it myself and the documentation really isn't clear. The "step" parameter seems to use the options from the keywords.txt file, but so far I've only got 14 of these to work; the PWM_FWDx and PWM_REVx commands. These definitely do work when put into the code like this:

lego.SingleOutput(0, PWM_REV2, RED, CH1)

but others seem to have no impact.

Annoyingly, it seems the way out of this is to put all 14 nibbles into an array and address them from there.

Bob, thank you. Without your help I wouldn't have explored the int path, which made things a lot simpler than I thought.

The line:

lego.SingleOutput(0, (PWMstring, PWMvalue), RED, CH1)

was working not because of PWMstring, but because of PWMvalue, which was declared as an int and which, by luck (as you said) corresponded to the numbers expected in the library for commands PWM_FWD1 to PWM_FWD7. The reverse motor speeds are numbered 9-15 for PWM_REV7 to PWM_REV1. So by changing the value of in PWMvalue, I was able to make this command work across all directions and speeds (a value of 8 is motor stop):

lego.SingleOutput(0, (PWMvalue), RED, CH1)

Thanks again.

lego.SingleOutput(0, (PWMvalue), RED, CH1)Why the brackets inside the brackets ?

...because I don't completely know what I'm doing. This is the first programming language I've ever learnt.