Optical encoder value corrupted ONLY during motor operation.

The project I'm working on uses a drive shaft which is driven — only some of the time — by a 24v brushed DC motor. This shaft's positions is also recordered using a 600p/r optical encoder.

Now when the motor is not running, the encoder works seamlessly with perfect precision. However, when the motor is running, the encoder's value becomes corrupted by up to a 10 steps per revolution (this progressivley becomes worse with each revolution until eventually it's obvious that the value has become corrupted as some point).

My initial thoughts were that the motor was causing electrical noise / interference with encoder hence why the encoder only malfunctions during motor operation. I carried out the following practices to eliminate this:

  • Not routing any high voltage wiring alongside or near the encoder wiring
  • Grounding the one end of the shield wire that runs along the rest of the encoder wires
  • Installing a 1uf ceramic capacitor across the terminals of the motor

With the fault still present, I disconnected the chain that links the motor to the shaft and ran the motor off a seperate power source (isolating it, so it's no longer being controlled by the microcontroller). Then I manually spun the shaft and montiored the encoder value to see if any interference was being created by the motor. The result: the encoder was working fine again.

This leads me to beleive that the root cause is not inteference being induced into the encoder wiring, otherwise the fault would have been present during the above test. And so it could be something to do with my code instead?

Board: ESP32
Motor: 24v Motor (rated at 18.5a but consumes about 8amps when under full load in my application)
Encoder
[Solid State Relays](http://www.photosensor.com.tw/Solid State Relay/Single Phase Solid State Relay (DC to DC SSR)/SSR-10DD.htm)
Voltage regulator
Motor driver

#define BLYNK_PRINT Serial

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
BlynkTimer timer;

char auth[] = "removed";

// WiFi credentials.
char ssid[] = "removed";
char pass[] = "removed";

// Optical Encoder
const int CLK = 22; // White
const int DT = 23;  // Green
volatile unsigned int temp, counter = 500;    // Encoder value starts at 500 instead of 0

// 24v motor
const int relayPin = 13;  //SSR used to creat open circuit when motor is off
const int ledpin = 12;
const int DCdir = 14;
const int freq = 5000; 
const int ledChannel = 0; 
const int resolution = 8;

// Connections to A4988
const int dirPin = 4;  // Direction
const int stepPin = 2; // Step
const int enablePin = 15;
 
// Motor steps per rotation
int STEPS_PER_REV = 20720; // CHANGE TO  for old stepper motor. This equals 1 turn  1036

boolean Trigger = false;    // FOr when stepper is released / engaged as part of programme
boolean Trigger2 = false;   // For when stepper is operated via phone

int conValue;     // Used to keep track of stepper motor position
byte oldconValue;

void setup()
{
// Debug console
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass);   // for if you want to specify server. See Blynk website for more info

// Setup a function to be called. In this case every 200milliseconds. 
  timer.setInterval(200L, myTimerEvent);

//Setting up input pins + interrupts for optical encoder
  pinMode(CLK, INPUT_PULLUP);
  pinMode(DT, INPUT_PULLUP);
  attachInterrupt(DT, ai0, RISING);
  attachInterrupt(CLK, ai1, RISING);

  ledcSetup(ledChannel, freq, resolution);
  ledcAttachPin(ledpin, ledChannel);
  
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enablePin,OUTPUT);
  pinMode(relayPin, OUTPUT);
}


