"Constant Image Height" screen masking control code opinions

Hello,

I just getting started in this hobby with a need based project but intend to continue on for some other foreseeable projects and for something to do with my grandchildren when they’re a little older.

I pieced together a sketch from several sources (including this forum) and it works well as intended. Even though it works, I can’t help but think that maybe it could be simplified or maybe that there could be something lacking. I would welcome any suggestion for improvements.

What I have is a masking system for my constant image height screen in my home theater. Without getting into the mechanics of the masking system, it’s easier to just think of a shuttle moving back and forth on a timing belt with limit switches at both ends. It is driven by a 24v dc motor with a quadrature encoder.

I control it by IR remote. There are six memory position buttons and a momentary button for both forward and reverse. When either limit switch is triggered, the DC motor is stopped for safety, but lm_2 also resets the encoder to zero. Also if a particular limit is trigger, it prevents any further movement in that direction if another button is pushed once the motor is stopped.

The only unforeseen issue I had was when I push the remote button to fully close the screen, it did stop at 0, but it was 10mm short of being fully closed and of contacting the limit switch. I assuming the discrepancy is when the screen is fully closed and the limit switch is pressed and the encoder is being reset to 0, the encoder doesn’t start it’s actual count until it moves off the switch when it moves in the opposite direction. I had to compensate for this by changing memPosition[0] = 0; to –400.

I can provide a hardware list or wiring diagram if it would help.

Thanks in advance.

Here is my code:

#include <DualG2HighPowerMotorShield.h>
DualG2HighPowerMotorShield24v14 md;

// //Set up the IR remote
#include <IRremote.h>
#define MAX_TIME 150 // max ms between codes
long lastPressTime = 0;
int state = LOW;
const int receiver_pin = 1; //output pin of IR receiver to pin 1 of Pololu A-Star 32U4
IRrecv receiver(receiver_pin);
decode_results output;

const int M1DIR = 7; //pin 7 of Pololu A-Star 32U4 
const int M1PWM = 9; //pin 9 of Pololu A-Star 32U4
const int lm_1 = 11; // front limit switch that triggers fully open
const int lm_2 = 12; // rear limit switch that triggers fully closed
const uint8_t MANUAL = 1; //a constant to indicate manual mode
const uint8_t AUTOMATIC = 2; //a constant to indicate automatic mode

//Set up the motor encoder
#include <Encoder.h> //http://www.pjrc.com/teensy/arduino_libraries/Encoder.zip
Encoder myEnc(2, 3); //pins 2 and 3 are interrupt pins (best performance of the encoder data).
long oldPosition  = -999;
long targetPosition = 0;
long memPosition[] = {0, 0, 0, 0, 0, 0,};
#define ACCURACY 40 // (10-less accurate)accuracy of target position. Higher accuracy may result in 
// a bit of jitter as the motor nears the position
uint8_t MODE = MANUAL;

//Set up limit switches
byte lm1state;
byte lm2state;
byte last_lm1state;
byte last_lm2state;
unsigned long lastCheckTime;
byte checkInterval = 50; //50ms between switch reads for debounce

void setup() {
  Serial.begin(115200);
  receiver.enableIRIn(); // Start the receiver
  pinMode(M1DIR, OUTPUT);
  pinMode(M1PWM, OUTPUT);
  pinMode(lm_1, INPUT_PULLUP);
  pinMode(lm_2, INPUT_PULLUP);
  lastCheckTime = millis();
}

