「ESP32」カテゴリーアーカイブ

LILYGO T-Embed SI4732:2

今回は、前回、こいつにSW用のFWが上がっていたので、いれてみたが
まったく受信してくれないので、その前にいれていたFMラジオを再インストール

これは、受信できる。

同じ方のオリジナルソースを、自分ようにカスタマイズした。

1.起動時、音量を40
2.起動時、ボリュームの位置(カーソル)
3.起動後、30秒で画面消灯
4.起動後、1時間でスリープ
5.SW長押しでスリープ

ベッドサイドFMラジオなのだ!

寝るときにつける(リセットで起動)

オリジナルソースの提供者は下記のYoutubeで確認できます。
mini SI4732 ESP32ラジオ用の新しいファームウェア

ただし、ソースなので、自分でコンパイルしてアップロードする必要がある。

FW自体の公開はされていない。

ちなみにBunchan747バージョンは以下になる。
Arduino IDEで必要なライブラリをそろえることでビルドできる。
もちろん、オリジナルを入手して、それをコンパイルできるようになったうえで
このソースを使用しないと、以下だけでは何もできない事をご理解ください。

//ORIGINAL SOURCE
//https://github.com/Xinyuan-LilyGO/T-Embed
//MySource
//Bunchan747:https://bunchan.com/2026/02/11/lilygo-t-embed-si47322/
//起動時:音量40
//起動後:30秒五画面消灯
//起動後:1時間でスリープ
//SW長押しでスリープ
//起動時はボリューム変更位置
#include <lvgl.h> //8.4.0 lv_conf.h #if 1 lv_font 10.12.14.16.30=1
#include "ui.h"
#include <Wire.h>
#include <TFT_eSPI.h>
#include <RotaryEncoder.h>
#include <SI4735.h>
#include <EEPROM.h>
#include <esp_sleep.h>
unsigned long previousMillis = 0;       // Stores the last time the action was performed
const unsigned long interval = 500; 

#define BATTERY_PIN 4         // ADC pin (npr. GPIO34 na ESP32)
#define ADC_MAX 4095.0         // 12-bitni ADC na ESP32
#define VREF 3.3               // Referentni napon (obično 3.3V)
#define R1 100000.0            // Gornji otpornik (npr. 100kΩ)
#define R2 100000.0  

#define FM_BAND_TYPE 0
#define MW_BAND_TYPE 1
#define SW_BAND_TYPE 2
#define LW_BAND_TYPE 3

#define LSB 0
#define USB 1

// ADD Bunchan747
#define START_FREQ 7000   // 70Mhz 
#define END_FREQ 10800    // 108<hz
const int btn = 0;
bool pressed = false;
unsigned long t = 0;
unsigned long bootTime = 0;
unsigned long lastActivityTime;
//

int whole,deci;

int searchDir=10;

int brightness=100;
long n=9660;
int rssi=0;
int snr =0;
 String station;
    String text;
//rotary encoder pins
#define PIN_IN1 2
#define PIN_IN2 1
#define PIN_LCD_BL    15
#define RESET_PIN     16           
#define AUDIO_MUTE    17           

// Enconder PINs
#define ENCODER_PIN_A  2           // GPIO01 
#define ENCODER_PIN_B  1

RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
String chosenLBL[4]={"VOLUME","TUNE","TUNE","SEARCH"};

int freq[4]={0,9,2,7};
int digMax[7]={3,9,9,9,9,2,63};
int chosen=0;
int volume=40; //0= volume 1 =freq 2=seek
int deb=0;
bool change=1;
bool change2=0;
bool fChange=0;
bool seek=false;
int pos[7]={-125,62,19,113,0,0,0};

// I2C bus pin on Lilygo T-Display
#define ESP32_I2C_SDA 18           // GPIO43 
#define ESP32_I2C_SCL 8     

SemaphoreHandle_t mutex; 

SI4735 rx;

#define LV_BUF_SIZE (320 * 40)  // ili npr. 320 * 40 = 12800
TFT_eSPI tft = TFT_eSPI();


// === LVGL flush funkcija ===
static void lv_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors((uint16_t *)&color_p->full, w * h, true);
    lv_disp_flush_ready(disp);
}

