// Code adapted from https://github.com/espressif/esp32-camera

#include "esp_camera.h"
#include "esp_log.h"

#include "py/nlr.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "py/binary.h"

#define TAG "camera"

//WROVER-KIT PIN Map
#define CAM_PIN_PWDN    32 //power down is not used
#define CAM_PIN_RESET   -1 //software reset will be performed
#define CAM_PIN_XCLK     0
#define CAM_PIN_SIOD    26 // SDA
#define CAM_PIN_SIOC    27 // SCL

#define CAM_PIN_D7      35
#define CAM_PIN_D6      34
#define CAM_PIN_D5      39
#define CAM_PIN_D4      36
#define CAM_PIN_D3      21
#define CAM_PIN_D2      19
#define CAM_PIN_D1      18
#define CAM_PIN_D0       5
#define CAM_PIN_VSYNC   25
#define CAM_PIN_HREF    23
#define CAM_PIN_PCLK    22

static camera_config_t camera_config = {
    .pin_pwdn  = CAM_PIN_PWDN,
    .pin_reset = CAM_PIN_RESET,
    .pin_xclk = CAM_PIN_XCLK,
    .pin_sscb_sda = CAM_PIN_SIOD,
    .pin_sscb_scl = CAM_PIN_SIOC,

    .pin_d7 = CAM_PIN_D7,
    .pin_d6 = CAM_PIN_D6,
    .pin_d5 = CAM_PIN_D5,
    .pin_d4 = CAM_PIN_D4,
    .pin_d3 = CAM_PIN_D3,
    .pin_d2 = CAM_PIN_D2,
    .pin_d1 = CAM_PIN_D1,
    .pin_d0 = CAM_PIN_D0,
    .pin_vsync = CAM_PIN_VSYNC,
    .pin_href = CAM_PIN_HREF,
    .pin_pclk = CAM_PIN_PCLK,

    //XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
    .xclk_freq_hz = 20000000,
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,

    .pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
    .frame_size = FRAMESIZE_XGA,//QQVGA-UXGA Do not use sizes above QVGA when not JPEG

    .jpeg_quality = 12, //10-63 lower number means higher quality
    .fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
};

#include "esp_system.h"
#include "esp_spi_flash.h"


STATIC mp_obj_t camera_init(){
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera Init Failed");
        return mp_const_false;
    }

    return mp_const_true;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_init_obj, camera_init);


STATIC mp_obj_t camera_deinit(){
    esp_err_t err = esp_camera_deinit();
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera deinit Failed");
        return mp_const_false;
    }

    return mp_const_true;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_deinit_obj, camera_deinit);


STATIC mp_obj_t camera_capture(){
    //acquire a frame
    camera_fb_t * fb = esp_camera_fb_get();
    if (!fb) {
        ESP_LOGE(TAG, "Camera Capture Failed");
        return mp_const_false;
    }

    mp_obj_t image = mp_obj_new_bytes(fb->buf, fb->len);

    //return the frame buffer back to the driver for reuse
    esp_camera_fb_return(fb);
    return image;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_capture_obj, camera_capture);

STATIC mp_obj_t camera_set_vflip(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Vertical flipping Failed");
        return mp_const_false;
      }
    int direction = mp_obj_get_int(what);
    s->set_vflip(s, direction);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_vflip_obj, camera_set_vflip);

STATIC mp_obj_t camera_get_vflip(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Vertical flipping Failed");
        return mp_const_false;
      }
    int direction = s->status.vflip;
    return mp_obj_new_int(direction);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_vflip_obj, camera_get_vflip);

STATIC mp_obj_t camera_set_hmirror(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Vertical flipping Failed");
        return mp_const_false;
      }
    int direction = mp_obj_get_int(what);
    s->set_hmirror(s, direction);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_hmirror_obj, camera_set_hmirror);

STATIC mp_obj_t camera_get_hmirror(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Getting vertical flip failed");
        return mp_const_false;
      }
    int direction = s->status.hmirror;
    return mp_obj_new_int(direction);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_hmirror_obj, camera_get_hmirror);

