今回は、前回、こいつに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で明るさ調整
}