// === LVGL inicijalizacija ekrana ===
void lvgl_init_display()
{
    lv_init();

    tft.begin();
    tft.setRotation(3);
   
    tft.fillScreen(TFT_BLACK);

    static lv_color_t *buf1 = (lv_color_t *)ps_malloc(sizeof(lv_color_t) * LV_BUF_SIZE);
    static lv_color_t *buf2 = (lv_color_t *)ps_malloc(sizeof(lv_color_t) * LV_BUF_SIZE);
    assert(buf1 && buf2);

    static lv_disp_draw_buf_t draw_buf;
    lv_disp_draw_buf_init(&draw_buf, buf1, buf2, LV_BUF_SIZE);

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 320;  // tvoja širina
    disp_drv.ver_res = 170;  // tvoja visina
    disp_drv.flush_cb = lv_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);
}

void setup()
{
  mutex = xSemaphoreCreateMutex(); 
  Serial.begin(115200);
  Serial.println("lets go");
  
  // ADD Bunchan747
  bootTime = millis(); // 起動時のタイムスタンプ
  // バックライトPWM
  // ledcSetup(0, 5000, 8);             // チャンネル0, 周波数5kHz, 8bit解像度
  // ledcAttachPin(PIN_LCD_BL, 0);     // GPIO 15 にPWM割り当て


  pinMode(46, OUTPUT);
  digitalWrite(46, HIGH);

  Wire.begin(ESP32_I2C_SDA, ESP32_I2C_SCL);
  lvgl_init_display();
  ui_init();

  rx.setI2CFastModeCustom(100000);
  
  int16_t si4735Addr = rx.getDeviceI2CAddress(RESET_PIN); // Looks for the I2C bus address and set it.  Returns 0 if error

  if ( si4735Addr == 0 ) {
    tft.setTextSize(2);
    tft.setTextColor(TFT_RED, TFT_BLACK);
    tft.println("Si4735 not detected");
    while (1);
  }  
  
  EEPROM.begin(3);
  n=EEPROM.read(0)*100+EEPROM.read(1)*10;

  if(n<START_FREQ || n>END_FREQ)
  n=9100;

  rx.setup(RESET_PIN, FM_BAND_TYPE);
  rx.setAudioMuteMcuPin(AUDIO_MUTE);    
  delay(300);
  rx.setVolume(volume);
  rx.setFM(START_FREQ, END_FREQ, n, 10);
  longToFreqDigitsFM();
   


  xTaskCreatePinnedToCore(core1Task, "Core1Task", 20480, NULL, 2, NULL, 1); // Adjust stack size
  xTaskCreatePinnedToCore(core2Task, "Core2Task", 20480, NULL, 1, NULL, 0);

}

