Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
Russell Hui
Published © CC BY-NC-SA

Web Enabled, 3D Printed, Motorized Sliding Window Retrofit

This 3D printed device will retrofit onto existing sliding windows to allow web enabled, motorized open/close function.

IntermediateFull instructions provided2 hours650
Web Enabled, 3D Printed, Motorized Sliding Window Retrofit

Things used in this project

Hardware components

Particle Photon
×1
Adafruit INA219 Break Out Board
×1
Adafruit Powerboost 500C
×1
Pololu U3V12F12
×1
Adafruit TB6612 DC and Stepper Motor Driver
×1
Micro Limit Switch
×1
18650 Lithium Battery
×1
Pin and Terminal Kit
×1
20 Gauge Wire
×1
Metrix Hex Cap Screws
×1
DFRobot DF Robot 12V Geared Motor 50RPM
×1
Button Switch
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE

Hand tools and fabrication machines

M2, M3, M4 Hex Tools
Pin Crimp Tool
Soldering Iron

Story

Read more

Custom parts and enclosures

Sliding Window Opener

Schematics

Wiring Diagram

This is a fritzing diagram to hook up all of the breakout boards/components

Code

Sliding Window Opener

Arduino
Source code for Particle Photon.
/*
 * Project Window_Controller_ST_Machine v1.0
 * Description: This Script is intended for single motor sliding window for use with Particle Photon
 * Author: Russell Hui
 * Date: 12/20/20
 */

//Librareis
#include <adafruit-ina219.h>



//Case names for motor commands
enum Mot_Cmd {m_open, m_close, m_hold, m_stop};
enum Mot_Ort {h_left, h_right, s_left, s_right, undef};
enum Win_Cmd {win_move, win_learn, win_diag, set_cur_sens, man_mov, set_ind, err, batt_stat, stored_val, reset_pos, set_mot_ort, diag_matrix, user_stop, sys_reset};

//EEPROM Adresses
int stall_curr_add = 0;  //Type int = 4 bytes
int open_count_add = 5; //type int = 4 bytes
int close_count_add = 10; //type int = 4 bytes
int mot_ort_add = 15; //type int = 4 bytes

//Setup Battery Management Pin via Powerboost 1000C LBO Pin
int batt_LBO_pin = A5;

//Setup delay time for states
int state_delay = 1000;

//Setup counter for state
int loop_counter = 0;


//define Motor_CU class
class Motor_CU
{
    //#######Class variables###########

    //Limit switch Pins
    int SW_Open;
    int SW_Close;

    //TB6612 Motor Driver pins
    int DRV_AIN1; 
    int DRV_AIN2;
    int DRV_A_PWM;
    int DRV_SLP;

    //Private Variables
    Adafruit_INA219 curr_obj; //object for current
    Mot_Ort mot_ort = undef; //motor orientation default is undef
    int target_pos = -9; //target position


    public:
    //Public Variables
    int track_length = 2; //max track positions (0 = close, 1 = ajar, 2 = open)
    int current_ma_sum; //sum of previous current for averaging
    int stall_curr; //motor stall current is negative
    int Diag_Matrix[1200][2]; //matrix for diagnostics. index at 0, (assume 60 seconds to full open and 50ms state delay)
    int current_state = 7; //current state motor is in. default to error
    String window_state = ""; //reflects state of motor
    int current_pos = -9; //current position
    int stop_flag = 0; //Flag indicating motor should stop with condition (0 = OK, 1 = over current, 2 = interrupt)
    int open_count = -9; //number of counts to fully open
    int close_count = -9; //number of counts to fully close