void loop() {
  md.enableM1Driver();
  delay(1);  // The drivers require a maximum of 1ms to elapse when brought out of sleep mode.

  if (millis() - lastCheckTime >= checkInterval)
  {
    lastCheckTime += checkInterval;
    checkswitches();
  }

  if (receiver.decode(&output))
  {
    receiver.resume();

    // Remote Button 8/CH+ Hold to stay on CW (Open Screen)
    if (output.value == 0x800F8408 && lm1state == LOW) {
      if (state == LOW) {
        state = HIGH;  // Remote Button pressed, so set state to HIGH
        openSreen();
        MODE = MANUAL;
      }
      lastPressTime = millis();
    }
    // Remote Button 7/CH- Hold to stay on CCW (Close Screen)
    if (output.value == 0x800F0407 && lm2state == LOW) {
      if (state == LOW) {
        state = HIGH;
        closeSreen();
        MODE = MANUAL;
      }
      lastPressTime = millis();
    }

    // Remote Button 0/Close - Press once to fully close screen
    if (output.value == 0x800F8400) {
      Serial.println("btnPos0");
      MODE = AUTOMATIC;
      targetPosition = memPosition[0];
    }

    // Remote Button 1/1.33 Press once for 1.33
    if (output.value == 0x800F0401) {
      Serial.println("btnPos1");
      MODE = AUTOMATIC;
      targetPosition = memPosition[1];
    }
    // Remote Button 2/1.78 Press once 1.78
    if (output.value == 0x800F8402) {
      Serial.println("btnPos2");
      MODE = AUTOMATIC;
      targetPosition = memPosition[2];
    }

    // Remote Button 3/1.85 Press once for 1.85
    if (output.value == 0x800F0403) {
      Serial.println("btnPos3");
      MODE = AUTOMATIC;
      targetPosition = memPosition[3];
    }
    // Remote Button 4/2.35 Press once for 2.35
    if (output.value == 0x800F8404) {
      Serial.println("btnPos4");
      MODE = AUTOMATIC;
      targetPosition = memPosition[4];
    }

    // Remote Button 5/2.40 Press once for 2.40(fully open)
    if (output.value == 0x800F0405) {
      Serial.println("btnPos5");
      MODE = AUTOMATIC;
      targetPosition = memPosition[5];
    }

    // Remote Button 9 Press once to stop 
    if (output.value == 0x800F8409) {
      stopMotor();
      Serial.println("stopMotor");
      MODE = MANUAL;
    }

    // Use this space for additional IR commands

  }
  if (state == HIGH && millis() - lastPressTime > MAX_TIME) {
    state = LOW; // Haven't heard from the Remote Button for a while, so not pressed
    digitalWrite(M1PWM, LOW);
    digitalWrite(M1DIR, LOW);
  }

  memPosition[0] = -400;     // fully closed 
  memPosition[1] = 30900;    // 1.33 note - 49 counts per mm of travel
  memPosition[2] = 41350;    // 1.78
  memPosition[3] = 43100;    // 1.85
  memPosition[4] = 54550;    // 2.35
  memPosition[5] = 56100;    // 2.40 fully open

  //check the encoder to see if the position has changed
  long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    Serial.println(newPosition);
  }

  if (MODE == AUTOMATIC && newPosition != targetPosition ) {
    Serial.print("Target/Actual:"); Serial.print(targetPosition); Serial.print(" / "); Serial.print(newPosition); Serial.print(" ["); Serial.print(abs(targetPosition - newPosition)); Serial.println("]");
    if (targetPosition < newPosition && lm2state == LOW) {
      Serial.println("AUTO CLOSE");
      closeSreen();
      MODE = AUTOMATIC;
    }
    if (targetPosition > newPosition && lm1state == LOW) {
      Serial.println("AUTO OPEN");
      openSreen();
      MODE = AUTOMATIC;
    }
    if ( (targetPosition == newPosition) || abs(targetPosition - newPosition) <= ACCURACY) {
      Serial.println("AUTO STOP");
      stopMotor();
      MODE = MANUAL;
    }
  }

  //added to reset encoder when "closed" limit switch hit.- need to check after going to NC switch
  if (lm2state == HIGH && newPosition < 31000) {
    Serial.println("reset");
    myEnc.write(0);
  }

  //md.disableDrivers(); // Put the MOSFET drivers into sleep mode.
}

void openSreen() {
  Serial.println("openSreen");
  digitalWrite(M1DIR, HIGH);
  digitalWrite(M1PWM, HIGH);
  //analogWrite(M1PWM, 400); use to change speed of motor with 400 as highest.
}

void closeSreen() {
  Serial.println("closeSreen");
  digitalWrite(M1DIR, LOW);
  digitalWrite(M1PWM, HIGH);
  //analogWrite(M1PWM, 400); use to change speed of motor with 400 as highest.
}