void core2Task(void *pvParameters)
{
  
  while(1)
  {
  static int pos = 0;

  if(seek)
  {
    if (xSemaphoreTake(mutex, portMAX_DELAY)) { 
    n=n+searchDir;
    if(n>END_FREQ) n=START_FREQ;
     if(n<START_FREQ) n=END_FREQ;
  //digits[5]=0;
     rx.setFrequency(n);
  
     delay(62);                                   // Kratko čekanje da se signal stabilizira
     rx.getCurrentReceivedSignalQuality(); 
     rssi = rx.getCurrentRSSI(); 
     snr  = rx.getCurrentSNR();
    if(rssi>25) seek=false; longToFreqDigitsFM();
      change=1;
      xSemaphoreGive(mutex); 
    }
  }

  unsigned long currentMillis = millis(); // Get the current time
  if (currentMillis - previousMillis >= interval && seek==false) {
    previousMillis = currentMillis; // Update the last execution time
   if (xSemaphoreTake(mutex, portMAX_DELAY)) { 
    change2=1;
     rx.getCurrentReceivedSignalQuality(); 
     rssi = rx.getCurrentRSSI(); 
     snr  = rx.getCurrentSNR();
    xSemaphoreGive(mutex); 
   }
  }

  encoder.tick();

  int newPos = encoder.getPosition();
  if (xSemaphoreTake(mutex, portMAX_DELAY)) { 

  if (!digitalRead(btn)) {
    if (!pressed) {
      t = millis(); pressed = true;
    } else if (millis() - t > 500) {
      esp_deep_sleep_start(); // 長押しでSleep
    }
  } else if (pressed) {
    if (millis() - t < 1000) {
      chosen = (chosen + 1) % 4;
      seek = false; change = 1;
    }
    lastActivityTime = millis();            // アクティビティ更新
    digitalWrite(PIN_LCD_BL, HIGH);         // バックライトON
    pressed = false;
  }

//  if(digitalRead(0)==0)
//  {
//    delay(500);
//    if(digitalRead(0)==0)
//    {
//      // Deep Sleep開始
//      esp_deep_sleep_start();
//    }
//
  //  if(deb==0)
//      {
//        deb=1; chosen++; if(chosen>3) chosen=0;
//        seek=false;
//        change=1;
//      }
//  }else deb=0;

  if(!seek){

  

    
  if (pos != newPos) {

        if(chosen==3)
    {
    if(newPos<pos)
    {seek=1; searchDir=10;}

      if(newPos>pos)
    {seek=1; searchDir=-10;}

    
    change=1;
    }
    
    if(chosen==2)
    {
    if(newPos<pos)
    {n=n+100; if(n>END_FREQ) n=START_FREQ; }
    if(newPos>pos)
    {n=n-100; if(n<START_FREQ) n=END_FREQ; }
    fChange=1;
    change=1;
    }
   
    if(chosen==1)
    {
    if(newPos<pos)
    {n=n+10; if(n>END_FREQ) n=START_FREQ; }
    if(newPos>pos)
    {n=n-10; if(n<START_FREQ) n=END_FREQ; }
    fChange=1;
    change=1;
    }

    if(chosen==0)
    {
      if(newPos<pos)
        {volume++; if(volume>63) volume=0; }
        if(newPos>pos)
          {volume--; if(volume<0) volume=63; }
      
        change=1;
      }

      pos = newPos;
      lastActivityTime = millis();            // アクティビティ更新
      digitalWrite(PIN_LCD_BL, HIGH);         // バックライトON

    } 
    if(change){
   

     if(fChange)
     {
      rx.setFrequency(n);
      longToFreqDigitsFM();
      delay(25);                                   // wait for signal stabilize
      rx.getCurrentReceivedSignalQuality(); 
      rssi = rx.getCurrentRSSI(); 
      snr  = rx.getCurrentSNR();
      whole = n / 100;      // 96
      deci = (n % 100) / 10;  // 7
      EEPROM.write(0,whole);
      EEPROM.write(1,deci);
      EEPROM.commit();
      //text = rx.getRdsText();  
      fChange=0;
     }


   if(chosen==0)
   rx.setVolume(volume);
    } }
   xSemaphoreGive(mutex);
   }
   vTaskDelay(pdMS_TO_TICKS(2)); 
  }
}


void core1Task(void *pvParameters)
{
  
  while(1)
  {
    lv_timer_handler();
        if (xSemaphoreTake(mutex, portMAX_DELAY)) { 
        if(change){
        lv_label_set_text(ui_volLBL,String(volume).c_str());
        if(freq[0]!=0)
        lv_label_set_text(ui_dig1,String(freq[0]).c_str());
        else
        lv_label_set_text(ui_dig1,"");

        lv_label_set_text(ui_chosen,chosenLBL[chosen].c_str());
        lv_label_set_text(ui_dig2,String(freq[1]).c_str());
        lv_label_set_text(ui_dig3,String(freq[2]).c_str());
        lv_label_set_text(ui_dig4,String(freq[3]).c_str());
      
       
        lv_label_set_text(ui_rssi,String(rssi).c_str());
        lv_label_set_text(ui_sn,String(snr).c_str());
      
        lv_bar_set_value(ui_Bar1, rssi, LV_ANIM_OFF);  
        lv_bar_set_value(ui_Bar3, snr, LV_ANIM_OFF);  

        if(chosen==5)
        lv_obj_clear_flag(ui_selectedSSB, LV_OBJ_FLAG_HIDDEN); 
        else
        lv_obj_add_flag(ui_selectedSSB, LV_OBJ_FLAG_HIDDEN);  


       
        int per=readBatteryVoltage()*10;
        lv_bar_set_value(ui_Bar2, per, LV_ANIM_OFF);  
        lv_obj_set_pos(ui_selected, pos[chosen], 76);
        change=false;
        }
        if(change2)
        {
          lv_label_set_text(ui_rssi,String(rssi).c_str());
          lv_label_set_text(ui_sn,String(snr).c_str());
          lv_bar_set_value(ui_Bar1, rssi, LV_ANIM_OFF);  
          lv_label_set_text(ui_voltage,String(readBatteryVoltage()).c_str());
          change2=0;
        }
        xSemaphoreGive(mutex); 
        } 
      
      
   
  vTaskDelay(pdMS_TO_TICKS(10)); 
  }
}

