OV2640を使ってjpegデータ表示と設定変更(ESP32-WROVER-B)

#概要

ESP32-WROVER-Bを使ってOV2640からjpegデータを取得してみました。ESP32-WROVER-Bでサーバーを立てて、ブラウザから各レジスタを制御できるようにしています。

![aa1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426480/5343d730-ea80-2912-1d40-9e5fb8ef07b2.jpeg)

コードは下記に公開しています。
https://github.com/koki-iwaizumi/esp32-ov2640-stream/tree/v1.0

OV7725を使って同様のことをしています。
https://qiita.com/koki-iwaizumi/items/146b8b08f9146327f657

公式
https://github.com/espressif/esp32-camera

#設定
esp-idfのフォルダ内でsubmoduleをクローンします。

```
git submodule add https://github.com/espressif/esp32-camera.git components/esp32-camera
mv esp-idf/components/esp32-camera/driver/private_include/sccb.h esp-idf/components/esp32-camera/driver/include/sccb.h
mv esp-idf/components/esp32-camera/driver/private_include/sensor.h esp-idf/components/esp32-camera/driver/include/sensor.h
```


#ESP32(サーバー)
ESP32でサーバーを立てて、ローカルアドレスでアクセスできるようにしました。ブラウザで<ローカルアドレス>/へアクセスすると画像及び各レジスタの値が表示されます。

image,config0,config1,writeはajax及びXMLHttpRequestでサーバと通信しています。

- <ローカルアドレス>/　html
- <ローカルアドレス>/image　image/jpegのバイナリデータ
- <ローカルアドレス>/config0　0xFF==0の場合のレジスタ情報のjson
- <ローカルアドレス>/config1　0xFF==1の場合のレジスタ情報のjson
- <ローカルアドレス>/write　レジスタ変更

