#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_adc/adc_oneshot.h"
#include "lcd1602.h"
#include "esp_random.h"
#define I2C_HOST I2C_NUM_1
#define SDA_PIN 22
#define SCL_PIN 21
#define CLK_SPEED (100000)
#define SCREEN_ADDR 0x27
#define ADC_UNIT ADC_UNIT_1
#define ADC_CHANNEL ADC_CHANNEL_4
#define SUBMIT_PIN 14
#define RED_PIN 4
#define GREEN_PIN 2
#define STATE_INPUT 1
#define STATE_RESULT 2
static lcd_i2c_handle_t *display = NULL;
static adc_oneshot_unit_handle_t adc_handle;
static QueueHandle_t valQueue;
static QueueHandle_t ansQueue;
static uint16_t machine_value;
static uint16_t user_value;
static char state = STATE_INPUT;
void init_led(void) {
gpio_set_direction(RED_PIN, GPIO_MODE_OUTPUT);
gpio_set_direction(GREEN_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(RED_PIN, 1);
gpio_set_level(GREEN_PIN, 1);
}
void init_btn(void) {
gpio_set_direction (SUBMIT_PIN, GPIO_MODE_INPUT);
gpio_set_pull_mode (SUBMIT_PIN, GPIO_PULLUP_ONLY);
gpio_set_intr_type (SUBMIT_PIN, GPIO_INTR_NEGEDGE);
}
void init_input(void) {
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle));
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_12,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL, &config));
}
void init_i2c(void) {
i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = SDA_PIN,
.scl_io_num = SCL_PIN,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = CLK_SPEED,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_HOST, &i2c_conf));
ESP_ERROR_CHECK(i2c_driver_install(I2C_HOST, I2C_MODE_MASTER, 0, 0, 0));
}
void init_lcd(void) {
display = (lcd_i2c_handle_t*) malloc(sizeof(lcd_i2c_handle_t));
lcd_i2c_handle_t tmp = {
.address = SCREEN_ADDR,
.num = I2C_HOST,
.backlight = 1,
.size = DISPLAY_16X02
};
*display = tmp;
lcd_i2c_init(display);
}
void vTaskInput(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
int adc_raw;
uint16_t value;
while (1) {
adc_oneshot_read(adc_handle, ADC_CHANNEL, &adc_raw);
value = 1 + (uint16_t) round(adc_raw / 455.0);
xQueueSendToBackFromISR(valQueue, &value, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
// printf("value=%d\n\r", value);
vTaskDelay(pdMS_TO_TICKS(100));
}
vTaskDelete(NULL);
}
void vTaskPrint(void) {
uint16_t value;
while (1) {
if (xQueueReceive(valQueue, &value, pdMS_TO_TICKS(10000)) == pdPASS) {
vTaskDelay(pdMS_TO_TICKS(50));
if (state == STATE_INPUT) {
user_value = value;
lcd_i2c_cursor_set(display, 0, 0);
lcd_i2c_print(display, "Value: %2d ", value);
}
}
}
vTaskDelete(NULL);
}
void blink(int pin) {
vTaskDelay(pdMS_TO_TICKS(20));
gpio_set_level(pin, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
gpio_set_level(pin, 1);
}
void generate_random() {
machine_value = 1 + (uint16_t) round(esp_random() % 10);
}
void vTaskAnswer(void) {
uint16_t value;
while (1) {
if (xQueueReceive(ansQueue, &value, pdMS_TO_TICKS(10000)) == pdPASS) {
state = STATE_RESULT;
vTaskDelay(pdMS_TO_TICKS(100));
if (value == machine_value) {
lcd_i2c_clear(display);
lcd_i2c_cursor_set(display, 0, 0);
lcd_i2c_print(display, "Correct!");
blink(GREEN_PIN);
generate_random();
} else if (value > machine_value) {
lcd_i2c_clear(display);
lcd_i2c_cursor_set(display, 0, 0);
lcd_i2c_print(display, "Less than %d", value);
blink(RED_PIN);
} else {
lcd_i2c_clear(display);
lcd_i2c_cursor_set(display, 0, 0);
lcd_i2c_print(display, "More than %d", value);
blink(RED_PIN);
}
state = STATE_INPUT;
}
}
vTaskDelete(NULL);
}
void IRAM_ATTR submit_press(void *param) {
if (state == STATE_INPUT) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
int16_t input = user_value;
xQueueSendToBackFromISR(ansQueue, &input, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void app_main() {
init_led();
init_btn();
init_i2c();
init_lcd();
init_input();
generate_random();
gpio_install_isr_service(0);
gpio_isr_handler_add(SUBMIT_PIN, submit_press, 0);
valQueue = xQueueCreate(10, sizeof(uint16_t));
ansQueue = xQueueCreate(10, sizeof(uint16_t));
xTaskCreate(vTaskPrint, "PRINT", 2048, NULL, 1, NULL);
xTaskCreate(vTaskInput, "INPUT", 2048, NULL, 1, NULL);
xTaskCreate(vTaskAnswer, "ANSWER", 2048, NULL, 1, NULL);
}