Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
| ||||||
|
Have you ever been driving to work in the rain, and wondering whether you've left the kitchen window open? Then you come home and your kitchen is all wet? Having this happen to me a couple of times helped to inspire this project. With some free time here and there, I've managed to put this project together over the last couple of years.
There are still some quirks to work out, but hopefully this will help!
Demonstration:The window can be opened, closed or stopped via a web based command or by pressing a switch on the window opener device.
Switch:
Web Command:
HardwareThe window opener is affixed to the sliding window sash with double sided tape. A track is affixed to the sliding window frame with double sided tape. A geared DC motor housed inside the window assembly is connected to the track through a spur gear. When commanded to move, the DC motor spins and pulls the window sash along the track. The limit switch hits a stop at the end of the track and that will allow the device to know when it has fully opened/closed.
The window opener housing, track and gears are all 3D printed. The STL files can be downloaded at the end of this post.
An exploded view of the assembly is below. All parts are assembled using M2-M4 hex cap screws/nuts posted in the kit (not shown below).
Notes:
You can print multiple lengths of track and string them together for bigger windows.
An adjustable track stop is also included in the files. This can be used to adjust the limit switches.
The files are for sliding windows that shut on the left side. Print the mirror of the body, cover, mount and slider for windows that shut on the right side.
Microcontroller:The "brains" in this window opener is a Particle Photon microcontroller. The microcontroller can take user commands over the web through the Particle Cloud. This platform enables the user to open/close a window as well as query the window's current state from any computer/mobile phone.
Basic open/close commands are sent through a web exposed function. This can be accessed through your account on the Particle Web.
In addition, with the access token for your Particle Photon, the window opener can also be called through a REST API. Below is a snippet of code I wrote for a Python Front End used to call the window opener's web exposed function.
This setup should allow "smarter" commands to be processed outside of the window opener..... future features :)
The code for the Particle Photon is included in the attachments. If you are using the particle WEB IDE - be sure to include the Adafruit INA219 library to your project.
The code in its current state gets the device working, but could definitely use some improvement. Look forward to hearing if the community has suggestions for the implementing the following features:
- Enabling current sensing while motor is under PWM
- Enabling sleep mode with a method to wake from wifi
OtherElectronics
You'll notice in the parts list there are a couple of other break out board being used. Below is a quick description of what they're used for:
INA219 Current Sensor - This is used to enable "pinch detection" functions for the window opener. The threshold for "pinch" can be set through the Particle Cloud or the microcontroller can learn this threshold after cycling opening/closing the window.
TB6612 Motor Driver - This chip enables PWM control of two DC motors. Only one is used at this time. But a second could operate say... a window lock?
Powerboost 500C - This board allows for charging of the 18650 Lithium battery and also provides a 5V source for the Particle Photon
U3V12F12 - This provides a 12V source from the 18650 battery for the motor
Wiring:The Fritzing diagram above explains how the break our boards and switches are connected. To simplify the setup, I was able to print a PCB through OshPark to solder all of these break out boards together (see below).
The board includes Dupont terminals to make plugging in the switches and battery a bit easier.
/*
* 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.
Comments