    //Constructor input: current sensor pin, current sensor i2C address, A/BIN1, A/BIN2, SLP
    Motor_CU(uint8_t ina_add, int drv_1, int drv_2, int drv_3, int drv_sp, int sw_op, int sw_cl) 
    {

        //Create adafruit-ina219 current sensor object
        Adafruit_INA219 curr_ob(ina_add);
        curr_obj = curr_ob;
        curr_obj.begin();

        //Set motor driver pins
        DRV_AIN1 = drv_1;
        DRV_AIN2 = drv_2;
        DRV_A_PWM = drv_3;
        DRV_SLP = drv_sp;

        pinMode(DRV_AIN1, OUTPUT);
        pinMode(DRV_AIN2, OUTPUT);
        pinMode(DRV_A_PWM, OUTPUT);
        pinMode(DRV_SLP, OUTPUT);

        //Set limit switch pins
        SW_Open = sw_op;
        SW_Close = sw_cl;

        pinMode(SW_Open, INPUT_PULLUP); //set pull up resistor
        pinMode(SW_Close, INPUT_PULLUP); //set pull up resistor

        //By default motor driver is disabled
        Enable_Mot(0);

    }
    //Functions
    //Get open_count 
    int Get_Open_Count(bool perm_mem = false)
    {
        int count;
        if (perm_mem == true){
            EEPROM.get(open_count_add, count);
            if(count == 0XFFFF){
                //EEPROM is empty, return uninit value
                count = -9;
            }
        }
        else{
            count = open_count;
        }
        return count;
    }
    //Set open_count
    void Set_Open_Count(int count, bool perm_mem = false)
    {
        open_count = count;
        if (perm_mem == true)
        {
            //Write open count to EEPROM
            EEPROM.put(open_count_add, count);
        }
    }
    //Get close_count
    int Get_Close_Count(bool perm_mem = false)
    {
        int count;
        if (perm_mem == true){
            EEPROM.get(close_count_add, count);
            if(count == 0XFFFF){
                //EEPROM is empty, return uninit value
                count = -9;
            }
        }
        else{
            count = close_count;
        }
        return count;
    }
    //Set cloes_count
    void Set_Close_Count(int count, bool perm_mem = false)
    {
        close_count = count;
        if (perm_mem == true)
        {
            //Write open count to EEPROM
            EEPROM.put(close_count_add, count);
        }
    }

    //Get open limit switch position
    int Get_Open_SW()
    {
        //For normally closed limit switch
        //Returns 1 if switch is tripped
        if (digitalRead(SW_Open) == LOW)
        {
            return 0;
        }
        else {
            return 1;
        }
    }
    //Get close limit switch position
    int Get_Close_SW()
    {
        //For normally closed limit switch
        //Returns 1 if switch is tripped
        if (digitalRead(SW_Close) == LOW)
        {
            return 0;
        }
        else {
            return 1;
        }
    }
    //Get current value based on moving average
    int Get_Current_MAVG() 
    {
        //3 sample moving average
        float current_ma = 0;
        //call Adafruit object member function
        current_ma = abs(curr_obj.getCurrent_mA()); //always keep current value positive
        // if current_ma_sum = 0, this is a new movement and moving average resets
        if (current_ma_sum == 0) {
            for (int i = 1; i < 4; i++)
            {
                current_ma_sum = current_ma_sum + abs(curr_obj.getCurrent_mA());
                delay(5);
            }
        }
        else
        {
            current_ma_sum = current_ma_sum - (current_ma_sum/3) + current_ma;
        }
        return current_ma_sum/3; 
    }

    //Setter for Stall Current
    void Set_Stall_Current(int cur, bool perm_mem = false)
    {
        //Set stall current
        stall_curr = cur;
        if (perm_mem == true)
        {
            //Write stall current to EEPROM
            EEPROM.put(stall_curr_add, stall_curr);
        }
    }
    //Getter for Stall Current
    int Get_Stall_Current(bool perm_mem = false)
    {
        int curr;
        //Retrieve stall current from EEPROM
        if (perm_mem == true)
        {
            EEPROM.get(stall_curr_add, curr);
            if(curr == 0XFFFF){
                //EEPROM is empty, restore unlearned current value
                curr = 0;
            }
        }
        else
        {
            curr = stall_curr;
        }
        return curr;
    }

    //Setter for target position
    void Set_Target_Pos(int pos)
    {
        //Check to make sure target pos is within range
        if (pos < 0) {
            target_pos = 0;
        }
        else if (pos > track_length) {
            target_pos = track_length;
        }
        else {
            target_pos = pos;
        }
        //write target pos into S-Ram?
    }
    //Getter for target position
    int Get_Target_Pos(){
       return target_pos; 
    }

