#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_adc/adc_oneshot.h"
// ---------------- Pin configuration -------------
#define LED_RED 6
#define LED_BLUE 16
#define ADC_CH_X ADC_CHANNEL_3 // Joystick X-axis
#define ADC_CH_Y ADC_CHANNEL_4 // Joystick Y-axis
// ---------------- Joystick thresholds ----------------
#define ADC_BITS 12
#define ADC_MAX ((1 << ADC_BITS) - 1) // 4095 for 12 bits
#define CENTER 2048 // joystick center (approx half)
#define DEADZONE 500 // ignore small variations
#define SAMPLES 8 // average this many samples
// ---------------- Zones ------------------------------
//CHANGED RED_3s with BLUE_3S TGH
#define ZONE_NONE 0
#define ZONE_RED_1S 1
#define ZONE_BLUE_1S 2
#define ZONE_RED_3S 3
#define ZONE_BLUE_3S 4
// ADC1 handle (created in adc_oneshot_setup and used everywhere)
static adc_oneshot_unit_handle_t adc1_handle = NULL;
// ================== Function Declarations =====================
void init_output(gpio_num_t pin);
void leds_off();
void blink_led_period(gpio_num_t pin, int period_ms);
void blink_red_1s();
void blink_red_3s();
void blink_blue_1s();
void blink_blue_3s();
void adc_oneshot_setup();
int adc_read_avg(adc_channel_t ch, int samples);
int select_zone(int x, int y);
// ============================= Main ========================================
void app_main() {
init_output(LED_RED);
init_output(LED_BLUE);
leds_off();
adc_oneshot_setup();
while (1) {
int x = adc_read_avg(ADC_CH_X, SAMPLES);
int y = adc_read_avg(ADC_CH_Y, SAMPLES);
printf("x=%d y=%d\n", x, y);
int z = select_zone(x, y);
switch (z) {
case ZONE_RED_1S: blink_red_1s(); break;
case ZONE_RED_3S: blink_red_3s(); break;
case ZONE_BLUE_1S: blink_blue_1s(); break;
case ZONE_BLUE_3S: blink_blue_3s(); break;
default:
leds_off();
vTaskDelay(pdMS_TO_TICKS(60));
break;
}
}
}
// ========================= Function Definitions ============================
/*
Function name- init_output
Description- Configure a GPIO as output; disable pulls and interrupts for clean drive.
Parameters- pin (gpio_num_t): GPIO to configure
return type- none
*/
void init_output(gpio_num_t pin) {
gpio_set_direction(pin, GPIO_MODE_OUTPUT);
gpio_pullup_dis(pin);
gpio_pulldown_dis(pin);
gpio_intr_disable(pin);
}
/*
Function name- leds_off
Description- Turn both LEDs off.
Parameters- none
return type- none
*/
void leds_off() {
gpio_set_level(LED_RED, 0);
gpio_set_level(LED_BLUE, 0);
}
/*
Function name- blink_led_period
Description- Blink a given GPIO with the specified period (ms), 50% duty.
Parameters- pin (gpio_num_t), period_ms (int)
return type- none
*/
void blink_led_period(gpio_num_t pin, int period_ms) {
gpio_set_level(pin, 1);
vTaskDelay(pdMS_TO_TICKS(period_ms / 2));
gpio_set_level(pin, 0);
vTaskDelay(pdMS_TO_TICKS(period_ms / 2));
}
/*
Function name- blink_red_1s
Description- Blink RED LED with 1 second period.
Parameters- none
return type- none
*/
void blink_red_1s() { blink_led_period(LED_RED, 1000); }
/*
Function name- blink_red_3s
Description- Blink RED LED with 3 second period.
Parameters- none
return type- none
*/
void blink_red_3s() { blink_led_period(LED_RED, 3000); }
/*
Function name- blink_blue_1s
Description- Blink BLUE LED with 1 second period.
Parameters- none
return type- none
*/
void blink_blue_1s() { blink_led_period(LED_BLUE, 1000); }
/*
Function name- blink_blue_3s
Description- Blink BLUE LED with 3 second period.
Parameters- none
return type- none
*/
void blink_blue_3s() { blink_led_period(LED_BLUE, 3000); }
/*
Function name- adc_oneshot_setup
Description- Create ADC1 oneshot unit and configure X/Y channels (12-bit, 11 dB).
Parameters- none
return type- none
*/
void adc_oneshot_setup() {
adc_oneshot_unit_init_cfg_t unit_cfg = {
.unit_id = ADC_UNIT_1
};
adc_oneshot_new_unit(&unit_cfg, &adc1_handle);
adc_oneshot_chan_cfg_t chan_cfg = {
.bitwidth = ADC_BITWIDTH_12,
.atten = ADC_ATTEN_DB_11
};
adc_oneshot_config_channel(adc1_handle, ADC_CH_X, &chan_cfg);
adc_oneshot_config_channel(adc1_handle, ADC_CH_Y, &chan_cfg);
}
/*
Function name- adc_read_avg
Description- Read 'samples' oneshot ADC values from channel 'ch' and return the average.
Parameters- ch (adc_channel_t), samples (int)
return type- int (average raw ADC count)
*/
int adc_read_avg(adc_channel_t ch, int samples) {
long sum = 0;
for (int i = 0; i < samples; ++i) {
int raw = 0;
adc_oneshot_read(adc1_handle, ch, &raw);
sum += raw;
vTaskDelay(pdMS_TO_TICKS(2)); // small pause for stability
}
return (int)(sum / samples);
}
/*
Function name- select_zone
Description- Map joystick X/Y positions to a zone label using center + deadzone.
Parameters- x (int), y (int)
return type- int (ZONE_* constant)
*/
/*
I changed the directions to make them accurate again as well as switching blue 1s with red 3s and changed
the greater than or equal to as well as the less than or equal to in order to match them with the direction of the joystick
TGH
*/
int select_zone(int x, int y) {
if (x > CENTER + DEADZONE) return ZONE_RED_1S; // left
if (y < CENTER - DEADZONE) return ZONE_BLUE_3S; // up
if (x < CENTER - DEADZONE) return ZONE_RED_3S; // right
if (y > CENTER + DEADZONE) return ZONE_BLUE_1S; // down
return ZONE_NONE; // centered
}