Witam! Szybki tani projekt rolet automatycznych na arduino nano. Układ obsługuje 2 rolety niezależnie. Posiada podstawowe funkcje takie jak :
- automatyczne odsuwanie i zasuwanie o żądanej godzinie, ustawiane poprzez menu na wyświetlaczu oled ssd1306 128x32
- ręczne odsuwanie i zasuwanie.
- pozycje krańcowe wykorzystują czujniki kontaktronowe NC,
- dodatkowe zabezpieczenie czasu pracy silnika
- układ RTC DS3231 do odmierzania czasu.
Program napisany "kompilatorem" Chat GTP 4.0
Z lewej strony na sterowniku, w menu ustawiamy za pomocą przycisków czasy zasuwania /odsuwania, natomiast z prawej mamy ręczne sterowanie. Gdy naciśniemy dany kierunek roleta zaczyna swój cykl odmierzając jednocześnie ustawiony programowo czas cyklu. Ponowne naciśnięcie przycisku powoduje jej zatrzymanie. Gdy roleta dojedzie na swoją pozycję krańcową czujnik zasygnalizuje rozwarcie i zatrzyma się roleta.
Zmiana kierunków obrotów jest wykonana na 2 przekaźnikach dla każdej rolety (łącznie 4szt). Układ zasilany z zasilacza 12V 1A. Zastosowane silniki z przekładnia 77Rpm 12V (nie wiem dlaczego ale ich obroty się różnią )które zostana zabudowane. Silniki sprzężone z roletami są za pomocą koralików, które skróciłem na malutkie odcinki. PCB, obudowa, laminat grawerski wykonane frezarką CNC3018 Wszystkie części wykorzystane z tak zwanych przydasiów, ale jak ktoś zakupi u pana chińczyka to zmieści się w 100zł za cały komplet.
Film z pracy :
Program:
#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RTC_DS3231 rtc;
// Pin assignments
const int leftUpPin = 2;
const int leftDownPin = 3;
const int rightUpPin = 4;
const int rightDownPin = 5;
const int leftTopSensor = 6;
const int leftBottomSensor = 7;
const int rightTopSensor = 8;
const int rightBottomSensor = 9;
// Rolety button pins
const int leftUpButton = 10;
const int leftDownButton = 11;
const int rightUpButton = 12;
const int rightDownButton = 13;
// Menu button pins
const int enterButton = A0;
const int backButton = A1;
const int upButton = A2;
const int downButton = A3;
// Variables for settings
int leftUpHour = 9, leftUpMinute = 30, leftUpSecond = 0;
int leftDownHour = 17, leftDownMinute = 0, leftDownSecond = 0;
int rightUpHour = 9, rightUpMinute = 30, rightUpSecond = 0;
int rightDownHour = 17, rightDownMinute = 0, rightDownSecond = 0;
unsigned long leftMotorTimeout = 36000; // Max run time for left motor (ms)
unsigned long rightMotorTimeout = 36000; // Max run time for right motor (ms)
// Debounce settings
unsigned long debounceDelay = 50;
unsigned long lastDebounceTime[8] = {0};
// Button states
bool buttonState[8] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
bool lastButtonState[8] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
// State variables
bool leftMotorActive = false, rightMotorActive = false;
unsigned long leftMotorStartTime = 0;
unsigned long rightMotorStartTime = 0;
bool leftMovingUp = false, leftMovingDown = false;
bool rightMovingUp = false, rightMovingDown = false;
// Pause state variables
bool leftMotorPaused = false;
bool rightMotorPaused = false;
// Menu variables
enum MenuState { MAIN_MENU, EDIT_LEFT_UP, EDIT_LEFT_DOWN, EDIT_RIGHT_UP, EDIT_RIGHT_DOWN };
MenuState currentMenu = MAIN_MENU;
int editCursorPosition = 0; // 0: Hour, 1: Minute
bool editing = false;
unsigned long lastInteractionTime = 0;
const unsigned long screenTimeout = 120000; // 30 seconds
void setup() {
Serial.begin(9600);
pinMode(leftUpPin, OUTPUT);
pinMode(leftDownPin, OUTPUT);
pinMode(rightUpPin, OUTPUT);
pinMode(rightDownPin, OUTPUT);
pinMode(leftTopSensor, INPUT_PULLUP);
pinMode(leftBottomSensor, INPUT_PULLUP);
pinMode(rightTopSensor, INPUT_PULLUP);
pinMode(rightBottomSensor, INPUT_PULLUP);
pinMode(leftUpButton, INPUT_PULLUP);
pinMode(leftDownButton, INPUT_PULLUP);
pinMode(rightUpButton, INPUT_PULLUP);
pinMode(rightDownButton, INPUT_PULLUP);
pinMode(enterButton, INPUT_PULLUP);
pinMode(backButton, INPUT_PULLUP);
pinMode(upButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting time to compile time.");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.display();
}
void loop() {
DateTime now = rtc.now();
unsigned long currentMillis = millis();
// Automatic control
handleAutomaticControl(now, currentMillis);
// Manual control
handleRoletyButtonControl(currentMillis);
// Menu control
handleMenuControl(currentMillis);
// Stop motors if timeout or sensor triggered
checkMotorStopConditions(currentMillis);
// Update display
if ((currentMillis - lastInteractionTime) < screenTimeout) {
updateDisplay(now);
} else {
display.clearDisplay();
display.display();
}
// Serial commands for setting time
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
handleSerialCommand(command);
}
}
void handleAutomaticControl(DateTime now, unsigned long currentMillis) {
if (now.hour() == leftUpHour && now.minute() == leftUpMinute && now.second() == leftUpSecond && !leftMotorActive) {
moveLeftUp(currentMillis);
}
if (now.hour() == leftDownHour && now.minute() == leftDownMinute && now.second() == leftDownSecond && !leftMotorActive) {
moveLeftDown(currentMillis);
}
if (now.hour() == rightUpHour && now.minute() == rightUpMinute && now.second() == rightUpSecond && !rightMotorActive) {
moveRightUp(currentMillis);
}
if (now.hour() == rightDownHour && now.minute() == rightDownMinute && now.second() == rightDownSecond && !rightMotorActive) {
moveRightDown(currentMillis);
}
}
void handleRoletyButtonControl(unsigned long currentMillis) {
int buttons[4] = {leftUpButton, leftDownButton, rightUpButton, rightDownButton};
void (*actions[4])(unsigned long) = {moveLeftUp, moveLeftDown, moveRightUp, moveRightDown};
bool *motorActive[4] = {&leftMotorActive, &leftMotorActive, &rightMotorActive, &rightMotorActive};
bool *motorPaused[4] = {&leftMotorPaused, &leftMotorPaused, &rightMotorPaused, &rightMotorPaused};
void (*stopActions[4])() = {stopLeftMotor, stopLeftMotor, stopRightMotor, stopRightMotor};
for (int i = 0; i < 4; i++) {
int reading = digitalRead(buttons[i]);
if (reading != lastButtonState[i]) {
lastDebounceTime[i] = currentMillis;
}
if ((currentMillis - lastDebounceTime[i]) > debounceDelay) {
if (reading != buttonState[i]) {
buttonState[i] = reading;
if (buttonState[i] == LOW) { // Button pressed
if (*motorActive[i]) {
if (*motorPaused[i]) {
// Jeśli silnik jest wstrzymany, uruchom go ponownie
actions[i](currentMillis);
*motorPaused[i] = false;
} else {
// Jeśli silnik działa, zatrzymaj go
stopActions[i]();
*motorPaused[i] = true;
}
} else {
// Jeśli silnik nie działa, uruchom go
actions[i](currentMillis);
}
}
}
}
lastButtonState[i] = reading;
}
}
void handleMenuControl(unsigned long currentMillis) {
// Update last interaction time
if (digitalRead(enterButton) == LOW || digitalRead(backButton) == LOW ||
digitalRead(upButton) == LOW || digitalRead(downButton) == LOW) {
lastInteractionTime = currentMillis;
}
if (digitalRead(enterButton) == LOW) {
delay(200); // Debounce
if (currentMenu == MAIN_MENU) {
switch (editCursorPosition) {
case 0: currentMenu = EDIT_LEFT_UP; break;
case 1: currentMenu = EDIT_LEFT_DOWN; break;
case 2: currentMenu = EDIT_RIGHT_UP; break;
case 3: currentMenu = EDIT_RIGHT_DOWN; break;
}
editing = true;
} else {
editing = !editing;
}
}
if (digitalRead(backButton) == LOW) {
delay(200); // Debounce
if (editing) {
editing = false;
} else {
currentMenu = MAIN_MENU;
}
}
if (digitalRead(upButton) == LOW) {
delay(200); // Debounce
if (currentMenu == MAIN_MENU) {
editCursorPosition = (editCursorPosition + 3) % 4; // Wrap around
} else if (editing) {
adjustTime(true);
}
}
if (digitalRead(downButton) == LOW) {
delay(200); // Debounce
if (currentMenu == MAIN_MENU) {
editCursorPosition = (editCursorPosition + 1) % 4; // Wrap around
} else if (editing) {
adjustTime(false);
}
}
}
void adjustTime(bool increase) {
int *hour, *minute;
switch (currentMenu) {
case EDIT_LEFT_UP: hour = &leftUpHour; minute = &leftUpMinute; break;
case EDIT_LEFT_DOWN: hour = &leftDownHour; minute = &leftDownMinute; break;
case EDIT_RIGHT_UP: hour = &rightUpHour; minute = &rightUpMinute; break;
case EDIT_RIGHT_DOWN: hour = &rightDownHour; minute = &rightDownMinute; break;
}
if (increase) {
*minute += 1;
if (*minute >= 60) {
*minute = 0;
*hour = (*hour + 1) % 24;
}
} else {
*minute -= 1;
if (*minute < 0) {
*minute = 59;
*hour = (*hour - 1 + 24) % 24;
}
}
}
void updateDisplay(DateTime now) {
display.clearDisplay();
// Display current time
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(30, 0);
display.print("Time: ");
display.print(now.hour());
display.print(":");
if (now.minute() < 10) display.print("0");
display.print(now.minute());
// Display menu
display.setCursor(3, 12);
display.print("LU: ");
display.print(leftUpHour);
display.print(":");
if (leftUpMinute < 10) display.print("0");
display.print(leftUpMinute);
display.setCursor(64, 12);
display.print("RU: ");
display.print(rightUpHour);
display.print(":");
if (rightUpMinute < 10) display.print("0");
display.print(rightUpMinute);
display.setCursor(3, 22);
display.print("LD: ");
display.print(leftDownHour);
display.print(":");
if (leftDownMinute < 10) display.print("0");
display.print(leftDownMinute);
display.setCursor(64, 22);
display.print("RD: ");
display.print(rightDownHour);
display.print(":");
if (rightDownMinute < 10) display.print("0");
display.print(rightDownMinute);
// Highlight the selected menu item
if (currentMenu == MAIN_MENU) {
int yPos[] = {11, 21, 11, 21};
int xPos[] = {2, 2, 63, 63};
display.drawRect(xPos[editCursorPosition] - 2, yPos[editCursorPosition] - 1, 60, 11, SSD1306_WHITE);
}
// Flash the editable field if in edit mode
if (editing && (millis() / 500) % 2) {
int yPos = (editCursorPosition % 2 == 0) ? 11 : 21;
int xPos = (editCursorPosition < 2) ? 2 : 63;
display.fillRect(xPos, yPos, 60, 11, SSD1306_BLACK);
}
display.display();
}
void checkMotorStopConditions(unsigned long currentMillis) {
if (leftMotorActive) {
if ((currentMillis - leftMotorStartTime > leftMotorTimeout) ||
(leftMovingUp && digitalRead(leftTopSensor) == HIGH) || // Sensor triggered
(leftMovingDown && digitalRead(leftBottomSensor) == HIGH)) {
stopLeftMotor();
}
}
if (rightMotorActive) {
if ((currentMillis - rightMotorStartTime > rightMotorTimeout) ||
(rightMovingUp && digitalRead(rightTopSensor) == HIGH) || // Sensor triggered
(rightMovingDown && digitalRead(rightBottomSensor) == HIGH)) {
stopRightMotor();
}
}
}
void moveLeftUp(unsigned long currentMillis) {
if (digitalRead(leftTopSensor) == LOW) { // Not at the top
digitalWrite(leftUpPin, HIGH);
digitalWrite(leftDownPin, LOW);
leftMotorActive = true;
leftMotorStartTime = currentMillis;
leftMovingUp = true;
leftMovingDown = false;
Serial.println("Left motor moving up");
}
}
void moveLeftDown(unsigned long currentMillis) {
if (digitalRead(leftBottomSensor) == LOW) { // Not at the bottom
digitalWrite(leftDownPin, HIGH);
digitalWrite(leftUpPin, LOW);
leftMotorActive = true;
leftMotorStartTime = currentMillis;
leftMovingDown = true;
leftMovingUp = false;
Serial.println("Left motor moving down");
}
}
void moveRightUp(unsigned long currentMillis) {
if (digitalRead(rightTopSensor) == LOW) { // Not at the top
digitalWrite(rightUpPin, HIGH);
digitalWrite(rightDownPin, LOW);
rightMotorActive = true;
rightMotorStartTime = currentMillis;
rightMovingUp = true;
rightMovingDown = false;
Serial.println("Right motor moving up");
}
}
void moveRightDown(unsigned long currentMillis) {
if (digitalRead(rightBottomSensor) == LOW) { // Not at the bottom
digitalWrite(rightDownPin, HIGH);
digitalWrite(rightUpPin, LOW);
rightMotorActive = true;
rightMotorStartTime = currentMillis;
rightMovingDown = true;
rightMovingUp = false;
Serial.println("Right motor moving down");
}
}
void stopLeftMotor() {
digitalWrite(leftUpPin, LOW);
digitalWrite(leftDownPin, LOW);
leftMotorActive = false;
leftMotorStartTime = 0;
leftMovingUp = false;
leftMovingDown = false;
Serial.println("Left motor stopped");
}
void stopRightMotor() {
digitalWrite(rightUpPin, LOW);
digitalWrite(rightDownPin, LOW);
rightMotorActive = false;
rightMotorStartTime = 0;
rightMovingUp = false;
rightMovingDown = false;
Serial.println("Right motor stopped");
}
void handleSerialCommand(String command) {
if (command.startsWith("SET ")) {
int index = command.indexOf(' ');
String data = command.substring(index + 1);
int value = data.toInt();
if (data.startsWith("LU ")) {
leftUpHour = value / 100;
leftUpMinute = value % 100;
Serial.println("Left up time updated");
} else if (data.startsWith("LD ")) {
leftDownHour = value / 100;
leftDownMinute = value % 100;
Serial.println("Left down time updated");
} else if (data.startsWith("RU ")) {
rightUpHour = value / 100;
rightUpMinute = value % 100;
Serial.println("Right up time updated");
} else if (data.startsWith("RD ")) {
rightDownHour = value / 100;
rightDownMinute = value % 100;
Serial.println("Right down time updated");
}
}
}
Pliki wraz z pcb (sprintLayout 6.0)
Fajne? Ranking DIY