void loop()
{
  // バックライト消灯(1分後)
  if (millis() - lastActivityTime > 60000) {
    digitalWrite(PIN_LCD_BL, LOW);  // 🔕 バックライトOFF
    // setBrightness(20); // PWM
  }
  // ⏱ 1時間経過チェック
  if (millis() - bootTime > (3600000 / 2)) {
    esp_deep_sleep_start(); // 自動でスリープ
  }
  delay(1000);  // CPU負荷軽減しつつループ継続
  // vTaskDelete(NULL);
  // Preporučena mala pauza
}

float readBatteryVoltage() {
  int raw = analogRead(BATTERY_PIN);
  float voltageAtPin = (raw / ADC_MAX) * VREF;
  float batteryVoltage = voltageAtPin * 2.1; // koristi isti faktor kao originalni kod
  return batteryVoltage;
}

void longToFreqDigitsFM() {
    int temp = n / 10;
    freq[0] = (temp < 1000) ? 0 : (temp / 1000);          
    freq[1] = (temp / 100) % 10;                      
    freq[2] = (temp / 10) % 10;                       
    freq[3] = temp % 10;             
}

void setBrightness(uint8_t level) {
  ledcWrite(0, level);            // 0〜255で明るさ調整
}

BCL:SW(ats-mini)

今日は、新しいFW(V2.33)にした状態でBCL

まず、朝鮮の声が聴こえた。 9650khz 16:00~

アンテナはヤフオクで入手した2.5mロッドアンテナのエンドフード
3.7~54Mhz対応で聞いてみた。(6998円:個人自作品)

設置はカメラの三脚に固定して室内で聞いている。
※アンテナベースもヤフオク(2700円:個人自作)

綺麗に受信できている。

始めにATS-miniのコントローラをUSBで接続していたが、どうもノイズが
ひどいのでPCとの接続をやめた。

USBアイソレータがいるね・・・

もう少ししたら、KBSも始まる・・・

LILYGO T-Embed SI4732

これもATS-mimiと同じようにESP32でSi4732を制御するRADIOだ

LILYGO T-Embed SI4732 – LILYGO Wiki

有志が開発したファームを入れて楽しめる。

(629) Volos Projects – YouTube
VolosMiniRadio/VolosRadio.zip at main · VolosR/VolosMiniRadio · GitHub
新登場!カスタムESP32 + SI4732 SSB無線ファームウェア!(フルインストール&デモ)

自分でもファームを開発できる。
サンプルソースが公開されていたので、環境さえあれば
コンパイルして更新できる。

今回はVolosさんのFWを入れてSWが聴けるか確認します。

ATS-mini RADIO

これが発売されてるのを見つけてすぐポチッた・・・

2台購入したが、1台は保管したまま・・・どこだ

ATS-miniのバージョンが今はVer4?結構進化している。

自分のは初号機だと思う

今回ファームの最新版があるのを見つけて更新してみた。

ATS Mini documentation

Releases · esp32-si4732/ats-mini

Ver.2.33を入れてみた

※QSPI版

なんか、面白い機能が実装されている。
外部コントロール機能
PCでコントロール、スクリーンショットもできる 楽しいATS MINI
GitHub – mattafaak/miniradio4: ATS-Mini Radio Controller
ATS Mini Controller

RADIO-mini(ATS-mini)

いま界隈で熱い?MiniRadio(ESP32-S3 + Si4732)

New ファームが出たとYoutubeで発見したので、早速実行だ!

手順に従い、進める・・・

自分のは
ESP32-S3 WROOM-1 MCN8R2 を一覧から、対象のBINを調べておく
Githibから対象のBINをDownloadして
ESP Toolでアップロード開始!

うまくいきました!

Version 2.28

LILYGO:T-embed(SI4732)

LILYGOのxT-embedのRADIOバージョンを入手

SI4732が実装されていて、ラジオが受信できます。
短波・中波・VHF・・・

オリジナルのファームは、他でも目にするRADIO Miniの画面と同じなので
同じファームなのかな?