    //set motor orientation
    void Set_MotorOrient(Mot_Ort mot_p, bool perm_mem = false)
    {
        //Setting mtoor oreintation will write into EEPROM
        mot_ort = mot_p;
        if (perm_mem == true)
        {
            //Write mot_ort into EEPROM
            EEPROM.put(mot_ort_add, mot_ort);
        }
     }
    //get motor orientation from EEPROM
    Mot_Ort Get_MotorOrient(bool perm_mem = false)
    {
        Mot_Ort mot_p;
        //return from EEPROM
        if (perm_mem = true)
        {
            EEPROM.get(mot_ort_add, mot_p);
            if(mot_p == 0XFFFF)
            {
                //EEPROM is empty, set undef
                mot_p = undef;
            }
        }
        //return from class variable
        return mot_p;
    }
    //enable/disable motor driver pin
    void Enable_Mot(int enable_flag)
    {
        switch(enable_flag)
        {
            case 0:
                digitalWrite(DRV_SLP, LOW);
            break;
            case 1:
                digitalWrite(DRV_SLP, HIGH);
            break;
        }
    }
    //PWM motor driver
    void Set_PWM(int speed, Mot_Cmd direction)
    {
        //For Debug Purpose
        //Particle.publish("Set_PWM_Direction", String(direction), PRIVATE);
        if (mot_ort != undef ) {
            Mot_Cmd x = direction;
            switch (x)
            {
                case m_open:
                    switch (mot_ort)
                    {
                        case h_left: //left hung motor
                        case s_left: //left sliding
                            digitalWrite(DRV_AIN2, HIGH);
                            digitalWrite(DRV_AIN1, LOW);
                            analogWrite(DRV_A_PWM, speed);
                        break;

                        case h_right: //right hung motor
                        case s_right: //right sliding
                            digitalWrite(DRV_AIN1, HIGH);
                            digitalWrite(DRV_AIN2, LOW);
                            analogWrite(DRV_A_PWM, speed);
                        break;


                    }
                break;

                case m_close:
                    switch (mot_ort)
                    {
                        case h_left: //left hung motor
                        case s_left: //left sliding
                            digitalWrite(DRV_AIN1, HIGH);
                            digitalWrite(DRV_AIN2, LOW);
                            analogWrite(DRV_A_PWM, speed);
                        break;

                        case h_right: //right hung motor
                        case s_right: //right sliding
                            digitalWrite(DRV_AIN2, HIGH);
                            digitalWrite(DRV_AIN1, LOW);
                            analogWrite(DRV_A_PWM, speed);
                        break;
                    }
                break;

                case m_hold:  //powered stop
                    digitalWrite(DRV_AIN1, HIGH);
                    digitalWrite(DRV_AIN2, HIGH);
                break;

                case m_stop: //unpowered stop
                    digitalWrite(DRV_AIN1, LOW);
                    digitalWrite(DRV_AIN2, LOW);
                break;

            }
        }
        else {
            Particle.publish("Set_PWM", "mot_ort is undefined", PRIVATE);
        }

    }
   
};

//Create motor objects
Motor_CU mot = Motor_CU(0x40, D4, D3, D2, D7, A1, A2); //INA219 Add, DRV AIN1, DRV AIN2, DRV SLP, Switch open, switch close


//Set up variable to return
String window_state;