void stopMotor() {
  Serial.println("stopMotor");
  digitalWrite(M1DIR, LOW);
  digitalWrite(M1PWM, LOW);
}

void checkswitches()
{
  lm1state = digitalRead(lm_1);
  if (lm1state == HIGH && last_lm1state == LOW) //state change N.C. switch
  {
    stopMotor();
  }

  lm2state = digitalRead(lm_2);
  if (lm2state == HIGH && last_lm2state == LOW) //state change N.C. switch
  {
    stopMotor();
  }
  last_lm1state = lm1state;
  last_lm2state = lm2state;
}

Hi,
Welcome to the forum.

Can the 10mm be any over run of the motor, when you stop it, after you initially encounter the limit switch and reset the encoder?

Do you reset the encoder to 0 when you encounter the limit switch or when you move back off the limit.

Over run will add an offset to the encoder position.

If you have issued the Home/Fully closed action, why don't you look for the closed limit, rather than count position?
It will close completely everytime then.

Tom.... :slight_smile:

Or close at full speed until the encoder reads 10 and then go at slower speed until the switch is triggered. Reset the encoder to zero at the switch.

Thanks for the responses. I don’t believe it’s an over run issue. If it were an over run wouldn’t it read in negative numbers? The motor stopped at 0 right where it should, it’s just that its 10 mm short (technically 5mm, the gap between the to panels coming together is 10mm) of it’s starting position and not encounter the limit switch yet.

The encoder is reset to zero when it encounters the limit switch and from what I can tell it keeps resetting to zero until in comes off. I think that’s where the issue is.

To answer your last question, I did fixed the problem by changing the “close” position from 0 to –400, that way it won’t stop until it hits the limit switch and then it reset to 0. I believe that’s what you’re suggesting? It works but I didn’t know if that’s the most “eloquent” way to address it.

Again, I'm new at this and literally pieced together the code from at least five separate examples of using IR remotes, limit switches, encoders, direction control and memory position. I had to adapt sketches from push button to IR input. Try and decide what parts of some code to throw out hoping that just because it didn’t seem to effect what I was doing that some how it wasn’t still relevant. I got it to work from educated guesses, but mostly trial and error.

JHauser:
I assuming the discrepancy is when the screen is fully closed and the limit switch is pressed and the encoder is being reset to 0, the encoder doesn’t start it’s actual count until it moves off the switch when it moves in the opposite direction.

You could write a short program to test that hypothesis.

...R

Thanks for the input but I'm completely clueless on how to do that. And if I could prove my hypothesis correct, I wouldn't know what to do with the results.

Can you show my what program you would use and how you apply the results to my existing sketch? I learn better from examples.

JHauser:
Thanks for the input but I'm completely clueless on how to do that.

I had assumed you had written the program in your Original Post, or at least understand how it works.

I am not sufficiently familiar with your hardware to propose a program without having the hardware for testing.

...R

Maybe I should have been more clear when I said: “I pieced together a sketch from several sources (including this forum) “ in post #1,

And when I reiterated it in post #3: “*Again, I'm new at this and literally pieced together the code from at least five separate examples of using IR remotes, limit switches, encoders, direction control and memory position. I had to adapt sketches from push button to IR input. Try and decide what parts of some code to throw out hoping that just because it didn't seem to effect what I was doing that some how it wasn't still relevant. I got it to work from educated guesses, but mostly trial and error.” *

I didn’t write the program, I more or less assemble/modified it. I do understand most of how it works through trial and error, but not all.

The majority of the code came mostly from these sources. Written by people a lot better at this than I am.

Code & Syntax Overview

Limit Switches - cattledog

https://forum.arduino.cc/index.php?topic=506458.30

Receiving with the IRremote library

Programmable Positions

https://shiftautomation.com/linear-actuator-control-programmable-positions

What I can do is look at examples and modify them to work in my situation. I already have something that works “as is”.

What I was hoping for was that members of this forum would critique what I did and maybe point out to me some unforeseen shortcomings or where it could be improved or simplified.

I do understand the importance of being able to write my own code (and I will), and but right now I have to button this project up.

I also appreciate your quote “Two or three hours spent thinking and reading documentation solves most programming problems.”