じつは、そのRADIO Miniは持っている。

今回はLILYGOの製品ですが、ファームをいれてみたが、いまいち?

VOLOSというところで、FMラジオ専用のファームを公開している。

しかもオープンソースで・・・すごい

早速、ダウンロードしてビルドしてみたが、いろいろエラーがでるでるでる・・・

いろいろとアドバイスをもらいながら、なんとかビルドでききた。

アップロードして見事FMラジオの完成!

LILYGO T-Embed SI4732 – LILYGO Wiki

(1577) Volos Projects – YouTube

詳細はまた今度・・・

ちなみにAmzonでも購入できます。
Amazon.co.jp: LILYGO T-Embed SI4732 ESP32-S3 マイクスピーカー TTGO 開発ボード 1.9インチ ST7789V IPS TFT LCD付き : パソコン・周辺機器

自分はLilygoで購入しました。

しばらく遊べそう・・・

MyDns.jp更新装置

MyDnsでDDNSを登録して取得しているが、IPの更新が手間なので
M5StickCPlusを使った更新装置の記事を見た・・・どこだっけ?

今使っているソース(自前用に改修したやつ)
XXXXXXなどWifiやMyDns.jpの情報を変更して使用できます。

#include <M5StickCPlus.h>
#include <HTTPClient.h>
#include <NTPClient.h>
#include <Time.h>
#include <TimeLib.h>
#include <FS.h>
#include <WiFi.h>
#include <WiFiClient.h>

#define SERIAL_SPEED 115200

// wifiの設定(my)
const char* ssid     = "XXXXXXXXXXX"; // WiFiのSSID
const char* password = "XXXXXXXXXXX"; // WiFiのパスワード

// mydns info.(xxxxx.mydns.jp)
const char* id="mydnsXXXXXX"; //マスターID
const char* pw="XXXXXXXXXXX"; //パスワード

//  NTP用
WiFiUDP ntpUDP;
const char *NTP_SERVER = "ntp.nict.jp";
const int TIME_OFFSET = 32400;  // UTC+9h (JST)
NTPClient timeClient(ntpUDP, NTP_SERVER, TIME_OFFSET);
const unsigned long NTP_INTERVAL_TIME = 86000000;  // 24h毎に更新1000 24h=86400000
unsigned long ntp_interval = 0;
unsigned long pasttime = 0;

String getmsg = "";

void setup() {
  pinMode(M5_BUTTON_HOME, INPUT);
  Serial.begin(SERIAL_SPEED);
  Serial.println("");
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Axp.ScreenBreath(100);
  M5.Lcd.fillScreen(BLACK);

  // connect to WiFi
  M5.Lcd.setCursor(0, 0, 1);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("WiFi connecting");

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) { // 1秒おきに接続状態を確認
    delay(1000);
    M5.Lcd.print(".");
  }
  M5.Lcd.println("\nconnected");

  M5.Lcd.print("");
  //M5.Lcd.print("WiFi Connected.");
  M5.Lcd.print("SSID:");
  M5.Lcd.print(WiFi.SSID());
  M5.Lcd.print("\n");
  M5.Lcd.print("IP:");
  M5.Lcd.print(WiFi.localIP());
  M5.Lcd.print("\n");

  // NTP
  timeClient.begin();
  timeClient.update();
  setTime(timeClient.getEpochTime());
  M5.Lcd.print("NTP st.");
  ntp_interval = millis();
  M5.Lcd.print(timeClient.getFormattedTime());
  M5.Lcd.print("\n");


  delay(3000);
  M5.Lcd.print("First Req");
  M5.Lcd.print("\n");
  notice();
}