void loop()
{
  Blynk.run();
  timer.run();  //Initiates Blynk timer

  noInterrupts();
  int count_copy = counter;   // Protects the varible from corruption by stopping interupts.
  interrupts();
  count_copy = constrain(count_copy, 0, 2000);
  if( count_copy != temp ){
    Serial.println(count_copy);
    temp = count_copy;
  }
       
  if(count_copy > 1500){    // UPPER THRESHOLD (turn 24v motor on)
    
      digitalWrite(relayPin, HIGH);  // Enable motore control via solid state relay
      digitalWrite(DCdir, LOW);      // Set motor direction
      ledcWrite(ledChannel, 155);   // Motor on
        if (Trigger == false){      // Release caliper. The if Trigger ensures this is only done once.
          digitalWrite(enablePin, LOW);
              for(int x = 0; x < (STEPS_PER_REV / 360 * conValue); x++) {
              // Set motor direction counterclockwise
              digitalWrite(dirPin,LOW);
              digitalWrite(stepPin,HIGH);
              delayMicroseconds(500);
              digitalWrite(stepPin,LOW); 
              delayMicroseconds(500);
              }
        digitalWrite(enablePin, HIGH);
        Trigger = true;             // Set trigger to True
        }
  }
  
  if(count_copy < 600 && Trigger == true){    // LOWER THRESHOLD (turn 24v motor off)
    
      ledcWrite(ledChannel, 0);       // Motor off
      digitalWrite(relayPin, LOW);    // Disable motor control
      
      //Code for reapplying stepper motor.
    digitalWrite(enablePin, LOW);     // Enable stepper motor
      for(int x = 0; x < (STEPS_PER_REV / 360 * conValue); x++) {
    digitalWrite(dirPin,HIGH);    // Set stepper motor direction counterclockwise
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(500);
    }
    digitalWrite(enablePin, HIGH);      //Disable stepper motor
    Trigger = false;
    repCountOnce = true;
  }
}

void ai0() {
  if(digitalRead(CLK)==LOW) {
  counter++;
  
  }else{
  counter--;
  }
 }
   
void ai1() {
  if(digitalRead(DT)==LOW) {
  counter--;
  
  }else{
  counter++;
  }
 }

 void myTimerEvent()
  {
// Send info to Blynk app. You can send any value at any time. But do not send more than 10 values per second.
  Blynk.virtualWrite(V0, count_copy);
}

It appears to me that it is noise generated by the motor or the field it creates. A schematic would be a big help. Are your ground connections good and no motor current is going through your arduino. What is the impedance of your sensor, is it active or passive. More information would be a big help.

I've attached the wiring diagram to the original post. All connections both power and ground are secure. The system doesn't use a chassis return for the ground; it's wired exaclty as shown in the diagram (although the diagram doesn't show the shield wire of the encoder which I just spliced to the ground wire that goes into the ESP32 - is this right?)

I'm not sure on the specifics of the sensor as I can't seem to find a detailed datasheet online.

It appears to me that it is noise generated by the motor or the field it creates.

Surely the measures I've already put in place would elimate any noise being created on the encoder wires?

A wiring diagram is fine as wall art, not for actually communicating how your project is wired. That's what we use schematic diagrams for. There's something marked "motor driver" and below it two completely mysterious black things...

Brushed DC motors are known to cause lots of interference. Caps between the terminals and from each terminal to the motor chassis (which should be grounded as well) are recommended, especially as you seem to have a really powerful motor. Higher current, higher voltage means more sparky sparky.

Wiring is important as well; the ground of the motor and the ground of the ESP32 board should only be connected at one point, close to the power supply. That keeps noise at bay as well.

You seem to be driving 2 ss relays from a gpio output through a buck converter.
I wouldnt be happy with that arrangement, gpio's arent intended to supply current.

I dont see a link to the specs or data sheets for the parts - other than the encoder.
So I cant check to see what current the ss relays take, or how the buck converter will affect that.

