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;
}