```c:app_main.c

#include <string.h>
#include <esp_wifi.h>
#include <esp_event_loop.h>
#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include <esp_http_server.h>
#include <esp_camera.h>

#include "mbedtls/base64.h"
#include "cJSON.h"

#include "sensor.h"
#include "sccb.h"

/********* io ***********/
#define CAM_PIN_PWDN	2
#define CAM_PIN_RESET	15
#define CAM_PIN_XCLK	27
#define CAM_PIN_SIOD	25
#define CAM_PIN_SIOC	23
#define CAM_PIN_VSYNC	22
#define CAM_PIN_HREF	26
#define CAM_PIN_PCLK	21
#define CAM_PIN_D9		19
#define CAM_PIN_D8		36
#define CAM_PIN_D7		18
#define CAM_PIN_D6		39
#define CAM_PIN_D5		5
#define CAM_PIN_D4		34
#define CAM_PIN_D3		32
#define CAM_PIN_D2		35

/********* wifi ***********/
#define EXAMPLE_WIFI_SSID "*"
#define EXAMPLE_WIFI_PASS "*"

static const char *TAG = "APP";
sensor_t* sensor = NULL;

static const uint8_t regs_0[][1] = {
	{0x05},
	{0x44},
	{0x50},
	{0x51},
	{0x52},
	{0x53},
	{0x54},
	{0x55},
	{0x56},
	{0x57},
	{0x5A},
	{0x5B},
	{0x5C},
	{0x7C},
	{0x7D},
	{0x86},
	{0x87},
	{0x8C},
	{0xC0},
	{0xC1},
	{0xC2},
	{0xC3},
	{0xD3},
	{0xDA},
	{0xE0},
	{0xED},
	{0xF0},
	{0xF7},
	{0xF8},
	{0xF9},
	{0xFA},
	{0xFB},
	{0xFC},
	{0xFD},
	{0xFE},
	{0xFF}
};

static const uint8_t regs_1[][1] = {
	{0x00},
	{0x03},
	{0x04},
	{0x08},
	{0x09},
	{0x0A},
	{0x0B},
	{0x0C},
	{0x10},
	{0x11},
	{0x12},
	{0x13},
	{0x14},
	{0x15},
	{0x17},
	{0x18},
	{0x19},
	{0x1A},
	{0x1C},
	{0x1D},
	{0x24},
	{0x25},
	{0x26},
	{0x2A},
	{0x2B},
	{0x2D},
	{0x2E},
	{0x2F},
	{0x32},
	{0x34},
	{0x45},
	{0x46},
	{0x47},
	{0x48},
	{0x49},
	{0x4B},
	{0x4E},
	{0x4F},
	{0x50},
	{0x5D},
	{0x5E},
	{0x5F},
	{0x60},
	{0x61},
	{0x62}
};

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_D9,
	.pin_d6 = CAM_PIN_D8,
	.pin_d5 = CAM_PIN_D7,
	.pin_d4 = CAM_PIN_D6,
	.pin_d3 = CAM_PIN_D5,
	.pin_d2 = CAM_PIN_D4,
	.pin_d1 = CAM_PIN_D3,
	.pin_d0 = CAM_PIN_D2,
	.pin_vsync = CAM_PIN_VSYNC,
	.pin_href = CAM_PIN_HREF,
	.pin_pclk = CAM_PIN_PCLK,

	.xclk_freq_hz = 6000000,
	.ledc_timer = LEDC_TIMER_0,
	.ledc_channel = LEDC_CHANNEL_0,

	.pixel_format = PIXFORMAT_JPEG,
	.frame_size = FRAMESIZE_SVGA, //FRAMESIZE_UXGA

	.jpeg_quality = 12,
	.fb_count = 1
};

esp_err_t index_get_handler(httpd_req_t *req)
{
	extern const unsigned char index_start[] asm("_binary_index_html_start");
	extern const unsigned char index_end[]   asm("_binary_index_html_end");
	const size_t index_size = (index_end - index_start);

	httpd_resp_send_chunk(req, (const char *)index_start, index_size);
	httpd_resp_sendstr_chunk(req, NULL);

	return ESP_OK;
}

esp_err_t image_get_handler(httpd_req_t *req)
{
	camera_fb_t *fb = esp_camera_fb_get();
	if( ! fb){
		ESP_LOGE(TAG, "Camera capture failed");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	esp_err_t res = httpd_resp_set_type(req, "image/jpeg") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, (const char *)fb->buf, fb->len);
	esp_camera_fb_return(fb);

	return res;
}

esp_err_t config0_get_handler(httpd_req_t *req)
{
	esp_err_t res = SCCB_Write(sensor->slv_addr, 0xFF, 0);
	if(res != ESP_OK){
		ESP_LOGE(TAG, "Failed SCCB_Write - 0xFF 0");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	int data_json_size = 10 * 1000;
	char *data_json = calloc(data_json_size, sizeof(char));
	if(data_json == NULL){
		ESP_LOGE(TAG, "Failed to allocate frame buffer - data_json");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	
	int regs_0_num = sizeof regs_0 / sizeof regs_0[0];
	strcat(data_json, "{");
	for(int i = 0; i < regs_0_num; i++){
		char data_json_buf[255] = {'\0'};
		sprintf(data_json_buf, "\"%d\":\"%d\"", regs_0[i][0], SCCB_Read(sensor->slv_addr, regs_0[i][0]));
		strcat(data_json, data_json_buf);
		if(i != regs_0_num - 1) strcat(data_json, ",");
	}
	strcat(data_json, "}");

	ESP_LOGI(TAG, "data_json=%s", data_json);
	ESP_LOGI(TAG, "data_json length=%d", strlen((const char*)data_json));

	res = httpd_resp_set_type(req, "application/json") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, (const char *)data_json, strlen((const char*)data_json));
	free(data_json);

	return res;
}

esp_err_t config1_get_handler(httpd_req_t *req)
{
	esp_err_t res = SCCB_Write(sensor->slv_addr, 0xFF, 1);
	if(res != ESP_OK){
		ESP_LOGE(TAG, "Failed SCCB_Write - 0xFF 1");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	int data_json_size = 10 * 1000;
	char *data_json = calloc(data_json_size, sizeof(char));
	if(data_json == NULL){
		ESP_LOGE(TAG, "Failed to allocate frame buffer - data_json");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	
	int regs_1_num = sizeof regs_1 / sizeof regs_1[0];
	strcat(data_json, "{");
	for(int i = 0; i < regs_1_num; i++){
		char data_json_buf[255] = {'\0'};
		sprintf(data_json_buf, "\"%d\":\"%d\"", regs_1[i][0], SCCB_Read(sensor->slv_addr, regs_1[i][0]));
		strcat(data_json, data_json_buf);
		if(i != regs_1_num - 1) strcat(data_json, ",");
	}
	strcat(data_json, "}");

	ESP_LOGI(TAG, "data_json=%s", data_json);
	ESP_LOGI(TAG, "data_json length=%d", strlen((const char*)data_json));

	res = httpd_resp_set_type(req, "application/json") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, (const char *)data_json, strlen((const char*)data_json));
	free(data_json);

	return res;
}

esp_err_t write_get_handler(httpd_req_t *req)
{
	size_t buf_len = httpd_req_get_url_query_len(req) + 1;
	if(buf_len <= 1){
		ESP_LOGE(TAG, "Failed buf_len");
		httpd_resp_send_500(req);
		return ESP_FAIL;	
	}

	char* buf = malloc(buf_len);
	if(httpd_req_get_url_query_str(req, buf, buf_len) != ESP_OK){
		free(buf);
		ESP_LOGE(TAG, "Failed httpd_req_get_url_query_str");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	char bank[4], key[4], value[4];
	ESP_LOGI(TAG, "Found URL query => %s", buf);
	if(httpd_query_key_value(buf, "bank", bank, sizeof(bank)) != ESP_OK || httpd_query_key_value(buf, "key", key, sizeof(key)) != ESP_OK || httpd_query_key_value(buf, "value", value, sizeof(value)) != ESP_OK){
		free(buf);
		ESP_LOGE(TAG, "Failed httpd_query_key_value");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}
	free(buf);

	int key_i = atoi(key);
	int value_i = atoi(value);
	int bank_i = atoi(bank);

	ESP_LOGI(TAG, "Found URL query parameter => key=%s, value=%s, bank=%s", key, value, bank);
	ESP_LOGI(TAG, "Found URL query parameter => key=%d, value=%d, bank=%d", key_i, value_i, bank_i);

	if(SCCB_Write(sensor->slv_addr, 0xFF, bank_i) != ESP_OK || SCCB_Write(sensor->slv_addr, key_i, value_i) != ESP_OK){
		ESP_LOGE(TAG, "Failed SCCB_Write");
		httpd_resp_send_500(req);
		return ESP_FAIL;
	}

	const char *resp = "{\"status\":\"success\"}";
	esp_err_t res = httpd_resp_set_type(req, "application/json") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, resp, strlen(resp));
	return res;
}

httpd_uri_t index_uri = {
	.uri = "/",
	.method = HTTP_GET,
	.handler = index_get_handler,
};

httpd_uri_t image_uri = {
	.uri = "/image",
	.method = HTTP_GET,
	.handler = image_get_handler,
};

httpd_uri_t config0_uri = {
	.uri = "/config0",
	.method = HTTP_GET,
	.handler = config0_get_handler,
};

httpd_uri_t config1_uri = {
	.uri = "/config1",
	.method = HTTP_GET,
	.handler = config1_get_handler,
};

httpd_uri_t write_uri = {
	.uri = "/write",
	.method = HTTP_GET,
	.handler = write_get_handler,
};

httpd_handle_t start_webserver(void)
{
	httpd_handle_t server = NULL;
	httpd_config_t config = HTTPD_DEFAULT_CONFIG();

	ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
	if(httpd_start(&server, &config) == ESP_OK){
		ESP_LOGI(TAG, "Registering URI handlers");
		httpd_register_uri_handler(server, &index_uri);
		httpd_register_uri_handler(server, &image_uri);
		httpd_register_uri_handler(server, &config0_uri);
		httpd_register_uri_handler(server, &config1_uri);
		httpd_register_uri_handler(server, &write_uri);
		return server;
	}

	ESP_LOGI(TAG, "Error starting server!");
	return NULL;
}

void stop_webserver(httpd_handle_t server)
{
	httpd_stop(server);
}

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
	httpd_handle_t *server = (httpd_handle_t *) ctx;

	switch(event->event_id){
		case SYSTEM_EVENT_STA_START:
			ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START");
			ESP_ERROR_CHECK(esp_wifi_connect());
			break;
		case SYSTEM_EVENT_STA_GOT_IP:
			ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP");
			ESP_LOGI(TAG, "Got IP: '%s'", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));

			if(*server == NULL){
				*server = start_webserver();
			}
			break;
		case SYSTEM_EVENT_STA_DISCONNECTED:
			ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED");
			ESP_ERROR_CHECK(esp_wifi_connect());

			if(*server){
				stop_webserver(*server);
				*server = NULL;
			}
			break;
		default:
			break;
	}
	return ESP_OK;
}

static void initialise_wifi(void *arg)
{
	tcpip_adapter_init();
	ESP_ERROR_CHECK(esp_event_loop_init(event_handler, arg));
	wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
	ESP_ERROR_CHECK(esp_wifi_init(&cfg));
	ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
	wifi_config_t wifi_config = {
		.sta = {
			.ssid = EXAMPLE_WIFI_SSID,
			.password = EXAMPLE_WIFI_PASS,
		},
	};
	ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
	ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
	ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
	ESP_ERROR_CHECK(esp_wifi_start());
}

void app_main()
{
	static httpd_handle_t server = NULL;
	ESP_ERROR_CHECK(nvs_flash_init());

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

	sensor = esp_camera_sensor_get();

	vTaskDelay(1000 / portTICK_RATE_MS);
	initialise_wifi(&server);
}

```