bradj98:
With the fault still present, I disconnected the chain that links the motor to the shaft and ran the motor off a seperate power source (isolating it, so it's no longer being controlled by the microcontroller). Then I manually spun the shaft and montiored the encoder value to see if any interference was being created by the motor. The result: the encoder was working fine again.

This leads me to beleive that the root cause is not inteference being induced into the encoder wiring, otherwise the fault would have been present during the above test. And so it could be something to do with my code instead?

Manual spinning the shaft isn't sending heavy currents down the motor wires, so its not the correct test.

Interference from motors is usually electromagnetic induction due to heavy currents being switched
rapidly, ie comes from the motor driver. There can also be nasty spikes put on the supply wires from
a motor driver.

You best defence is adding 1k pullup resistors to the two encoder lines, as well as the shielding and routing
precautions you took.

Replace that 1µF that's across the motor with more like 10nF, and make sure its ceramic. 1µF will be
making the current spikes through the wiring worse, the capacitor is to filter out RF interference which
requires a high frequency capacitor of modest size only.

The wires between motor and driver should be twisted pair if possible.

wvmarle:
Caps between the terminals and from each terminal to the motor chassis (which should be grounded as well) are recommended, especially as you seem to have a really powerful motor.

Okay I'll install a chassis ground for the power supply unit, motor and esp32 (and add capacitors from +ve and -ve lines of the motor to ground).

johnerrington:
You seem to be driving 2 ss relays from a gpio output through a buck converter.
I wouldnt be happy with that arrangement, gpio's arent intended to supply current.

I've just attached the links to the components I'm using in the original post. Those SSR only draw about 17.5ma according to the data sheet and the buck converter can put out up to 2a. Is there a safer way I could be controlling the relays via the esp32 than what I have at the moment?

MarkT:
Manual spinning the shaft isn't sending heavy currents down the motor wires, so its not the correct test.

Sorry I should have clarified, I was only spinning the shaft to see if the value was still reliable after a few turns whilst the motor was running in the background. But what you said in next about the interference coming from fast switching control of the motor driver means that my test wasn't correct. My bad. So the cause of all this is likely to still be interference, god I hate how sensitive some of this stuff can be to extenal voltages!

You best defence is adding 1k pullup resistors to the two encoder lines, as well as the shielding and routing
precautions you took.

Probably a stupid question so please forgive me, should I add the pullup resistors in the code or physically outside the esp32? (I'm guessing neither matters, though the first is easier but just want to be sure)

Replace that 1µF that's across the motor with more like 10nF, and make sure its ceramic. 1µF will be
making the current spikes through the wiring worse, the capacitor is to filter out RF interference which
requires a high frequency capacitor of modest size only.

How does it make the spikes worse? Surely the only downside of using a capitor too large or small is that it only filters out a frequency that isn't disrupting your circuit anyway. I'll do what you've said, but I'm just still abit unsure on how the selection of capitors works for filtering out noise.

EDIT: I've made a mistake on the wiring diagram I'm actually using a 0.1uF capacitor not 1uF.

Check the datasheet of the ESP32 - that 17.5 mA may well be too much for its pins to deliver (it's little brother, the ESP8266, can't do this for sure, iirc its limit is 12 mA, could be less even).

Page 42 of the ESP32 data sheet and a few other places online list the max output current of the gpio pins as being 40mA so I think I should be ok

posibly not the cause of your problems but you may want to look at this

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html

also the Voh for the io pin is Vdd * 0.8 = 2.6V

all just a little marginal perhaps, given that the buck converter even if 100% efficient will need almost 30mA.

An easy check - measure Vin to the buck converter when the relays are on.

So 17.5 mA per SSR, that's 35 mA to be delivered by the converter. The page linked to claims 90% typical efficiency, which is reasonable for such a device.

5V, 35 mA out requires close to 60 mA at 3.3V in. That's not accounting for the pin's internal resistance, which based on the numbers given in the datasheet (2.64V at 40 mA) is around 17.5Ω, again a very reasonable number. A single SSR operated at 5V through that boost converter would be pushing the limit of the pin already...

My proposed solution: use the buck converter to produce a 5V power source from your main power supply, and switch the SSRs through an NPN transistor or n-channel MOSFET on the low side. That allows the buck converter to comfortably produce all the current needed, and the ESP32 only has to supply the tiny current needed to operate the transistor.

@wv the trigger current for one SSR from the spec (linked post 1) is 7.5mA not 17.5 - unless its a misprint.

I'd go with your solution though.

I accidently typed 17.5mA instead of 7.5 earlier on in the post so @wvmarle was only referencing what I had said (sorry)

I'll do as you've both reccomended though and use a transitior to switch the relays. This would also mean I can just scrap off the buck converter right? As the input voltage can be anything below 32vdc and my power supply is only 24v.

At 7.5 mA that is better - but still you'd need some 25 mA from the pin, before taking internal resistance into account.

Indeed it sounds like you don't need that boost converter (a buck converter is to bring down the voltage) in your case, if you switch with a transistor. If you pick a MOSFET, make sure it's logic level. It may be more practical to go with a good old BJT for that reason - e.g. a BC547 with a 1k-5k resistor on the base.

So it would look like this?

That's wired high side. You have to place the transistor low side.

Yes that looks good. But with the transistor in the ground connection ie as wv says low side.
1k in the base should be fine - Voh=2.6V so 2V 1k = 2mA Ib.

But no largere as you do want the transistor to saturate.

Good teamwork @wvmarle +karma.

Ah yes, transitor on the low side. Got it!

Thanks for your input guys, it's really appreciated. I'll let you know how I get on.

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