Casharradan waa qayb ka mid ah: Makerfabs MaTouch AI ESP32S3 2.8" Camera
Guddiga ugu cusub ee MaTouch AI wuxuu ku daray codka I2S ee soo gelaya/I2S speaker/ kamarad 3 million ah OV3660/ shaashad 320*240 resolution, oo leh processor ESP32S3 oo xooggan iyo awoodda Wifi, taasoo ka dhigaysa guddigan qalab/goob wanaagsan oo lagu horumariyo AI-ga ESP32.
Makerfabs MaTouch ESP32-S3 2.8" Kamarada iyo Taabashada: 3MP Sawirro oo loo kaydiyo Kaadhka SD
Muuqaal toos ah, sawiro 3 megapixel ah, oo lagu kaydiyo SD-ga oo leh waqti dhab ah
App kamarad shaqeeya: muuqaal toos ah oo qiyaastii 25 sawir ilbiriqsi ah, saddex badhamo taabasho ah, iyo sawiro lagu kaydiyo kaadhka microSD-ga oo lagu calaamadeeyay taariikhda iyo waqtiga saxda ah ee saacadda baytariyada ku shaqeysa ee guddiga.
Kamarad iyo Shaashad Taabasho oo ka shaqeysa guddiga MaTouch AI ESP32-S3
Saddexda badhamad
SNAP - sawir degdeg ah oo ku qaadanaya xallinta muuqaalka tooska ah. Dhaqso badan oo had iyo jeer shaqeeya.
3 MP - sawir dhab ah oo 2048×1536 ah. Shaashadu madoobaataa qiyaastii laba ilbiriqsi; taasi waa dib-u-bilowga darawalka kamaradda, waxaana hoos lagu sharaxay.
FLIP - sawirka gaddiisa marka aad lenska ka jeediso adiga.
Sababta shaashadu u madoobaato sawirka 3 MP
Qaabka pixels-ka ee kamaraddu waa go'an marka darawalku bilaabmo. Waxaad beddeli kartaa xallinta si degdeg ah, laakiin ma beddeli kartid RGB565 (waxa shaashadu u baahan tahay) una beddesho JPEG (waxa faylku u baahan yahay) adigoon dib u bilaabin darawalka. Sidaa darteed dariiqa xallinta sare wuxuu u yeedhaa esp_camera_deinit(), dib u bilaabaa qaabka JPEG xallin buuxda ah, qaadaa sawirka, ka dibna soo celiyaa muuqaalka tooska ah. Hakadku waa daacadnimo, ma aha cillad.
Waqtiyada aan internetka lahayn
Sawirrada waxaa lagu kaydiyaa sida /IMG_260730_143052.jpg - sannad, bil, maalin, saacad, daqiiqad, ilbiriqsi. Taasi waxay ka timaadaa saacadda dhabta ah ee PCF8563T, oo ay sii wado baytariyadu, sidaa darteed waqtigu wuxuu badbaadaa marka korontada la gooyo. Haddii saacaddu ay lumiso koronto, skic-ku wuxuu ka bilaabaa daqiiqadii la soo ururiyay skic-ka.
Sixitaan ku saabsan tusaalaha soo-saaraha
Tusaalaha kamaradda ee iibiyuhu wuxuu ku qeexayaa kaadhka SD-ga pin-yada 2, 42 iyo 41. Lambarradaas pin-yada waxaa laga koobiyay guddi ka duwan - kan waxay yihiin pin-yada makrofoonka. Kaadhka SD-ga dhab ahaantii wuxuu ku yaal baska SPI ee shaashadda oo leh xulasho gaar ah oo IO47 ah, taas oo ah waxa koodhkani isticmaalayo. Tani waxay muhiim tahay haddii aad mar walba rabto in kamaradda iyo makrofoonadu isku skic ku shaqeeyaan.
Wadaagista hal bas oo SPI ah
Shaashadda iyo kaadhka SD-ga waxay ku fadhiyaan saddexda silig ee isku midka ah, sidaa darteed labaduba waa inay maraan darawalka SPI ee isku midka ah. Skic-kani wuxuu isticmaalaa Arduino_HWSPI oo leh shayga caadiga ah ee SPI sababtaas dartiis. Isticmaalka darawal ka duwan mid kasta waa sababta caadiga ah ee shaashad ku xayiranta daqiiqadda kaadhka SD-ga la rakibo.
Ku saabsan guddiga MaTouch AI ESP32-S3 2.8"
Mashruuc kasta oo ku yaal boggan wuxuu ku shaqeeyaa MaTouch AI ESP32-S3 2.8" TFT ST7789V oo ka socda Makerfabs. Waa guddi dhammaan-waa-mid: shaashad taabasho midab leh, kamarad 3 megapixel ah, laba makrofoon iyo cod-weyneeye dhab ah, dhammaantood waxaa wadaa ESP32-S3 oo leh 8 MB oo PSRAM ah. Isku-darkaas ayaa ka dhigaya mashruucyadan AI-ga inay suurtagal noqdaan hal guddi oo aan wax kale ku xirnayn.
8 MB ee PSRAM-ka ayaa ka muhiimsan lambar kasta oo halkan ku qoran. Waa waxa u oggolaanaya guddiga inuu ku hayo qaab kamarad, dhawr ilbiriqsi oo maqal la duubay, ama sawir base64-ku-kooban xusuusta isku waqti - kuwaas oo midkoodna aan ku habboonayn RAM-ka caadiga ah ee ESP32.
Dokumeentiyada soo-saaraha: Bogga wiki-ga Makerfabs.
Sifooyinka muhiimka ah
Processor: ESP32-S3, laba xudun oo 240 MHz ah, WiFi 2.4 GHz + Bluetooth 5.0
Xusuus: 16 MB flash, 8 MB PSRAM (waxa u baahan yahay ku dhawaad mashruuc kasta oo halkan ah)
Shaashad: 2.8" IPS, 320×240, darawal ST7789V, SPI
Taabasho: GT911 capacitive, waxay raadraacdaa 5 farood isku mar
Kamarad: OV3660, 3 megapixel, ilaa 2048×1536
Makrofoonno: laba INMP441 I2S makrofoon dhijitaal ah (lamaan dhab ah oo stereo ah)
Cod-weyneeye: MAX98357A class-D amplifier, 3.2 W ilaa 4 Ω
Kayd: godka kaadhka microSD (qaabka SPI)
Koronto: USB-C, gogol-xirka baytariyada JST, charger TP4056, furaha korontada
Sidoo kale guddiga ku yaal: WS2812B RGB LED, saacadda dhabta ah ee baytariyada ku shaqeysa PCF8563T, iyo cabbiraadda baytariyada MAX17048 oo aan ku qoran sifooyinka rasmiga ah
Labada dekedood ee USB-C isku mid ma aha. Cod-weyneeyaha guddigu wuxuu wadaagaa pin-yada calaamadihiisa (IO19 iyo IO20) ee dekedda asalka ah ee USB, sababtoo ah pin-yadaas waa khadadka xogta ee USB-ga ee ESP32-S3 ee si adag ugu xiran. Had iyo jeer ku shub oo ku shide dekedda CH340K USB-C (ta ku xigta badhanka RESET), oo deji USB CDC On Boot si Disabled. Isticmaal dekedda khaldan waxaana dhici doona in maqalku si khaldan u shaqeeyo ama shubista ay guuldareysato.
Dejinta Arduino IDE
Dejintan ayaa muhiim ah. Dhibaatooyinka intooda badan ee dadku ka soo sheegaan guddigan waa mid ka mid ah kuwan oo khaldan, waxayna dib u dejiyaan markaad beddesho nooca core-ka, sidaa darteed mar kale hubi ka dib isbeddel kasta.
Dejin | Qiimo |
|---|---|
Guddi | ESP32S3 Dev Module |
Nooca core-ka ESP32 | 2.0.17 |
PSRAM | OPI PSRAM |
Cabirka Flash-ka | 16MB (128Mb) |
Qorshaha Qaybinta | 16M Flash (3MB APP/9.9MB FATFS) |
USB CDC On Boot | Disabled |
Xawaaraha Shubista | 921600 |
Tirtir Dhammaan Flash-ka Kahor Shubista | Disabled |
Deked | dekedda CH340K USB-C |
Isticmaal ESP32 core 2.0.17, ee ha isticmaalin 3.x. Espressif waxay ka saartay moodellada lagu ogaanayo wejiga ee ku jira core 3, marka mashruucyada wejiga kuma xirmin halkaas. Ku xidhista 2.0.17 waxay ilaalisaa in mashruuc kasta oo ku yaal boggan uu shaqeeyo hal qaab. Guddiga Maareeyaha (Boards Manager), liiska hoos-u-dhaca ee nooca wuxuu kuu ogolaanayaa inaad ku beddesho dib iyo hor markasta oo aad rabto.
Isticmaal GFX Library for Arduino nooca 1.5.6, ee ha isticmaalin 1.6.x. Noocyada 1.6 waxay u dhisan yihiin ESP32 core 3 waxayna ku xayi karaan bilowga core 2.0.17. Haddii shaashaddaadu ay madowga ku sii jirto ka dib markaad soo gudbiso, tani waa waxa ugu horreeya ee la hubiyo.
Maktabado loo baahan yahay
Ku rakib kuwan iyada oo loo marayo Tools → Manage Libraries gudaha Arduino IDE. Nambarada nooca ayaa muhiim ah - fadlan isticmaal kuwa lagu qoray.
Maktabad | Nooc | Qoraa |
|---|---|---|
GFX Library for Arduino | 1.5.6 | moononournation |
bb_captouch | 1.3.1 | Larry Bank |
Cilad-baaris
Calaamad | Sababta iyo xalka |
|---|---|
Shaashaddu madow ayay ku sii jirtaa | Nooc khaldan oo GFX library ah (isticmaal 1.5.6) ama dejinta guddiga oo khaldan. |
|
|
Waxba ma soo gudbiso / ma jiro COM port | USB-C port khaldan, ama darawalka CH340 lama rakibin. |
Kamaraddu way fashilantaa oo dib uma soo kabsato | Khadka reset-ka ee kamaradda wuxuu ku xiran yahay badhanka RESET ee guddiga, marka software-ku dib uma soo celin karo. Riix RESET. Haddii ay weli fashilanto, dib u geli fiilada ribbon-ka ee kamaradda. |
Soo deji koodhka
Qoraalka buuxa ee Arduino ee mashruucan, oo ay weheliyaan pins.h iyo wax kasta oo kale oo uu u baahan yahay, waa bilaash in la soo dejiyo.
Fur, fur faylka .ino gudaha Arduino IDE, hubi dejinta kor ku xusan, oo ku soo gudbi iyada oo loo marayo CH340K USB-C port.
Halkee laga iibsadaa guddigan
Kuwani waa xiriiriyeyaal affiliate ah. Wax kuma kaa qaadayaan dheeraad ah waxayna kaalmeeyaan taageeridda koodhka bilaashka ah iyo casharrada boggan - mahadsanid.
Casharradan waa qayb ka mid ah: Makerfabs MaTouch AI ESP32S3 2.8" Camera
- Makerfabs MaTouch ESP32-S3 2.8" kamarad Offline Weji-qaadashada Wajiga ee ESP32-S3 - Internet ma laha, Furaha API ma laha
- Makerfabs MaTouch ESP32-S3 2.8" kamarad Ku dhis AI Codka Caawiyaha ee ESP32-S3 (Azure + DeepSeek)
- Makerfabs MaTouch ESP32-S3 2.8" kamarad ESP32-S3 AI Aragti: Tilmaan Kamaradda oo Weydii Waxay Aragto
/*
* ===========================================================================
* 02_Camera_Touch — MaTouch AI ESP32-S3 2.8" TFT ST7789V
* ===========================================================================
*
* ---------------------------------------------------------------------------
----------
* ROBOJAX.COM - MaTouch AI ESP32-S3 2.8" project series
*
* WATCH THE VIDEO
* https://youtu.be/6AL3g3tC_Hk
*
* WRITTEN TUTORIALS - every project, with photos and full explanation
* Camera and touchscreen.... https://robojax.com/RTJ849
* Offline face recognition.. https://robojax.com/RTJ850
* AI voice assistant........ https://robojax.com/RTJ851
* AI vision................. https://robojax.com/RTJ852
*
* GET THE BOARD - SAVE $5 with coupon code: Robojax_Makerfab
* https://www.makerfabs.com/matouch-ai-esp32s3-2-8-tft-st7789v.html
* (enter the code at checkout)
*
* All of this code is free. If it helped you, a subscribe on YouTube is
* the best way to support more of it.
*
* Watching the video first will save you time - it shows the Arduino IDE
* settings and the library versions being set up step by step.
* ---------------------------------------------------------------------------
*
* Live camera viewfinder with a touch interface, saving real photos to the
* microSD card, each one stamped with the true date and time from the board's
* battery-backed RTC.
*
* Three buttons down the right hand side:
*
* SNAP quick save. Converts the frame already on screen to JPEG.
* Instant, 240x240, always works.
*
* 3 MP the real thing. Tears the camera down, brings it back up at
* 2048x1536 JPEG, takes the shot, and restores the viewfinder.
* Takes a couple of seconds. This is the OV3660's full
* 3 megapixels, not an upscale.
*
* MIRROR flips the image, for when you turn the board around to point
* the lens at someone else.
*
* Photos land on the SD card as /IMG_260729_143052.jpg
*
* No WiFi, no API keys, no internet.
*
* ---------------------------------------------------------------------------
* WHY THE 3 MP BUTTON HAS TO WORK SO HARD
* A camera's pixel format is fixed when the driver starts. You can change the
* resolution on the fly, but you cannot switch RGB565 (what the screen wants)
* to JPEG (what the SD card wants) without restarting the driver. So the
* hi-res path deinitialises and reinitialises. That is normal, and it is why
* the screen goes black for a moment.
*
* ---------------------------------------------------------------------------
* BOARD SETTINGS - see pins.h. In short: ESP32S3 Dev Module, core 2.0.17,
* OPI PSRAM, 16MB flash, 16M partition scheme, upload via the CH340K port.
*
* LIBRARIES
* GFX Library for Arduino v1.5.6
* bb_captouch v1.3.1
*
* ---------------------------------------------------------------------------
* FUNCTIONS IN THIS SKETCH
* rtcRead() / rtcWrite(...) read/set the PCF8563 battery-backed clock
* rtcSeedFromBuildTime() set the clock from compile time if it lost power
* getTouch(&x,&y) read the touch panel, mapped to screen coordinates
* inBox(...) hit-test a touch against a button rectangle
* camStart(...) start the camera in a given format/size
* camStartPreview() start the 240x240 RGB565 live-view camera
* makeFilename(...) /IMG_YYMMDD_HHMMSS.jpg from the RTC time
* writeToSD(...) save a buffer to the SD card
* snapQuick() SNAP button: re-encode the preview frame to JPEG
* snapHiRes() 3MP button: restart camera in JPEG mode, full res
* drawButton(...) / drawButtons() / drawStatus() the touch UI
* setup() / loop() boot / viewfinder + edge-detected button taps
*
* Robojax.com
* ===========================================================================
*/
#include <Arduino_GFX_Library.h>
#include <bb_captouch.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include "esp_camera.h"
#include "img_converters.h" // frame2jpg()
#include "pins.h"
/* The TFT and SD share pins 13/12/48, so both must go through the SAME SPI
* peripheral: Arduino_HWSPI wraps the standard SPI object the SD library
* uses. (Arduino_ESP32SPI would grab a different peripheral, and the display
* goes dark the moment the SD card claims the pins.) */
Arduino_HWSPI *bus = new Arduino_HWSPI(
TFT_DC, TFT_CS, TFT_SCLK, TFT_MOSI, TFT_MISO, &SPI, true /* shared bus */);
Arduino_GFX *gfx = new Arduino_ST7789(bus, TFT_RES, 1, true);
BBCapTouch bbct;
/* --- layout: 240x240 viewfinder on the left, 80px button column on the right */
#define VIEW_W 240
#define BTN_X 242
#define BTN_W 78
#define BTN_SNAP_Y 2
#define BTN_HIRES_Y 62
#define BTN_MIRROR_Y 122
#define BTN_H 56
#define STATUS_Y 186
bool ok_sd = false;
bool ok_rtc = false;
bool mirrored = true; // the lens is upside down relative to the screen
int shot_count = 0;
char status[48] = "ready";
/* --- the RTC time structure ------------------------------------------------
* Defined up here, before any function, with explicit prototypes below.
* WHY: the Arduino IDE auto-generates function prototypes and can insert them
* ABOVE this struct - a function returning RtcTime then fails to compile with
* "RtcTime does not name a type". Writing the prototypes ourselves stops the
* IDE generating its own, so the order stays correct.
* ------------------------------------------------------------------------- */
struct RtcTime {
int year, mon, day, hour, min, sec;
bool valid;
};
// explicit prototypes - do not remove (see comment above)
RtcTime rtcRead();
void rtcWrite(int year, int mon, int day, int hour, int min, int sec);
void rtcSeedFromBuildTime();
bool getTouch(uint16_t *x, uint16_t *y);
bool camStart(pixformat_t fmt, framesize_t size, int fb_count, int quality);
bool camStartPreview();
void makeFilename(char *out, size_t n);
bool writeToSD(const char *path, const uint8_t *data, size_t len);
void snapQuick();
void snapHiRes();
void drawButton(int y, const char *label, uint16_t colour);
void drawButtons();
void drawStatus();
/* ===========================================================================
* RTC — PCF8563T
* =========================================================================== */
static inline uint8_t bcd2dec(uint8_t b) { return (uint8_t)((b >> 4) * 10 + (b & 0x0F)); }
static inline uint8_t dec2bcd(uint8_t d) { return (uint8_t)(((d / 10) << 4) | (d % 10)); }
RtcTime rtcRead() {
RtcTime t = {2026, 1, 1, 0, 0, 0, false};
Wire.beginTransmission(ADDR_PCF8563);
Wire.write(0x02);
if (Wire.endTransmission(false) != 0) return t;
if (Wire.requestFrom((int)ADDR_PCF8563, 7) != 7) return t;
uint8_t r[7];
for (uint8_t i = 0; i < 7; i++) r[i] = Wire.read();
t.valid = !(r[0] & 0x80); // bit 7 = Voltage Low: time is unreliable
t.sec = bcd2dec(r[0] & 0x7F);
t.min = bcd2dec(r[1] & 0x7F);
t.hour = bcd2dec(r[2] & 0x3F);
t.day = bcd2dec(r[3] & 0x3F);
t.mon = bcd2dec(r[5] & 0x1F);
t.year = bcd2dec(r[6]) + 2000;
return t;
}
void rtcWrite(int year, int mon, int day, int hour, int min, int sec) {
Wire.beginTransmission(ADDR_PCF8563);
Wire.write(0x02);
Wire.write(dec2bcd(sec)); // writing clears the Voltage Low flag
Wire.write(dec2bcd(min));
Wire.write(dec2bcd(hour));
Wire.write(dec2bcd(day));
Wire.write(0x00); // weekday - we do not use it
Wire.write(dec2bcd(mon)); // century bit left at 0 => 20xx
Wire.write(dec2bcd(year % 100));
Wire.endTransmission();
}
/* If the RTC has lost power, seed it from the moment this sketch was compiled.
* Close enough for photo timestamps, and it means the clock is never blank. */
void rtcSeedFromBuildTime() {
const char *mon_names = "JanFebMarAprMayJunJulAugSepOctNovDec";
char mon_str[4] = {0};
int day, year, hour, min, sec;
sscanf(__DATE__, "%3s %d %d", mon_str, &day, &year);
sscanf(__TIME__, "%d:%d:%d", &hour, &min, &sec);
int mon = 1;
for (int i = 0; i < 12; i++)
if (strncmp(mon_str, mon_names + i * 3, 3) == 0) { mon = i + 1; break; }
rtcWrite(year, mon, day, hour, min, sec);
Serial.printf("RTC seeded from build time: %04d-%02d-%02d %02d:%02d:%02d\n",
year, mon, day, hour, min, sec);
}
/* ===========================================================================
* Touch
* =========================================================================== */
bool getTouch(uint16_t *x, uint16_t *y) {
TOUCHINFO ti;
if (!bbct.getSamples(&ti)) return false;
if (ti.count < 1) return false;
*x = ti.y[0];
*y = (ti.x[0] > 240) ? 0 : (240 - ti.x[0]);
return true;
}
static inline bool inBox(uint16_t x, uint16_t y, int bx, int by, int bw, int bh) {
return (x >= bx && x < bx + bw && y >= by && y < by + bh);
}
/* ===========================================================================
* Camera
* =========================================================================== */
bool camStart(pixformat_t fmt, framesize_t size, int fb_count, int quality) {
camera_config_t c;
c.ledc_channel = LEDC_CHANNEL_0;
c.ledc_timer = LEDC_TIMER_0;
c.pin_d0 = CAM_PIN_D0; c.pin_d1 = CAM_PIN_D1;
c.pin_d2 = CAM_PIN_D2; c.pin_d3 = CAM_PIN_D3;
c.pin_d4 = CAM_PIN_D4; c.pin_d5 = CAM_PIN_D5;
c.pin_d6 = CAM_PIN_D6; c.pin_d7 = CAM_PIN_D7;
c.pin_xclk = CAM_PIN_XCLK;
c.pin_pclk = CAM_PIN_PCLK;
c.pin_vsync = CAM_PIN_VSYNC;
c.pin_href = CAM_PIN_HREF;
/* Share the I2C bus Wire already drives (touch/RTC live there too) instead
* of letting the camera install a second driver on the same pins - that
* kills touch and the RTC. Wire.begin() must run before this function. */
c.pin_sccb_sda = -1;
c.pin_sccb_scl = -1;
c.sccb_i2c_port = 0; // Wire = I2C port 0
c.pin_pwdn = CAM_PIN_PWDN;
c.pin_reset = CAM_PIN_RESET;
c.xclk_freq_hz = 20000000;
c.frame_size = size;
c.pixel_format = fmt;
c.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
c.fb_location = CAMERA_FB_IN_PSRAM;
c.jpeg_quality = quality;
c.fb_count = fb_count;
esp_err_t err = esp_camera_init(&c);
if (err != ESP_OK) {
Serial.printf("esp_camera_init failed: 0x%x\n", err);
return false;
}
sensor_t *s = esp_camera_sensor_get();
if (s) {
s->set_hmirror(s, mirrored ? 1 : 0);
s->set_vflip(s, mirrored ? 1 : 0);
s->set_brightness(s, 1);
s->set_saturation(s, 0);
}
return true;
}
bool camStartPreview() {
if (camStart(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, 12)) return true;
/* Retry with ONE frame buffer. The camera's DMA buffer must live in
* internal RAM, and if something else grabbed it first this is the
* difference between working and "cam_dma_config failed". */
Serial.println("camera: retrying with a single frame buffer (low DMA RAM)");
return camStart(PIXFORMAT_RGB565, FRAMESIZE_240X240, 1, 12);
}
/* ===========================================================================
* Saving
* =========================================================================== */
void makeFilename(char *out, size_t n) {
RtcTime t = rtcRead();
snprintf(out, n, "/IMG_%02d%02d%02d_%02d%02d%02d.jpg",
t.year % 100, t.mon, t.day, t.hour, t.min, t.sec);
}
bool writeToSD(const char *path, const uint8_t *data, size_t len) {
if (!ok_sd) { snprintf(status, sizeof(status), "no SD card"); return false; }
File f = SD.open(path, FILE_WRITE);
if (!f) { snprintf(status, sizeof(status), "SD open failed"); return false; }
size_t written = f.write(data, len);
f.close();
if (written != len) { snprintf(status, sizeof(status), "SD write short"); return false; }
Serial.printf("Saved %s (%u bytes)\n", path, (unsigned)len);
return true;
}
/* Quick save: re-encode the RGB565 frame already in hand. */
void snapQuick() {
snprintf(status, sizeof(status), "saving...");
drawStatus();
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) { snprintf(status, sizeof(status), "no frame"); return; }
uint8_t *jpg = NULL;
size_t jpg_len = 0;
bool ok = frame2jpg(fb, 90, &jpg, &jpg_len);
esp_camera_fb_return(fb);
if (!ok || !jpg) { snprintf(status, sizeof(status), "jpeg encode failed"); return; }
char path[32];
makeFilename(path, sizeof(path));
if (writeToSD(path, jpg, jpg_len)) {
shot_count++;
snprintf(status, sizeof(status), "%s %uKB", path + 1, (unsigned)(jpg_len / 1024));
}
free(jpg);
}
/* Full resolution: restart the driver in JPEG mode at 2048x1536. */
void snapHiRes() {
snprintf(status, sizeof(status), "3MP: restarting cam");
drawStatus();
gfx->fillRect(0, 0, VIEW_W, 240, BLACK);
gfx->setTextColor(YELLOW);
gfx->setCursor(60, 110);
gfx->print("capturing 3 MP...");
esp_camera_deinit();
delay(120);
if (!camStart(PIXFORMAT_JPEG, FRAMESIZE_QXGA, 1, 10)) {
snprintf(status, sizeof(status), "3MP init failed");
delay(120);
if (!camStartPreview()) snprintf(status, sizeof(status), "CAM DEAD - press RESET");
return;
}
// Throw away the first few frames so exposure and white balance settle,
// otherwise the hi-res shot comes out darker than the viewfinder.
for (int i = 0; i < 3; i++) {
camera_fb_t *w = esp_camera_fb_get();
if (w) esp_camera_fb_return(w);
delay(120);
}
camera_fb_t *fb = esp_camera_fb_get();
if (fb && fb->len > 0) {
char path[32];
makeFilename(path, sizeof(path));
if (writeToSD(path, fb->buf, fb->len)) {
shot_count++;
snprintf(status, sizeof(status), "3MP %ux%u %uKB",
fb->width, fb->height, (unsigned)(fb->len / 1024));
}
} else {
snprintf(status, sizeof(status), "3MP capture failed");
}
if (fb) esp_camera_fb_return(fb);
// Back to the viewfinder
esp_camera_deinit();
delay(120);
if (!camStartPreview()) {
snprintf(status, sizeof(status), "CAM DEAD - press RESET");
gfx->fillRect(0, 0, VIEW_W, 240, RED);
}
drawButtons();
}
/* ===========================================================================
* UI
* =========================================================================== */
void drawButton(int y, const char *label, uint16_t colour) {
gfx->fillRoundRect(BTN_X, y, BTN_W, BTN_H, 6, colour);
gfx->drawRoundRect(BTN_X, y, BTN_W, BTN_H, 6, WHITE);
gfx->setTextSize(2);
gfx->setTextColor(WHITE);
int len = strlen(label);
gfx->setCursor(BTN_X + (BTN_W - len * 12) / 2, y + (BTN_H - 16) / 2);
gfx->print(label);
gfx->setTextSize(1);
}
void drawButtons() {
drawButton(BTN_SNAP_Y, "SNAP", gfx->color565(0, 90, 160));
drawButton(BTN_HIRES_Y, "3 MP", gfx->color565(140, 70, 0));
drawButton(BTN_MIRROR_Y, "FLIP", gfx->color565(80, 80, 90));
}
void drawStatus() {
gfx->fillRect(BTN_X, STATUS_Y, BTN_W, 240 - STATUS_Y, BLACK);
gfx->setTextColor(CYAN);
gfx->setTextSize(1);
gfx->setCursor(BTN_X + 2, STATUS_Y + 2);
gfx->printf("shots:%d", shot_count);
RtcTime t = rtcRead();
gfx->setCursor(BTN_X + 2, STATUS_Y + 14);
gfx->setTextColor(ok_rtc ? GREEN : RED);
gfx->printf("%02d:%02d:%02d", t.hour, t.min, t.sec);
// Status text wraps across the narrow column, 13 characters at a time
gfx->setTextColor(YELLOW);
int row = 0;
for (size_t i = 0; i < strlen(status) && row < 3; i += 13, row++) {
char chunk[14] = {0};
strncpy(chunk, status + i, 13);
gfx->setCursor(BTN_X + 2, STATUS_Y + 26 + row * 10);
gfx->print(chunk);
}
}
/* ===========================================================================
* SETUP
* =========================================================================== */
void setup() {
Serial.begin(115200);
delay(400);
Serial.println("\n=== 02 Camera + Touch | Robojax.com ===");
pinMode(TFT_BLK, OUTPUT);
digitalWrite(TFT_BLK, LOW);
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
// one shared SPI bus for TFT + SD, started before either device
SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI);
gfx->begin();
gfx->fillScreen(BLACK);
gfx->setTextSize(1);
digitalWrite(TFT_BLK, HIGH);
gfx->setTextColor(CYAN);
gfx->setCursor(4, 4);
gfx->print("MaTouch AI - camera demo (Robojax.com)");
// Touch. Pulses IO18, which is also the LCD reset - harmless at this point.
bbct.init(TOUCH_SDA, TOUCH_SCL, TOUCH_RST, TOUCH_INT);
delay(50);
// RTC on the shared I2C bus (must be up before the camera - shared SCCB)
Wire.begin(I2C_SDA, I2C_SCL, 100000);
delay(20);
RtcTime t = rtcRead();
ok_rtc = (t.year >= 2020);
if (ok_rtc && !t.valid) {
Serial.println("RTC lost power - seeding from build time");
rtcSeedFromBuildTime();
}
gfx->setCursor(4, 16);
gfx->setTextColor(ok_rtc ? GREEN : RED);
gfx->print(ok_rtc ? "RTC ok" : "RTC not found");
/* ---- CAMERA BEFORE SD. This order matters. ----
* The camera's DMA buffer must come from internal (not PSRAM) memory, and
* mounting the SD card first can take enough of it that the camera driver
* fails with "cam_dma_config failed / 0xffffffff". Camera first, SD second,
* and both fit. (On ESP32 core 3.x the allocation is looser and the old
* order happened to work - which is why this only appeared on 2.0.17.) */
gfx->setCursor(4, 28);
gfx->setTextColor(YELLOW);
gfx->print("starting camera...");
Serial.printf("free internal RAM before camera: %u bytes\n",
(unsigned)heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
if (!camStartPreview()) {
gfx->fillScreen(RED);
gfx->setTextColor(WHITE);
gfx->setTextSize(2);
gfx->setCursor(20, 90);
gfx->print("CAMERA FAILED");
gfx->setTextSize(1);
gfx->setCursor(20, 120);
gfx->print("0xffffffff / cam_dma_config = out of DMA memory:");
gfx->setCursor(20, 132);
gfx->print(" check Tools > PSRAM = OPI PSRAM, then press RESET.");
gfx->setCursor(20, 150);
gfx->print("Any other error = reseat the camera FPC cable.");
gfx->setCursor(20, 162);
gfx->print("The camera reset line is tied to board RESET, so it");
gfx->setCursor(20, 174);
gfx->print("cannot be restarted from software - use the button.");
while (1) delay(1000);
}
// SD card LAST. Same three wires as the display, its own chip select on IO47.
const uint32_t speeds[] = {40000000, 20000000, 10000000, 4000000};
for (uint8_t i = 0; i < 4 && !ok_sd; i++)
if (SD.begin(SD_CS, SPI, speeds[i])) ok_sd = true;
digitalWrite(SD_CS, HIGH);
gfx->setCursor(4, 40);
gfx->setTextColor(ok_sd ? GREEN : RED);
gfx->print(ok_sd ? "SD ok" : "SD FAILED - photos will not save");
Serial.println(ok_sd ? "SD ok" : "SD failed");
delay(600);
gfx->fillScreen(BLACK);
drawButtons();
drawStatus();
Serial.println("Running. SNAP = quick, 3 MP = full resolution, FLIP = mirror.");
}
/* ===========================================================================
* LOOP
* =========================================================================== */
void loop() {
static uint32_t last_status = 0;
/* touch edge detection: a button fires exactly once per physical tap.
* touch_down latches on the first touched frame and only releases after
* several consecutive untouched frames (the GT911 occasionally skips a
* sample mid-press, which must not count as a release). */
static bool touch_down = false;
static uint8_t release_count = 0;
/* FPS: averaged over half a second and redrawn only then - redrawing it
* every frame just makes it flicker unreadably. */
static uint32_t fps_window_start = 0;
static uint16_t fps_frames = 0;
// --- viewfinder ---
camera_fb_t *fb = esp_camera_fb_get();
if (fb) {
gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)fb->buf, fb->width, fb->height);
esp_camera_fb_return(fb);
fps_frames++;
}
// --- touch: act only on the moment the finger first lands ---
uint16_t x, y;
if (getTouch(&x, &y)) {
release_count = 0;
if (!touch_down) { // new tap, not a held finger
touch_down = true;
if (inBox(x, y, BTN_X, BTN_SNAP_Y, BTN_W, BTN_H)) {
snapQuick();
drawStatus();
} else if (inBox(x, y, BTN_X, BTN_HIRES_Y, BTN_W, BTN_H)) {
snapHiRes();
drawStatus();
} else if (inBox(x, y, BTN_X, BTN_MIRROR_Y, BTN_W, BTN_H)) {
mirrored = !mirrored;
sensor_t *s = esp_camera_sensor_get();
if (s) { s->set_hmirror(s, mirrored ? 1 : 0); s->set_vflip(s, mirrored ? 1 : 0); }
snprintf(status, sizeof(status), mirrored ? "mirrored" : "normal");
drawStatus();
}
}
} else if (touch_down) {
// require ~4 consecutive empty reads (~130ms) before declaring release
if (++release_count >= 4) { touch_down = false; release_count = 0; }
}
// --- FPS, averaged, top left corner of the viewfinder ---
uint32_t now = millis();
if (now - fps_window_start >= 500) {
float fps = fps_frames * 1000.0f / (now - fps_window_start);
fps_window_start = now;
fps_frames = 0;
gfx->fillRect(2, 2, 56, 10, BLACK);
gfx->setTextColor(GREEN);
gfx->setTextSize(1);
gfx->setCursor(4, 3);
gfx->printf("FPS %.1f", fps);
}
if (now - last_status > 1000) { last_status = now; drawStatus(); }
}
Waad u baahan tahay
-
Amaazon
-
QalProduct page for MaTouch AI ESP32S3 2.8" TFT ST7789Vmakerfabs.com
Kheyraadka & Tixraacyada
-
DukumentiMakerfabs MaTouch ESP32-S3 2.8" Camera and Touchscreen: User's Manualwiki.makerfabs.com
-
Dukumenti
-
DukumentiProduct page for MaTouch AI ESP32S3 2.8" TFT ST7789Vmakerfabs.com
-
Soo dajiArduino GFX Library on GitHubgithub.com
Faylasha📁
Faylka Loo Baahan Yahay (.h)
Faylal Kale
Muuqaal
-
MaTouch_AI 2.8“ MaTouch AI ESP32S3 2.8" TFT ST7789V schematicThe latest MaTouch AI board integrate I2S voice input/I2S speaker/ 3 million camera OV3660/ 320*240 resolution display, with ESP32S3 strong processor& Wifi ability, to make this board a good tool/platform for AI development with ESP32.
MaTouch_AI 2.8“ SPI TFT ST7789V V1.1.PDF0.15 MB