void setup() {
//Define web functions
    Particle.function("Input", Input_Command);
//Setup Debug Variables
    Particle.variable("Window_State", window_state);

//Setup Battery LBO Pin
    pinMode(batt_LBO_pin, INPUT); //will be pulled to low when battery is low (5V pin)
    
//Setup interrupt pin
    pinMode(A0, INPUT_PULLUP); //set pull up resistor
    attachInterrupt(A0, Button_Push_Interrupt, RISING);

//Setup sleep/wake pin
    pinMode(A3, INPUT);


//Restore motor parameters from EEPROM
//Motor Orientation
    if (mot.Get_MotorOrient(true) != undef)
    {
        //Restore motor orientation if exists
        mot.Set_MotorOrient(mot.Get_MotorOrient(true), false);
    }

//Stall Current
    if (mot.Get_Stall_Current(true) > 0){
        //If stall current exists in EEPROM, write motor stall current to RAM
        mot.Set_Stall_Current(mot.Get_Stall_Current(true), false);
    }
//Restore motor open/close loop counts from EEPROM  if exists
    if (mot.Get_Open_Count(true) >= 0 ){
        mot.Set_Open_Count(mot.Get_Open_Count(true), false);
    }
    if (mot.Get_Close_Count(true) >= 0){
        mot.Set_Close_Count(mot.Get_Close_Count(true), false);
    }
//Restore motor position based on S-Ram

//Find current state based on limit switches
//default is error state set in class definition
    if (mot.Get_Close_SW() == 1 && mot.Get_Open_SW() == 0) {
        //closed state
        mot.current_state = 3;
        mot.current_pos = 0;
        mot.Set_Target_Pos(0);
    }
    if (mot.Get_Close_SW() == 0 && mot.Get_Open_SW() == 1) {
        //open state
        mot.current_state = 0;
        mot.current_pos = mot.track_length;
        mot.Set_Target_Pos(mot.track_length);
    }
    if (mot.Get_Close_SW() == 0 && mot.Get_Open_SW() == 0)
    {
        //ajar state is defaulted 
        mot.current_state = 0;
        mot.current_pos = 1;
        mot.Set_Target_Pos(1);
    }
    
}

void loop() {

    //Report back current state to cloud
    window_state = mot.window_state;

    //Switch states
    switch(mot.current_state){
        case 0:
        //Window is Open
            state_open();
        break;

        case 1:
        //Window is Opening
            state_opening();
        break;

        case 2:
        //Window is Closing
            state_closing();
        break;

        case 3:
        //Window is Closed
            state_closed();
        break;

        case 4:
        //Window is Stopped
            state_stop();
        break;

        case 5:
        //Window is Learning
            state_learn();
        break;

        case 6:
        //Diagnostics State
            state_diag();
        break;

        case 7:
        //Error state
            state_error();
        break;
    }

    delay(state_delay);

}


//Helper Functions

//State Change Function
void state_change(int state) {
    mot.current_state = state;
}

//Function to monitor motor position and check for any errors
void Monitor_Position(Mot_Cmd direction)
{
    int m_cur = 0; //Temporary storage for motor current values

    //Populate diag matrix with current on each loop
    m_cur = mot.Get_Current_MAVG();
    mot.Diag_Matrix[loop_counter][0] = m_cur;
    //Populate time elapsed?
    //increment loop counter
    if (loop_counter < 1201){
        //loop counter max is set due to limited size of Diag Matrix
        loop_counter = loop_counter + 1; 
    }

    //Check to see if limit switch is hit
    switch(direction) {

        case m_open:
            //Window is ajar
            if (mot.Get_Close_SW() == 0 )
            {
                mot.current_pos = 1;
            }
            //Window is open
            if (mot.Get_Open_SW() == 1)
            {
                mot.current_pos = 2;
            }
        break;

        case m_close:
            //Window is ajar
            if (mot.Get_Open_SW() == 0)
            {
                mot.current_pos = 1;
            }
            //Window is closed
            if (mot.Get_Close_SW() == 1)
            {
                mot.current_pos = 0;
            }
        break;
    }

        
    //Check to make sure motor has not stalled
    //Delay in current sensing to allow mag field build up (750s based on 50ms state loop delay)
    if (loop_counter == 1)
    {
            //Reset moving average for current
            mot.current_ma_sum = 0;
    }
    
    if (loop_counter >= 15)
    {
        //Compare absolute current value
        if (m_cur > mot.Get_Stall_Current())
        {
            //Eliminate state loop delay
            state_delay = 0;
            //Set fault flag
            mot.stop_flag = 1;
        }
    }
 
}

//Initiates motor and initial ramp up when opening/closing
void Change_Position(Mot_Cmd direction)
{
    //Clear diagnostic matrix
    memset(mot.Diag_Matrix, 0, sizeof(mot.Diag_Matrix));
    //Enable motor 
    mot.Enable_Mot(1);
    mot.Set_PWM(255, direction);


}


