前言:由于AtomS3R-CAM 和 AtomS3R-M12 两款开发板的唯一区别就是图像传感器,而且AtomS3R-CAM只有分辨率0.3MP,因此打算更换成M12同款的OV3660(像素瞬间提高10倍)
但是发现网上并没有相关拆解方法以及内部结构图,所以只好自己试一试了...
一、硬件介绍
本次拆解的是M5STACK ATOM系列的AtomS3R-CAM开发板;
产品尺寸只有 24.0 x 24.0 x 13.5mm,重量 7.4g,非常的小巧,长宽大致不到小型面包板长度的一半;

其他ATOM系列的开发板外部布局都大致相同,都可以采用以下介绍的拆卸方法;


二、拆卸方法
硬件准备:
1、一张硬卡片(银行卡、身份证等都行)
2、镊子(直尖头)
拆卸方法:
1、先将尾部后侧的贴纸撕掉;
2、用硬卡片从后侧往里卡出一个缝隙;
3、然后用镊子向箭头两侧所指的方向,往内向下撬,直到听到卡扣脱落的清脆声音;


内部卡扣结构


整体上下外壳


整体内部图,看上去布局很紧凑;
摄像头排线长度大约10mm,不过市面上的最短基本大多都在21mm;
所以要替换其他摄像头(OV3660 / OV5640)的话,还需要单独定制一个相同长度的...
(不过单独定制的价格还是挺“昂贵”的...)


侧面视角

底部视角

三、摄像头测试代码
如果更换了摄像头,以下是OV3660的功能测试代码;
#include <WiFi.h>
#include "esp_camera.h"
#define USE_ATOMS3R_CAM
#define STA_MODE
// #define AP_MODE
const char* ssid = "yourssid";
const char* password = "yourpasswd";
WiFiServer server(80);
camera_fb_t* fb = NULL;
uint8_t* out_jpg = NULL;
size_t out_jpg_len = 0;
static void jpegStream(WiFiClient* client);
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sscb_sda = SIOD_GPIO_NUM,
.pin_sscb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_RGB565,
.frame_size = FRAMESIZE_QVGA,
.jpeg_quality = 12,
.fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST,
.sccb_i2c_port = 0,
};
void setup()
{
Serial.begin(115200);
pinMode(POWER_GPIO_NUM, OUTPUT);
digitalWrite(POWER_GPIO_NUM, LOW);
delay(500);
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.println("Camera Init Fail");
delay(1000);
esp_restart();
} else {
Serial.println("Camera Init Success");
}
delay(100);
#ifdef STA_MODE
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.setSleep(false);
Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#endif
#ifdef AP_MODE
if (!WiFi.softAP(ssid, password)) {
log_e("Soft AP creation failed.");
while (1);
}
Serial.println("AP SSID:");
Serial.println(ssid);
Serial.println("AP PASSWORD:");
Serial.println(password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
#endif
server.begin();
}
void loop()
{
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the
jpegStream(&client);
}
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
}
// used to image stream
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
static void jpegStream(WiFiClient* client)
{
Serial.println("Image stream start");
client->println("HTTP/1.1 200 OK");
client->printf("Content-Type: %s\r\n", _STREAM_CONTENT_TYPE);
client->println("Content-Disposition: inline; filename=capture.jpg");
client->println("Access-Control-Allow-Origin: *");
client->println();
static int64_t last_frame = 0;
if (!last_frame) {
last_frame = esp_timer_get_time();
}
for (;;) {
fb = esp_camera_fb_get();
if (fb) {
frame2jpg(fb, 255, &out_jpg, &out_jpg_len);
Serial.printf("pic size: %d\n", out_jpg_len);
client->print(_STREAM_BOUNDARY);
client->printf(_STREAM_PART, out_jpg_len);
int32_t to_sends = out_jpg_len;
int32_t now_sends = 0;
uint8_t* out_buf = out_jpg;
uint32_t packet_len = 8 * 1024;
while (to_sends > 0) {
now_sends = to_sends > packet_len ? packet_len : to_sends;
if (client->write(out_buf, now_sends) == 0) {
goto client_exit;
}
out_buf += now_sends;
to_sends -= now_sends;
}
int64_t fr_end = esp_timer_get_time();
int64_t frame_time = fr_end - last_frame;
last_frame = fr_end;
frame_time /= 1000;
Serial.printf("MJPG: %luKB %lums (%.1ffps)\r\n", (long unsigned int)(out_jpg_len / 1024),
(long unsigned int)frame_time, 1000.0 / (long unsigned int)frame_time);
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
}
if (out_jpg) {
free(out_jpg);
out_jpg = NULL;
out_jpg_len = 0;
}
} else {
Serial.println("Camera capture failed");
}
}
client_exit:
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
}
if (out_jpg) {
free(out_jpg);
out_jpg = NULL;
out_jpg_len = 0;
}
client->stop();
Serial.printf("Image stream end\r\n");
}四、安装方法
安装非常简单,只需上下外壳对齐卡扣后向下按压即可;
1、先将开发板放置到底部配件上;
2、再将USB口、摄像头对齐上半部分配件;
3、对齐后,向下按压即可合体;

我要赚赏金