#ブラウザ側

```html:index.html

<!doctype html>
<html lang="ja">
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.4/css/all.css">
		<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
		<title>OV2640</title>
		<style>
			.alert{
				display:none;
			}
			#overlay{   
				position: fixed;
				top: 0;
				z-index: 100;
				width: 100%;
				height:100%;
				display: none;
				background: rgba(0,0,0,0.6);
			}
			.cv-spinner{
				height: 100%;
				display: flex;
				justify-content: center;
				align-items: center;  
			}
			.spinner{
				width: 40px;
				height: 40px;
				border: 4px #ddd solid;
				border-top: 4px #2e93e6 solid;
				border-radius: 50%;
				animation: sp-anime 0.8s infinite linear;
			}
			@keyframes sp-anime{
				0% { 
					transform: rotate(0deg); 
				}
				100% { 
					transform: rotate(359deg); 
				}
			}
			.is-hide{
				display:none;
			}
			.ov_a{
				height:600px;
				overflow-y:auto;
			}
		</style>
	</head>
	<body>
 
		<div id="overlay">
			<div class="cv-spinner">
				<span class="spinner"></span>
			</div>
		</div>

		<div class="text-center mt-3 mb-3">
			<h1>OV2640</h1>
		</div>

		<div class="container-fluid">
			<div class="row">
				<div class="col-lg-6 text-center mb-4">
					<div class="row">
						<div class="col-md-12">
							<div class="alert alert-success" role="alert"></div>
							<div class="alert alert-danger" role="alert"></div>
						</div>
						<div class="col-md-12">
							<img id="img" class="mw-100">
						</div>
					</div>
				</div>

				<div class="col-lg-6">
					<div class="ov_a">
						<div class="text-center mt-1 mb-1">
							<span>0xFF=00</span>
						</div>
						<table id="table_regs0" class="table table-striped">
							<tr>
								<th>Address(Hex)</th>
								<th>Name</th>
								<th>Value(Hex)</th>
								<th></th>
							</tr>
						</table>
						<div class="text-center mt-1 mb-1">
							<span>0xFF=01</span>
						</div>
						<table id="table_regs1" class="table table-striped">
							<tr>
								<th>Address(Hex)</th>
								<th>Name</th>
								<th>Value(Hex)</th>
								<th></th>
							</tr>
						</table>
					</div>
				</div>
			</div>
		</div>
 
		<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
		<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

		<script type="text/javascript">
			$(function(){

				var regs0 = {
					0x05: "R_BYPASS",
					0x44: "Qs",
					0x50: "CTRLI",
					0x51: "HSIZE",
					0x52: "VSIZE",
					0x53: "XOFFL",
					0x54: "YOFFL",
					0x55: "VHYX",
					0x56: "DPRP",
					0x57: "TEST",
					0x5A: "ZMOW",
					0x5B: "ZMOH",
					0x5C: "ZMHH",
					0x7C: "BRADDR",
					0x7D: "BPDATA",
					0x86: "CTRL2",
					0x87: "CTRL3",
					0x8C: "SIZEL",
					0xC0: "HSIZE8",
					0xC1: "VSIZE8",
					0xC2: "CTRL0",
					0xC3: "CTRL1",
					0xD3: "R_DVP_SP",
					0xDA: "IMAGE_MODE",
					0xE0: "RESET",
					0xED: "REGED",
					0xF0: "MS_SP",
					0xF7: "SS_ID",
					0xF8: "SS_CTRL",
					0xF9: "MC_BIST",
					0xFA: "MC_AL",
					0xFB: "MC_AH",
					0xFC: "MC_D",
					0xFD: "P_CMD",
					0xFE: "P_STATUS",
					0xFF: "RA_DLMT",
				};

				var regs1 = {
					0x0: "GAIN",
					0x3: "COM1",
					0x4: "REG04",
					0x8: "REG08",
					0x9: "COM2",
					0xA: "PIDH",
					0xB: "PIDL",
					0xC: "COM3",
					0xC: "COM3",
					0x10: "AEC",
					0x11: "CLKRC",
					0x12: "COM7",
					0x13: "COM8",
					0x14: "COM9",
					0x15: "COM10",
					0x17: "HREFST",
					0x18: "HREFEND",
					0x19: "VSTART",
					0x1A: "VEND",
					0x1C: "MIDH",
					0x1D: "MIDL",
					0x24: "AEW",
					0x25: "AEB",
					0x26: "VV",
					0x2A: "REG2A",
					0x2B: "FRARL",
					0x2D: "ADDVSL",
					0x2E: "ADDVSH",
					0x2E: "ADDVSH",
					0x2F: "YAVG",
					0x32: "REG32",
					0x34: "ARCOM2",
					0x45: "REG45",
					0x46: "FLL",
					0x47: "FLH",
					0x48: "COM19",
					0x49: "ZOOMS",
					0x4B: "COM22",
					0x4E: "COM25",
					0x4F: "BD50",
					0x50: "BD60",
					0x5D: "REG5D",
					0x5E: "REG5E",
					0x5F: "REG5F",
					0x60: "REG60",
					0x61: "HISTO_LOW",
					0x62: "HISTO_HIGH",
				};

				function get_image(){
					var xhr = new XMLHttpRequest();
					xhr.onreadystatechange = function(){
						if(this.readyState == 4 && this.status == 200){
							var img = document.getElementById("img");
							img.src = window.URL.createObjectURL(this.response);
							get_image();
						}
					}
					xhr.open('GET', "./image");
					xhr.responseType = 'blob';
					xhr.send();
				};

				function write_config(bank, k, v){
					$.ajax({
						url: "./write",
						type: "GET",
						dataType: "json",
						data: {
							bank: bank,
							key: k,
							value: v
						},
						beforeSend: function() {
							$(".alert").css("display", "none");
						}
					})
					.done((data) => {
						$.each(data, function(i, item){
							if(item == "success"){
								$(".alert-success").css("display", "block");
								$(".alert-success").text('bank:' + bank + ' key:0x' + Number(k).toString(16) + ' value:0x' + Number(v).toString(16) + ' success');
							}else{
								$(".alert-danger").css("display", "block");
								$(".alert-danger").text('bank:' + bank + ' key:0x' + Number(k).toString(16) + ' value:0x' + Number(v).toString(16) + ' failed');
							}
						});
					})
                    .fail((data) => {
						$(".alert-danger").css("display", "block");
						$(".alert-danger").text('bank:' + bank + ' key:0x' + Number(k).toString(16) + ' value:0x' + Number(v).toString(16) + ' failed');
                    });
				};

				$(document).ready(function(){
					$.ajax({
						url: "./config0",
						type: "GET",
						dataType: "json",
						async: false,
					})
					.done((data) => {
						$.each(data, function(i, item){
							$("#table_regs0").append('<tr><td>0x' + Number(i).toString(16) + '</td><td>' + regs0[i] + '</td><td id="value_0_' + i + '">0x' + Number(item).toString(16) + '(0b' + Number(item).toString(2) + ')</td><td><input type="range" class="w-100 slider0" name="' + i + '" value="' + item + '" min="0" max="255" step="1"></td></tr>');
						});
					});

					$.ajax({
						url: "./config1",
						type: "GET",
						dataType: "json",
						async: false,
					})
					.done((data) => {
						$.each(data, function(i, item){
							$("#table_regs1").append('<tr><td>0x' + Number(i).toString(16) + '</td><td>' + regs1[i] + '</td><td id="value_1_' + i + '">0x' + Number(item).toString(16) + '(0b' + Number(item).toString(2) + ')</td><td><input type="range" class="w-100 slider1" name="' + i + '" value="' + item + '" min="0" max="255" step="1"></td></tr>');
						});
					});

					get_image();

					$(".slider0").on("input change", function(){
						$("#value_0_" + $(this).attr("name")).text("0x" + Number($(this).val()).toString(16) + "(0b" + Number($(this).val()).toString(2) + ")");
					});

					$(".slider1").on("input change", function(){
						$("#value_1_" + $(this).attr("name")).text("0x" + Number($(this).val()).toString(16) + "(0b" + Number($(this).val()).toString(2) + ")");
					});

					$(".slider0").on("change", function(){
						write_config(0, $(this).attr("name"), $(this).val())
					});

					$(".slider1").on("change", function(){
						write_config(1, $(this).attr("name"), $(this).val())
					});
				});
			});
		</script>
	</body>
</html>

```

#これから
・50Hzのバンドフィルターが上手く動作していないため、原因を探る
・色彩が少し淡いので、もう少しはっきりさせたい
・Qsの限界値を探る
・暗闇時の赤外線を当てた場合