제조공정
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
|
Arduino로 스마트 관개 컨트롤러 만들기
동적 물 순환으로 마당에 지능적으로 관개하십시오. 비가 오거나 마지막 물을 뿌린 후 비가 온다면 마당에 물을 주지 마십시오. 광 센서를 사용하여 일출 시간을 감지하고 그에 따라 물 시작 시간을 자동으로 조정합니다. 마당이 너무 추우면 물을 주지 마십시오.
기능 목록
<울>
스마트 관개 컨트롤러를 구축하는 데 필요한 부품 <울>
배선도
OLED 디스플레이
메뉴 버튼을 눌러 메뉴를 표시하고 버튼을 계속 눌러 모든 메뉴 옵션을 순환합니다. 메뉴는 30초 동안 활동이 없으면 자동으로 제거됩니다. 선택 버튼을 눌러 원하는 메뉴 기능을 수행하세요.
그렇다면 IO 확장기를 사용하는 이유는 무엇입니까? <울>
시스템 구축
Arduino Nano를 IO Expander에 연결하고 다음 코드로 프로그래밍합니다. 6핀 헤더는 소프트웨어 직렬 디버그 포트이며 최종 설치에서는 필요하지 않습니다.
1-Wire에서 I2C 주소와 일치하도록 ONEWIRE_TO_I2C_ROM1 및 ONEWIRE-TO_I2C_ROM2 정의 주소를 변경했는지 확인하십시오.
/* IO Expander 스케치 최적화
*
* 관개 시스템 v1.1
*
*/
#include <수학 .h>
#include // 파일 위치 \Program Files (x86)\Arduino\hardware\tools\avr\avr\include\time.h
#include
#include
#include
#include "IOExpander.h"
#화씨 정의 #define INIT_BOARD "g5w1;g11w1;g11d0,75;g12w1;g12d0,75;rsf"
#define ONEWIRE_TO_I2C_ROM1 "i4scc"
#define ONEWIRE_TO_I2C_ROM2" "t6s0300"
#define RTC_SENSOR "s4te"
#define I2C_EEPROM "s4tf"
#define I2C_OLED "s4t10"
#define I2C_LIGHT "s3t9;sc0"
# OPTICAL_SENSOR "g5a" 정의
#define BUTTON1 "g11d"
#define BUTTON2 "g12d"
#define WATER_TIME_BEFORE_SUNRISE 60
#define SUNRISE_LUX 100
# RAIN_DETECT_LEVEL 4.0 정의
#define DO_NOT_WATER_TEMP 4.4444 // 40F
#define MAX_ZONES 4
#define HOUR_IN_DA Y 24L
#define MIN_IN_HOUR 60L
#define SEC_IN_MIN 60L
#define SEC_IN_HOUR(MIN_IN_HOUR * SEC_IN_MIN)
#define SEC_IN_DAY (HOUR_IN_DAY * SEC_IN_HOUR_define)
#define SEC_IN_WEEK(SEC_IN_DAY * DAYS_IN_WEEK)
#define SUN 0x01
#define MON 0x02
#define TUE 0x04
#define WED 0x08
#define THR 0x10
#define FRI 0x20
#define SAT 0x40
#define EVERYDAY(일 | 월 | 화 | 수 | THR | 금 | 토)
#define SUNRISE 0x80
#define MENU_OPTIONS 9
#define MENU_TIME 30
#define OFF 0
#define ON 1
#define STATE_ON_OFF 0x01
//#define SERIAL_DEBUG
#ifdef SERIAL_DEBUG
SoftwareSerial swSerial(8,7 );
#endif
요일문자[][4] ={"SUN","MON","TUE","WED","THU","FRI"," SAT"};
문자 메뉴[][13] ={"다음",
"물",
"재설정",
"최소 시계 +" ,
"최소 시계 -",
"시계 시간 +",
"시계 시간 -",
"일출",
"ON/OFF"};
열거형 {
MENU_NEXT,
MENU_WATER,
MENU_RESET,
MENU_CLOCK_MIN_PLUS,
MENU_CLOCK_MIN_MINUS,
MENU_CLOCK_HOUR_PLUS,
MENU_CLOCK_HOUR_MINUS,
MENU_SUNRISE,
MENU_ON_OFF
};
typedef struct {
문자 설명[16];
uint8_t 릴레이;
} ZONE;
typedef struct {
uint8_t 영역;
uint8_t일;
int8_t시간;
int8_t분;
uint8_t 기간;
} 일정;
typedef 구조체 {
time_t 일출_시간;
time_t last_water_time;
uint8_t water_schedule;
uint8_t water_duration;
uint8_t 비[MAX_ZONES];
uint8_t 상태;
uint8_t crc;
} NVRAM;
열거 {
ZONE1,
ZONE2,
ZONE3,
ZONE4
};
열거형 {
RELAY1 =1,
RELAY2,
RELAY3,
RELAY4
};
ZONE 영역[] ={
{"앞 오른쪽", RELAY1},
{"앞 왼쪽", RELAY2},
{"덤불", RELAY3},
{"왼쪽", RELAY4},
};
스케줄 일정[] ={
{ZONE1, SUNRISE | 매일, -1, 0, 4},
{ZONE2, 매일, 6, 15, 5},
{ZONE3, 매일, 6, 0, 10},
{ZONE4, 매일 , 6, 10, 6},
};
NVRAM nvram;
부울 업데이트_nvram =false;
uint8_t crc8(uint8_t* 데이터, uint16_t 길이)
{
uint8_t crc =0;
동안 (길이--) {
crc =_crc8_ccitt_update(crc, *data++);
}
return crc;
}
int led =13;
bool init_oled =true;
bool update_oled =true;
bool init_board =true;
#ifdef 화씨
#define C2F(temp) 섭씨에서 화씨로(온도)
섭씨에서 화씨로 부동(섭씨로 부동)
{
return((섭씨) * 9) / 5) + 32;
}
#else
#define C2F(temp)(temp)
#endif
SerialPrint( const char* str, float decimal, char error)
{
Serial.print(str);
if (error) Serial.print(F("NA"));
else Serial.print(십진수, 1);
}
time_t NextScheduleTime(time_t last_time, uint8_t* next_schedule)
{
time_t next_time =-1;
time_t clk_time;
uint8_t i;
tm clk;
uint8_t wday;
for (i =0; i if (schedule[i].days &SUNRISE) {
clk_time =nvram.sunrise_time;
clk_time +=schedule[i].hour * SEC_IN_HOUR;
clk_time +=일정[i].min * SEC_IN_MIN;
localtime_r(&clk_time, &clk);
}
else {
localtime_r(&last_time, &clk);
clk. tm_hour =일정[i].시간;
clk.tm_min =일정[i].min;
clk.tm_sec =0;
clk_time =mktime(&clk);
}
wday =clk.tm_wday;
while (clk_time <=last_time || !(schedule[i].days &(1 < {
clk_time + =SEC_IN_DAY;
if (++wday> SATURDAY) wday =SUNDAY;
if (wday ==clk.tm_wday) 휴식; // 일주일만 체크
}
if (clk_time next_time =clk_time;
*next_schedule =i;
}
}
return next_time;
}
무효 StartScheduleTime(time_t start_time, uint8_t start_schedule)
{
uint8_t i;
nvram. last_water_time =start_time;
nvram.water_schedule =start_schedule;
nvram.water_duration =schedule[start_schedule].duration+1;
update_nvram =true;
// 비가 왔는지 확인
i =일정[start_schedule].zone;
if (i 0) {
if (nvram.rain[i]> nvram.water_duration) nvram .water_duration =0;
else nvram.water_duration -=nvram.rain[i];
nvram.rain[i] =0;
}
}
무효 WaterScheduleTime(무효)
{
uint8_t i;
nvram.water_duration--;
update_nvram =true;
i =일정[ nvram.water_schedule].zone;
if (i Serial.print("r");
Serial.print(zone[i].relay);
if (nvram.water_duration> 0) Serial.println("o");
else Serial.println("f");
SerialReadUntilDone();
}
}
무효 설정() {
Serial.begin(115200);
#ifdef SERIAL_DEBUG
swSerial.begin(115200);
#endif
pinMode(led, OUTPUT);
//delay(1000);
wdt_enable(WDTO_8S);
}
void 루프() {
정적 tm rtc;
tm clk, 일출_clk;
time_t rtc_time;
time_t clk_time;
정적 time_t next_time;
정적 uint8_t last_sec;
정적 uint8_t last_min;
bool error_rtc;
bool error_light;
bool error_temp;
정적 긴 럭스 =0;
정적 부동 온도, 비;
정적 uint8_t 일출_카운터 =MIN_IN_HOUR;
정적 bool check_sunrise =false;
uint8_t i;
정적 bool read_nvram =true;
정적 time_t water_time;
정적 uint8_t water_schedule;
uint8_t sz;
uint8_t wday;
long n;
bool button1, button2;
정적 int8_t menu_select =-1;
정적 time_t menu_time =0;
Serial.println();
if (SerialReadUntilDone()) {
if (init_board) {
SerialCmdDone(INIT_BOARD);
init_board =false;
}
if (init_oled) {
if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM1)) {
SerialCmdDone (I2C_OLED ";si;sc;sd");
init_oled =false;
}
}
if (SerialCmdDone(RTC_SENSOR)) {
error_rtc =!SerialReadTime(&rtc);
if (!error_rtc) {
clk =rtc; // mktime()은 구조체를 변경할 수 있습니다. tm
rtc_time =mktime(&clk);
localtime_r(&rtc_time, &rtc); // wday를 가져옵니다.
}
if (read_nvram) {
if (SerialCmdNoError(I2C_EEPROM)) {
SerialReadEEPROM((uint8_t*)&nvram, 0, sizeof (nvram));
if (nvram.crc !=crc8((uint8_t*)&nvram, sizeof(nvram)-sizeof(uint8_t))) {
//swSerial.println("CRC8 실패! ");
// nvram 초기화
memset(&nvram, 0, sizeof(nvram));
clk =rtc;
clk.tm_hour =6;
clk .tm_min =0;
clk.tm_sec =0;
nvram.sunrise_time =mktime(&clk);
if (nvram.sunrise_time update_nvram =true;
}
// 마지막 물 시간을 1주일 이상 확인
if (rtc_time - nvram.last_water_time> SEC_IN_WEEK) nvram.last_water_time =rtc_time - SEC_IN_WEEK;
// 일출 시간 확인
if (rtc_time> nvram.sunrise_time) {
localtime_r(&nvram.sunrise_time, &sunrise_clk);
clk =rtc;
clk.tm_hour =일출_clk.tm_hour;
clk.tm_min =일출_clk.tm_min;
clk.tm_sec =일출_clk.tm_sec;
nvram.sunrise_time =mktime(&clk);
if (nvram.sunrise_time }
if (nvram.water_duration) {
nvram. water_duration++;
water_time =nvram.last_water_time;
}
else {
clk_time =(nvram.last_water_time) ? nvram.last_water_time :rtc_time;
water_time =NextScheduleTime(clk_time, &water_schedule);
}
read_nvram =false;
}
}
}
// 1분에 한 번만 처리
if (rtc.tm_min !=last_min)
{
// 1-Wire 온도 측정을 요청합니다. 나중에 읽으십시오.
error_temp =!SerialCmdNoError(ONEWIRE_TEMPERATURE);
if (!error_temp) SerialCmdDone("tt");
error_light =!SerialCmdNoError(ONEWIRE_TO_I2C_ROM2 ";oo0" );
if (!error_light) {
SerialCmdDone(I2C_LIGHT); // 오버드라이브를 사용하지 않음
SerialCmd("sr");
SerialReadInt(&lux);
SerialReadUntilDone();
}
if (SerialCmd (OPTICAL_SENSOR)) {
SerialReadFloat(&rain);
SerialReadUntilDone();
}
error_temp =!SerialCmdNoError(ONEWIRE_TEMPERATURE);
if(! error_temp) {
SerialCmd("tr");
SerialReadFloat(&temp);
SerialReadUntilDone();
}
// 일출인가요?
if (lux if (sunrise_counter> 0) 일출_카운터--;
else check_sunrise =true;
}
else {
if (일출_카운터 일출_카운터++;
if (check_sunrise &&일출_카운터 ==MIN_IN_HOUR) {
nvram.sunrise_time =rtc_time + (SEC_IN_DAY - SEC_IN_HOUR 체크);
update_nvram =true;
}
}
}
// 비가 오나요?
if (rain <=RAIN_DETECT_LEVEL) {
for (i =0, i if (nvram.rain[i] <-1) nvram.rain[i]++; }
update_nvram =true;
}
// 일정 확인
if (menu_select ==-1 &&!nvram.water_duration) {
while (water_time + (schedule[water_schedule].duration * SEC_IN_MIN) water_time =NextScheduleTime(water_time, &water_schedule);
}
if (water_time <=rtc_time) {
StartScheduleTime(water_time, water_schedule);
if (temp <=DO_NOT_WATER_TEMP || nvram.state &STATE_ON_OFF ==OFF)
nvram.water_duration =0;
}
}
// 물을 주어야 합니까?
if (nvram.water_duration) {
WaterScheduleTime();
if (!nvram.water_duration)
water_time =NextScheduleTime(water_time, &water_schedule);
}
last_min =rtc.tm_min;
update_oled =true;
}
// 버튼 확인
button1 =SerialReadButton(BUTTON1);
if (button1 ) {
if (menu_select ==-1) menu_select =0;
else {
if (++menu_select>=MENU_OPTIONS)
menu_select =0;
}
menu_time =rtc_time;
update_oled =true;
}
if (menu_select>=0) {
button2 =SerialReadButton(BUTTON2);
if( button2) {
clk_time =rtc_time;
switch(menu_select) {
case MENU_NEXT:
case MENU_RESET:
if (nvram.water_duration) {
nvram .water_duration =1;
WaterScheduleTime();
}
water_time =NextScheduleTime((menu_select ==MENU_NEXT) ? water_time :rtc_time, &water_schedule);
break;
case MENU_WATER:
StartScheduleTime(water_time, water_schedule);
WaterScheduleTime();
break;
case MENU_CLOCK_MIN_PLUS:
clk_time +=SEC_IN_MIN;
break;
case MENU_CLOCK_MIN_MINUS:
clk_time -=SEC_IN_MIN;
break;
case MENU_CLOCK_HOUR_PLUS:
clk_time +=SEC_IN_HOUR;
break;
case MENU_CLOCK_HOUR_MINUS:
clk_time -=SEC_IN_HOUR;
break;
case MENU_ON_OFF:
nvram.state ^=STATE_ON_OFF;
update_nvram =true;
중단;
}
if (clk_time !=rtc_time) {
if (SerialCmdDone(RTC_SENSOR)) {
localtime_r( &clk_time, &clk);
SerialWriteTime(&clk);
rtc_time =clk_time;
}
}
menu_time =rtc_time;
update_oled =true;
}
}
if (menu_select>=0 &&rtc_time - menu_time> MENU_TIME) {
menu_select =-1;
update_oled =true;
}
if (update_oled) {
if (S erialCmdNoError(ONEWIRE_TO_I2C_ROM1)) {
Serial.print("st10;so1;sc;sf0;sa0;sd0,0,\"");
if (nvram.water_duration) Serial.print(nvram. water_duration);
else {
if ((nvram.state &STATE_ON_OFF) ==OFF) Serial.print("OFF");
else if (rain <=RAIN_DETECT_LEVEL) Serial.print ("비");
else if (temp <=DO_NOT_WATER_TEMP) Serial.print("추운");
else Serial.print("v1.1");
}
Serial.print("\";sf2;sa1;sd75,0,\"");
if (menu_select ==7) { // 일출
clk_time =nvram.sunrise_time;
localtime_r(&clk_time, &clk);
}
else clk =rtc;
Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0));
Serial.print(":");
if (clk.tm_min <10) Serial.print("0");
Serial.print(clk.tm_min);
Serial.println("\"");
SerialReadUntilDone();
Serial.print("sf1;sa0;sd79,8,\"");
직렬 .print((clk.tm_hour>12)?"PM":"AM");
Serial.print("\";sf0;sa1;sd127,1,\"");
직렬 .print(평일[clk.tm_wday]);
Serial.print("\";sd127,13,\"");
Serial.print(clk.tm_mon+1);
Serial.print("/");
Serial.print(clk.tm_mday);
Serial.println("\"");
SerialReadUntilDone();
Serial.print("sf0;sa0;sd1 ,36,\"");
i =schedule[water_schedule].zone;
if (i localtime_r(&water_time , &clk);
if (water_time - rtc_time> SEC_IN_DAY) {
Serial.print("\";sa1;sd126,36,\"");
Serial.print(clk. tm_mon+1);
Serial.print("/");
Serial.print(clk.tm_mday);
Serial.print(" ");
Serial.print (clk.tm_hour-((clk.tm_hour>12)?12:0));
Serial.print(":");
if (clk.tm_min <10) Serial.print(" 0");
Serial.print(clk.tm_min);
Serial.print(" ");
}
else {
Serial.print("\ ";sf1;sa1;sd111,30,\"");
Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0));
Serial.print( ":");
if (clk.tm_min <10) Serial.print("0");
Serial.print(clk.tm_min);
세리아 l.print("\";sf0;sd126,36,\"");
}
Serial.print((clk.tm_hour>12)?"오후":"오전");
if (nvram.water_duration) Serial.print("\";so2;sc0,29,128,19");
Serial.println();
SerialReadUntilDone();
if (menu_select ==-1) {
//Serial.print("\";sa0;sd0,52,\"");
//Serial.print(rain);
SerialPrint("\";so1;sa2;sd63,52,\"", C2F(temp), error_temp);
if (!error_temp) Serial.print("\",248,\ ""
#ifdef 화씨
"F"
#else
"C"
#endif
);
Serial.print(" / ");
Serial.print(lux);
}
else {
Serial.print("\";so0;sc0,51,128,14;sf0;sa2; sd63,52,\"");
if (menu_select ==MENU_ON_OFF) {
Serial.print((nvram.state &STATE_ON_OFF) ? "OFF" :"ON");
}
else Serial.print(menu[menu_select]);
}
Serial.println("\";sd");
SerialReadUntilDone();
update_oled =false;
}
else init_oled =true;
}
if (update_nvram) {
if (SerialCmdNoError(I2C_EEPROM)) {
nvram.crc =crc8((uint8_t*)&nvram, sizeof(nvram)-sizeof(uint8_t));
//swSerial.println(nvram.crc , HEX);
SerialWriteEEPROM((uint8_t*)&nvram, 0, sizeof(nvram));
update_nvram =false;
}
}
delay(50);
}
else {
digitalWrite(led, HIGH);
delay(500);
digitalWrite(led, LOW);
지연(500);
init_board =true;
init_oled =true;
}
wdt_reset();
}
참고: USB 포트를 사용하여 Arduino Nano를 프로그래밍하는 경우 동일한 단일 직렬 포트도 사용하므로 IO Expander에서 연결을 해제해야 합니다. 대신 디버그하려면 ICSP 포트를 사용하여 ATmega328P를 프로그래밍하십시오. 소프트웨어 디버깅 포트를 활성화하려면 SERIAL_DEBUG 정의의 주석 처리를 제거하십시오.
스플리터는 먼저 1-Wire 원격 센서 라인에서 광학 적외선 센서 데이터 라인을 분리하도록 구성해야 합니다. R2에서 0옴 0603 저항에 납땜합니다.
PG7 및 PG11의 경우 오른쪽에 있는 작은 인클로저에 7/16" 구멍을 뚫고 더 큰 인클로저에 11/16" 구멍을 뚫습니다. dremel 도구를 사용하여 글랜드가 꼭 맞을 때까지 구멍을 약간 확대합니다. PG7은 원격 센서에 전원을 공급하고 PG11은 12VDC, 24VAC, 매니폴드 와이어 및 RJ11 원격 센서 와이어에 전원을 공급합니다.
SPST 순간 누름 버튼 마이크로 스위치를 배선하고 RJ11 나사 터미널에 연결합니다. 열수축 튜브를 사용하여 접점을 절연하십시오.
모든 전선을 연결하고 모든 부품을 대형 인클로저에 조립/공급합니다. 원격 센서용 50피트 RJ11 와이어는 절단 없이 PG11 글랜드에 딱 맞아야 합니다.
광학 적외선 수분 센서용 작은 인클로저의 상단에 9/16" 구멍을 뚫습니다. dremel 도구를 사용하여 센서가 맞을 때까지 구멍을 약간 확대합니다. 작은 원격 센서 인클로저는 꼭 맞지만 내용물이 있으면 RJ11 와이어를 가능한 짧게 만들면 더 작은 인클로저에 모두 넣는 데 도움이 됩니다. 일단 조립되면 너트를 조이기 전에 글랜드 너트 와셔에 해양 접착제를 추가하는 것이 좋습니다. 더 나은 인장을 만들기 위해.
원격 센서 인클로저를 외부에 설치하고 광학 적외선 수분 센서와 광 센서가 장애물 없이 하늘을 향하도록 하여 집의 동쪽으로 높게 장착합니다.
대형 인클로저의 상단 중간 하단에 1/4" 구멍을 뚫고 버튼을 장착합니다. 드레멜 도구를 사용하여 버튼이 맞을 때까지 구멍을 약간 확대합니다.
시스템을 테스트하고 모든 것이 올바르게 작동하는지 확인하십시오. 릴레이와 센서를 테스트하려면 IO Expander에서 Arduino를 분리하고 컴퓨터에 직접 연결하여 수동으로 제어합니다. 모든 것이 제대로 작동하는지 확인한 후 양면 테이프와 포장 폼을 사용하여 모든 부품을 인클로저에 조립하여 보드를 고정하고 Smart Irrigation Controller의 이점과 비용 절감 효과를 누리십시오.
작동 중인 비디오 <비디오 너비="100%" 높이="100%" preload="metadata" controls="controls"><소스 유형="video/mp4" src="http://www.zevendevelopment.com/videos/SmartIrrigationSystem .mp4" />http://www.zevendevelopment.com/videos/SmartIrrigationSystem.mp4
2019년 9월 12일 업데이트
시스템이 며칠 동안 정전되는 경우 시작 문제를 수정한 v1.1을 출시했습니다.
2019년 10월 2일 업데이트
1-Wire to I2C를 DS3231에 연결한 다음 SSD1306 OLED 화면에 연결하면 아래 원으로 표시된 이미지와 같이 SDA 및 SCL 라인에 총 3개의 다른 풀업이 생깁니다. 이렇게 하면 4.7k / 3 =1.56k 풀업이 발생하여 너무 강력하여 임의의 화면이 손상될 수 있습니다.
DS3231은 다른 라인에서 사용되는 저항 팩을 사용하므로 다른 풀업 저항을 제거합니다.
<울>/* IO Expander 스케치 최적화 * * Irrigation System v1.1 * */#include섹션>#include // 파일 위치 \Program 파일(x86)\Arduino\hardware\tools\avr\avr\include\time.h#include #include #include #include "IOExpander. h#define FAHRENHEIT#define INIT_BOARD "g5w1;g11w1;g11d0,75;g12w1;g12d0,75;rsf#define ONEWIRE_TO_I2C_ROM1 "#STURE_TO_I2C_ROM1 ""#WIRE_TO_I2C_ROM2" define I2C_EEPROM "s4tf#define I2C_OLED "s4t10#define I2C_LIGHT "s3t9;sc0#define OPTICAL_SENSOR "g5a#define BUTTON1 "g11d#define BUTTON1 "g11d#define_BUTTON2 "g12d#SUNRISefine WATER_TIME_BEFORE_#SUNRIS 정의 정의 DO_NOT_WATER_TEMP 4.4444 // 40F#define MAX_ZONES 4#define HOUR_IN_DAY 24L#define MIN_IN_HOUR 60L#define SEC_IN_MIN 60L#define SEC_IN_HOUR(_SEC_IN_HOUR_MIN_DAY |OUR)#define DAYS_IN_WEEK 7#define SEC_IN_WEEK (SEC_IN_DAY * DAYS_IN_WEEK)#define SUN 0x01#define MON 0x02#define TUE 0x04#define WED 0x08#define THR 0#define FDAY0#define 월 | 화 | 수 | THR | 금 | SAT)#define SUNRISE 0x80#define MENU_OPTIONS 9#define MENU_TIME 30#define OFF 0#define ON 1#define STATE_ON_OFF 0x01//#define SERIAL_DEBUG#ifdef SERIAL_DEBUGSoftwareSerial swdaySerial)[if4]주 ={"일","월","화","수","목","금","토"};문자 메뉴[][13] ={"다음", "물", "재설정" , "분 시계 +", "분 시계 -", "시 시계 +", "시 시계 -", "일출", "ON/OFF"};enum { MENU_NEXT, MENU_WATER, MENU_RESET, MENU_CLOCK_MIN_PLUS, MENU_CLOCK_MIN_MINUS, MENU_CLOCK_MIN_MINUS, MENU_CLOCK_HOUR_ MENU_CLOCK_HOUR_MINUS, MENU_SUNRISE, MENU_ON_OFF};typedef struct { 문자 설명[16]; uint8_t 릴레이;} ZONE, typedef 구조체 { uint8_t 영역; uint8_t일; int8_t 시간; int8_t분; uint8_t 지속 시간;} 일정;typedef 구조체 { time_t 일출 시간; time_t last_water_time; uint8_t water_schedule; uint8_t water_duration; uint8_t 비[MAX_ZONES]; uint8_t 상태; uint8_t crc;} NVRAM;enum { ZONE1, ZONE2, ZONE3, ZONE4};enum { RELAY1 =1, RELAY2, RELAY3, RELAY4};ZONE 영역[] ={ {"앞 오른쪽", RELAY1}, {"앞 왼쪽" , RELAY2}, {"덤불", RELAY3}, {"왼쪽", RELAY4},};스케줄 일정[] ={ {구역1, 일출 | 매일, -1, 0, 4}, {영역2, 매일, 6, 15, 5}, {영역3, 매일, 6, 0, 10}, {영역4, 매일, 6, 10, 6},}; NVRAM nvram;bool update_nvram =false;uint8_t crc8(uint8_t* 데이터, uint16_t 길이){ uint8_t crc =0; 동안 (길이--) { crc =_crc8_ccitt_update(crc, *data++); } return crc;}int led =13;bool init_oled =true;bool update_oled =true;bool init_board =true;#ifdef FAHRENHEIT#define C2F(온도) 섭씨ToFahrenheit(temp)float 섭씨ToFahrenheit(섭씨 부동 소수점){ return ((섭씨 온도 * 9) / 5) + 32;}#else#define C2F(temp) (temp)#endifvoid SerialPrint(const char* str, float decimal, char error){ Serial.print(str); if (오류) Serial.print(F("NA")); else Serial.print(십진수, 1);}time_t NextScheduleTime(time_t last_time, uint8_t* next_schedule){ time_t next_time =-1; time_t clk_time; uint8_t 나; 티엠 클락; uint8_t wday; for (i =0; i SATURDAY) wday =SUNDAY; if (wday ==clk.tm_wday) 휴식; // 일주일만 확인 } if (clk_time 0) { if (nvram.rain[i]> nvram.water_duration) nvram.water_duration =0; 그렇지 않으면 nvram.water_duration -=nvram.rain[i]; nvram.rain[i] =0; }}무효 WaterScheduleTime(무효) { uint8_t i; nvram.water_duration--; update_nvram =참; i =일정[nvram.water_schedule].zone; if (i 0) Serial.println("o"); 그렇지 않으면 Serial.println("f"); SerialReadUntilDone(); }} 무효 설정() { Serial.begin(115200);#ifdef SERIAL_DEBUG swSerial.begin(115200);#endif pinMode(led, OUTPUT); //지연(1000); wdt_enable(WDTO_8S);} 무효 루프() { 정적 tm rtc; tm clk, 일출_clk; time_t rtc_time; time_t clk_time; 정적 time_t next_time; 정적 uint8_t last_sec; 정적 uint8_t last_min; 부울 error_rtc; 부울 error_light; 부울 error_temp; 정적 긴 럭스 =0; 정적 부동 온도, 비; 정적 uint8_t 일출_카운터 =MIN_IN_HOUR; 정적 부울 check_sunrise =거짓; uint8_t 나; 정적 부울 read_nvram =true; 정적 time_t water_time; 정적 uint8_t water_schedule; uint8_t sz; uint8_t wday; 긴 n; 부울 버튼1, 버튼2; 정적 int8_t menu_select =-1; 정적 time_t menu_time =0; 직렬.println(); if (SerialReadUntilDone()) { if (init_board) { SerialCmdDone(INIT_BOARD); init_board =거짓; } if (init_oled) { if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM1)) { SerialCmdDone(I2C_OLED ";si;sc;sd"); init_oled =거짓; } } if (SerialCmdDone(RTC_SENSOR)) { error_rtc =!SerialReadTime(&rtc); if (!error_rtc) { clk =rtc; // mktime() can change struct tm rtc_time =mktime(&clk); localtime_r(&rtc_time, &rtc); // Get wday. } if (read_nvram) { if (SerialCmdNoError(I2C_EEPROM)) { SerialReadEEPROM((uint8_t*)&nvram, 0, sizeof(nvram)); if (nvram.crc !=crc8((uint8_t*)&nvram, sizeof(nvram)-sizeof(uint8_t))) { //swSerial.println("CRC8 Failure!"); // Initialize nvram memset(&nvram, 0, sizeof(nvram)); clk =rtc; clk.tm_hour =6; clk.tm_min =0; clk.tm_sec =0; nvram.sunrise_time =mktime(&clk); if (nvram.sunrise_time SEC_IN_WEEK) nvram.last_water_time =rtc_time - SEC_IN_WEEK; // Check sunrise time if (rtc_time> nvram.sunrise_time) { localtime_r(&nvram.sunrise_time, &sunrise_clk); clk =rtc; clk.tm_hour =sunrise_clk.tm_hour; clk.tm_min =sunrise_clk.tm_min; clk.tm_sec =sunrise_clk.tm_sec; nvram.sunrise_time =mktime(&clk); if (nvram.sunrise_time 0) sunrise_counter--; else check_sunrise =true; } else { if (sunrise_counter =MENU_OPTIONS) menu_select =0; } menu_time =rtc_time; update_oled =true; } if (menu_select>=0) { button2 =SerialReadButton(BUTTON2); if (button2) { clk_time =rtc_time; switch(menu_select) { case MENU_NEXT:case MENU_RESET:if (nvram.water_duration) { nvram.water_duration =1; WaterScheduleTime(); } water_time =NextScheduleTime((menu_select ==MENU_NEXT) ? water_time :rtc_time, &water_schedule); 부서지다; case MENU_WATER:StartScheduleTime(water_time, water_schedule); WaterScheduleTime(); 부서지다; case MENU_CLOCK_MIN_PLUS:clk_time +=SEC_IN_MIN; 부서지다; case MENU_CLOCK_MIN_MINUS:clk_time -=SEC_IN_MIN; 부서지다; case MENU_CLOCK_HOUR_PLUS:clk_time +=SEC_IN_HOUR; 부서지다; case MENU_CLOCK_HOUR_MINUS:clk_time -=SEC_IN_HOUR; 부서지다; case MENU_ON_OFF:nvram.state ^=STATE_ON_OFF; update_nvram =true; 부서지다; } if (clk_time !=rtc_time) { if (SerialCmdDone(RTC_SENSOR)) { localtime_r(&clk_time, &clk); SerialWriteTime(&clk); rtc_time =clk_time; } } menu_time =rtc_time; update_oled =true; } } if (menu_select>=0 &&rtc_time - menu_time> MENU_TIME) { menu_select =-1; update_oled =true; } if (update_oled) { if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM1)) { Serial.print("st10;so1;sc;sf0;sa0;sd0,0,\""); if (nvram.water_duration) Serial.print(nvram.water_duration); else { if ((nvram.state &STATE_ON_OFF) ==OFF) Serial.print("OFF"); else if (rain <=RAIN_DETECT_LEVEL) Serial.print("Rain"); else if (temp <=DO_NOT_WATER_TEMP) Serial.print("Cold"); else Serial.print("v1.1"); } Serial.print("\";sf2;sa1;sd75,0,\""); if (menu_select ==7) { // Sunrise clk_time =nvram.sunrise_time; localtime_r(&clk_time, &clk); } else clk =rtc; Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0)); Serial.print(":"); if (clk.tm_min <10) Serial.print("0"); Serial.print(clk.tm_min); Serial.println("\""); SerialReadUntilDone(); Serial.print("sf1;sa0;sd79,8,\""); Serial.print((clk.tm_hour>12)?"PM":"AM"); Serial.print("\";sf0;sa1;sd127,1,\""); Serial.print(weekday[clk.tm_wday]); Serial.print("\";sd127,13,\""); Serial.print(clk.tm_mon+1); Serial.print("/"); Serial.print(clk.tm_mday); Serial.println("\""); SerialReadUntilDone(); Serial.print("sf0;sa0;sd1,36,\""); i =schedule[water_schedule].zone; if (i SEC_IN_DAY) { Serial.print("\";sa1;sd126,36,\""); Serial.print(clk.tm_mon+1); Serial.print("/"); Serial.print(clk.tm_mday); Serial.print(" "); Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0)); Serial.print(":"); if (clk.tm_min <10) Serial.print("0"); Serial.print(clk.tm_min); Serial.print(" "); } else { Serial.print("\";sf1;sa1;sd111,30,\""); Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0)); Serial.print(":"); if (clk.tm_min <10) Serial.print("0"); Serial.print(clk.tm_min); Serial.print("\";sf0;sd126,36,\""); } Serial.print((clk.tm_hour>12)?"PM":"AM"); if (nvram.water_duration) Serial.print("\";so2;sc0,29,128,19"); 직렬.println(); SerialReadUntilDone(); if (menu_select ==-1) { //Serial.print("\";sa0;sd0,52,\""); //Serial.print(rain); SerialPrint("\";so1;sa2;sd63,52,\"", C2F(temp), error_temp); if (!error_temp) Serial.print("\",248,\"" #ifdef FAHRENHEIT "F" #else "C" #endif ); Serial.print(" / "); Serial.print(lux); } else { Serial.print("\";so0;sc0,51,128,14;sf0;sa2;sd63,52,\""); if (menu_select ==MENU_ON_OFF) { Serial.print((nvram.state &STATE_ON_OFF) ? "OFF" :"ON"); } else Serial.print(menu[menu_select]); } Serial.println("\";sd"); SerialReadUntilDone(); update_oled =false; } else init_oled =true; } if (update_nvram) { if (SerialCmdNoError(I2C_EEPROM)) { nvram.crc =crc8((uint8_t*)&nvram, sizeof(nvram)-sizeof(uint8_t)); //swSerial.println(nvram.crc, HEX); SerialWriteEEPROM((uint8_t*)&nvram, 0, sizeof(nvram)); update_nvram =false; } } delay(50); } else { digitalWrite(led, HIGH); delay(500); digitalWrite(led, LOW); delay(500); init_board =true; init_oled =true; } wdt_reset();}
제조공정
구성품 및 소모품 Arduino UNO × 1 Espressif ESP8266 ESP-01 × 1 DHT11 온도 및 습도 센서(4핀) × 1 ControlEverything.com I2C용 4채널 릴레이 컨트롤러 × 1 IR 장애물 센서 × 1 카메라 × 1 점퍼 와이어(일반) × 1 앱 및 온라인 서비스 Arduino IDE Amazon Alexa Alexa 기
FMI의 보고서에 따르면 글로벌 산업용 로봇 컨트롤러 시장의 성장은 공장 자동화의 가속화 활동에 의해 지속되고 주도되고 있습니다. FMI의 연구에 따르면 산업용 로봇 컨트롤러 시장은 2018년에 약 6억 3,260만 달러로 평가되었으며 2019년과 2029년 사이에 9.1%의 성장률을 보일 것으로 예상됩니다. 보고서의 결과에 따르면 전 세계 산업용 로봇 컨트롤러 시장은 예측 기간 동안 상당한 성장을 보일 것으로 예상됩니다. 이는 인간 기계 상호 작용의 채택 증가, 하드웨어 제어를 향상시키는 데이터 및 클라우드 기능, 산업용