//Functions for state
void state_open() {
    //if state change occured publish state change
    if (mot.window_state != "Window Open")
    {
        if (mot.current_pos == 1)
        {
            Particle.publish("State", "Window Ajar", PRIVATE);
            mot.window_state = "Window Open";
            //Reset loop counter
            loop_counter = 0;
        }
        else if (mot.current_pos == 2)
        {
            Particle.publish("State", "Window Open", PRIVATE);
            mot.window_state = "Window Open";
            //Reset loop counter
            loop_counter = 0;
        }
    }
    //Check if new target position has been set
    if (mot.Get_Target_Pos() != mot.current_pos)
    {
        //Error state
        if(mot.Get_Target_Pos() < 0)
        {
            //Go to error state
            state_change(7);
        }
        else if(mot.Get_Target_Pos() < mot.current_pos)
        {
            //Go to closing state
            Change_Position(m_close);
            state_change(2);
        }
        else if(mot.Get_Target_Pos() > mot.current_pos)
        {
            //Go to opening state
            Change_Position(m_open);
            state_change(1);
        }
    }

    //Slow down the delay rate at closed state
    state_delay = 1000;
    /*
    //logic to increase the state delay with no activity
    loop_counter = loop_counter + 1;
    //Go to sleep after 30 seconds
    if (loop_counter > 30)
    {
        mot.window_state = "Sleep Open";
        
        SystemSleepConfiguration config;
        config.mode(SystemSleepMode::STOP)
        .gpio(A3, RISING);

        System.sleep(config);
    }
    */
}

void state_opening() {
    //if state change occured publish state change
    if (mot.window_state != "Opening")
    {
        Particle.publish("State", "Opening", PRIVATE);
        mot.window_state = "Opening";
        //reset loop counter before entering monitor loop
        loop_counter = 0;
    }
    //when in this state the motor should be running 
    if (mot.current_pos != mot.Get_Target_Pos())
    {
        state_delay = 50; //Change state delay for faster response
        //Continue to monitor position
        Monitor_Position(m_open);
    }

    //Check if met target position or if a stop is flagged
    if (mot.current_pos == mot.Get_Target_Pos() || mot.stop_flag != 0)
    {
    //Apply powered motor braking
        mot.Set_PWM(0, m_hold);
        delay(100);
    //Go to stop state if position is met
        state_change(4);
    }
}

void state_stop() {
    //Power off motors in stop state
    mot.Set_PWM(0, m_stop);
    //disable motor driver
    mot.Enable_Mot(0);
    
    //Declare state on entry
    if (mot.window_state != "Stop")
    {
        Particle.publish("State", "Stop", PRIVATE);
        mot.window_state = "Stop";
    }

    //Decide which state to transition to
    if (mot.current_pos != 0 && mot.stop_flag == 0) {
        state_change(0); //open state
    }
    else if (mot.current_pos == 0 && mot.stop_flag == 0) {
        state_change(3); // close state
    }

    //Fault condition (Stop_Flag = 1) move to error state
    else if (mot.stop_flag != 0)
    {
      state_change(7);
    }
}

void state_closed() {
    //if state change occured publish state change
    if (mot.window_state != "Window Closed")
    {
        Particle.publish("State", "Window Closed", PRIVATE);
        mot.window_state = "Window Closed";
        loop_counter = 0;
    }



    //Check if new target position has been set
    if (mot.Get_Target_Pos() != mot.current_pos)
    {

        if(mot.Get_Target_Pos() < mot.current_pos)
        {
            //Error state
            state_change(7);
        }
        else if(mot.Get_Target_Pos() > mot.current_pos)
        {
            //Go to opening state
            Change_Position(m_open);
            state_change(1);
        }

    //Slow down the delay rate at closed state
    state_delay = 1000;
    /*
    //logic to increase the state delay with no activity
    loop_counter = loop_counter + 1;
    //Go to sleep after 30 seconds
    if (loop_counter > 30)
    {
        mot.window_state = "Sleep Close";
        
        SystemSleepConfiguration config;
        config.mode(SystemSleepMode::STOP)
        .gpio(A3, RISING);
        //.duration(5min);
        System.sleep(config);
    }
    */
    
    }
}