STATIC mp_obj_t camera_set_pixformat(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Pixformat Failed");
        return mp_const_false;
      }
    int size = mp_obj_get_int(what);
    if (size == 0) {
      s->set_pixformat(s, PIXFORMAT_JPEG); // JPEG (default) compress
    } else if (size == 1) {
      s->set_pixformat(s, PIXFORMAT_GRAYSCALE); // Grayscale 1 byte/pixel
    } else if (size == 2) {
      s->set_pixformat(s, PIXFORMAT_RGB565); // Red Green Blue 3 bytes/pixcel
    }
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_pixformat_obj, camera_set_pixformat);

STATIC mp_obj_t camera_set_framesize(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Set framesize Failed");
        return mp_const_false;
      }
    int size = mp_obj_get_int(what);
    //    if (size == 1) {
    //      s->set_framesize(s, FRAMESIZE_QQVGA); // 160x120
    //    } else if (size == 2) {
    //      s->set_framesize(s, FRAMESIZE_QQVGA2); // 128x160
    //    } else if (size == 3) {
    //      s->set_framesize(s, FRAMESIZE_QCIF); // 176x144 
    //    } else if (size == 4) {
    //      s->set_framesize(s, FRAMESIZE_HQVGA); // 240x176
    //    } else if (size == 5) {
    //      s->set_framesize(s, FRAMESIZE_QVGA); // 320x240
    //    } else if (size == 6) {
    //      s->set_framesize(s, FRAMESIZE_CIF); // 400x296
    //    } else if (size == 7) {
    //      s->set_framesize(s, FRAMESIZE_VGA); // 640x480
    //    } else if (size == 8) {
    //      s->set_framesize(s, FRAMESIZE_SVGA); // 800x600
    //    } else if (size == 9) {
    //      s->set_framesize(s, FRAMESIZE_XGA); // 1024x768  (default)
    //    } else if (size == 10) {
    //      s->set_framesize(s, FRAMESIZE_SXGA); // 1280x1024
    //    } else if (size == 11) {
    //      s->set_framesize(s, FRAMESIZE_UXGA); // 1600x1200
    //    } else if (size == 12) {
    //      s->set_framesize(s, FRAMESIZE_QXGA); // 2048x1536
    //    }
    s->set_framesize(s,size);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_framesize_obj, camera_set_framesize);

STATIC mp_obj_t camera_get_framesize(){
  int framesize;
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Get framesize Failed");
        return mp_const_false;
      }
    framesize = s->status.framesize;
    return mp_obj_new_int(framesize);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_framesize_obj, camera_get_framesize);

STATIC mp_obj_t camera_set_quality(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Quality Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what); // 10-63 lower number means higher quality
    s->set_quality(s, val);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_quality_obj, camera_set_quality);

STATIC mp_obj_t camera_get_quality(){
    int quality;
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Get quality Failed");
        return mp_const_false;
      }
    quality = s->status.quality;
    return mp_obj_new_int(quality);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_quality_obj, camera_get_quality);

STATIC mp_obj_t camera_set_contrast(mp_obj_t what){
    //acquire a frame
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Set contrast Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what); // -2,2 (default 0). 2 highcontrast
    s->set_contrast(s, val);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_contrast_obj, camera_set_contrast);

STATIC mp_obj_t camera_get_contrast(){
    int contrast;  
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Get contrast Failed");
        return mp_const_false;
      }
    contrast = s->status.contrast;
    return  mp_obj_new_int(contrast);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_contrast_obj, camera_get_contrast);

STATIC mp_obj_t camera_set_saturation(mp_obj_t what){
    //acquire a frame
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Set saturation Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_saturation(s, val); // -2,2 (default 0). -2 grayscale
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_saturation_obj, camera_set_saturation);

STATIC mp_obj_t camera_get_saturation(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Get saturation Failed");
        return mp_const_false;
      }
    int saturation = s->status.saturation;
    return mp_obj_new_int(saturation);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_saturation_obj, camera_get_saturation);

STATIC mp_obj_t camera_set_gainceiling(mp_obj_t what){
    //acquire a frame
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting gain ceiling failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_gainceiling(s, val); // -2,2 (default 0). -2 grayscale
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_gainceiling_obj, camera_set_gainceiling);

