Controlling multiple vibration motors with force sensors, at three force levels

Hello,
I have this code where 3 (or more) vibration motors are controlled by corresponding resistive force sensors. Each force sensor's analog value gets assigned a force level 0-3, 0 being no touch, and 3 being the maximum force. Every time the force level changes, the vibration motor that corresponds to that sensor should pulse. The RGB LED should show the current force level as a color (although this will be mixed if more than one sensor is pressed, not ideal).

This was working great with 1 sensor, one motor. When I added more, I knew I didn't want to just copy and paste, it would be bulky and hard to edit. As I did some research, it seemed arrays and for loops would give me the ability to scale it up. And it has worked, the LED color changing works, and the vibration motors pulse, for each set of sensors/motors. But maybe someone can help me fix the following-

The issues I know of:

  1. When I press sensor no. 1 and no. 2 for example, I get all three vibration motors going off, though somewhat sporadically. But how is motor no. 3 getting any output from the for loop if sensor no. 3 is not being pressed and showing a value of 0?

  2. The last force sensor in the array seems to be giving the other 2 a small signal of 20-40 when pressed (happens after this sensor reaches force level 2). I think this is the root of the problem but cannot figure out why it is happening.

  3. I don't think my assignForceLevel() or pulseLed() are in the tidiest form, and am thinking that could be the source of the above problem. There is a lot of repetition in both, with a string of if/else statements. I would love any ideas for restructuring anything and everything to achieve the functionality and the scalability.

Code and schematic are posted below.

Thank you!

const int NUM_LEDS = 3;     //number of leds + 1 for off state
//const int calButton = 3;        //Button for calibration
const int sensorPin[] = {18,19,20};  //sensor pins 
const int NUM_FINGERS = sizeof(sensorPin)/sizeof(sensorPin[0]); // set number of fingers equal to number of sensor pins defined
const int vtPin[NUM_FINGERS] = {4,5,6};
const int LEDPinArray[NUM_LEDS] = { 10,11,12 }; //array used to set up all leds

unsigned long currentMillis;
unsigned long previousMillis;
const long pulseTime = 150;

int sensorValue[NUM_FINGERS] = {0,0,0};        //intitial sensorValue

int forceLevel[NUM_FINGERS] = {0,0,0};
bool flag1[NUM_FINGERS];
bool flag2[NUM_FINGERS];
bool flag3[NUM_FINGERS];

const int goalminV = 0;         //low end of range to map to
const int goalmaxV = 1023;      //high end of range to map to                
int minV = 0;                   //intitial minimum to be 
int maxV = 1023;                //intitial maximum to be 
int split1 = goalmaxV / NUM_LEDS;      //splitting point between LED 1 and 2
int split2 = split1 * 2;        //splitting point between LED 2 and 3
                
String p1 = "_";

void powerOnSelectedLEDOnly(int index)  //power on each led based on whether it is selected
{
  for (int i = 0; i < NUM_LEDS; i++) {
    if (i == (index - 1)) {
      digitalWrite(LEDPinArray[i], HIGH);
    } else {
      digitalWrite(LEDPinArray[i], LOW);
    }
  }
}
                              
void initAllLEDs()   {                     //set up all led pins as OUTPUT
  for (int i = 0; i < NUM_LEDS; i++) {
    pinMode(LEDPinArray[i], OUTPUT);
  }
}

void initAllVTs()   {                     //set up all VT pins as OUTPUT
  for (int i = 0; i < NUM_FINGERS; i++) {
    pinMode(vtPin[i], OUTPUT);
  }
}

void assignForceLevel() {
  for (int i = 0; i < NUM_FINGERS; i++) {
      if ((sensorValue[i] > 0) && (sensorValue[i] < split1)) {
      forceLevel[i] = 1;
    } else if ((sensorValue[i] > split1) && (sensorValue[i] < split2)) {
      forceLevel[i] = 2;
    } else if (sensorValue[i] > split2) {
      forceLevel[i] = 3;
    } else {
      forceLevel[i] = 0;
    }
  }
}

