Intro
The weeks goal an input, I modified the quentorres made in week 4. by using the xiao sense (with camera) and the square form factor I keep thinking it would be very useful t make a timelapse camera that I can use fot take animated GIF’s that can be used for documentation. I simplified the queentorres to just keep a button and a display LED hoping to minimize its footprint.
Objectives
- Use the example code for the XIAO sense to take a set of timelapse pictures
- Modify it to make it very easy to take a timelapse with the button. Use the LED to show that the XIAO is recording
- SeeedStudio XIAO ESP32S3
- XIAO Camera Module
- Arduino IDE
- Quentorres Board
Process
Initially I cloned and tested the example code for the XIAO ESP32S3 Sense Camera, specifically the take photos example.
There are a couple changes that need to be made, one main change is that when the button in the quentorres is pressed i want to “record” a timelapse, maning that it will save a picture every 1 seconds in specific folder. After that i should be able to plug the SD card in a computer an generate a gif
CODE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
| #include "esp_camera.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
#include "camera_pins.h"
// Define GPIOs for the Button and the LED
#define BUTTON_PIN 2
#define LED_PIN 1
unsigned long lastCaptureTime = 0; // Last shooting time
int imageCount = 1; // File Counter
bool camera_sign = false; // Check camera status
bool sd_sign = false; // Check sd status
int folderCount = 0; // Folder Counter
bool buttonPressed = false;
bool lastButtonState = HIGH; // Added to track the last button state
unsigned long lastDebounceTime = 0;// Added to track the last debounce time
unsigned long debounceDelay = 50; // Debounce delay in milliseconds
// Save pictures to SD card
void photo_save(const char * fileName) {
// Take a photo
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Failed to get camera frame buffer");
return;
}
// Save photo to file
writeFile(SD, fileName, fb->buf, fb->len);
// Release image buffer
esp_camera_fb_return(fb);
Serial.println("Photo saved to file");
}
// SD card write file
void writeFile(fs::FS &fs, const char * path, uint8_t * data, size_t len) {
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file) {
Serial.println("Failed to open file for writing");
return;
}
if(file.write(data, len) == len) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
void setup() {
Serial.begin(115200);
while(!Serial); // Wait for serial monitor to connect
pinMode(BUTTON_PIN, INPUT); // Set button pin as input with pull-up
pinMode(LED_PIN, OUTPUT); // Set LED pin as output
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.frame_size = FRAMESIZE_UXGA;
config.pixel_format = PIXFORMAT_JPEG; // for streaming
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.jpeg_quality = 12;
config.fb_count = 1;
if(config.pixel_format == PIXFORMAT_JPEG){
if(psramFound()){
config.jpeg_quality = 10;
config.fb_count = 2;
config.grab_mode = CAMERA_GRAB_LATEST;
} else {
// Limit the frame size when PSRAM is not available
config.frame_size = FRAMESIZE_SVGA;
config.fb_location = CAMERA_FB_IN_DRAM;
}
} else {
// Best option for face detection/recognition
config.frame_size = FRAMESIZE_240X240;
#if CONFIG_IDF_TARGET_ESP32S3
config.fb_count = 2;
#endif
}
// Initialize the camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
camera_sign = true; // Camera initialization check passes
// Initialize SD card
if(!SD.begin(21)) {
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
// Determine if the type of SD card is available
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
sd_sign = true; // SD initialization check passes
Serial.println("Setup complete, press the button to take photos.");
}
void loop() {
buttonPressed = digitalRead(BUTTON_PIN);
if(camera_sign && sd_sign){
if (buttonPressed) {
buttonPressed = false; // Reset the button press flag
digitalWrite(LED_PIN, HIGH); // Turn on the LED
char folderName[30];
sprintf(folderName, "/Folder_%d", ++folderCount);
SD.mkdir(folderName); // Create a new folder for this set
int photoCount = 0;
unsigned long startMillis = millis();
while (photoCount < 10) { // Take 10 photos
if (millis() - startMillis >= 1000 * photoCount) { // Every 1 seconds
char filename[50];
sprintf(filename, "%s/image%d.jpg", folderName, ++photoCount);
photo_save(filename);
}
}
digitalWrite(LED_PIN, LOW); // Turn off the LED after shooting
}
}
}
|
this are the pictures taken by the camera:
and here is the gif generated using the ezgif website, showcasing my friend dancing:
Conclusion
This was easier than I initially expected , using the example code and deleting most of the unused parts the code was fairly trivial to implement.
Files