#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "esp_log.h"
#define TAG "MOCHI_DAZAI"
// پینهای I2C برای ESP32-C3
#define I2C_MASTER_SCL_IO 6 // GPIO 6 برای SCL
#define I2C_MASTER_SDA_IO 5 // GPIO 5 برای SDA
#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_FREQ_HZ 400000 // 400kHz
#define I2C_MASTER_TX_BUF_DISABLE 0
#define I2C_MASTER_RX_BUF_DISABLE 0
// آدرس OLED SSD1306
#define OLED_ADDRESS 0x3C
// دستورات SSD1306
#define OLED_CMD_DISPLAY_OFF 0xAE
#define OLED_CMD_DISPLAY_ON 0xAF
#define OLED_CMD_SET_START_LINE 0x40
// تابع ارسال دستور به OLED
static esp_err_t oled_send_cmd(uint8_t cmd) {
i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
i2c_master_start(cmd_handle);
i2c_master_write_byte(cmd_handle, (OLED_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd_handle, 0x00, true); // Control byte: Co=0, D/C#=0
i2c_master_write_byte(cmd_handle, cmd, true);
i2c_master_stop(cmd_handle);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd_handle, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd_handle);
return ret;
}
// تابع ارسال داده به OLED
static esp_err_t oled_send_data(uint8_t *data, size_t len) {
i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
i2c_master_start(cmd_handle);
i2c_master_write_byte(cmd_handle, (OLED_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd_handle, 0x40, true); // Control byte: Co=0, D/C#=1
i2c_master_write(cmd_handle, data, len, true);
i2c_master_stop(cmd_handle);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd_handle, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd_handle);
return ret;
}
// تابع تنظیم موقعیت برای نوشتن
static void oled_set_position(uint8_t page, uint8_t column) {
oled_send_cmd(0xB0 + page); // Set page address
oled_send_cmd(column & 0x0F); // Set lower column address
oled_send_cmd(0x10 | ((column >> 4) & 0x0F)); // Set higher column address
}
// تابع پاک کردن صفحه
static void oled_clear(void) {
uint8_t zero_buffer[128];
memset(zero_buffer, 0, sizeof(zero_buffer));
for (int page = 0; page < 8; page++) {
oled_set_position(page, 0);
oled_send_data(zero_buffer, sizeof(zero_buffer));
}
}
// مقداردهی اولیه I2C
static void i2c_master_init(void) {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf));
ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode,
I2C_MASTER_RX_BUF_DISABLE,
I2C_MASTER_TX_BUF_DISABLE, 0));
}
// مقداردهی اولیه OLED
static void oled_init(void) {
vTaskDelay(pdMS_TO_TICKS(100));
// دنباله دستورات راهاندازی SSD1306
oled_send_cmd(0xAE); // Display off
oled_send_cmd(0xD5); // Set display clock divide ratio/oscillator frequency
oled_send_cmd(0x80);
oled_send_cmd(0xA8); // Set multiplex ratio
oled_send_cmd(0x3F); // 1/64 duty
oled_send_cmd(0xD3); // Set display offset
oled_send_cmd(0x00); // No offset
oled_send_cmd(0x40); // Set start line
oled_send_cmd(0x8D); // Charge pump setting
oled_send_cmd(0x14); // Enable charge pump
oled_send_cmd(0x20); // Memory mode
oled_send_cmd(0x00); // Horizontal addressing mode
oled_send_cmd(0xA1); // Segment remap
oled_send_cmd(0xC8); // COM output scan direction
oled_send_cmd(0xDA); // Set COM pins hardware configuration
oled_send_cmd(0x12);
oled_send_cmd(0x81); // Set contrast control
oled_send_cmd(0xCF); // Contrast value
oled_send_cmd(0xD9); // Set pre-charge period
oled_send_cmd(0xF1);
oled_send_cmd(0xDB); // Set VCOMH deselect level
oled_send_cmd(0x40);
oled_send_cmd(0xA4); // Entire display ON
oled_send_cmd(0xA6); // Set normal display
oled_send_cmd(0xAF); // Display ON
ESP_LOGI(TAG, "OLED initialized successfully");
}
// طراحی چشمهای باز موچی دازای
static void draw_mochi_eyes_open(void) {
uint8_t buffer[128];
// پاک کردن صفحه
oled_clear();
// طراحی چشم چپ (موقعیت: صفحه 2-3، ستون 30-45)
for (int page = 2; page <= 3; page++) {
memset(buffer, 0, sizeof(buffer));
if (page == 2) {
// بالای چشم
for (int col = 30; col <= 45; col++) {
buffer[col] = 0xFF;
}
} else if (page == 3) {
// وسط چشم (با مردمک)
for (int col = 30; col <= 45; col++) {
if (col >= 33 && col <= 42) {
buffer[col] = 0x00; // مردمک خالی
} else {
buffer[col] = 0xFF; // دور چشم
}
}
}
oled_set_position(page, 0);
oled_send_data(buffer, sizeof(buffer));
}
// طراحی چشم راست (موقعیت: صفحه 2-3، ستون 80-95)
for (int page = 2; page <= 3; page++) {
memset(buffer, 0, sizeof(buffer));
if (page == 2) {
// بالای چشم
for (int col = 80; col <= 95; col++) {
buffer[col] = 0xFF;
}
} else if (page == 3) {
// وسط چشم (با مردمک)
for (int col = 80; col <= 95; col++) {
if (col >= 83 && col <= 92) {
buffer[col] = 0x00; // مردمک خالی
} else {
buffer[col] = 0xFF; // دور چشم
}
}
}
oled_set_position(page, 0);
oled_send_data(buffer, sizeof(buffer));
}
// طراحی دهن کوچک (صفحه 5، ستون 60-70)
memset(buffer, 0, sizeof(buffer));
for (int col = 60; col <= 70; col++) {
buffer[col] = 0xFF;
}
oled_set_position(5, 0);
oled_send_data(buffer, sizeof(buffer));
ESP_LOGI(TAG, "Mochi eyes OPEN drawn");
}
// طراحی چشمهای نیمه باز (در حال پلک زدن)
static void draw_mochi_eyes_half(void) {
uint8_t buffer[128];
// پاک کردن فقط قسمت چشمها
for (int page = 2; page <= 3; page++) {
memset(buffer, 0, sizeof(buffer));
oled_set_position(page, 0);
oled_send_data(buffer, sizeof(buffer));
}
// طراحی چشمهای نیمه باز (خط باریک)
memset(buffer, 0, sizeof(buffer));
for (int col = 30; col <= 45; col++) {
buffer[col] = 0xFF; // چشم چپ
}
for (int col = 80; col <= 95; col++) {
buffer[col] = 0xFF; // چشم راست
}
oled_set_position(2, 0);
oled_send_data(buffer, sizeof(buffer));
ESP_LOGI(TAG, "Mochi eyes HALF drawn");
}
// طراحی چشمهای بسته
static void draw_mochi_eyes_closed(void) {
uint8_t buffer[128];
// پاک کردن قسمت چشمها
for (int page = 2; page <= 3; page++) {
memset(buffer, 0, sizeof(buffer));
oled_set_position(page, 0);
oled_send_data(buffer, sizeof(buffer));
}
// طراحی خط چشمهای بسته (در صفحه 3)
memset(buffer, 0, sizeof(buffer));
for (int col = 30; col <= 45; col++) {
buffer[col] = 0xFF; // چشم چپ بسته
}
for (int col = 80; col <= 95; col++) {
buffer[col] = 0xFF; // چشم راست بسته
}
oled_set_position(3, 0);
oled_send_data(buffer, sizeof(buffer));
// طراحی دهن خندان (چون چشمها بستهاند)
memset(buffer, 0, sizeof(buffer));
for (int col = 58; col <= 72; col++) {
if (col == 58 || col == 72) buffer[col] = 0xFF;
if (col >= 60 && col <= 70) buffer[col] = 0xFF;
}
oled_set_position(5, 0);
oled_send_data(buffer, sizeof(buffer));
ESP_LOGI(TAG, "Mochi eyes CLOSED drawn");
}
// طراحی چهره شاد موچی دازای
static void draw_mochi_happy(void) {
oled_clear();
uint8_t buffer[128];
// چشمهای گرد و بزرگ
for (int page = 2; page <= 4; page++) {
memset(buffer, 0, sizeof(buffer));
if (page == 2 || page == 4) {
// بالا و پایین چشم
for (int col = 30; col <= 45; col++) {
if (col >= 32 && col <= 43) buffer[col] = 0xFF;
}
for (int col = 80; col <= 95; col++) {
if (col >= 82 && col <= 93) buffer[col] = 0xFF;
}
} else if (page == 3) {
// وسط چشم با مردمک
for (int col = 30; col <= 45; col++) {
if (col == 30 || col == 45) buffer[col] = 0xFF;
if (col >= 37 && col <= 38) buffer[col] = 0xFF; // مردمک
}
for (int col = 80; col <= 95; col++) {
if (col == 80 || col == 95) buffer[col] = 0xFF;
if (col >= 87 && col <= 88) buffer[col] = 0xFF; // مردمک
}
}
oled_set_position(page, 0);
oled_send_data(buffer, sizeof(buffer));
}
// دهن خندان بزرگ
memset(buffer, 0, sizeof(buffer));
for (int col = 50; col <= 80; col++) {
if (col == 50 || col == 80) buffer[col] = 0xFF;
if (col >= 55 && col <= 75) buffer[col] = 0xFF;
}
oled_set_position(5, 0);
oled_send_data(buffer, sizeof(buffer));
// گونههای سرخ (نقاط)
memset(buffer, 0, sizeof(buffer));
buffer[25] = 0x18;
buffer[26] = 0x18;
buffer[85] = 0x18;
buffer[86] = 0x18;
oled_set_position(4, 0);
oled_send_data(buffer, sizeof(buffer));
ESP_LOGI(TAG, "Mochi HAPPY face drawn");
}
// انیمیشن پلک زدن
static void mochi_blink_animation(void) {
ESP_LOGI(TAG, "Starting blink animation");
// حالت اول: چشمها باز
draw_mochi_eyes_open();
vTaskDelay(pdMS_TO_TICKS(2000));
// انیمیشن پلک زدن
for (int i = 0; i < 2; i++) {
// نیمه بسته
draw_mochi_eyes_half();
vTaskDelay(pdMS_TO_TICKS(100));
// کاملاً بسته
draw_mochi_eyes_closed();
vTaskDelay(pdMS_TO_TICKS(150));
// نیمه بسته
draw_mochi_eyes_half();
vTaskDelay(pdMS_TO_TICKS(100));
// باز
draw_mochi_eyes_open();
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// حالتهای مختلف چهره
static void mochi_expressions(void) {
ESP_LOGI(TAG, "Showing different expressions");
// حالت عادی
draw_mochi_eyes_open();
vTaskDelay(pdMS_TO_TICKS(2000));
// پلک زدن
mochi_blink_animation();
// حالت شاد
draw_mochi_happy();
vTaskDelay(pdMS_TO_TICKS(3000));
// بازگشت به حالت عادی
draw_mochi_eyes_open();
vTaskDelay(pdMS_TO_TICKS(1000));
}
void app_main(void) {
ESP_LOGI(TAG, "Starting Mochi Dazai Eyes Animation");
// راهاندازی I2C
i2c_master_init();
ESP_LOGI(TAG, "I2C initialized");
// راهاندازی OLED
oled_init();
// پاک کردن صفحه
oled_clear();
ESP_LOGI(TAG, "Display ready! Starting animations...");
int animation_counter = 0;
while (1) {
ESP_LOGI(TAG, "Animation cycle #%d", ++animation_counter);
// نمایش انیمیشنهای مختلف
mochi_expressions();
// هر 3 چرخه یکبار مکث بیشتر
if (animation_counter % 3 == 0) {
ESP_LOGI(TAG, "Taking a longer break...");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
}