void pulseVts() {
    for (int i = 0; i < NUM_FINGERS; i ++) {    //turn on VT
    
      if ((forceLevel[i] == 1) && (flag1[i] == 0)) {
        digitalWrite(vtPin[i], HIGH);
        flag1[i] = 1;
        previousMillis = millis() + pulseTime;  //get the current "time" (actually the number of milliseconds since the program started)
      }
      if ((digitalRead(vtPin[i] == HIGH)) && (millis() >= previousMillis)) {
        digitalWrite(vtPin[i], LOW);
        if (forceLevel[i] != 1) {
        flag1[i] = 0;
        }
      }
      if ((forceLevel[i] == 2) && (flag2[i] == 0)) {
        digitalWrite(vtPin[i], HIGH);
        flag2[i] = 1;
        previousMillis = millis() + pulseTime;  //get the current "time" (actually the number of milliseconds since the program started)
      }
      if ((digitalRead(vtPin[i] == HIGH)) && (millis() >= previousMillis)) {
        digitalWrite(vtPin[i], LOW);
        if (forceLevel[i] != 2) {
        flag2[i] = 0;
        }
      }
      if ((forceLevel[i] == 3) && (flag3[i] == 0)) {
        digitalWrite(vtPin[i], HIGH);
        flag3[i] = 1;
        previousMillis = millis() + pulseTime;  //get the current "time" (actually the number of milliseconds since the program started)
      }
      if ((digitalRead(vtPin[i] == HIGH)) && (millis() >= previousMillis)) {
        digitalWrite(vtPin[i], LOW);
        if (forceLevel[i] != 3) {
        flag3[i] = 0;
        }
      }
    }    
  }

void setup() {
  // put your setup code here, to run once:
  initAllLEDs();
  initAllVTs();
  //pinMode(calButton, INPUT);
  //pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  previousMillis = millis();  
}

void loop() {
  // put your main code here, to run repeatedly:
  
  for (int i = 0; i < NUM_FINGERS; i ++) {    //read sensorValues continuously
  int readVal = analogRead(sensorPin[i]);
  sensorValue[i] = readVal;
  }
  
  assignForceLevel();

  pulseVts();

  for (int i = 0; i < NUM_FINGERS; i ++) {    //print sensor values
  Serial.println(sensorValue[i] + p1 + sensorValue[i] + p1 + minV + p1 + maxV + p1 + forceLevel[i] + p1 + flag2[i] + p1 + digitalRead(vtPin[i]));
  
  powerOnSelectedLEDOnly(forceLevel[i]);
  }
  delay(50);
  //calibrateWhile();
}
/*void calibrate() {
  digitalWrite(LED_BUILTIN, HIGH);
  
    for (int i = 0; i < NUM_FINGERS; i ++) {
    int readVal = analogRead(sensorPin[i]);
    sensorValue[i] = readVal;
    //maxV = 0;
      if (sensorValue[i] > maxV) {
        maxV = sensorValue[i];
      }
      if (sensorValue[i] < minV) {
        minV = sensorValue[i];
      }
  }
}

void calibrateWhile(){
 while (digitalRead(calButton) == 1)  //recalibrate min and max when button is pressed
  {
    maxV = 0;
    calibrate();
    Serial.println("Calibrating...");
      for (int i = 0; i < NUM_FINGERS; i ++) {
      Serial.println(analogRead(sensorPin[i]) + p1 + goalminV + p1 + goalmaxV + p1 + forceLevel[i]);
      }
    if (digitalRead(calButton) == 0) {
      break;
    }   
  }
  digitalWrite(LED_BUILTIN, LOW);  // turn led off to show calibration is done
}*/```

Sorry I do not work well with word problems or frizzy pictures. Post an annotated schematic showing all connections and power sources. Links to technical information on each of the hardware items will save all of us a lot of time.

You cannot power motors, even tiny ones, from the Arduino output pins.

It might work for a short while, but doing so will sooner or later destroy the Arduino.

Each motor requires a separate transistor, base or gate resistor and a voltage spike protection diode across the motor terminals. Example circuits

Thanks for the feedback, and critical information on how to power even tiny motors on arduino pins. I have added these circuits, and am still having the same issues listed in the OP. I have also included a schematic, which should be clearer, I apologize I am just learning this stuff.

So, it seems likely to me that the code has some glaring issues that I am missing/don't understand. I want the sensor's signal to only affect the motor that it corresponds to, and right now there is some scrambling going on... I would really appreciate someone just giving it a read and double checking my method. Also open to different approaches.

Vibration motor datasheet
FSR Datasheet

I think you're messing up your timing in pulseVts().
I only have one milles() in my code at the beginning of Loop();
currentMillis = millis();
I use always currentMillis as the current time.

In pulseVts() the previousMilles is reset without splitting the 3 timings. Use an array for the different timings.

A proposal for assignForceLevel

void assignForceLevel() {
  for (int i = 0; i < NUM_FINGERS; i++) {
	forceLevel[i] = 0;	
	if (sensorValue[i]) {
		if (sensorValue[i] < split1) forceLevel[i] = 1;
	    else sensorValue[i] = (sensorValue[i] < split2) ? 2 : 3;
    }
  }
}

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