Smart Coop - init
A smart coop with four-way door and cleaner can be scheduled at specific time from remote command.
Components and supplies
1
Power Supply 12 V
1
Arduino® Nano ESP32 with headers
2
Feetech Mini Servo motor 120 degrees 9g
1
Brushless DC Motor
3
Registor 10 kohm
1
Bread Board
1
40 colored male-female jumper wires
2
Slider
1
DC Barrel Jack Adapter - Female
Tools and machines
1
drill, screwdriver, soldering iron
1
hand saw
Apps and platforms
1
Arduino IoT Cloud
1
Arduino IDE
Project description
Code
ESP32_project_oct27a
cpp
1#include "arduino_secrets.h" 2#include <ESP32Servo.h> 3#include <LinkedList.h> 4#include <TimeLib.h> 5#include <ESPDateTime.h> 6 7 8/* 9 Sketch generated by the Arduino IoT Cloud Thing "Untitled" 10 https://create.arduino.cc/cloud/things/4112e99c-79b3-4482-9968-b32a3349c579 11 12 Arduino IoT Cloud Variables description 13 14 The following variables are automatically generated and updated when changes are made to the Thing 15 16 int servo_degree; 17 18 Variables which are marked as READ/WRITE in the Cloud Thing will also have functions 19 which are called when their values are changed from the Dashboard. 20 These functions are generated with the Thing and added at the end of this sketch. 21*/ 22 23#include "thingProperties.h" 24 25#define PIN_PWN 11 26#define PIN_DIRECION 10 27#define PIN_TRIGGER 9 28#define PIN_INNER_SERVO 8 29#define PIN_OUTER_SERVO 7 30#define PIN_MOTOR_POSITION A0 31#define CHECK_MOTOR_DURATION 200 32 33struct Task { 34 int id; 35 int func; 36 int time; //unit: s 37}; 38String last_version = ""; 39String circular_command = ""; 40bool hasTime = false; 41int currentDay = 0; 42bool isWifiConnected = false; 43Servo innerServo; 44Servo outerServo; 45LinkedList<Task> taskList = LinkedList<Task>(); 46int currentStatus = 0; 47long last_check_time = 0; 48int motor_pwm = 0; 49bool isCleaning = false; 50int statusBeforeCleaning = 0; 51 52void setup() { 53 pinMode(PIN_PWN, OUTPUT); 54 pinMode(PIN_DIRECION, OUTPUT); 55 pinMode(PIN_TRIGGER, INPUT_PULLUP); 56 pinMode(PIN_MOTOR_POSITION, INPUT); 57 stopMotor(); 58 // Initialize serial and wait for port to open: 59 Serial.begin(9600); 60 // This delay gives the chance to wait for a Serial Monitor without blocking if none is found 61 delay(1500); 62 63 // Defined in thingProperties.h 64 initProperties(); 65 66 // Connect to Arduino IoT Cloud 67 ArduinoCloud.begin(ArduinoIoTPreferredConnection); 68 69 /* 70 The following function allows you to obtain more information 71 related to the state of network and IoT Cloud connection and errors 72 the higher number the more granular information you’ll get. 73 The default is 0 (only errors). 74 Maximum is 4 75 */ 76 setDebugMessageLevel(2); 77 ArduinoCloud.printDebugInfo(); 78 innerServo.attach(PIN_INNER_SERVO); 79 outerServo.attach(PIN_OUTER_SERVO); 80} 81 82void loop() { 83 ArduinoCloud.update(); 84 // Your code here 85 setRemoteTime(); 86 // If can't connect internet, just set local time through serial input 87 setLocalTime(); 88 scheduleTask(); 89 checkCurrentMotorState(); 90} 91 92void checkCurrentMotorState() { 93 if (ArduinoCloud.connected() != 1) { 94 return; 95 } 96 long current = millis(); 97 if (current - last_check_time > CHECK_MOTOR_DURATION) { 98 last_check_time = current; 99 // change the resolution to 12 bits and read A0 100 analogReadResolution(12); 101 int sensorValue = analogRead(PIN_MOTOR_POSITION); 102 // Map the analog value to 0, 1, or 2 103 switch (sensorValue) { 104 case 0 ... 299: 105 currentStatus = 0; 106 break; 107 case 1000 ... 1999: 108 currentStatus = 1; 109 break; 110 case 2000 ... 2999: 111 currentStatus = 2; 112 break; 113 case 3899 ... 4095: 114 currentStatus = 3; 115 break; 116 default: 117 Serial.println("motor position occurs exception!"); 118 break; 119 } 120 Serial.printf("Device value:%d, status: %d, remote status %d.\n", sensorValue, currentStatus, motor_status); 121 if (motor_status != currentStatus) { 122 moveMotor(); 123 } else { 124 stopMotor(); 125 revertFloor(); 126 } 127 } 128} 129 130void moveMotor() { 131 if (motor_status > currentStatus) { 132 // 逆时针旋转 133 digitalWrite(PIN_DIRECION, 1); 134 } else { 135 // 顺时针旋转 136 digitalWrite(PIN_DIRECION, 0); 137 } 138 if (motor_pwm != 0) { 139 motor_pwm = 0; 140 analogWrite(PIN_PWN, motor_pwm); 141 } 142} 143 144void stopMotor() { 145 // todo: stop motor, better using a power switch avoid motor works when reset. 146 if (motor_pwm != 255) { 147 motor_pwm = 255; 148 analogWrite(PIN_PWN, 255); 149 } 150} 151 152void setLocalTime() { 153 if (!hasTime && Serial.available() > 0) { 154 Serial.print("Please enter date at first to schedule task ==> "); 155 String date = Serial.readString(); 156 Serial.println(date); 157 resetTime(date); 158 hasTime = true; 159 } 160} 161 162void setRemoteTime() { 163 // can alse use ArduinoCloud.getLocalTime() 164 if (!isWifiConnected && ArduinoCloud.connected() == 1) { 165 // setup this after wifi connected 166 // you can use custom timeZone,server and timeout 167 // DateTime.setTimeZone("CST-8"); 168 // DateTime.setServer("asia.pool.ntp.org"); 169 // DateTime.begin(15 * 1000); 170 DateTime.setTimeZone("CST-8"); 171 // this method config ntp and wait for time sync 172 // default timeout is 10 seconds 173 DateTime.begin(60 * 1000); 174 if (DateTime.isTimeValid()) { 175 hasTime = true; 176 Serial.print("remote time : "); 177 Serial.println(DateTime.toUTCString()); 178 time_t now = DateTime.now(); 179 resetTime(localtime(&now)); 180 } else { 181 Serial.println("Failed to get time from server."); 182 } 183 isWifiConnected = true; 184 } 185} 186 187void scheduleTask() { 188 if (hasTime) { 189 if (taskList.size() > 0) { 190 Task todo = taskList.get(0); 191 if (todo.time <= getTime(hour(), minute(), second())) { 192 taskList.remove(0); 193 Serial.printf("do task[%d,%d] at %d:%d:%d.\n", todo.id, todo.func, hour(), minute(), second()); 194 switch (todo.id) { 195 case 10: 196 scheduleDoor(todo.func); 197 break; 198 case 20: 199 scheduleFloor(todo.func); 200 break; 201 default: 202 break; 203 } 204 } 205 } else { 206 // add task list next day. 207 if (currentDay != day()) { 208 currentDay = day(); 209 addTaskByCommand(circular_command); 210 } 211 } 212 } 213} 214 215void scheduleDoor(int func) { 216 // sync servo degree in cloud after invoke ArduinoCloud.update() 217 switch (func) { 218 case 3: 219 // can in and out 220 inner_latch_degree = 90; 221 outer_latch_degree = 90; 222 openFloor(); 223 break; 224 case 2: 225 // only in 226 inner_latch_degree = 0; 227 outer_latch_degree = 90; 228 break; 229 case 1: 230 // only out 231 inner_latch_degree = 90; 232 outer_latch_degree = 0; 233 break; 234 default: 235 // close 236 inner_latch_degree = 0; 237 outer_latch_degree = 0; 238 break; 239 } 240 innerServo.write(inner_latch_degree); 241 outerServo.write(outer_latch_degree); 242} 243 244void scheduleFloor(int func) { 245 switch (func) { 246 case 3: 247 cleanFloor(); 248 break; 249 case 2: 250 forbidFeeding(); 251 break; 252 case 1: 253 // open 254 openFloor(); 255 break; 256 default: 257 // close 258 closeFloor(); 259 break; 260 } 261} 262 263void cleanFloor() { 264 isCleaning = true; 265 statusBeforeCleaning = motor_status; 266 if (motor_status == 0) { 267 openFloor(); 268 } else { 269 closeFloor(); 270 } 271} 272 273void revertFloor() { 274 if (isCleaning) { 275 isCleaning = false; 276 motor_status = statusBeforeCleaning; 277 } 278} 279 280void forbidFeeding() { 281 motor_status = 1; 282} 283 284void openFloor() { 285 motor_status = 3; 286} 287 288void closeFloor() { 289 motor_status = 0; 290} 291 292/* 293 Since ServoDegree is READ_WRITE variable, onServoDegreeChange() is 294 executed every time a new value is received from IoT Cloud. 295*/ 296void onInnerLatchDegreeChange() { 297 // Add your code here to act upon ServoDegree change 298 Serial.printf("change inner latch degree to %d.\n", inner_latch_degree); 299 innerServo.write(inner_latch_degree); 300} 301 302void onOuterLatchDegreeChange() { 303 // Add your code here to act upon ServoDegree change 304 Serial.printf("change outer latch degree to %d.\n", outer_latch_degree); 305 outerServo.write(outer_latch_degree); 306} 307 308/* 309 Since MotorStatus is READ_WRITE variable, onMotorStatusChange() is 310 executed every time a new value is received from IoT Cloud. 311*/ 312void onMotorStatusChange() { 313 // Add your code here to act upon MotorStatus change 314 Serial.printf("change motor status to %d.\n", motor_status); 315} 316 317/* 318 Since TaskCommand is READ_WRITE variable, onTaskCommandChange() is 319 executed every time a new value is received from IoT Cloud. 320*/ 321void onTaskCommandChange() { 322 // Parse command, like: 2023-10-19-01-0:103-06:00:00/101-06:30:00/203-08:00:00/103-11:30:00/201-12:00:00/102-17:00:00/100-18:30:00; 323 String version = task_command.substring(0, 13); 324 Serial.printf("task command version[%s].\n", version); 325 if (last_version == "" || version > last_version) { 326 last_version = version; 327 char taskType = task_command.charAt(14); 328 String command = task_command.substring(16, task_command.length()); 329 if (taskType == '0') { 330 currentDay = day(); 331 Serial.printf("get circular command at day[%d]\n.", currentDay); 332 circular_command = command; 333 } else { 334 Serial.println("get oneshot command."); 335 } 336 addTaskByCommand(command); 337 Serial.println("onTaskCommandChange done!"); 338 } 339} 340 341void addTaskByCommand(String command) { 342 Serial.print("add task by command: "); 343 Serial.println(command); 344 int length = command.length(); 345 // split by / 346 int startIndex = 0; 347 for (int i = 0; i < length; i++) { 348 char currentChar = command.charAt(i); 349 if (currentChar == '/' || currentChar == ';' || i == length - 1) { 350 String taskStr = command.substring(startIndex, i); 351 Serial.printf("get task string[%s].\n", taskStr); 352 enqueueTaskAtTime(getTask(taskStr)); 353 startIndex = i + 1; 354 } 355 } 356} 357 358/* 359 103-06:00:00 360*/ 361Task getTask(String taskStr) { 362 int id = taskStr.substring(0, 2).toInt(); 363 int func = taskStr.substring(2, 3).toInt(); 364 int hh = taskStr.substring(4, 6).toInt(); 365 int mm = taskStr.substring(7, 9).toInt(); 366 int ss = taskStr.substring(10, 12).toInt(); 367 Serial.printf("get task[%d,%d,%d,%d,%d].\n", id, func, hh, mm, ss); 368 Task task; 369 task.id = id; 370 task.func = func; 371 task.time = getTime(hh, mm, ss); 372 return task; 373} 374 375int getTime(int hh, int mm, int ss) { 376 return hh * 3600 + mm * 60 + ss; 377} 378 379/* 380 reset time by dateTime like 2023/10/02 20:49:08 381*/ 382void resetTime(String time) { 383 int year = time.substring(0, 4).toInt(); 384 int month = time.substring(5, 7).toInt(); 385 int day = time.substring(8, 10).toInt(); 386 int hh = time.substring(11, 13).toInt(); 387 int mm = time.substring(14, 16).toInt(); 388 int ss = time.substring(17, 19).toInt(); 389 setTime(hh, mm, ss, day, month, year); 390} 391 392void resetTime(tm* dt) { 393 int year = dt->tm_year + 1900; 394 int month = dt->tm_mon; 395 int day = dt->tm_mday; 396 int hh = dt->tm_hour; 397 int mm = dt->tm_min; 398 int ss = dt->tm_sec; 399 Serial.printf("%04d/%02d/%02d %02d:%02d:%02d\n", year, month, day, hh, mm, ss); 400 setTime(hh, mm, ss, day, month, year); 401} 402 403/* 404 enqueue task at time 405*/ 406void enqueueTaskAtTime(Task task) { 407 int i = 0; 408 for (; i < taskList.size(); i++) { 409 Task item = taskList.get(i); 410 if (item.time > task.time) { 411 break; 412 } 413 } 414 taskList.add(i, task); 415}
Downloadable files
circuit
circuit
circuit.jpg

Comments
Only logged in users can leave comments