STATIC mp_obj_t camera_get_gainceiling(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting gain ceiling failed");
        return mp_const_false;
      }
    int gainceiling = s->status.gainceiling;
    return mp_obj_new_int(gainceiling);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_gainceiling_obj, camera_get_gainceiling);

STATIC mp_obj_t camera_set_colorbar(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting color bar failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_colorbar(s, val); 
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_colorbar_obj, camera_set_colorbar);

STATIC mp_obj_t camera_get_colorbar(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting color bar failed");
        return mp_const_false;
      }
    int colorbar = s->status.colorbar;
    return mp_obj_new_int(colorbar);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_colorbar_obj, camera_get_colorbar);

STATIC mp_obj_t camera_set_brightness(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Brightness Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_brightness(s, val); // -2,2 (default 0). 2 brightest
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_brightness_obj, camera_set_brightness);

STATIC mp_obj_t camera_get_brightness(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting color bar failed");
        return mp_const_false;
      }
    int brightness = s->status.brightness;
    return mp_obj_new_int(brightness);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_brightness_obj, camera_get_brightness);

STATIC mp_obj_t camera_set_speffect(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Special Effect Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_special_effect(s, val); // 0-6 (default 0). 
                                   // 0 - no effect
				   // 1 - negative
				   // 2 - black and white
				   // 3 - reddish
				   // 4 - greenish
				   // 5 - blue
				   // 6 - retro
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_speffect_obj, camera_set_speffect);

STATIC mp_obj_t camera_get_speffect(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Special Effect Failed");
        return mp_const_false;
      }
    int special_effect = s->status.special_effect;
                                   // 0-6 (default 0). 
                                   // 0 - no effect
				   // 1 - negative
				   // 2 - black and white
				   // 3 - reddish
				   // 4 - greenish
				   // 5 - blue
				   // 6 - retro
    return mp_obj_new_int(special_effect);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_speffect_obj, camera_get_speffect);

STATIC mp_obj_t camera_set_wb_mode(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "White Balance Mode Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_wb_mode(s, val); // 0-4 (default 0).
                                   // 0 - no effect
                                   // 1 - sunny
                                   // 2 - cloudy
                                   // 3 - office
                                   // 4 - home
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_wb_mode_obj, camera_set_wb_mode);

STATIC mp_obj_t camera_get_wb_mode(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "White Balance Mode Failed");
        return mp_const_false;
      }
    int wb_mode = s->status.wb_mode; // 0-4 (default 0).
                                     // 0 - no effect
                                     // 1 - sunny
                                     // 2 - cloudy
                                     // 3 - office
                                     // 4 - home
    return mp_obj_new_int(wb_mode);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_wb_mode_obj, camera_get_wb_mode);

STATIC mp_obj_t camera_set_whitebal(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "White Balance Failed");
        return mp_const_false;
    }
    int val = mp_obj_get_int(what);
    s->set_whitebal(s, val); 
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_whitebal_obj, camera_set_whitebal);
 
STATIC mp_obj_t camera_get_whitebal(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "White Balance Failed");
        return mp_const_false;
      }
    int whitebal = s->status.awb;
    return mp_obj_new_int(whitebal);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_whitebal_obj, camera_get_whitebal);

STATIC mp_obj_t camera_set_aelevel(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting ae level failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_ae_level(s, val); // -2 to +2 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_aelevel_obj, camera_set_aelevel);

STATIC mp_obj_t camera_get_aelevel(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Getting ae level failed");
        return mp_const_false;
      }
    int ae_level = s->status.ae_level;
    return mp_obj_new_int(ae_level);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_aelevel_obj, camera_get_aelevel);

STATIC mp_obj_t camera_set_aec_value(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "AEC Value Failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_aec_value(s, val);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_aec_value_obj, camera_set_aec_value);

STATIC mp_obj_t camera_get_aec_value(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "AEC Value Failed");
        return mp_const_false;
      }
    int aec_value = s->status.aec_value;
    return mp_obj_new_int(aec_value);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_aec_value_obj, camera_get_aec_value);

STATIC mp_obj_t camera_set_aec2(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting aec2 failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_aec2(s, val); // 0 to 1200 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_aec2_obj, camera_set_aec2);

STATIC mp_obj_t camera_get_aec2(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Getting aec2 failed");
        return mp_const_false;
      }
    int aec2 = s->status.aec2; 
    return mp_obj_new_int(aec2);;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_aec2_obj, camera_get_aec2);