void state_closing() {
    //if state change occured publish state change
    if (mot.window_state != "Closing")
    {
        Particle.publish("State", "Closing", PRIVATE);
        mot.window_state = "Closing";
        //reset loop counter before entering monitor
        loop_counter = 0;
    }
    //when in this state the motor should be running 
    if (mot.current_pos != mot.Get_Target_Pos())
    {
        state_delay = 50; //Change state delay for faster response
        //Continue to monitor position
        Monitor_Position(m_close);
    }
    

    //Check to see if met target position or if a stop flag is thrown
    if (mot.current_pos == mot.Get_Target_Pos() || mot.stop_flag != 0)
    {
        //Apply powered motor braking
        mot.Set_PWM(0, m_hold);
        delay(100);

        state_change(4);
    }

}

void state_learn() {
    //if state change occured publish state change
    if (mot.window_state != "Learning")
    {
        Particle.publish("State", "Learning", PRIVATE);
        mot.window_state = "Learning";
    }

    //Check to see if the motor is located at position 0
    if (mot.Get_Close_SW() == 0) {
        Particle.publish("Position Error", "Window is not closed", PRIVATE);
        }

    else{
    //Continue with learn if check is OK                    

        //set close position
        mot.current_pos = 0;

        //Save stall current value and disable stall current
        int cur_m = 0;
        //Set high stall current for learning purposes
        mot.Set_Stall_Current(500);

        //Open the window
        Particle.publish("Learning", "Opening Window", PRIVATE);
        
        //Change target position to full open
        mot.Set_Target_Pos(mot.track_length);
        //reset loop counter
        loop_counter = 0;
        //Power up motor
        Change_Position(m_open);
        //Create loop to monitor
        while (mot.current_pos < mot.Get_Target_Pos()){
            Monitor_Position(m_open);
            delay(100);
        }
        //Motor Braking
        mot.Set_PWM(0, m_hold);
        delay(50);
        //Power off motor when target position reached
        mot.Set_PWM(0, m_stop);
        //disable motor driver
        mot.Enable_Mot(0);
        //Set number of loop counts for full open to EEPROM
        mot.Set_Open_Count(loop_counter, true);

        //find max_current from diagnostics matrix (ignore first 500ms based on 50ms state loop)
        for (int i = 10; i <= mot.Get_Open_Count(); i++)
            {
                if (mot.Diag_Matrix[i][0] > cur_m)
                    {
                        cur_m = mot.Diag_Matrix[i][0] *1.12; //set cur_m to largest current value while opening with buffer
                    }
            }
        //Delay to let system settle
        delay(500);
        //Close the window    
        Particle.publish("Learning", "Closing Window", PRIVATE);
        //Change target position to full open
        mot.Set_Target_Pos(0);
        //Reset loop counter
        loop_counter = 0;
        //Power up motor
        Change_Position(m_close);
        //Create loop to monitor
        while (mot.current_pos > mot.Get_Target_Pos()){
            Monitor_Position(m_close);
            delay(100);
        }
        //Motor Braking
        mot.Set_PWM(0, m_hold);
        delay(50);
        //Power off motor when target position reached
        mot.Set_PWM(0, m_stop);
        //disable motor driver
        mot.Enable_Mot(0);
        //Set number of loop counts for full close
        mot.Set_Close_Count(loop_counter, true);

        //find max_current (ignore first 500ms based on 50ms state loop)
        for (int i = 10; i <= mot.Get_Close_Count(); i++) 
            {
            if (mot.Diag_Matrix[i][0] > cur_m)
                {
                    cur_m = mot.Diag_Matrix[i][0] *1.12; //set cur_m to larget current value while closing with buffer
                }    
            }

        //set the stall current equal to the largest current during learning
                mot.Set_Stall_Current(cur_m, true); //write new stall current to EEPROM
        }
    //return to closed state
    state_change(3);
}

