18 Jan 2021

Membuat Robot Mobil RC Kamera Menggunakan ESP32 Cam

Halo agan agan sekalian, kalau pernah punya mobil-mobilan remote control alias RC sekarng mungkin sudah biasa, tapi kalau mobil RC nya ada kamera kayanya sedikit luar biasa. Apalagi kalau agan sendiri yang buatnya tambah luar biasa.

Ikutin tutorial ini nanti agan bakal ditunjukin alat-alat dan komponen apa saja yang diperlukan serta bagaimana cara membuatnya.

Jika agan ingin membuat PCB dan sedang mencari jasa cetak PCB, bisa langsung order saja ke PCBWAY.COM, harganya lebih murah $5 bisa mendapatkan papan PCB 10 keping dengan kualitas PCB yang sangat bagus.

Daftar Komponen:

  • 1 x PCB ESP32-Cam Motor Shield (PCBWAY)
  • 1 x ESP32-Cam Board
  • 1 x USB to TTL CP2102 Module
  • 1 x IC L293D Driver Motor
  • 2 x Transistor NPN BC547
  • 1 x Voltage Regulator 5V AMS1117
  • 1 x Diode 1N4001
  • 1 x LED Red 3mm
  • 5 x Resistor 10K Ohm
  • 2 x Resistor 1K Ohm
  • 1 x Capacitor 100uF/16V
  • 1 x Active Buzzer 5V
  • 1 x Tact Switch
  • 1 x SPDT Slide Switch
  • 3 x 2 Pin Terminal Block Screw
  • 4 x 10 pin Male Header
  • 1 x 8 pin Female Header
  • 1 x 6 pin Female Header
  • 2 x 8 pin Long Legs Female Header

  • Order PCB (PCBWAY)

    Untuk membuat proyek ini kamu perlu memesan prototipe PCB di PCBWAY. Cara pemesanannya sangat mudah, kamu akan mendapatkan 10 keping PCB seharga $5 dengan kualitas PCB yang sangat bagus.

    Langkah-langkah order:

    1. SignUp/Log in di pcbway.com

    2. Buka tautan proyek PCB ini.

    3. Klik Tambahkan ke troli.

    4. Tunggu sebentar untuk review PCB, lalu Klik Check Out.

    Menyolder Komponen SMD

    Proyek kali ini memiliki satu komponen SMD, jangan khawatir komponen ini tidak terlalu sulit untuk disolder karena ukurannya cukup besar, agan hanya membutuhkan pinset sebagai alat bantu untuk menahan komponen saat disolder. Langkah pertama beri lapisan timah pada tembaga PCB untuk tempat kaki komponen SMD.

    Kemudian ditahan menggunakan pinset lalu di panaskan kembali timah hingga menempel dengan kaki komponen SMD.

    Memasang Komponen

    Pasang semua komponen pada PCB mengikuti gambar/simbol komponen yang ada pada bagian atas PCB, untuk lebih jelasnya dapat mengikuti step by step pada video youtube.

    Menyolder Komponen

    Solder semua kaki komponen pada bagian bawah PCB, untuk lebih jelasnya dapat mengikuti step by step pada video youtube.

    Kemudian potong kaki komponen yang panjang menggunakan tang pemotong, hati-hati ketika memotong pastikan memakai kacamata safety.

    Memasang Socket ESP32-Cam

    Terdapat dua jenis female pin header 8 pin yang akan digunakan, yaitu dengan kaki yang pendek dan kaki yang panjang.
    Untuk kaki yang pendek bengkokan semua kaki pin menggunakan tang hingga membentuk sudut 90 derajat.

    Kemudian pasang pada PCB bagian depan seperti terlihat pada gambar.

    Langkah selanjutnya solder pada bagian belakang PCB.

    Jenis yang kedua female pin header 8 pin dengan kaki yang panjang, disini menggunakan sebanyak 2 buah.
    Pertama bengkokan terlebih dahulu salah satu female pin header.

    Setelah itu sambungkan dengan cara memasukan ke female pin header yang satunya lagi dan jangan terlalu dalam. Pastikan setiap pin tersambung dengan menggunakan multitester untuk mengetes continuity.
    Setelah itu lem menggunakan power glue supaya tidak mudah terlepas.

    Dengan bantuan ESP32-Cam board sebagai mal pasangkan pada PCB.

    Langkah terakhir solder pada bagian belakang PCB.

    Memasang IC, ESP32-Cam & USB to TTL Modul

    Pasang IC L293D pada soket IC, lihat posisinya jangan terbalik. Kemudian pasang modul USB to TTL CP2102 yang sudah memiliki pin header.

    Memasang ESP32-Cam Motor Shield ke Chassis

    Agan bisa menggunakan chasis custom untuk mobil robot yang banyak dipasaran bisa didapat dari toko online yang biasa digunakan untuk membangun robot manggunakan Arduino Uno, karena ukuran PCB dan jarak lubang sama dengan Arduino Uno.
    Untuk jumlah roda/motor penggerak tidak masalah jika agan menggunakan empat atau dua motor, sebenarnya output dari ESP32-Cam Motor Shield motor hanya dua yaitu kiri dan kanan, kalau ingin menggunakan empat roda/motor penggerak hanya dipasang secara paralel, berikut gambar rangkaiannya.

    Hubungkan motor kiri dan motor kanan ke terminal sekrup pada output driver motor ESP32-Cam Motor Shield. Hubungkan baterai 2 x 18650 3.7V ke sekrup terminal catu daya pada ESP32-Cam Motor Shield.

    Mengisi Program ke ESP32-Cam Board

    Untuk membuat WiFi Kamera RC Car berfungsi dan dapat dikendalikan oleh smartphone Android, agan harus memprogram ESP32-Cam terlebih dahulu. Caranya cukup mudah hanya cukup menggunakan smartphone Android tidak memerlukan lagi Laptop/PC, ikuti langkah berikut:

    - Install ESP32 Camera WiFi Robot Car dari Google Playstore.

    - Hubungkan antara smartphone Android dengan ESP32-Cam Motor Shield menggunakan kabel USB dan USB OTG adapter. (pastikan HP agan support USB OTG)

    - Untuk menjadikan ESP32-Cam board siap untuk diisi program, dalam kondisi menyala agan perlu menekan serta menahan tombol GPIO0 kemudian menekan tombol reset pada ESP32-Cam board. setelah itu lepas kan keduanya maka sekarang posisi chip ESP32 siap untuk diisi program melalui USB to TTL CP2102.

    - Buka aplikasi ESP32 Camera Wifi Robot Car, pada menu bar atas tekan icon Circuit diagram & Code. Kemudian setelah terbuka tampilan gambar rangkaian dan kode pada menu bar atasnya tekan ikon tiga titik (menu lainnya), buka pilihan Motor Driver to be Used lalu pilih ESP32-Cam Motor Shield, kemudian buka pilihan Upload Firmware Via lalu pilih USB OTG, jika ingin menghubungkan Mobil RC ke sebuah router jaringan rumah agan bisa set SSID name & Password. Setelah selesai konfigurasi langkah selanjutnya tekan ikon UPLOAD, dan tunggu sampai proses uploadnya selesai. Terakhir tekan tombol reset untuk menjalankan program.

    Selain mengisi program melalui smartphone Android, agan dapat juga memprogram ESP32-Cam melalui komputer menggunakan software Arduino IDE menggunakan kode program dibawah ini:

    Catatan: Software Arduino IDE agan harus sudah disetting untuk dapat digunakan untuk memprogram ESP32 sebelumnya.

    Simpan sketch di bawah dengan nama file "esp32cam_wifi_robot_car_l293d.ino"

    
    #include "esp_camera.h"
    #include <WiFi.h>
    #include <ArduinoOTA.h>
    
    /* Wifi Crdentials */
    String sta_ssid = "$your_ssid_maximum_32_characters";     // set Wifi network you want to connect to
    String sta_password = "$your_pswd_maximum_32_characters";   // set password for Wifi network
    
    /* define CAMERA_MODEL_AI_THINKER */
    #define PWDN_GPIO_NUM     32
    #define RESET_GPIO_NUM    -1
    #define XCLK_GPIO_NUM      0
    #define SIOD_GPIO_NUM     26
    #define SIOC_GPIO_NUM     27
    #define Y9_GPIO_NUM       35
    #define Y8_GPIO_NUM       34
    #define Y7_GPIO_NUM       39
    #define Y6_GPIO_NUM       36
    #define Y5_GPIO_NUM       21
    #define Y4_GPIO_NUM       19
    #define Y3_GPIO_NUM       18
    #define Y2_GPIO_NUM        5
    #define VSYNC_GPIO_NUM    25
    #define HREF_GPIO_NUM     23
    #define PCLK_GPIO_NUM     22
    
    /* Defining motor and servo pins */
    extern int DRV_A = 12;
    extern int DRV_B = 13;
    extern int DIR_A = 14;
    extern int DIR_B = 15;
    
    extern int ledVal = 20;  // setting bright of flash LED 0-255
    
    extern int ledPin = 4;  // set digital pin GPIO4 as LED pin (use biult-in LED)
    extern int buzzerPin = 2;  // set digital pin GPIO2 as LED pin (use Active Buzzer)
    extern int servoPin = 2;  // set digital pin GPIO2 as servo pin (use SG90)
    
    unsigned long previousMillis = 0;
    
    void startCameraServer();
    
    void initServo() {
      ledcSetup(8, 50, 16); /*50 hz PWM, 16-bit resolution and range from 3250 to 6500 */
      ledcAttachPin(servoPin, 8);
    }
    
    void initLed() {
      ledcSetup(7, 5000, 8); /* 5000 hz PWM, 8-bit resolution and range from 0 to 255 */
      ledcAttachPin(ledPin, 7);
    }
    
    void setup() {
      Serial.begin(115200);         // set up seriamonitor at 115200 bps
      Serial.setDebugOutput(true);
      Serial.println();
      Serial.println("*ESP32 Camera Remote Control - L293D Bluino Shield*");
      Serial.println("--------------------------------------------------------");
    
      // Set all the motor control pin to Output
      pinMode(DRV_A, OUTPUT);
      pinMode(DRV_B, OUTPUT);
      pinMode(DIR_A, OUTPUT);
      pinMode(DIR_B, OUTPUT);
      
      pinMode(ledPin, OUTPUT); // set the LED pin as an Output
      pinMode(buzzerPin, OUTPUT); // set the buzzer pin as an Output
      pinMode(servoPin, OUTPUT); // set the servo pin as an Output
    
      // Initial state - turn off motors, LED & buzzer
      digitalWrite(DRV_A, LOW);
      digitalWrite(DRV_B, LOW);
      digitalWrite(DIR_A, LOW);
      digitalWrite(DIR_B, LOW);
      digitalWrite(ledPin, LOW);
      digitalWrite(buzzerPin, LOW);
      digitalWrite(servoPin, LOW);
    
      /* Initializing Servo and LED */
      initServo();
      initLed();
      
      camera_config_t config;
      config.ledc_channel = LEDC_CHANNEL_0;
      config.ledc_timer = LEDC_TIMER_0;
      config.pin_d0 = Y2_GPIO_NUM;
      config.pin_d1 = Y3_GPIO_NUM;
      config.pin_d2 = Y4_GPIO_NUM;
      config.pin_d3 = Y5_GPIO_NUM;
      config.pin_d4 = Y6_GPIO_NUM;
      config.pin_d5 = Y7_GPIO_NUM;
      config.pin_d6 = Y8_GPIO_NUM;
      config.pin_d7 = Y9_GPIO_NUM;
      config.pin_xclk = XCLK_GPIO_NUM;
      config.pin_pclk = PCLK_GPIO_NUM;
      config.pin_vsync = VSYNC_GPIO_NUM;
      config.pin_href = HREF_GPIO_NUM;
      config.pin_sscb_sda = SIOD_GPIO_NUM;
      config.pin_sscb_scl = SIOC_GPIO_NUM;
      config.pin_pwdn = PWDN_GPIO_NUM;
      config.pin_reset = RESET_GPIO_NUM;
      config.xclk_freq_hz = 20000000;
      config.pixel_format = PIXFORMAT_JPEG;
      //init with high specs to pre-allocate larger buffers
      if(psramFound()){
        config.frame_size = FRAMESIZE_UXGA;
        config.jpeg_quality = 10;
        config.fb_count = 2;
      } else {
        config.frame_size = FRAMESIZE_SVGA;
        config.jpeg_quality = 12;
        config.fb_count = 1;
      }
    
      // camera init
      esp_err_t err = esp_camera_init(&config);
      if (err != ESP_OK) {
        Serial.printf("Camera init failed with error 0x%x", err);
        return;
      }
    
      //drop down frame size for higher initial frame rate
      sensor_t * s = esp_camera_sensor_get();
      s->set_framesize(s, FRAMESIZE_QVGA);
    
      // Set NodeMCU Wifi hostname based on chip mac address
      char chip_id[15];
      snprintf(chip_id, 15, "%04X", (uint16_t)(ESP.getEfuseMac()>>32));
      String hostname = "esp32cam-" + String(chip_id);
    
      Serial.println();
      Serial.println("Hostname: "+hostname);
    
      // first, set NodeMCU as STA mode to connect with a Wifi network
      WiFi.mode(WIFI_STA);
      WiFi.begin(sta_ssid.c_str(), sta_password.c_str());
      Serial.println("");
      Serial.print("Connecting to: ");
      Serial.println(sta_ssid);
      Serial.print("Password: ");
      Serial.println(sta_password);
    
      // try to connect with Wifi network about 10 seconds
      unsigned long currentMillis = millis();
      previousMillis = currentMillis;
      while (WiFi.status() != WL_CONNECTED && currentMillis - previousMillis <= 10000) {
        delay(500);
        Serial.print(".");
        currentMillis = millis();
      }
    
      // if failed to connect with Wifi network set NodeMCU as AP mode
      IPAddress myIP;
      if (WiFi.status() == WL_CONNECTED) {
        Serial.println("");
        Serial.println("*WiFi-STA-Mode*");
        Serial.print("IP: ");
        myIP=WiFi.localIP();
        Serial.println(myIP);
        delay(2000);
      } else {
        WiFi.mode(WIFI_AP);
        WiFi.softAP(hostname.c_str());
        myIP = WiFi.softAPIP();
        Serial.println("");
        Serial.println("WiFi failed connected to " + sta_ssid);
        Serial.println("");
        Serial.println("*WiFi-AP-Mode*");
        Serial.print("AP IP address: ");
        Serial.println(myIP);
        delay(2000);
      }
    
      // Start camera server to get realtime view
      startCameraServer();
      Serial.print("Camera Ready! Use 'http://");
      Serial.print(myIP);
      Serial.println("' to connect ");
    
      ArduinoOTA.begin();   // enable to receive update/upload firmware via Wifi OTA
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      ArduinoOTA.handle();
    }
    

    Simpan sketch di bawah dengan nama file "app_httpd.cpp"

    
    #include "esp_http_server.h"
    #include "esp_timer.h"
    #include "esp_camera.h"
    #include "img_converters.h"
    #include "Arduino.h"
    
    /* Initializing pins */
    extern int DRV_A;
    extern int DRV_B;
    extern int DIR_A;
    extern int DIR_B;
    extern int ledPin;
    extern int buzzerPin;
    extern int servoPin;
    extern int ledVal;
    
    typedef struct {
            httpd_req_t *req;
            size_t len;
    } jpg_chunking_t;
    
    #define PART_BOUNDARY "123456789000000000000987654321"
    static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
    static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
    static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
    
    httpd_handle_t stream_httpd = NULL;
    httpd_handle_t camera_httpd = NULL;
    
    
    static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
        jpg_chunking_t *j = (jpg_chunking_t *)arg;
        if(!index){
            j->len = 0;
        }
        if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
            return 0;
        }
        j->len += len;
        return len;
    }
    
    static esp_err_t capture_handler(httpd_req_t *req){
        camera_fb_t * fb = NULL;
        esp_err_t res = ESP_OK;
        int64_t fr_start = esp_timer_get_time();
    
        fb = esp_camera_fb_get();
        if (!fb) {
            Serial.printf("Camera capture failed");
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
    
        httpd_resp_set_type(req, "image/jpeg");
        httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
    
        size_t fb_len = 0;
        if(fb->format == PIXFORMAT_JPEG){
            fb_len = fb->len;
            res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
        } else {
            jpg_chunking_t jchunk = {req, 0};
            res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
            httpd_resp_send_chunk(req, NULL, 0);
            fb_len = jchunk.len;
        }
        esp_camera_fb_return(fb);
        int64_t fr_end = esp_timer_get_time();
        Serial.printf("JPG: %uB %ums", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
        return res;
    }
    
    static esp_err_t stream_handler(httpd_req_t *req){
        camera_fb_t * fb = NULL;
        esp_err_t res = ESP_OK;
        size_t _jpg_buf_len = 0;
        uint8_t * _jpg_buf = NULL;
        char * part_buf[64];
    
        static int64_t last_frame = 0;
        if(!last_frame) {
            last_frame = esp_timer_get_time();
        }
    
        res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
        if(res != ESP_OK){
            return res;
        }
    
        while(true){
            fb = esp_camera_fb_get();
            if (!fb) {
                Serial.printf("Camera capture failed");
                res = ESP_FAIL;
            } else {
                if(fb->format != PIXFORMAT_JPEG){
                    bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
                    esp_camera_fb_return(fb);
                    fb = NULL;
                    if(!jpeg_converted){
                        Serial.printf("JPEG compression failed");
                        res = ESP_FAIL;
                    }
                } else {
                    _jpg_buf_len = fb->len;
                    _jpg_buf = fb->buf;
                }
            }
            if(res == ESP_OK){
                size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
                res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
            }
            if(res == ESP_OK){
                res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
            }
            if(res == ESP_OK){
                res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
            }
            if(fb){
                esp_camera_fb_return(fb);
                fb = NULL;
                _jpg_buf = NULL;
            } else if(_jpg_buf){
                free(_jpg_buf);
                _jpg_buf = NULL;
            }
            if(res != ESP_OK){
                break;
            }
            int64_t fr_end = esp_timer_get_time();
    
            int64_t frame_time = fr_end - last_frame;
            last_frame = fr_end;
            frame_time /= 1000;
        }
    
        last_frame = 0;
        return res;
    }
    
    
    static esp_err_t cmd_handler(httpd_req_t *req){
        char*  buf;
        size_t buf_len;
        char variable[32] = {0,};
        char value[32] = {0,};
    
        buf_len = httpd_req_get_url_query_len(req) + 1;
        if (buf_len > 1) {
            buf = (char*)malloc(buf_len);
            Serial.println(buf);
            if(!buf){
                httpd_resp_send_500(req);
                return ESP_FAIL;
            }
            if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
                if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
                    httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
                } else {
                    free(buf);
                    Serial.println(buf);
                    httpd_resp_send_404(req);
                    return ESP_FAIL;
                }
            } else {
                free(buf);
                Serial.println(buf);
                httpd_resp_send_404(req);
                return ESP_FAIL;
            }
            Serial.println(buf);
            free(buf);
        } else {
            httpd_resp_send_404(req);
            Serial.println(ESP_FAIL);
            return ESP_FAIL;
        }
    
        int val = atoi(value);
        sensor_t * s = esp_camera_sensor_get();
        int res = 0;
    
        if(!strcmp(variable, "framesize")) {
            if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
        }
        else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
        else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
        else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
        else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
        else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val);
        else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val);
        else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val);
        else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val);
        else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val);
        else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
        else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
        else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val);
        else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val);
        else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val);
        else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val);
        else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val);
        else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val);
        else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val);
        else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val);
        else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val);
        else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val);
        else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
        else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);
        else {
            res = -1;
        }
    
        if(res){
            return httpd_resp_send_500(req);
        }
    
        httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
        return httpd_resp_send(req, NULL, 0);
    }
    
    static esp_err_t status_handler(httpd_req_t *req){
        static char json_response[1024];
    
        sensor_t * s = esp_camera_sensor_get();
        char * p = json_response;
        *p++ = '{';
    
        p+=sprintf(p, "\"framesize\":%u,", s->status.framesize);
        p+=sprintf(p, "\"quality\":%u,", s->status.quality);
        p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
        p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
        p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
        p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
        p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
        p+=sprintf(p, "\"awb\":%u,", s->status.awb);
        p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain);
        p+=sprintf(p, "\"aec\":%u,", s->status.aec);
        p+=sprintf(p, "\"aec2\":%u,", s->status.aec2);
        p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level);
        p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value);
        p+=sprintf(p, "\"agc\":%u,", s->status.agc);
        p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain);
        p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling);
        p+=sprintf(p, "\"bpc\":%u,", s->status.bpc);
        p+=sprintf(p, "\"wpc\":%u,", s->status.wpc);
        p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma);
        p+=sprintf(p, "\"lenc\":%u,", s->status.lenc);
        p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
        p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
        p+=sprintf(p, "\"colorbar\":%u", s->status.colorbar);
        *p++ = '}';
        *p++ = 0;
        httpd_resp_set_type(req, "application/json");
        httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
        return httpd_resp_send(req, json_response, strlen(json_response));
    }
    
    
    static esp_err_t state_handler(httpd_req_t *req){
        char*  buf;
        size_t buf_len;
        char cmd[32] = {0,};
    
        buf_len = httpd_req_get_url_query_len(req) + 1;
        if (buf_len > 1) {
            buf = (char*)malloc(buf_len);
            Serial.println(buf);
            
            if(!buf){
                httpd_resp_send_500(req);
                return ESP_FAIL;
            }
            
            if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
                if (httpd_query_key_value(buf, "cmd", cmd, sizeof(cmd)) == ESP_OK) {
                  
                } else {
                    free(buf);
                    Serial.print("*");
                    Serial.println(ESP_FAIL);
                    httpd_resp_send_404(req);
                    return ESP_FAIL;
                }
            } else {
                free(buf);
                Serial.print("**");
                Serial.println(ESP_FAIL);
                httpd_resp_send_404(req);
                return ESP_FAIL;
            }
            free(buf);
            
        } else {
            Serial.print("***");
            Serial.println(ESP_FAIL);
            httpd_resp_send_404(req);
            return ESP_FAIL;
        }
    
        int res = 0;
    
        if(!strcmp(cmd, "F")) {
          Serial.println("Forward");
          digitalWrite(DRV_A, HIGH);
          digitalWrite(DRV_B, HIGH);
          digitalWrite(DIR_A, HIGH);
          digitalWrite(DIR_B, HIGH);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "B")) {
          Serial.println("Backward");
          digitalWrite(DRV_A, HIGH);
          digitalWrite(DRV_B, HIGH);
          digitalWrite(DIR_A, LOW);
          digitalWrite(DIR_B, LOW);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "R")) {
          Serial.println("Turn Right");
          digitalWrite(DRV_A, HIGH);
          digitalWrite(DRV_B, HIGH);
          digitalWrite(DIR_A, LOW);
          digitalWrite(DIR_B, HIGH);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "L")) {
          Serial.println("Turn Left");
          digitalWrite(DRV_A, HIGH);
          digitalWrite(DRV_B, HIGH);
          digitalWrite(DIR_A, HIGH);
          digitalWrite(DIR_B, LOW);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "G")) {
          Serial.println("Forward Left");
          digitalWrite(DRV_A, HIGH);
          digitalWrite(DRV_B, LOW);
          digitalWrite(DIR_A, HIGH);
          digitalWrite(DIR_B, HIGH);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "H")) {
          Serial.println("Backward Left");
          digitalWrite(DRV_A, HIGH);
          digitalWrite(DRV_B, LOW);
          digitalWrite(DIR_A, LOW);
          digitalWrite(DIR_B, LOW);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "I")) {
          Serial.println("Forward Right");
          digitalWrite(DRV_A, LOW);
          digitalWrite(DRV_B, HIGH);
          digitalWrite(DIR_A, HIGH);
          digitalWrite(DIR_B, HIGH);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "J")) {
          Serial.println("Backward Right");
          digitalWrite(DRV_A, LOW);
          digitalWrite(DRV_B, HIGH);
          digitalWrite(DIR_A, LOW);
          digitalWrite(DIR_B, LOW);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "S")) {
          Serial.println("Stop");
          digitalWrite(DRV_A, LOW);
          digitalWrite(DRV_B, LOW);
          digitalWrite(DIR_A, LOW);
          digitalWrite(DIR_B, LOW);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "V")) {
          Serial.println("Horn On");
          digitalWrite(buzzerPin, HIGH);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "v")) {
          Serial.println("Horn Off");
          digitalWrite(buzzerPin, LOW);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "W")) {
          Serial.println("LED On");
          ledcWrite(7, ledVal);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if(!strcmp(cmd, "w")) {
          Serial.println("LED Off");
          ledcWrite(7, 0);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else if (!strcmp(cmd, "x")){
          Serial.println("Flash Light : Low (20)");
          ledVal = 20;
          ledcWrite(7, ledVal);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "y")){
          Serial.println("Flash Light : Medium (50)");
          ledVal = 50;
          ledcWrite(7, ledVal);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "z")){
          Serial.println("Flash Light : Bright (100)");
          ledVal = 100;
          ledcWrite(7, ledVal);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }    
        else if (!strcmp(cmd, "Z")){
          Serial.println("Flash Light : Super Bright (255)");
          ledVal = 255;
          ledcWrite(7, ledVal);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }   
        
        /* Controlling the servo motor angle with PWM */
        /* ledcWrite(Channel, Dutycycle) dutycycle range : 3250-6500*/
        else if (!strcmp(cmd, "0")){
          Serial.println("Servo 0 (3250)");
          ledcWrite(8, 3250);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "1")){
          Serial.println("Servo 1 (3575)");
          ledcWrite(8, 3575);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "2")){
          Serial.println("Servo 2 (3900)");
          ledcWrite(8, 3900);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "3")){
          Serial.println("Servo 3 (4225)");
          ledcWrite(8, 4225);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "4")){
          Serial.println("Servo 4 (4550)");
          ledcWrite(8, 4550);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "5")){
          Serial.println("Servo 5 (4875)");
          ledcWrite(8, 4875);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "6")){
          Serial.println("Servo 6 (5200)");
          ledcWrite(8, 5200);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "7")){
          Serial.println("Servo 7 (5525)");
          ledcWrite(8, 5525);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "8")){
          Serial.println("Servo 8 (5850)");
          ledcWrite(8, 5850);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "9")){
          Serial.println("Servo 9 (6175)");
          ledcWrite(8, 6175);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        else if (!strcmp(cmd, "q")){
          Serial.println("Servo q (6500)");
          ledcWrite(8, 6500);
          httpd_resp_set_type(req, "text/html");
          return httpd_resp_send(req, "OK", 2);
        }
        
        else {
            res = -1;
        }
    
        if(res){
            return httpd_resp_send_500(req);
        }
    
        httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
        return httpd_resp_send(req, NULL, 0);
    }
    
    
    void startCameraServer(){
        httpd_config_t config = HTTPD_DEFAULT_CONFIG();
        
    
        httpd_uri_t status_uri = {
            .uri       = "/status",
            .method    = HTTP_GET,
            .handler   = status_handler,
            .user_ctx  = NULL
        };
    
        httpd_uri_t cmd_uri = {
            .uri       = "/control",
            .method    = HTTP_GET,
            .handler   = cmd_handler,
            .user_ctx  = NULL
        };
    
        httpd_uri_t capture_uri = {
            .uri       = "/capture",
            .method    = HTTP_GET,
            .handler   = capture_handler,
            .user_ctx  = NULL
        };
    
       httpd_uri_t stream_uri = {
            .uri       = "/stream",
            .method    = HTTP_GET,
            .handler   = stream_handler,
            .user_ctx  = NULL
        };
    
       httpd_uri_t state_uri = {
            .uri       = "/state",
            .method    = HTTP_GET,
            .handler   = state_handler,
            .user_ctx  = NULL
        };
    
        Serial.printf("Starting web server on port: '%d'", config.server_port);
        if (httpd_start(&camera_httpd, &config) == ESP_OK) {
            httpd_register_uri_handler(camera_httpd, &cmd_uri);
            httpd_register_uri_handler(camera_httpd, &capture_uri);
            httpd_register_uri_handler(camera_httpd, &status_uri);
            httpd_register_uri_handler(camera_httpd, &state_uri);
        }
    
        config.server_port += 1;
        config.ctrl_port += 1;
        Serial.printf("Starting stream server on port: '%d'", config.server_port);
        if (httpd_start(&stream_httpd, &config) == ESP_OK) {
            httpd_register_uri_handler(stream_httpd, &stream_uri);
        }
    }
    

    Mainkan Mobil RC Wifi Camera

    Mobil RC Wifi Camera dapat dikontrol melalui smartphone Android dengan dua cara, dikontrol langsung antara smartphone Android ke ESP32-Cam (mode AP), dengan cara hubungkan smartphone Android ke SSID Wifi dari ESP32-Cam kemudian atur alamat IP pada aplikasi menjadi 192.168.4.1.

    Cara kedua adalah ESP32-Cam (mode STA), dimana smartphone Android terhubung ke jaringan Wifi router yang sama kemudian atur alamat IP pada aplikasi sesuai dengan alamat IP yang dimiliki oleh ESP32-Cam (contoh:192.168.0.2).

    31 Des 2020

    Membuat Jam Digital Dot Matrix Lengkap dengan Jadwal Waktu Sholat

    Untuk kamu yang suka berkreasi dan ingin mempunyai alat-alat unik beda dari yang lain, kamu bisa mencoba membuat sebuah jam digital dot matrix yang unik, bisa menampilkan jam berikut ikon animasi bergerak, sangat recomen banget untuk di simpan dikamar kamu.

    Hampir disemua mesjid sekarang sudah terpasang jadwal pengingat waktu sholat digital, kamu juga bisa membuatnya sendiri dengan ukuran minimalis yang cocok untuk disimpan di kamar atau diruangan rumah/kantor. Jadi sealin bisa menampilkan jam, alat ini terdapat pilihan untuk menampilkan jadwal waktu sholat, kalender masehi dan kalender hijriah/islam.

    Jika kamu ingin membuat PCB dan sedang mencari jasa cetak PCB, bisa langsung order saja ke PCBWAY.COM, harganya lebih murah $5 bisa mendapatkan papan PCB 10 keping dengan kualitas PCB yang sangat bagus.

    Daftar Komponen:

  • 1 x PCB ESP Matrix Board (PCBWAY)
  • 1 x LED Dot Matrix MAX7219 4in1 Module
  • 1 x NodeMCU Amica ESP8266
  • 1 x USB to TTL CP2012 Module
  • 1 x RTC DS3231 Module
  • 1 x Active Buzzer 5V
  • 1 x Tact Switch 22mm
  • 1 x 5 pin Male Header 90 degree
  • 2 x 5 pin Male Header
  • 2 x 15 pin Female Header
  • 1 x 4 pin Female Header
  • USB OTG Adapter
  • Micro USB Cable
  • Case 3D Printing Part
  • Acrylic transparent 3mm

  • Order PCB (PCBWAY)

    Untuk membuat proyek ini kamu perlu memesan prototipe PCB di PCBWAY. Cara pemesanannya sangat mudah, kamu akan mendapatkan 10 keping PCB seharga $5 dengan kualitas PCB yang sangat bagus.

    Langkah-langkah order:

    1. SignUp/Log in di pcbway.com

    2. Buka tautan proyek PCB ini.

    3. Klik Tambahkan ke troli.

    4. Tunggu sebentar untuk review PCB, lalu Klik Check Out.

    Memasang Komponen

    Instal semua komponen pada PCB mengikuti gambar/simbol komponen yang ada pada bagian atas PCB, untuk lebih jelasnya dapat mengikuti step by step pada video youtube.

    Menyolder Komponen

    Solder semua kaki komponen pada bagian bawah PCB, untuk lebih jelasnya dapat mengikuti step by step pada video youtube.

    Merangkai USB Socket

    Socket USB ini dipergunakan apabila ingin memberi power 5V melalui belakang cover. Socket ini berfungsi hanya untuk power tidak bisa dipakai sebagai jalur untuk memprogram Nodemcu.

    Pasangkan male header siku ke PCB.

    Kemudian potong ujung kaki male header sehingga rata dengan PCB.

    Setelah itu solder dan jangan terlalu banyak timahnya.

    Pasang pada PCB ESP Matrix sesuai dengan gambar dibawah.

    Langkah terakhir solder dan potong kaki pin header.

    Merakit Modul LED Dot Matrix MAX7219

    Pertama lepaskan komponen LED dot matrix urutan ke 1 dan ke 3 dari sebelah kiri/input

    Pada bagian belakang PCB pasangkan dua male pin header 5 kaki, dengan posisi pemasangan seperti terlihat pada gambar.

    Kemudian solder kaki pin header pada bagian depan, Terakhir pasangkan kembali komponen LED dot matrix ke posisi semula.

    Pasang PCB ESP Matrix Board dengan lubang yang pas dengan pin header yang telah di pasang sebelumnya.

    Terakhir solder pin header pada bagian ESP Matrix Board.

    Pasang Modul RTC DS3231 dan NodeMCU ESP8266

    Setelah selesai semua terpasang dan tersolder, langkah selanjutnya pasang modul RTC DS3231 dan Nodemcu ESP8266 pada port yang sudah tersedia, perhatikan arah pemasangannya jangan sampai terbalik.

    Ngeprint 3D Printing Case

    Jam digital LED Dot Matrix ini bisa digunakan tanpa case/kemasan, dan kamu juga bisa berkreasi dengan membuat casenya dari kayu. Jika kamu mempunyai mesin 3D printer bisa langsung ngeprint case download file 3D disini.

    thingiverse.com/thing:4695742

    Memasang Bantalan Busa

    Ketika kamu akan merakitkan ESP Matrix Board yang sudah terkait kedalam case 3D printing, terlebih dahulu perlu memasang busa yang bisa kamu dapatkan dari bekas pembelian modul Nodemcu, pemasangan busa ini supaya ESP Matrix board tidak goyang didalam case 3D printing. Terdapat dua busa dan posisi pemasangannya seperti terlihat pada gambar dibawah

    Memasang case 3D printing

    Case 3D printing tidak memiliki sitem baud dibuat cukup ngepres terhadap modul ESP Matrix Board, sehingga ketika memasngkannya memerlukan sedikit penekanan. Masukan ESP Matrix Board secara miring dengan sisi yang terdapat modul Nodmcu masuk terlebih dahulu.

    Memasang Kaca Akrilik

    Jika ingin jam Digital LED Dot Matrix terlihat lebih rapi dan bagus kamu bisa memasangkan kaca akrilik berukuran 130mmx32mm dengan tebal 3mm.

    Upload/Mengisi firmware ke NodeMCU ESP8266

    Untuk mengisi firmware dari handphone android ke NodeMCU perlu memakai USB OTG adapter/cable cara pemasangan seperti terlihat pada gambar.

    Buka Google Playstore kemudian Instal ESP Matrix Offline.

    Buka aplikasi masuk ke menu pengaturan, pilih opsi "Getting Started", pada welcome screen tekan tombol "UPLOAD" dan tunggu sampai proses menulis firmware selesai.

    29 Nov 2020

    Cara Membuat Membuat Saklar Otomatis via Wifi Android 4 Channel (Internet of Things) IoT

    Kali ini kita akan membuat saklar otomatis berbasis WiFi dikendalikan melalui internet yang bisa digunakan untuk mengontrol 4 peralatan elektronik seperti lampu, kipas angin TV dan lain-lain, cukup dikontrol melalui handphone bisa kapan saja dan dimana saja.

    Jika kamu ingin membuat PCB dan sedang mencari jasa cetak PCB, bisa langsung order saja ke PCBWAY.COM, harganya lebih murah $5 bisa mendapatkan papan PCB 10 keping dengan kualitas PCB yang sangat bagus.

    Gambar Rangkaian

    Berikut gambar rangkaian PCB nya seperti dibawah:

    Daftar Komponen:

  • 1 x PCB SwitchIoT 4CH (PCBWAY)
  • 1 x NodeMCU Amica ESP8266
  • 4 x NPN Transistor BC547
  • 4 x Diode 1N4148
  • 4 x Yellow LED 3mm
  • 1 x Blue LED 3mm
  • 8 x Resistor 1K Ohm 1/4W
  • 1 x Resistor 330 Ohm 1/4W
  • 4 x Tact Switch 18mm
  • 4 x Relay 5VDC
  • 1 x Hi-Link Step-Down 220VAC to 5VDC
  • 2 x 15 pin Female Header
  • 5 x 2 pin Terminal Block Screw

  • Order PCB (PCBWAY)

    Untuk membuat proyek ini kamu perlu memesan prototipe PCB di PCBWAY. Cara pemesanannya sangat mudah, kamu akan mendapatkan 10 keping PCB seharga $5 dengan kualitas PCB yang sangat bagus.

    Langkah-langkah order:

    1. SignUp/Log in di pcbway.com

    2. Buka tautan proyek PCB ini.

    3. Klik Tambahkan ke troli.

    4. Tunggu sebentar untuk review PCB, lalu Klik Check Out.

    Memasang Komponen

    Instal semua komponen pada PCB mengikuti gambar/simbol komponen yang ada pada bagian atas PCB, untuk lebih jelasnya dapat mengikuti step by step pada video youtube.

    Menyolder Komponen

    Solder semua kaki komponen pada bagian bawah PCB, untuk lebih jelasnya dapat mengikuti step by step pada video youtube.

    Memotong kaki Komponen

    Setelah selesai menyolder semua kaki komponen, potong bagian kaiki komponen yang masih panjang menggunakan tang potong.

    Pasang NodeMCU ESP8266

    Setelah selesai semua komponen terpasang dan tersolder, langkah selanjutnya pasang board ESP8266 pada port yang sudah tersedia, perhatikan arah pemasangannya jangan sampai terbalik.

    Upload/Mengisi firmware ke NodeMCU ESP8266

    Untuk mengisi firmware dari handphone android ke NodeMCU perlu memakai USB OTG adapter/cable cara pemasangan seperti terlihat pada gambar.

    Perhatikan ketika akan mengisi firmware menggunakan Handphone Android, jangan menyambungkan perangkat ke sumber listrik 220VAC, untuk mengindari kerusakan pada handphone.

    Buka Google Playstore kemudian Instal Aplikasi SwitchIoT 4CH.

    Buka aplikasi SwitchIoT 4CH, masuk ke menu pengaturan widget, pilih opsi "Upload firmware via USB", lalu pada pilihan Relay_Pin Aktive set ke HIGH. Terakhir tekan tombol "UPLOAD" dan tunggu sampai proses menulis firmware selesai. Jika Anda tidak memiliki USB OTG atau Anda merasa berisiko dengan handphone apabila mengupload firmware dari handphone, kamu dapat menghasilkan file firmware dalam format *.bin (contoh: 0x00000_32e5_NodeMCU.bin) dengan cara menekan tombol "GENERATE .BIN" pada Aplikasi kemudian kamu bisa mengirimkannya ke email atau penyimpanan online, selanjutnya buka di komputer kamu kemudian upload firmware tersebut menggunakan software NodeMCU Flasher.

    Menghubungkan Perangkat Keras SwitchIoT 4CH ke Jaringan

    Setelah proses pengisian firmware selesai, hidupkan perangkat keras dan amati apa yang terjadi. Pertama kali LED status yang terhubung ke pin GPIO4 akan nyala berkedip cepat, itu tandanya perangkat tidak terhubung ke jaringan apa pun (atau dalam mode AP dengan nama host siot-xxxx), Kamu perlu menambahkan jaringan yang akan digunakan oleh perangkat.
    Buka Aplikasi SwitchIoT 4CH kemudian masuk ke menu setting dan pilih menu "Connect device to network", pastikan LED status masih nyala berkedip cepat, kemudian hubungkan smartphone kamu ke jaringan (sinyal wifi) dengan nama siot-xxxx, setelah itu isi SSID dan password jaringan yang akan digunakan oleh perangkat SwitchIoT. Lalu tekan "CONNECT", tunggu sampai selesai.

    Bagikan Token SwitchIoT Dengan Pengguna Lain

    Pemilik perangkat keras SwitchIoT 4CH dapat memberikan akses yang sama kepada orang lain, dan dengan demikian orang lain dapat menghidupkan/mematikan, mengubah schedule, timer, looptimer dan memberi nama perangkat menggunakan aplikasi SwitchIoT 4CH. Fungsi ini sangat cocok untuk keluarga serumah karena akan memungkinkan mereka untuk mengontrol bersama.

    Cara membagikan token perangkat keras SwitchIoT 4CH sangat mudah, kamu hanya perlu membagikan token unik kepada orang lain, dan mereka tinggal mengisikannya ke aplikasi SwitchIoT 4CH.

    Token dari setiap perangkat keras SwitchIoT 4CH adalah bersifat unik dan dihasilkan dari handphone Android kamu, oleh karena itu token ini bersifat permanen, Anda harus hati-hati untuk membagikannya dengan orang lain.

    Selamat Mencoba

    Untuk menghidupkan/mematikan ke 4 channel sakelar tersebut dilakukan melalui aplikasi secara online, kamu juga dapat menghidupkan/mematikan secara lokal dengan cara menekan tombol push button yang ada pada perangkat.
    Coba juga fitur menarik lainnya di aplikasi useperti Schedule, Timer dan Looptimer.