The motor functionality was added to the received phone app data. The motor was set up to turn in approximately 20 degrees at a time depending on the input received from the user. The manual mode open blinds from closed position to 100 degrees (letting in maximum amount of light to the room), meanwhile, the custom mode will set the blinds to specific blinds angle. The addition of the motor to the Arduino code is shown below.
After connecting the Arduino Nano to the blinds, phone app was used to communicate to the blinds. The connection was established and the manual mode was tested. The blinds were set in the closed position before starting the test, when the "DOWN" button was pressed, the blinds opened, but with a much larger angle then anticipated. This was corrected by adjusting the steps per revolution, which fixed the large angle incrementation. Unfortunately, when the "UP" button was pressed, the blinds did not close by 20 degrees but opened by another 20 degrees. After many attempts to rectify this issue, it was deemed that current set up of the code should be reworked in the way the current angle of the blinds is not calculated by angle of blinds, but position of the blinds (0-6). This way when calculating the new angle of the blinds, the negative step can be taken into account.
Unfortunately due to this set back, the CUSTOM mode was not tested since angle of the blinds is being taken into account the same way. Once the MANUAL mode is working, the CUSTOM mode can be tested. Currently the new fix is applied to 50% of the code and further testing will be conducted as soon as Wednesday.
Jordan and I ran into issue with boost converter not working correctly and sending too much current to the motor. It should be noted that pins 8, 9 on Arduino were always ON after first signal was sent to the motor module. This caused the motor to get hot and these pins staying high will be addressed in the code.
#include <Arduino.h>
#include <TimeLib.h>
#include <Stepper.h>
#include <SoftwareSerial.h>
// Define Bluetooth RX and TX pins
const int BT_RX = 2; // Bluetooth TX to Arduino RX
const int BT_TX = 3; // Bluetooth RX to Arduino TX
SoftwareSerial BTSerial(BT_RX, BT_TX);
enum BlindsMode { A, M, C };
BlindsMode currentMode = A;
String currentLumenRange = "A5"; // Default lumen for AUTO mode
int blindsAngle = 0; // 0 = fully closed, 100 = fully open (degrees)
const int angleStep = 20; // step size of increment
const int maxBlindsAngle = 100; // max blinds angle
//motor stuff
const int stepsPerRevolution = 1024; // change this to fit the number of steps per revolution
const int rolePerMinute = 10; // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
const int stepsPerDegree = stepsPerRevolution / 360;
const int stepsPerRotation = stepsPerDegree * 1000;
//end motor stuff
//initialize manual mode max timers, count of timers, and struct to store incomming timers
const int maxTimers = 10; // Maximum number of timers we can store
int timerCount = 0; // Number of timers currently stored
struct Timer {
int startHour;
int startMinute;
int endHour;
int endMinute;
int angle;
};
Timer customTimers[maxTimers]; // Array to store multiple custom mode timers
void setup() {
Serial.begin(9600);
BTSerial.begin(9600);
Serial.println("Waiting for time data from Bluetooth...");
//motor stuff
myStepper.setSpeed(rolePerMinute);
}
void loop() {
if (BTSerial.available()) {
String data = BTSerial.readStringUntil('\n');
//print to COM incomming data from bluetooth
Serial.print("Received data: ");
Serial.println(data); // Print the raw data received
if (data.startsWith("T:")) {
// Format: T:HH:MM:SS
String timeString = data.substring(2);
int hour, minute, second;
if (sscanf(timeString.c_str(), "%d:%d:%d", &hour, &minute, &second) == 3) {
setTime(hour, minute, second, day(), month(), year());
Serial.print("Time set to: ");
Serial.println(timeString);
} else {
Serial.println("Invalid time format received.");
}
} else if (data.startsWith("MODE:")) {
// Format: MODE:A/M/C
String modeString = data.substring(5);
if (modeString == "A") {
currentMode = A;
Serial.println("Mode set to AUTOMATIC");
} else if (modeString == "M") {
currentMode = M;
Serial.println("Mode set to MANUAL");
} else if (modeString == "C") {
currentMode = C;
Serial.println("Mode set to CUSTOM");
} else {
Serial.println("Invalid mode received.");
}
} else if (data.startsWith("L:")) { //AUTO mode
// Format: L:A1 - L:A6
String lumenRange = data.substring(2);
if (lumenRange == "A1" || lumenRange == "A2" || lumenRange == "A3" ||
lumenRange == "A4" || lumenRange == "A5" || lumenRange == "A6") {
currentLumenRange = lumenRange;
Serial.print("Lumen range set to: ");
Serial.println(currentLumenRange);
} else {
Serial.println("Invalid lumen range received.");
}
} else if (data.startsWith("M:")) { //manual mode
// Format: M:U or M:D this is manual mode up/ down received from App
String blindsCommand = data.substring(2);
if (blindsCommand == "D") { //to open blinds
moveBlindsByStep(angleStep);
} else if (blindsCommand == "U") { //to close blinds
moveBlindsByStep(0-angleStep);
} else {
Serial.println("Invalid blinds command received.");
}
} else if (data.startsWith("C:")) { //custom mode
// Format: C:hh:mm,hh:mm,angle,C:hh:mm,hh:mm,angle,...,
clearTimers(); // Clear existing timers before adding new ones
parseTimers(data);
printTimers(); // Print currently saved timers
}
}
// Which mode is used
switch (currentMode) {
case A:
// Automatic mode
handleAutomaticMode();
break;
case M:
// Manual mode behavior
handleManualMode();
break;
case C:
// Custom mode behavior
handleCustomMode();
break;
}
delay(1); // Adjust as needed
}
// automatic mode
void handleAutomaticMode() {
Serial.print("Running in AUTOMATIC mode with lumen range: ");
Serial.println(currentLumenRange);
int currentLumens = readLumensSensor(); // function reading lumens
if (currentLumenRange == "A1" && currentLumens >= 0 && currentLumens < 300) {
// lumen range A1 (0-300)
Serial.println("Adjusting blinds for lumen range A1 (0-300)");
adjustBlindsForLumens(currentLumens);
} else if (currentLumenRange == "A2" && currentLumens >= 300 && currentLumens < 600) {
// lumen range A2 (300-600)
Serial.println("Adjusting blinds for lumen range A2 (300-600)");
adjustBlindsForLumens(currentLumens);
} else if (currentLumenRange == "A3" && currentLumens >= 600 && currentLumens < 900) {
// lumen range A3 (600-900)
Serial.println("Adjusting blinds for lumen range A3 (600-900)");
adjustBlindsForLumens(currentLumens);
} else if (currentLumenRange == "A4" && currentLumens >= 900 && currentLumens < 1200) {
// lumen range A4 (900-1200)
Serial.println("Adjusting blinds for lumen range A4 (900-1200)");
adjustBlindsForLumens(currentLumens);
} else if (currentLumenRange == "A5" && currentLumens >= 1200 && currentLumens < 1500) {
// lumen range A5 (1200-1500)
Serial.println("Adjusting blinds for lumen range A5 (1200-1500)");
adjustBlindsForLumens(currentLumens);
} else if (currentLumenRange == "A6" && currentLumens >= 1500 && currentLumens < 1800) {
// lumen range A6 (1500-1800)
Serial.println("Adjusting blinds for lumen range A6 (1500-1800)");
adjustBlindsForLumens(currentLumens);
} else {
Serial.println("Current lumens not within the specified range.");
}
} //end AUTO
//manual mode selected
void handleManualMode() {
// manual mode
Serial.println("Running in MANUAL mode...");
} //end manual
//custom mode selected
void handleCustomMode() {
Serial.println("Running in CUSTOM mode...");
// Check the current time against custom mode timers
int currentHour = hour();
int currentMinute = minute();
Timer* latestTimer = nullptr;
//loop through all the timers to see if any of them match
for (int i = 0; i < timerCount; i++) {
if (isTimeWithinRange(currentHour, currentMinute, customTimers[i].startHour, customTimers[i].startMinute, customTimers[i].endHour, customTimers[i].endMinute)) {
latestTimer = &customTimers[i]; // Use the latest timer within range
}
}
if (latestTimer != nullptr) {
moveBlindsToAngle(latestTimer->angle);
}
} //end custom
//display time in COM
void digitalClockDisplay() {
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.println();
}
//if time is under 10, add 0 before that value
void printDigits(int digits) {
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
//reading sensors
int readLumensSensor() {
// function to read lumens from a sensor
// sensor reading code
return analogRead(A0); // Example analog read
}
//adjusting blinds based on read lumens in AUTO mode
void adjustBlindsForLumens(int lumens) {
// function to adjust blinds based on lumens
// actual motor code
Serial.print("Adjusting blinds for lumens: ");
Serial.println(lumens);
}
//move blinds by step, used for MANUAL mode
void moveBlindsByStep(int angleStep) {
int newAngle = blindsAngle + angleStep;
newAngle = constrain(newAngle, 0, maxBlindsAngle);
if (angleStep < 0)
moveBlinds(-angleStep);
else if (angleStep > 0)
moveBlinds(angleStep);
}
//move blinds to angle, used for CUSTOM mode
void moveBlindsToAngle(int angle) {
int steps = (angle) * stepsPerDegree;
myStepper.step(steps);
blindsAngle = angle;
}
//adjusting blinds by angle of 20*
void moveBlinds(int angle) {
int steps = angle * stepsPerDegree;
myStepper.step(steps);
blindsAngle = angle;
//print blinds angle to screen
Serial.print("Moving blinds to angle: ");
Serial.println(angle);
}
//check if current time in range of timers
bool isTimeWithinRange(int currentHour, int currentMinute, int startHour, int startMinute, int endHour, int endMinute) {
if (startHour < endHour || (startHour == endHour && startMinute < endMinute)) {
// Normal time range
return (currentHour > startHour || (currentHour == startHour && currentMinute >= startMinute)) &&
(currentHour < endHour || (currentHour == endHour && currentMinute <= endMinute));
} else {
// Overnight time range
return (currentHour > startHour || (currentHour == startHour && currentMinute >= startMinute)) ||
(currentHour < endHour || (currentHour == endHour && currentMinute <= endMinute));
}
}
//clear CUSTOM mode data
void clearTimers() {
timerCount = 0; // Reset timer count to clear current timers
}
//seperating input data for Custom Mode, where C: is detected for custom mode and ',' end marker
void parseTimers(String data) {
int timerIndex = 0;
int startIndex = 0;
int endIndex = data.indexOf(',');
while (endIndex != -1 && timerIndex < maxTimers) {
String timerString = data.substring(startIndex, endIndex);
Serial.print("Received timer string: ");
Serial.println(timerString); // Print the raw timer string received
int startHour, startMinute, endHour, endMinute, angle;
if (sscanf(timerString.c_str(), "C:%d:%d-%d:%d-%d", &startHour, &startMinute, &endHour, &endMinute, &angle) == 5) {
customTimers[timerCount] = {startHour, startMinute, endHour, endMinute, angle};
timerCount++;
Serial.print("Timer added: ");
Serial.println(timerString);
} else {
Serial.println("Invalid timer format received.");
}
//start reading next timer received and skip ','
startIndex = endIndex + 1;
endIndex = data.indexOf(',', startIndex);
timerIndex++;
}
} // end Custom Parsing data
//printing current timers to COM
void printTimers() {
Serial.println("Currently saved timers:");
for (int i = 0; i < timerCount; i++) {
Serial.print("Timer ");
Serial.print(i + 1);
Serial.print(": ");
Serial.print(customTimers[i].startHour);
Serial.print(":");
Serial.print(customTimers[i].startMinute);
Serial.print(" - ");
Serial.print(customTimers[i].endHour);
Serial.print(":");
Serial.print(customTimers[i].endMinute);
Serial.print(" Angle: ");
Serial.println(customTimers[i].angle);
}
}
Comments