void state_diag() {
    //if state change occured publish state change
    if (mot.window_state != "Diag")
    {
        Particle.publish("State", "Diag", PRIVATE);
        mot.window_state = "Diag";
    }
    
    //Publish diagnostic values
    Particle.publish("Open Count", String(mot.Get_Open_Count()), PRIVATE);
    delay(250);
    Particle.publish("Close_Count", String(mot.Get_Close_Count()), PRIVATE);
    delay(250);
    Particle.publish("Last Loop Count", String(loop_counter), PRIVATE);
    delay(500);
    Particle.publish("Current_Position", String(mot.current_pos), PRIVATE);
    delay(250);
    Particle.publish("Target_Position", String(mot.Get_Target_Pos()), PRIVATE);
    delay(250);
    Particle.publish("Current State", mot.window_state, PRIVATE);
    delay(250);
    Particle.publish("Stall Current", String(mot.Get_Stall_Current()), PRIVATE);
    delay(500);
    Particle.publish("Current_Sum", String(mot.current_ma_sum), PRIVATE);
    delay(250);
    Particle.publish("Open_Switch", String(mot.Get_Open_SW()), PRIVATE);
    delay(250);
    Particle.publish("Close_Switch", String(mot.Get_Close_SW()), PRIVATE);
    delay(250);
    Particle.publish("Loop_Counter", String(loop_counter), PRIVATE);
    delay(250);



    //go back to last state
    if (mot.current_pos == 0) {
        //go to closed state
        state_change(3);
    }
    else {
        //go to open state
        state_change(0);
    }
}

void state_error() {
    if (mot.window_state != "Error")
    {
        Particle.publish("State", "Error", PRIVATE);
        mot.window_state = "Error";
    }
//Determine next state
int next_state;

//Transition to Close State
    if (mot.Get_Close_SW() == 1 && mot.Get_Open_SW() == 0)
    {
        next_state = 3;
        //reset target position
        mot.Set_Target_Pos(0);
    }
//Transition to Open State
    else if (mot.Get_Close_SW() == 0 && mot.Get_Open_SW() == 0)
    {
        next_state = 0;
        //reset target_position
        mot.Set_Target_Pos(1);
    }
    else if (mot.Get_Close_SW() == 0 && mot.Get_Open_SW() == 1)
    {
        next_state = 0;
        //reset target position
        mot.Set_Target_Pos(2);
    }

//Check reason for error (stop flag)
    switch (mot.stop_flag)
    {
        case 0:
            //Error without stop flag is position error
            Particle.publish("Error: Target Position/Current Position", String(mot.Get_Target_Pos()) + " / " + String(mot.current_pos), PRIVATE);
            //Stays in current state
        break;
        case 1:
            //Stop flag due to over current
            delay(100); //delay form messaging
            Particle.publish("Error: Over Current", String(mot.Diag_Matrix[loop_counter][0]), PRIVATE);
            state_change(next_state);
            //reset stop flag
            mot.stop_flag = 0;
        break;
        case 2:
            //Stop flag due to interrupt
            Particle.publish("Error: User Interrupt", "1", PRIVATE);
            state_change(next_state);
            //reset stop flag
            mot.stop_flag = 0;

        break;
    }
}

//Get Battery Voltage Based on LBO Pin
String Get_Batt_Status()
    {
        //Max analog read val is 4095 @ 3.3V. 
        //only digital read is allowed due to >3V at the pin
        String output = "BATT OK";
        if (digitalRead(batt_LBO_pin) == LOW)
        {
            output = "LOW BATT";
        }
        return output;
    }

//Main body to take web function input
int Input_Command(String input_arg) {
    
    //input arg format is cmd[3]-value[2], ie mov-12
    int input_val = atoi(input_arg.substring(4,6)); //last 2 is an integer  
    input_arg = input_arg.substring(0,3); //first 3 is a string for command
    //Call enumerated objects
    Win_Cmd x;
    Mot_Cmd dir;
    //Convert input substring arg into enum window commands
    if (input_arg == "mov") { x = win_move; }
    else if (input_arg == "lrn") { x = win_learn; }
    else if (input_arg == "dia") { x = win_diag; }
    else if (input_arg == "cur") { x = set_cur_sens;}
    else if (input_arg == "man") { x = man_mov;}
    else if (input_arg == "rin") { x = set_ind;}
    else if (input_arg == "bat") { x = batt_stat;}
    else if (input_arg == "sto") { x = stored_val;}
    else if (input_arg == "res") { x = reset_pos;}
    else if (input_arg == "mor") { x = set_mot_ort;}
...

This file has been truncated, please download it to see its full contents.

Credits

Russell Hui
1 project • 1 follower

Comments