Search Code

Makerfabs MaTouch ESP32-S3 2.8" Kamẹra ati Ibi Ifọwọkan: Awọn Fọto 3MP si Kaadi SD

Makerfabs MaTouch ESP32-S3 2.8" Kamẹra ati Ibi Ifọwọkan: Awọn Fọto 3MP si Kaadi SD

  1. Wiwo kamẹra lọ̀, àwọn fọ́tò 3 megapixel, tí a fipamọ́ sí SD pẹ̀lú àkókò gidi
  2. Ọ̀nà kamẹra tí ń ṣiṣẹ́: wiwo kamẹra lọ̀ ní nǹkan bí 25 fírẹ́ẹ̀mù nìṣó, àwọn bọ́tìnì ifọwọ́kan mẹ́ta, àti àwọn fọ́tò tí a fipamọ́ sí káàdì microSD tí a fi àkókò àti ọjọ́ tí ó tọ́ sí i láti aago tí ń lo bátìrí tí ó wà nínú board.
  3. Camera and Touchscreen running on the MaTouch AI ESP32-S3 board Kamẹra àti iboju ifọwọ́kan tí ń ṣiṣẹ́ lórí MaTouch AI ESP32-S3 board
  4. Àwọn bọ́tìnì mẹ́ta

    • SNAP - fọ́tò lẹ́sẹ̀kẹsẹ̀ ní ìwọ̀n wiwo kamẹra. Yára àti ń ṣiṣẹ́ nígbà gbogbo.

    • 3 MP - fọ́tò gidi 2048×1536. Iboju yóò ṣókùnkùn fún nǹkan bí ìṣẹ́jú àáyá méjì; ìyẹn ni ẹ̀rọ kamẹra tí ń bẹ̀rẹ̀ padà, a ó sì ṣàlàyé rẹ̀ nísàlẹ̀.

    • FLIP - ń yí àwòrán padà nígbà tí o bá túbọ̀nà kamẹra sí ọ̀nà tí kì í ṣe ti ara rẹ.

    Ìdí tí iboju fi ń ṣókùnkùn fún fọ́tò 3 MP

    Ìrísí píxẹ́lì kamẹra jẹ́ àyànfẹ́ nígbà tí ẹ̀rọ bá bẹ̀rẹ̀. O lè yí ìwọ̀n padà lójúkan, ṣùgbọ́n o kò lè yí láti RGB565 (èyí tí iboju nílò) sí JPEG (èyí tí fáìlì nílò) láì bẹ̀rẹ̀ ẹ̀rọ padà. Nítorí náà, ọ̀nà ìwọ̀n-gíga ń pe esp_camera_deinit(), ó bẹ̀rẹ̀ padà ní ipo JPEG ní ìwọ̀n kíkún, ó ya fọ́tò, lẹ́yìn náà ó tún wiwo kamẹra padà. Ìdúró náà jẹ́ òtítọ́, kì í ṣe àṣìṣe.

    Àwọn àkókò láìsí intanẹ́ẹ̀tì

    A fipamọ́ àwọn fọ́tò gẹ́gẹ́ bí /IMG_260730_143052.jpg - ọdún, oṣù, ọjọ́, wákàtí, ìṣẹ́jú, ìṣẹ́jú àáyá. Ìyẹn wá láti aago gidi PCF8563T, èyí tí bátìrí ń mú kí ó máa ṣiṣẹ́, nítorí náà àkókò náà yóò wà nígbà tí a bá yọ ọ́ kúrò nínú ẹ̀rọ. Bí aago bá ti pàdánù agbára, kódù náà yóò fi àkókò tí a kó kódù náà sí i.

    Àtúnṣe sí àpẹẹrẹ olùṣelọ́pọ̀

    Àpẹẹrẹ kamẹra olùtajà fúnrarẹ̀ ń ṣàlàyé káàdì SD lórí àwọn pin 2, 42 àti 41. Àwọn nọ́mbà pin wọ̀nyẹn ni a dákọ́ láti board mìíràn - lórí èyí wọ́n jẹ́ àwọn pin máíkírófóònù. Káàdì SD wà lórí SPI bus iboju pẹ̀lú àṣàyàn chip tirẹ̀ lórí IO47, èyí ni kódù tí ó wà níbí ń lò. Èyí ṣe pàtàkì bí o bá fẹ́ kamẹra àti àwọn máíkírófóònù ṣiṣẹ́ papọ̀ nínú kódù kan náà.

    Pínpín SPI bus kan

    Iboju àti káàdì SD wà lórí àwọn wáyà mẹ́ta kan náà, nítorí náà àwọn méjèèjì gbọ́dọ̀ kọjá lọ nípasẹ̀ ẹ̀rọ SPI kan náà. Kódù yìí ń lo Arduino_HWSPI pẹ̀lú ohun èlò SPI ti ìwọ̀n rẹ̀ fún ìdí yìí gan-an. Lílo ẹ̀rọ tí ó yàtọ̀ fún ọ̀kọ̀ọ̀kan ni ìdí pàtàkì tí iboju fi ń dídi nígbà tí a bá fi káàdì SD sílẹ̀.

    Nípa MaTouch AI ESP32-S3 2.8" board

    Gbogbo iṣẹ́-ọwọ́ lórí ojú-ewé yìí ń ṣiṣẹ́ lórí MaTouch AI ESP32-S3 2.8" TFT ST7789V láti ọ̀dọ̀ Makerfabs. Ó jẹ́ board tí ó ní ohun gbogbo: iboju ifọwọ́kan aláwọ̀, kamẹra 3 megapixel, máíkírófóònù méjì àti amúlùtí gidi, gbogbo wọn ni ESP32-S3 pẹ̀lú 8 MB PSRAM ń darí. Àkópọ̀ yìí ni ó mú kí àwọn iṣẹ́-ọwọ́ AI wọ̀nyí ṣeé ṣe lórí board kan láìsí ohun mìíràn tí a so mọ́ ọ.

    8 MB PSRAM ṣe pàtàkì ju gbogbo nọ́mbà mìíràn níbí. Ó ni ó jẹ́ kí board lè di fírámù kamẹra, ìṣẹ́jú àáyá díẹ̀ ti ohùn tí a gbà sílẹ̀, tàbí fọ́tò tí a fi base64 kódù sílẹ̀ nínú iranti ní àkókò kan náà - èyí kò níbàá mu nínú RAM ìwọ̀n ESP32.

    Ìwé àṣẹ olùṣelọ́pọ̀: Ojú-ewé wiki Makerfabs.

    Àwọn ìsọfúnni pàtàkì

    • Processor: ESP32-S3, ẹ̀yà méjì 240 MHz, WiFi 2.4 GHz + Bluetooth 5.0

    • Ìrántí: 16 MB flash, 8 MB PSRAM (èyí tí ó fẹ́rẹ̀ẹ́ jẹ́ gbogbo iṣẹ́-ọwọ́ níbí nílò)

    • Ìfihàn: 2.8" IPS, 320×240, ẹ̀rọ ST7789V, SPI

    • Ifọwọ́kan: GT911 capacitive, ń tọpa àwọn ìka márùn-ún ní àkókò kan

    • Kamẹra: OV3660, 3 megapixel, títí dé 2048×1536

    • Àwọn máíkírófóònù: INMP441 I2S digital mics méjì (ọ̀nà ohùn méjì tòótọ́)

    • Amúlùtí: MAX98357A class-D amplifier, 3.2 W sí 4 Ω

    • Ìfipamọ́: ibi káàdì microSD (ipo SPI)

    • Agbára: USB-C, ibi bátìrí JST, ẹ̀rọ gbigba agbára TP4056, bọ́tìnì agbára

    • Ohun mìíràn lórí board: WS2812B RGB LED, aago gidi PCF8563T tí ń lo bátìrí, àti MAX17048 bátìrí fuel gauge tí a kò kọ sílẹ̀ nínú àwọn ìsọfúnni ìwé àṣẹ

    Àwọn ibi USB-C méjì náà kì í ṣe ohun kan náà. Amúlùtí board ń pín àwọn pin ifihan rẹ̀ (IO19 àti IO20) pẹ̀lú ibi USB ìbílẹ̀, nítorí àwọn pin wọ̀nyẹn ni àwọn laini dátà USB tí ESP32-S3 ti fi sílẹ̀. Nígbà gbogbo gbé àwọn kódù sílẹ̀ kí o sì fi agbára sí i nípasẹ̀ ibi USB-C CH340K (èyí tí ó wà lẹ́gbẹ̀ẹ́ bọ́tìnì RESET), kí o sì fi USB CDC On BootDisabled. Bí o bá lo ibi tí kò tọ́, ohùn yóò ṣiṣẹ́ bí kò ṣe tọ́ tàbí àwọn ìgbé sílẹ̀ yóò kùnà.

    Àwọn ètò Arduino IDE

    Àwọn ètò wọ̀nyí ṣe pàtàkì. Ọ̀pọ̀lọpọ̀ àwọn ìṣòro tí àwọn ènìyàn ń ròyìn nípa board yìí jẹ́ ọ̀kan nínú àwọn wọ̀nyí tí kò tọ́, wọ́n sì tún padà sí ìbẹ̀rẹ̀ nígbà tí o bá yí ẹ̀yà core padà, nítorí náà ṣàyẹ̀wò wọn lẹ́ẹ̀kan sí i lẹ́yìn ìyípadà kọ̀ọ̀kan.

    Ètò

    Iye

    Board

    ESP32S3 Dev Module

    Ẹ̀yà core ESP32

    2.0.17

    PSRAM

    OPI PSRAM

    Ìwọ̀n Flash

    16MB (128Mb)

    Ètò Pínpín

    16M Flash (3MB APP/9.9MB FATFS)

    USB CDC On Boot

    Disabled

    Ìyára Ìgbé sílẹ̀

    921600

    Pa Gbogbo Flash Rẹ́ Kí O Tó Gbé sílẹ̀

    Disabled

    Ibi

    ibi USB-C CH340K

    Lo ESP32 core 2.0.17 lo, kii ṣe 3.x. Espressif yọ awọn awoṣe wiwa oju lori ẹrọ kuro ninu core 3, nitorinaa awọn iṣẹ oju kii yoo ṣajọ nibẹ. Pinning 2.0.17 jẹ ki gbogbo iṣẹ lori oju-iwe yii ṣiṣẹ pẹlu iṣeto kan. Ninu Boards Manager, iwe-ọrọ version jẹ ki o yipada pada ati siwaju nigbakugba ti o ba fẹ.

    Lo GFX Library fun Arduino version 1.5.6, kii ṣe 1.6.x. Awọn itusilẹ 1.6 ti kọ fun ESP32 core 3 ati le duro ni ibẹrẹ lori core 2.0.17. Ti iboju rẹ ba duro dudu lẹhin gbigbe soke, eyi ni ohun akọkọ lati ṣayẹwo.

    Awọn ile-ikawe ti o nilo

    Fi awọn wọnyi sori ẹrọ nipasẹ Tools → Manage Libraries ninu Arduino IDE. Awọn nọmba version ṣe pataki - jọwọ lo awọn ti a ṣe akojọ.

    Ile-ikawe

    Version

    Onkọwe

    GFX Library fun Arduino

    1.5.6

    moononournation

    bb_captouch

    1.3.1

    Larry Bank

    Iṣoro ati atunṣe

    Aami aisan

    Idi ati atunṣe

    Iboju duro dudu

    Version GFX library ti ko tọ (lo 1.5.6) tabi awọn eto board ti ko tọ.

    PSRAM alloc failed tabi aṣiṣe kamẹra 0xffffffff

    Tools → PSRAM ko ṣeto si OPI PSRAM.

    Ko si ohun ti o gbe soke / ko si ibudo COM

    Ibufẹ USB-C ti ko tọ, tabi awakọ CH340 ko fi sori ẹrọ.

    Kamẹra kuna ati ko gba pada lailai

    Laini atunto kamẹra ti so mọ bọtini RESET board, nitorinaa sọfitiwia ko le tun bẹrẹ rẹ. Tẹ RESET. Ti o ba tun kuna, tun gbe okun ribbon kamẹra naa sipo.

    Ṣe igbasilẹ koodu naa

    Aworan Arduino pipe fun iṣẹ yii, pẹlu pins.h ati ohun gbogbo miiran ti o nilo, jẹ ọfẹ lati ṣe igbasilẹ.

    Ṣe igbasilẹ 02_Camera_Touch

    Yọọ rẹ, ṣii faili .ino ninu Arduino IDE, ṣayẹwo awọn eto loke, ki o si gbe soke nipasẹ ibufẹ CH340K USB-C.

    Nibo lati ra board yii

    Awọn ọna asopọ wọnyi jẹ ọna asopọ affiliate. Wọn ko na ọ ni afikun ati pe wọn ṣe iranlọwọ lati ṣe atilẹyin koodu ọfẹ ati awọn ẹkọ lori oju-iwe yii - o ṣeun.

    882-Arduin code for MaTouch AI ESP32S3 2.8in AI Camera: Camera and touch
    Ede: C++
    /*
     * ===========================================================================
     *  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(); }
    }
    

    Fáìlìs📁

    Faili ti a beere (.h)

    • secrets.h
      file for Makerfabs MaTouch AI ESP32S3 2.8" TFT Camera module
      secrets.h 0.01 MB

    Awọn faili miiran

    • pins.h
      pins file for MaTouch AI ESP32S3 2.8" camera LCD touch screen.
      pins.h 0.01 MB

    Sikemi

    • MaTouch_AI 2.8“ MaTouch AI ESP32S3 2.8" TFT ST7789V schematic
      The 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.PDF 0.15 MB