STATIC mp_obj_t camera_set_dcw(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting dcw failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_dcw(s, val); // 0 to 1200 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_dcw_obj, camera_set_dcw);

STATIC mp_obj_t camera_get_dcw(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting dcw failed");
        return mp_const_false;
      }
    int dcw = s->status.dcw;
    return mp_obj_new_int(dcw);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_dcw_obj, camera_get_dcw);

STATIC mp_obj_t camera_set_bpc(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting bpc failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_bpc(s, val); // 0 to 1200 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_bpc_obj, camera_set_bpc);

STATIC mp_obj_t camera_get_bpc(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting dcw failed");
        return mp_const_false;
      }
    int bpc = s->status.bpc;
    return mp_obj_new_int(bpc);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_bpc_obj, camera_get_bpc);

STATIC mp_obj_t camera_set_wpc(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting wpc failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_wpc(s, val); // 0 to 1200 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_wpc_obj, camera_set_wpc);

STATIC mp_obj_t camera_get_wpc(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting dcw failed");
        return mp_const_false;
      }
    int wpc = s->status.wpc;
    return mp_obj_new_int(wpc);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_wpc_obj, camera_get_wpc);

STATIC mp_obj_t camera_set_raw_gma(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting raw_gma failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_raw_gma(s, val); // 0 to 1200 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_raw_gma_obj, camera_set_raw_gma);

STATIC mp_obj_t camera_get_raw_gma(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting raw_gma failed");
        return mp_const_false;
      }
    int raw_gma = s->status.raw_gma; // 0 to 1200 (default 0).
    return mp_obj_new_int(raw_gma);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_raw_gma_obj, camera_get_raw_gma);

STATIC mp_obj_t camera_set_lenc(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting lenc failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_lenc(s, val); // 0 to 1200 (default 0).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_lenc_obj, camera_set_lenc);

STATIC mp_obj_t camera_get_lenc(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting lenc failed");
        return mp_const_false;
      }
    int lenc = s->status.lenc;
    return mp_obj_new_int(lenc);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_lenc_obj, camera_get_lenc);

STATIC mp_obj_t camera_set_agc_gain(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Set agc gain failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_agc_gain(s, val); // 0 to 30 (default 30).
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_agc_gain_obj, camera_set_agc_gain);

STATIC mp_obj_t camera_get_agc_gain(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Get agc gain failed");
        return mp_const_false;
      }
    int agc_gain = s->status.agc_gain;
    return mp_obj_new_int(agc_gain);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_agc_gain_obj, camera_get_agc_gain);

STATIC mp_obj_t camera_set_awb_gain(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting awb gain failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_awb_gain(s, val); 
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_awb_gain_obj, camera_set_awb_gain);

STATIC mp_obj_t camera_get_awb_gain(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Getting awb gain failed");
        return mp_const_false;
      }
    int awb_gain = s->status.awb_gain;
    return mp_obj_new_int(awb_gain);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_awb_gain_obj, camera_get_awb_gain);

STATIC mp_obj_t camera_set_gain_ctrl(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting gain control failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_gain_ctrl(s, val); 
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_gain_ctrl_obj, camera_set_gain_ctrl);

STATIC mp_obj_t camera_get_gain_ctrl(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting gain control failed");
        return mp_const_false;
      }
    int agc = s->status.agc;
    return mp_obj_new_int(agc);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_gain_ctrl_obj, camera_get_gain_ctrl);

STATIC mp_obj_t camera_set_exposure_ctrl(mp_obj_t what){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Setting exposure control failed");
        return mp_const_false;
      }
    int val = mp_obj_get_int(what);
    s->set_exposure_ctrl(s, val); 
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(camera_set_exposure_ctrl_obj, camera_set_exposure_ctrl);

