3

I'm kind of new to Arduino and C, and I was having trouble with this sketch. I'm working on a project to control 6 servos using virtual buttons. When I hold down the button, the servo moves forward smoothly to a certain fixed point, and when I release it, it comes back to a fixed point. I initially tested this with a single servo and an actual button on my breadboard using the following sketch.

#include<Servo.h>
int switch1;
float switch1Smoothed;
float switch1Prev;
Servo myservo1;
float a=1200.0; //lower angle limit

void setup() {

Serial.begin(115200); pinMode(A1,INPUT_PULLUP); myservo1.attach(9); myservo1.writeMicroseconds(a); switch1Prev=a; }

void loop() { //Motor 1 switch1 = digitalRead(A1);// read switch | will return 1 or 0 continuously //Serial.println(switch1); switch1 = switch1 * 2400; // multiply by angle limit

// *** smoothing ***

switch1Smoothed = (switch1 * 0.05) + (switch1Prev * 0.95); if(switch1Smoothed>=a){ switch1Prev = switch1Smoothed; } else{ switch1Prev = a; } // *** end of smoothing ***

// Serial.print(switch1); // print to serial terminal/plotter // Serial.print(" , ");
// Serial.println(switch1Smoothed); myservo1.writeMicroseconds(switch1Smoothed) ; delay(10); // repeat loop 100 times per second }

This works out just fine. Now, I'm working with NodeRed, a low code JavaScript flow creator, which is where my virtual button is, and I've set it up to send 0s or 1s as strings based on whether the button is pressed or not via serial communication. This is the config of the Serial Node on NodeRed:

Serial Port Config on NodeRed

And I wrote a short function on Arduino to parse the incoming string from the serial connection, convert it back to an integer, and then use that as my "button" input. However, it seems that either my logic is flawed, or my code is because my servo does not behave the same way it does with the physical button.

Here's the modified sketch:

#include <Servo.h> 
int switch1;
float switch1Smoothed;
float switch1Prev;
Servo myservo1;
float a=1200.0;
String readString, mot1, mot2;

void setup() { Serial.begin(9600); myservo1.attach(9); myservo1.writeMicroseconds(a); switch1Prev=a; }

void captureChar(int& n1){ //using reference to pass the value

while (Serial.available()) { delay(3); //delay to allow buffer to fill if (Serial.available() >0) { char c = Serial.read(); //gets one byte from serial buffer readString += c; //makes the string readString } }

if (readString.length() >0) {

  mot1 = readString.substring(0,1); 
  mot2 = readString.substring(1,2); 
  n1 = mot1.toInt();
  //n2 = mot2.toInt();
readString=&quot;&quot;;

} }

void loop(){ captureChar(switch1); switch1 = switch1 * 2400; // multiply by maximum angle limit

// *** smoothing ***

switch1Smoothed = (switch1 * 0.05) + (switch1Prev * 0.95); if(switch1Smoothed>=a){ switch1Prev = switch1Smoothed; } else{ switch1Prev = a; } // *** end of smoothing ***

// Serial.print(switch1); // print to serial terminal/plotter // Serial.print(" , ");
// Serial.println(switch1Smoothed); myservo1.writeMicroseconds(switch1Smoothed); delay(15); }

When I load this sketch and hold down the virtual button, i.e., send a continuous stream of string "1", the motor simply goes back and forth really fast, rather than slowly moving to the set maximum position and stopping there while the button is held.

rayank97
  • 31
  • 1

1 Answers1

3

There may be a problem with your smoothing logic, which is quite convoluted. However, if the first code works fine, you can keep it almost as-is, and only replace digitalRead() with a version that reads the virtual button. It looks like that is what captureChar() attempts to achieve. This function, however, has a couple of problems:

  • it has a loop and a delay, thus breaking the timing of the original code
  • its interface is very different from the one of digitalRead(), and thus cannot be used as a drop-in replacement.

Here is what I would propose as a digitalRead() replacement. It has the same signature as the original digitalRead(), and can be used as a drop-in replacement:

int virtual_switch_reading = LOW;

int virtualDigitalRead(uint8_t /* pin number ignored */) { int c = Serial.read(); if (c == '0') virtual_switch_reading = LOW; else if (c == '1') virtual_switch_reading = HIGH; return virtual_switch_reading; }

Note that calling Serial.available() is not useful: if no data is available, Serial.read() returns -1, which is neither equal to '0' nor equal to '1'.


Edit: addressing the requirement to have six virtual switches.

As stated in a comment, I suggest you encode the states of all the switches, within NodeRed, as a single byte, with one bit per switch. You would then send this byte to the Arduino, either periodically or whenever any switch changes state.

On the Arduino, in order to simulate digitalRead() on a virtual switch, you first have to map the pin numbers to the virtual switches:

// Map a pin number to a virtual switch.
const uint8_t virtual_switch_masks[NUM_DIGITAL_PINS] = {
    0,     // pin 0: not virtual
    0,     // pin 1: not virtual
    1<<0,  // pin 2: virtual switch 0
    1<<1,  // pin 3: virtual switch 1
    // etc...
};

Note that this table doesn't store switch numbers. It instead stores bit masks. For example, virtual switch 1 is stored as 1<<1 = 0b00000010, i.e. all bits are clear except for bit number 1 (rightmost being zero), which is set.

You can then simulate digitalRead() by extracting the relevant bit from the byte you last received from NodeRed, which is done by a bitwise and with the relevant mask:

// States of all virtual switches, one bit per switch.
uint8_t virtual_switch_states = 0;

int virtualDigitalRead(uint8_t pin) { uint8_t mask = virtual_switch_masks[pin];

// If this is not a virtual switch, do a regular digitalRead().
if (!mask)
    return digitalRead(pin);

// For a virtual switch, extract the relevant bit
// from virtual_switch_states.
if (virtual_switch_states &amp; mask)
    return HIGH;
else
    return LOW;

}

In order to update the states of the virtual switches from the data received, you just have to add this somewhere within loop():

if (Serial.available())
    virtual_switch_states = Serial.read();
Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81