void loop() {
  //M5ボタンでモード切替
  if(digitalRead(M5_BUTTON_HOME) == LOW){
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0, 1);
    M5.Lcd.setTextFont(4);
    M5.Lcd.setTextSize(1);
    M5.Lcd.print("\n");
    M5.Lcd.print(" Update Now!\n");
    M5.Lcd.print("\n");
    M5.Lcd.print("\n");
    notice(); 
    delay(5000);
  }

  // MDNS.update();
  // 定期的に時刻を合わせて、IPを通知。
  pasttime = millis() - ntp_interval;
  if (pasttime > NTP_INTERVAL_TIME) {
    //M5.Lcd.fillScreen(BLACK);
    M5.Lcd.fillRect(0,0,300,70,BLACK); // 塗りつぶし left, top, witdh, height
    M5.Lcd.setCursor(0, 0, 1);
    M5.Lcd.setTextFont(4);
    M5.Lcd.setTextSize(1);
    M5.Lcd.print("\n");
    M5.Lcd.print(" Update Now!\n");
    M5.Lcd.print("\n");
    M5.Lcd.print("\n");
    delay(5000);
    timeClient.update();
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0, 1);
    M5.Lcd.setTextColor(GREEN);
    M5.Lcd.setTextFont(7);
    M5.Lcd.setTextSize(1);
    M5.Lcd.print(timeClient.getFormattedTime());
    M5.Lcd.print("\n");
    M5.Lcd.print("\n");
    notice();
    ntp_interval = millis();
  }else{
    if(getmsg="200"){
      M5.Lcd.fillRect(0,0,300,70,BLACK); // 塗りつぶし left, top, witdh, height
    }else{
      M5.Lcd.fillScreen(RED);
    }
    M5.Lcd.setCursor(0, 0, 1);
    M5.Lcd.setTextColor(GREEN);
    M5.Lcd.setTextFont(7);
    M5.Lcd.setTextSize(1);
    M5.Lcd.print(timeClient.getFormattedTime());
    M5.Lcd.print("\n");
    M5.Lcd.setTextFont(4);
    M5.Lcd.setTextSize(1);
    M5.Lcd.print(NTP_INTERVAL_TIME / 1000);
    M5.Lcd.print(" > ");
    M5.Lcd.print(pasttime / 1000);
    M5.Lcd.print("\n");
    M5.Lcd.print("http GET= ");
    M5.Lcd.print(getmsg);
    M5.Lcd.print("\n");
    M5.Lcd.print("Push M5:update");
    M5.Lcd.print("\n");
    delay(1000);    
  }
}

void notice() {
  timeClient.update();
  // 通知
  WiFiClient client;
  HTTPClient http;
  http.begin(client, "http://ipv4.mydns.jp/login.html");
  http.setAuthorization(id, pw);
  M5.Lcd.setTextFont(4);
  M5.Lcd.setTextSize(1);
  M5.Lcd.print("http GET= ");
  getmsg = http.GET();
  M5.Lcd.print(getmsg);
  M5.Lcd.print("\n");
  timeClient.update();
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0, 1);
  M5.Lcd.setTextColor(GREEN);
  M5.Lcd.setTextFont(7);
  M5.Lcd.setTextSize(1);
  M5.Lcd.print(timeClient.getFormattedTime());
  M5.Lcd.print("\n");
  M5.Lcd.print("\n");
  ntp_interval = millis();
}

Arduino IDE 2.3.6

ESP32-WROVER-CAM

FREENOVE.COMのESP32-WROVER-CAMを購入した。

amazon

FREENOVE

Tutorial

バージョンはv3.0でSdカードスロット付きでUSB-Cでした。
2480円でした。
ESP32-WROVER-Eでした。
技適:201-20403
模範品もでているらしい。(もちろん技適なし)

たまねぎブログ」さんが纏めてくださっているサイト
あろしーど」さんが纏めてくださっているサイト

サンプルをいくつか試してみた

・ESP_Mail_Client
起動時にカメラ画像をGmailで送信するサンプル
準備:Googleアカウント作成、アプリパスワード取得、Wifi(SSID、パスワード)
送信先メールアドレス、Tutorialからサンプルソース等(ESP-Mail-Client)※最新版

//追加コード
camCfg.grab_mode = CAMERA_GRAB_LATEST;
//画質設定
sensor_t *sensor = esp_camera_sensor_get();
if (!sensor) {
Serial.println(“Failed to get camera sensor!”);
return;
}
sensor->set_contrast(sensor, 1); // コントラスト
sensor->set_brightness(sensor, 0); // 明るさ
sensor->set_saturation(sensor, -1); // 彩度

ん?画像がミラー?設定を調べないとね・・・

追加コード
// 画像の水平反転を無効にする
sensor->set_hmirror(sensor, 1); // 鏡像(0: 無効, 1: 有効)




・ESP_WebCamera
カメラ画像をWebで参照する

サンプルがあるので、いろいろ遊べそう

目標は防犯カメラというか、野良猫監視カメラ的な・・・