STATIC mp_obj_t camera_get_exposure_ctrl(){
    sensor_t * s = esp_camera_sensor_get();
    if (!s) {
        ESP_LOGE(TAG, "Getting exposure control failed");
        return mp_const_false;
      }
    int aec = s->status.aec;
    return mp_obj_new_int(aec);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(camera_get_exposure_ctrl_obj, camera_get_exposure_ctrl);

STATIC const mp_rom_map_elem_t camera_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_camera) },
    { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&camera_init_obj) },
    { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&camera_deinit_obj) },
    { MP_ROM_QSTR(MP_QSTR_capture), MP_ROM_PTR(&camera_capture_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_vflip), MP_ROM_PTR(&camera_set_vflip_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_vflip), MP_ROM_PTR(&camera_get_vflip_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_hmirror), MP_ROM_PTR(&camera_set_hmirror_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_hmirror), MP_ROM_PTR(&camera_get_hmirror_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_pixformat), MP_ROM_PTR(&camera_set_pixformat_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_framesize), MP_ROM_PTR(&camera_set_framesize_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_framesize), MP_ROM_PTR(&camera_get_framesize_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_quality), MP_ROM_PTR(&camera_set_quality_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_quality), MP_ROM_PTR(&camera_get_quality_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_contrast), MP_ROM_PTR(&camera_set_contrast_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_contrast), MP_ROM_PTR(&camera_get_contrast_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_saturation), MP_ROM_PTR(&camera_set_saturation_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_saturation), MP_ROM_PTR(&camera_get_saturation_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_gainceiling), MP_ROM_PTR(&camera_set_gainceiling_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_gainceiling), MP_ROM_PTR(&camera_get_gainceiling_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_gain_ctrl), MP_ROM_PTR(&camera_set_gain_ctrl_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_gain_ctrl), MP_ROM_PTR(&camera_get_gain_ctrl_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_exposure_ctrl), MP_ROM_PTR(&camera_set_exposure_ctrl_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_exposure_ctrl), MP_ROM_PTR(&camera_get_exposure_ctrl_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_colorbar), MP_ROM_PTR(&camera_set_colorbar_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_colorbar), MP_ROM_PTR(&camera_get_colorbar_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&camera_set_brightness_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_brightness), MP_ROM_PTR(&camera_get_brightness_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_speffect), MP_ROM_PTR(&camera_set_speffect_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_speffect), MP_ROM_PTR(&camera_get_speffect_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_wb_mode), MP_ROM_PTR(&camera_set_wb_mode_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_wb_mode), MP_ROM_PTR(&camera_get_wb_mode_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_whitebal), MP_ROM_PTR(&camera_set_whitebal_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_whitebal), MP_ROM_PTR(&camera_get_whitebal_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_aelevel), MP_ROM_PTR(&camera_set_aelevel_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_aelevel), MP_ROM_PTR(&camera_get_aelevel_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_aec_value), MP_ROM_PTR(&camera_set_aec_value_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_aec_value), MP_ROM_PTR(&camera_get_aec_value_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_aec2), MP_ROM_PTR(&camera_set_aec2_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_aec2), MP_ROM_PTR(&camera_get_aec2_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_dcw), MP_ROM_PTR(&camera_set_dcw_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_dcw), MP_ROM_PTR(&camera_get_dcw_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_bpc), MP_ROM_PTR(&camera_set_bpc_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_bpc), MP_ROM_PTR(&camera_get_bpc_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_wpc), MP_ROM_PTR(&camera_set_wpc_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_wpc), MP_ROM_PTR(&camera_get_wpc_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_raw_gma), MP_ROM_PTR(&camera_set_raw_gma_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_raw_gma), MP_ROM_PTR(&camera_get_raw_gma_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_lenc), MP_ROM_PTR(&camera_set_lenc_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_lenc), MP_ROM_PTR(&camera_get_lenc_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_awb_gain), MP_ROM_PTR(&camera_set_awb_gain_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_awb_gain), MP_ROM_PTR(&camera_get_awb_gain_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_agc_gain), MP_ROM_PTR(&camera_set_agc_gain_obj) },
    { MP_ROM_QSTR(MP_QSTR_get_agc_gain), MP_ROM_PTR(&camera_get_agc_gain_obj) },
};

STATIC MP_DEFINE_CONST_DICT(camera_module_globals, camera_module_globals_table);

const mp_obj_module_t camera_user_cmodule = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t*)&camera_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_camera, camera_user_cmodule, MODULE_ESP32_CAMERA_ENABLED);
