//#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/gptimer.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_http_server.h"
// --- WIFI CREDENTIALS ---
#define WIFI_SSID "POCO X7 Pro"
#define WIFI_PASS "oscar9546"
#define PIN_STEP_X 0
#define PIN_STEP_Y 1
#define PIN_DIR_X 2
#define PIN_DIR_Y 21
#define PIN_ENABLE 22
#define PIN_LIM_X 23
#define PIN_LIM_Y 16
// VOLATILE VARIABLES (Interrupt)
volatile int32_t current_x = 0, current_y = 0;
volatile int32_t target_x = 0, target_y = 0;
volatile int32_t dx, dy, sx, sy, err;
volatile bool is_moving = false;
volatile bool pulse_high = false;
//G-CODE CALIBRATION & MEMORY
#define STEPS_PER_MM 183.33f
float current_x_mm = 0.0;
float current_y_mm = 0.0;
// 50KB capacity
char mission_buffer[50000];
volatile bool mission_ready = false;
static bool IRAM_ATTR stepper_timer_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) {
if (!is_moving) return false;
if (pulse_high) {
gpio_set_level(PIN_STEP_X, 0);
gpio_set_level(PIN_STEP_Y, 0);
pulse_high = false;
if (current_x == target_x && current_y == target_y) {
is_moving = false;
}
} else {
//Bresenham's line algorithm calculation
if (current_x != target_x || current_y != target_y) {
int32_t e2 = 2 * err;
bool step_x = false;
bool step_y = false;
if (e2 >= dy) {
err += dy;
current_x += sx;
step_x = true;
}
if (e2 <= dx) {
err += dx;
current_y += sy;
step_y = true;
}
if (step_x) gpio_set_level(PIN_STEP_X, 1);
if (step_y) gpio_set_level(PIN_STEP_Y, 1);
pulse_high = true;
}
}
return false;
}
void move_to(int32_t x, int32_t y) {
// Redundancy check, gnore command if already at target
if (x == current_x && y == current_y) {
return;
}
// Wait for the previous movement to complete
while(is_moving) {
vTaskDelay(pdMS_TO_TICKS(10));
}
target_x = x;
target_y = y;
// Bresenham variables setup
dx = abs(target_x - current_x);
sx = current_x < target_x ? 1 : -1;
dy = -abs(target_y - current_y);
sy = current_y < target_y ? 1 : -1;
err = dx + dy;
// Set physical direction on drivers
gpio_set_level(PIN_DIR_X, sx > 0 ? 1 : 0);
gpio_set_level(PIN_DIR_Y, sy > 0 ? 1 : 0);
is_moving = true;
}
void execute_gcode(const char* gcode_line) {
float target_x_mm = current_x_mm;
float target_y_mm = current_y_mm;
// Parse X coordinate
char *x_ptr = strchr(gcode_line, 'X');
if (x_ptr != NULL) { target_x_mm = atof(x_ptr + 1); }
// Parse Y coordinate
char *y_ptr = strchr(gcode_line, 'Y');
if (y_ptr != NULL) { target_y_mm = atof(y_ptr + 1); }
// Update memory
current_x_mm = target_x_mm;
current_y_mm = target_y_mm;
// Convert millimeters to steps
int32_t steps_x = (int32_t)(target_x_mm * STEPS_PER_MM);
int32_t steps_y = (int32_t)(target_y_mm * STEPS_PER_MM);
move_to(steps_x, steps_y);
}
// initialization
void init_stepper_gpios() {
gpio_config_t io_conf = {0};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL< CONNECTED. IP FOR WEBPAGE: " IPSTR "/upload\n\n", IP2STR(&event->ip_info.ip));
}
}
esp_err_t upload_handler(httpd_req_t *req) {
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
if (req->method == HTTP_OPTIONS) {
httpd_resp_set_hdr(req, "Access-Control-Allow-Methods", "POST, OPTIONS");
httpd_resp_set_hdr(req, "Access-Control-Allow-Headers", "Content-Type");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
int total_len = req->content_len;
// RAM Protection: Reject files larger than the buffer
if (total_len >= sizeof(mission_buffer)) {
httpd_resp_send_500(req);
return ESP_FAIL;
}
int received = 0;
while (received < total_len) {
int ret = httpd_req_recv(req, mission_buffer + received, total_len - received);
if (ret <= 0) return ESP_FAIL;
received += ret;
}
mission_buffer[total_len] = '\0';
printf("G-Code received: %d bytes\n", total_len);
httpd_resp_sendstr(req, "Received on the ESP32 successfully");
// Trigger the main loop
mission_ready = true;
return ESP_OK;
}
void start_webserver() {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
httpd_uri_t uri_post = { .uri = "/upload", .method = HTTP_POST, .handler = upload_handler };
httpd_register_uri_handler(server, &uri_post);
httpd_uri_t uri_opts = { .uri = "/upload", .method = HTTP_OPTIONS, .handler = upload_handler };
httpd_register_uri_handler(server, &uri_opts);
}
}
void app_main(void) {
printf("Initializing CNC system with WiFi...\n");
init_stepper_gpios();
init_limits();
// Setup GPTimer for step generation
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000,
};
gptimer_new_timer(&timer_config, &gptimer);
gptimer_event_callbacks_t cbs = { .on_alarm = stepper_timer_cb };
gptimer_register_event_callbacks(gptimer, &cbs, NULL);
gptimer_alarm_config_t alarm_config = {
.alarm_count = 350, // Microseconds per step (Speed controller)
.reload_count = 0,
.flags.auto_reload_on_alarm = true,
};
gptimer_set_alarm_action(gptimer, &alarm_config);
gptimer_enable(gptimer);
gptimer_start(gptimer);
// Initialize WiFi Subsystem
nvs_flash_init();
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL);
wifi_config_t wifi_config = { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS } };
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();
// Start HTTP Server
start_webserver();
while(1) {
if (mission_ready) {
printf("Executing G-Code Mission...\n");
// WAKE UP MOTORS
gpio_set_level(PIN_ENABLE, 0);
vTaskDelay(pdMS_TO_TICKS(100)); // Allow coils to magnetize
char *saveptr;
char *line = strtok_r(mission_buffer, "\r\n", &saveptr);
// Process file line by line
while (line != NULL) {
if (strlen(line) > 2) {
execute_gcode(line);
}
line = strtok_r(NULL, "\r\n", &saveptr);
vTaskDelay(pdMS_TO_TICKS(2));
}
printf("Routing completed. Cooling down motors...\n");
gpio_set_level(PIN_ENABLE, 1);
mission_ready = false;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}