산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

스마트 관개 컨트롤러

구성품 및 소모품

Arduino Nano R3
× 1
IO 확장기
× 1
IO 확장기 번들
× 1

앱 및 온라인 서비스

Arduino IDE

이 프로젝트 정보

Arduino로 스마트 관개 컨트롤러 만들기

동적 물 순환으로 마당에 지능적으로 관개하십시오. 비가 오거나 마지막 물을 뿌린 후 비가 온다면 마당에 물을 주지 마십시오. 광 센서를 사용하여 일출 시간을 감지하고 그에 따라 물 시작 시간을 자동으로 조정합니다. 마당이 너무 추우면 물을 주지 마십시오.

기능 목록

<울>
  • 외부 온도 센서
  • 외부 레인 센서
  • 외부 조도 센서
  • 주간 일정을 위한 배터리 지원 실시간 시계
  • 비휘발성 저장 장치 - 정전으로 인해 물을 잃지 않음
  • 일출 감지
  • 스마트 급수는 수도 요금을 절약합니다.
  • 적절한 몸을 담글 수 있도록 해가 뜨기 전에 물을 주세요.
  • 밖이 너무 추우면 물주기를 중단하세요
  • 곰팡이 증식 감소
  • 간편한 일정 관리
  • 스마트 관개 컨트롤러를 구축하는 데 필요한 부품

    <울>
  • IO 확장기
  • I2C에 대한 x2 1-Wire
  • 1-Wire 접합
  • 광 커넥터
  • 분배기
  • 아두이노 나노.
  • 4채널 DC 5V 릴레이.
  • TSL2561 광 센서.
  • DS18B20 방수 온도 센서.
  • 광적외선 수위 센서.
  • DS3231 AT24C32 IIC 정밀 실시간 시계.
  • I2C SSD1306 OLED 128x64 디스플레이.
  • 200x120x75mm 투명 방수 플라스틱 인클로저.
  • 100x68x50mm 투명 방수 플라스틱 인클로저.
  • ip68 pg7 방수 나일론 케이블 글랜드.
  • ip68 pg11 방수 나일론 케이블 글랜드.
  • RJ11 키스톤 나사 단자 잭.
  • 50피트 4C4P RJ11 와이어.
  • 6피트 4C4P RJ11 와이어.
  • 2.54mm 헤더 와이어.
  • x2 2 핀 SPST 순간 푸시 버튼 마이크로 스위치
  • 12VDC 1A 벽면 어댑터 전원 공급 장치.
  • 배선도

    OLED 디스플레이

    메뉴 버튼을 눌러 메뉴를 표시하고 버튼을 계속 눌러 모든 메뉴 옵션을 순환합니다. 메뉴는 30초 동안 활동이 없으면 자동으로 제거됩니다. 선택 버튼을 눌러 원하는 메뉴 기능을 수행하세요.

    그렇다면 IO 확장기를 사용하는 이유는 무엇입니까?

    <울>
  • 간단한 디자인
  • 기성 부품
  • 작성할 1-Wire 드라이버 없음
  • 작성할 DS3231 RTC 드라이버 없음
  • 작성할 EEPROM 드라이버 없음
  • 작성할 OLED 디스플레이 드라이버 없음
  • Arduino 코드 공간을 차지하는 디스플레이 글꼴 없음
  • 작성할 온도 센서 드라이버 없음
  • 작성할 광학 레인 센서 드라이어 없음
  • Arduino에서 코드 공간을 절약합니다. 단 12710바이트(39%)
  • 코드 작성에 단 3일 소요
  • 표준 RJ11 전화 케이블을 사용하여 간편하게 배선
  • 센서 케이블 길이 문제 없음
  • 유사한 상용 시스템보다 구축 비용이 저렴
  • 개별 요구 사항에 맞게 변경하기 쉬움
  • 단일 전원 공급 장치
  • 시스템 구축

    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은 다른 라인에서 사용되는 저항 팩을 사용하므로 다른 풀업 저항을 제거합니다.

    <울>
  • I2C R3 및 R4에 대한 1-Wire.
  • SSD1306 OLED R6 및 R7.
  • <섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

    코드

    <울>
  • 스마트 관개 컨트롤러
  • 스마트 관개 컨트롤러C/C++
    Arduino를 사용하여 마당이나 정원에 지능적으로 물을 주세요.
    /* 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();}

    회로도

    Intelligently water your yard or garden

    제조공정

    1. 오픈 소스 스마트 농업 시스템 설계
    2. 에지 AI 개발 속도를 높이는 스마트 센서 보드
    3. Sensorflare 및 RaspberryPi가 포함된 433MHz 스마트 홈 컨트롤러
    4. 라즈베리 파이 온도 센서
    5. Cellular IoT:Smart Garbage Can
    6. Smart Bartender
    7. 감정 센서 / EEG
    8. 수분 센서가 있는 Win10 IOT 관개 컨트롤러
    9. 스마트 섬유용 초고감도, 탄력 센서
    10. 스마트 콘택트 렌즈용 초박형 센서