From 3282bc640392bda1a730c5a2c8dc98efb2226ca1 Mon Sep 17 00:00:00 2001 From: xypoon <2837264P@student.gla.ac.uk> Date: Mon, 23 Oct 2023 11:05:16 +0800 Subject: [PATCH 01/11] Add Init Ultrasonic Sensor --- frtos/rtos_car.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frtos/rtos_car.c b/frtos/rtos_car.c index 8084001..f7989b3 100644 --- a/frtos/rtos_car.c +++ b/frtos/rtos_car.c @@ -94,6 +94,8 @@ main (void) line_sensor_setup(); + init_ultrasonic(); + initialize_car_state(); // TODO: Could be common functionality, To confirm // during Integration launch(); From 5c70e4d1b72b5d86b27da4ce0daa8254f2713685 Mon Sep 17 00:00:00 2001 From: Devoalda Date: Thu, 26 Oct 2023 17:09:25 +0800 Subject: [PATCH 02/11] fix(Line sensor): Changed line sensor task --- frtos/line_sensor/line_sensor.h | 54 +++++++++++++++++---- frtos/line_sensor/line_sensor_test.c | 38 +++++++-------- frtos/magnetometer/magnetometer_direction.h | 10 ++-- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/frtos/line_sensor/line_sensor.h b/frtos/line_sensor/line_sensor.h index 23c30e0..ff6191d 100644 --- a/frtos/line_sensor/line_sensor.h +++ b/frtos/line_sensor/line_sensor.h @@ -14,14 +14,35 @@ * * @param params */ +//void +//monitor_left_sensor_task(__unused void *params) { +// for (;;) +// { +// if (xSemaphoreTake(g_left_sensor_sem, portMAX_DELAY) == pdTRUE) +// { +// if (left_sensor_triggered == pdTRUE) +// { +// printf("left sensor triggered\n"); +// // Get Current State +// state_t state = gpio_get(LEFT_SENSOR_PIN); +// +// xMessageBufferSend(left_sensor_msg_buffer, +// &state, +// sizeof(state_t), +// 0); +// // Reset the flag +// left_sensor_triggered = pdFALSE; +// } +// } +// } +//} + void monitor_left_sensor_task(__unused void *params) { for (;;) { if (xSemaphoreTake(g_left_sensor_sem, portMAX_DELAY) == pdTRUE) { - if (left_sensor_triggered == pdTRUE) - { printf("left sensor triggered\n"); // Get Current State state_t state = gpio_get(LEFT_SENSOR_PIN); @@ -30,9 +51,6 @@ monitor_left_sensor_task(__unused void *params) { &state, sizeof(state_t), 0); - // Reset the flag - left_sensor_triggered = pdFALSE; - } } } } @@ -45,12 +63,32 @@ monitor_left_sensor_task(__unused void *params) { * * @param params */ +//void +//monitor_right_sensor_task(void *params) { +// for (;;) { +// if (xSemaphoreTake(g_right_sensor_sem, portMAX_DELAY) == pdTRUE) { +// // Check the flag or receive the message +// if (right_sensor_triggered == pdTRUE) { +// printf("right sensor triggered\n"); +// // Get Current State +// state_t state = gpio_get(RIGHT_SENSOR_PIN); +// +// xMessageBufferSend(right_sensor_msg_buffer, +// &state, +// sizeof(state_t), +// 0); +// // Reset the flag +// right_sensor_triggered = pdFALSE; +// } +// } +// } +//} + void monitor_right_sensor_task(void *params) { for (;;) { if (xSemaphoreTake(g_right_sensor_sem, portMAX_DELAY) == pdTRUE) { // Check the flag or receive the message - if (right_sensor_triggered == pdTRUE) { printf("right sensor triggered\n"); // Get Current State state_t state = gpio_get(RIGHT_SENSOR_PIN); @@ -59,14 +97,10 @@ monitor_right_sensor_task(void *params) { &state, sizeof(state_t), 0); - // Reset the flag - right_sensor_triggered = pdFALSE; - } } } } - /** * @brief Monitor the direction and Oritentation of the car * diff --git a/frtos/line_sensor/line_sensor_test.c b/frtos/line_sensor/line_sensor_test.c index 44750da..eff8ddf 100644 --- a/frtos/line_sensor/line_sensor_test.c +++ b/frtos/line_sensor/line_sensor_test.c @@ -10,26 +10,26 @@ void launch() { // isr to detect left line sensor - gpio_set_irq_enabled(LEFT_SENSOR_PIN, GPIO_IRQ_EDGE_FALL, true); - gpio_add_raw_irq_handler(LEFT_SENSOR_PIN, h_line_sensor_handler); - - // isr to detect right line sensor - gpio_set_irq_enabled(RIGHT_SENSOR_PIN, GPIO_IRQ_EDGE_FALL, true); - gpio_add_raw_irq_handler(RIGHT_SENSOR_PIN, h_line_sensor_handler); - - irq_set_enabled(IO_IRQ_BANK0, true); - -// struct repeating_timer g_left_sensor_timer; -// add_repeating_timer_ms(LINE_SENSOR_READ_DELAY, -// h_left_sensor_timer_handler, -// NULL, -// &g_left_sensor_timer); +// gpio_set_irq_enabled(LEFT_SENSOR_PIN, GPIO_IRQ_EDGE_FALL, true); +// gpio_add_raw_irq_handler(LEFT_SENSOR_PIN, h_line_sensor_handler); // -// struct repeating_timer g_right_sensor_timer; -// add_repeating_timer_ms(LINE_SENSOR_READ_DELAY, -// h_right_sensor_timer_handler, -// NULL, -// &g_right_sensor_timer); +// // isr to detect right line sensor +// gpio_set_irq_enabled(RIGHT_SENSOR_PIN, GPIO_IRQ_EDGE_FALL, true); +// gpio_add_raw_irq_handler(RIGHT_SENSOR_PIN, h_line_sensor_handler); +// +// irq_set_enabled(IO_IRQ_BANK0, true); + + struct repeating_timer g_left_sensor_timer; + add_repeating_timer_ms(LINE_SENSOR_READ_DELAY, + h_left_sensor_timer_handler, + NULL, + &g_left_sensor_timer); + + struct repeating_timer g_right_sensor_timer; + add_repeating_timer_ms(LINE_SENSOR_READ_DELAY, + h_right_sensor_timer_handler, + NULL, + &g_right_sensor_timer); TaskHandle_t h_monitor_left_sensor_task; xTaskCreate(monitor_left_sensor_task, diff --git a/frtos/magnetometer/magnetometer_direction.h b/frtos/magnetometer/magnetometer_direction.h index 441e0cb..c7d7189 100644 --- a/frtos/magnetometer/magnetometer_direction.h +++ b/frtos/magnetometer/magnetometer_direction.h @@ -6,15 +6,13 @@ * * @details The direction of the car is calculated using the roll, pitch and yaw * The roll and pitch are calculated using the accelerometer data - * The yaw is calculated using the magnetometer data + * The yaw is calculated using the magnetometer and accelerometer data * The roll, pitch and yaw are combined to calculate the direction - * of the car - * - * The direction of the car is calculated using the complementary - * filter + * of the car with a complementary filter and compensating for the + * temperature. * * The complementary filter is used to combine the accelerometer - * and magnetometer data to calculate the direction of the car + * and magnetometer data (yaw) to calculate the direction of the car * * Source: * https://www.nxp.com/docs/en/application-note/AN3461.pdf From a40fa548c3bef9bc7dc9a45f69426b35ee264ba9 Mon Sep 17 00:00:00 2001 From: Richie <2837357W@student.gla.ac.uk> Date: Fri, 27 Oct 2023 13:07:55 +0800 Subject: [PATCH 03/11] update struct and pid for both wheels --- frtos/config/motor_config.h | 69 ++++++++++++++++++++++++------------- frtos/motor/motor_init.h | 46 ++++++++++++++++--------- frtos/motor/motor_pid.h | 48 ++++++++++++++++---------- frtos/motor/motor_speed.h | 16 ++++----- frtos/motor/motor_test.c | 64 +++++++++++++++++++++------------- 5 files changed, 152 insertions(+), 91 deletions(-) diff --git a/frtos/config/motor_config.h b/frtos/config/motor_config.h index 4219df3..1c8d231 100644 --- a/frtos/config/motor_config.h +++ b/frtos/config/motor_config.h @@ -24,36 +24,57 @@ #define PWM_CLK_DIV 250.f #define PWM_WRAP 5000U -/* - * ultimate gain Ku about 14, ultimate period Tu about 8 * 50 = 400ms - * Ku = 14, Tu = 400ms, - * Kp = 0.6 * Ku = 8.4 - * Ki = Kp / Tu = 0.021 - * Kd = Kp * Tu / 8 = 42 - */ -#define PID_KP 8.4f -#define PID_KI 0.021f // 0.005f -#define PID_KD 42.f // 0.05f - #define MAX_SPEED 4900U #define MIN_SPEED 0U // To be changed /*! - * @brief Structure for the motor speed - * @param target_speed The target speed of the wheel, in cm/s - * @param pwm_level The pwm level of the wheel, from 0 to 5000 - * @param sem The semaphore for the wheel - * @param p_slice_num The pointer to the slice number of the wheel - * @param channel The pwm channel of the wheel, left A or right B + * @brief Structure for the motor speed parameters + * @param target_speed_cms Target speed in cm/s + * @param current_speed_cms Current speed in cm/s + * @param distance_cm Distance travelled in cm */ typedef struct { - float target_speed_cms; - float current_speed_cms; - uint16_t pwm_level; - SemaphoreHandle_t sem; - uint slice_num; - uint pwm_channel; - float distance; + float target_speed_cms; + float current_speed_cms; + float distance_cm; } motor_speed_t; +/*! + * @brief Structure for the motor PWM parameters + * @param slice_num PWM slice number + * @param pwm_channel PWM channel, either A or B + * @param pwm_level PWM level, from 0 to 5000 + */ +typedef struct { + uint slice_num; + uint pwm_channel; + uint16_t pwm_level; +} motor_pwm_t; + +/*! + * @brief Structure for the motor PID parameters + * @param pid_kp Proportional gain + * @param pid_ki Integral gain + * @param pid_kd Derivative gain + */ +typedef struct { + float pid_kp; + float pid_ki; + float pid_kd; +} motor_pid_t; + +/*! + * @brief Structure for the motor parameters + * @param speed Motor speed parameters + * @param sem Semaphore for the motor speed + * @param pwm Motor PWM parameters + * @param pid Motor PID parameters + */ +typedef struct { + motor_speed_t speed; + SemaphoreHandle_t sem; + motor_pwm_t pwm; + motor_pid_t pid; +} motor_t; + #endif /* MOTOR_CONFIG_H */ \ No newline at end of file diff --git a/frtos/motor/motor_init.h b/frtos/motor/motor_init.h index 970b12e..576e08f 100644 --- a/frtos/motor/motor_init.h +++ b/frtos/motor/motor_init.h @@ -19,20 +19,34 @@ #include "motor_config.h" -motor_speed_t g_motor_speed_left = { .pwm_level = 2500u, - .pwm_channel = PWM_CHAN_A, - .distance = 0.0f,}; +// TODO: tune pid for both wheels again +/* + * ultimate gain Ku about 14, ultimate period Tu about 8 * 50 = 400ms + * Ku = 14, Tu = 400ms, + * Kp = 0.6 * Ku = 8.4 + * Ki = Kp / Tu = 0.021 + * Kd = Kp * Tu / 8 = 42 + */ +motor_t g_motor_left = { .pwm.pwm_level = 0u, + .pwm.pwm_channel = PWM_CHAN_A, + .speed.distance_cm = 0.0f, + .pid.pid_kp = 8.4f, + .pid.pid_ki = 0.021f, + .pid.pid_kd = 42.f,}; -motor_speed_t g_motor_speed_right = { .pwm_level = 2500u, - .pwm_channel = PWM_CHAN_B, - .distance = 0.0f,}; +motor_t g_motor_right = { .pwm.pwm_level = 0u, + .pwm.pwm_channel = PWM_CHAN_B, + .speed.distance_cm = 0.0f, + .pid.pid_kp = 0.0f, + .pid.pid_ki = 0.0f, + .pid.pid_kd = 0.0f,}; void motor_init(void) { // Semaphore - g_motor_speed_left.sem = xSemaphoreCreateBinary(); - g_motor_speed_right.sem = xSemaphoreCreateBinary(); + g_motor_left.sem = xSemaphoreCreateBinary(); + g_motor_right.sem = xSemaphoreCreateBinary(); gpio_init(SPEED_PIN_RIGHT); gpio_init(SPEED_PIN_LEFT); @@ -54,21 +68,21 @@ motor_init(void) gpio_set_function(PWM_PIN_LEFT, GPIO_FUNC_PWM); gpio_set_function(PWM_PIN_RIGHT, GPIO_FUNC_PWM); - g_motor_speed_left.slice_num = pwm_gpio_to_slice_num(PWM_PIN_LEFT); - g_motor_speed_right.slice_num = pwm_gpio_to_slice_num(PWM_PIN_RIGHT); + g_motor_left.pwm.slice_num = pwm_gpio_to_slice_num(PWM_PIN_LEFT); + g_motor_right.pwm.slice_num = pwm_gpio_to_slice_num(PWM_PIN_RIGHT); // NOTE: PWM clock is 125MHz for raspberrypi pico w by default // 125MHz / 250 = 500kHz - pwm_set_clkdiv(g_motor_speed_left.slice_num, PWM_CLK_DIV); - pwm_set_clkdiv(g_motor_speed_right.slice_num, PWM_CLK_DIV); + pwm_set_clkdiv(g_motor_left.pwm.slice_num, PWM_CLK_DIV); + pwm_set_clkdiv(g_motor_right.pwm.slice_num, PWM_CLK_DIV); // have them to be 500kHz / 5000 = 100Hz - pwm_set_wrap(g_motor_speed_left.slice_num, (PWM_WRAP - 1U)); - pwm_set_wrap(g_motor_speed_right.slice_num, (PWM_WRAP - 1U)); + pwm_set_wrap(g_motor_left.pwm.slice_num, (PWM_WRAP - 1U)); + pwm_set_wrap(g_motor_right.pwm.slice_num, (PWM_WRAP - 1U)); - pwm_set_enabled(g_motor_speed_left.slice_num, true); - pwm_set_enabled(g_motor_speed_right.slice_num, true); + pwm_set_enabled(g_motor_left.pwm.slice_num, true); + pwm_set_enabled(g_motor_right.pwm.slice_num, true); } #endif /* MOTOR_INIT_H */ \ No newline at end of file diff --git a/frtos/motor/motor_pid.h b/frtos/motor/motor_pid.h index cd4bf10..74e0b06 100644 --- a/frtos/motor/motor_pid.h +++ b/frtos/motor/motor_pid.h @@ -16,18 +16,20 @@ * @return The control signal */ float -compute_pid(const volatile float *target_speed, - const volatile float *current_speed, +compute_pid(const volatile motor_t *p_motor, float *integral, float *prev_error) { - float error = *target_speed - *current_speed; + float error = p_motor->speed.target_speed_cms - p_motor->speed.current_speed_cms; + *integral += error; float derivative = error - *prev_error; float control_signal - = PID_KP * error + PID_KI * (*integral) + PID_KD * derivative; + = p_motor->pid.pid_kp * error + + p_motor->pid.pid_ki * (*integral) + + p_motor->pid.pid_kd * derivative; *prev_error = error; @@ -37,35 +39,43 @@ compute_pid(const volatile float *target_speed, void motor_pid_task(void *p_param) { - motor_speed_t *p_motor_speed = p_param; - float integral = 0.0f; - float prev_error = 0.0f; + motor_t *p_motor = p_param; + float integral = 0.0f; + float prev_error = 0.0f; for (;;) { - float control_signal = compute_pid(&(p_motor_speed->target_speed_cms), - &(p_motor_speed->current_speed_cms), - &integral, &prev_error); - - if (p_motor_speed->pwm_level + control_signal > MAX_SPEED) + if (p_motor->speed.target_speed_cms == 0.0f) { - p_motor_speed->pwm_level = MAX_SPEED; + p_motor->pwm.pwm_level = 0; + pwm_set_chan_level(p_motor->pwm.slice_num, + p_motor->pwm.pwm_channel, + p_motor->pwm.pwm_level); + vTaskDelay(pdMS_TO_TICKS(50)); + continue; } - else if (p_motor_speed->pwm_level + control_signal < MIN_SPEED) + + float control_signal = compute_pid(p_motor, &integral, &prev_error); + + if (p_motor->pwm.pwm_level + control_signal > MAX_SPEED) { - p_motor_speed->pwm_level = MIN_SPEED; + p_motor->pwm.pwm_level = MAX_SPEED; + } + else if (p_motor->pwm.pwm_level + control_signal < MIN_SPEED) + { + p_motor->pwm.pwm_level = MIN_SPEED; } else { - p_motor_speed->pwm_level = p_motor_speed->pwm_level + control_signal; + p_motor->pwm.pwm_level = p_motor->pwm.pwm_level + control_signal; } // printf("control signal: %f\n", control_signal); // printf("new pwm: %hu\n\n", p_motor_speed->pwm_level); - pwm_set_chan_level(p_motor_speed->slice_num, - p_motor_speed->pwm_channel, - p_motor_speed->pwm_level); + pwm_set_chan_level(p_motor->pwm.slice_num, + p_motor->pwm.pwm_channel, + p_motor->pwm.pwm_level); vTaskDelay(pdMS_TO_TICKS(50)); } diff --git a/frtos/motor/motor_speed.h b/frtos/motor/motor_speed.h index 525601f..81b89d1 100644 --- a/frtos/motor/motor_speed.h +++ b/frtos/motor/motor_speed.h @@ -17,7 +17,7 @@ h_wheel_sensor_isr_handler(void) gpio_acknowledge_irq(SPEED_PIN_LEFT, GPIO_IRQ_EDGE_FALL); BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xSemaphoreGiveFromISR(g_motor_speed_left.sem, + xSemaphoreGiveFromISR(g_motor_left.sem, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } @@ -27,7 +27,7 @@ h_wheel_sensor_isr_handler(void) gpio_acknowledge_irq(SPEED_PIN_RIGHT, GPIO_IRQ_EDGE_FALL); BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xSemaphoreGiveFromISR(g_motor_speed_right.sem, + xSemaphoreGiveFromISR(g_motor_right.sem, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } @@ -42,8 +42,8 @@ h_wheel_sensor_isr_handler(void) void monitor_wheel_speed_task(void *pvParameters) { - volatile motor_speed_t *p_motor_speed = NULL; - p_motor_speed = (motor_speed_t *)pvParameters; + volatile motor_t *p_motor = NULL; + p_motor = (motor_t *)pvParameters; uint64_t curr_time = 0u; uint64_t prev_time = 0u; @@ -51,7 +51,7 @@ monitor_wheel_speed_task(void *pvParameters) for (;;) { - if (xSemaphoreTake(p_motor_speed->sem, pdMS_TO_TICKS(100)) + if (xSemaphoreTake(p_motor->sem, pdMS_TO_TICKS(100)) == pdTRUE) { curr_time = time_us_64(); @@ -62,17 +62,17 @@ monitor_wheel_speed_task(void *pvParameters) // distance = circumference / 20 // circumference = 2 * pi * 3.25 cm = 20.4203522483 cm // distance = 20.4203522483 cm / 20 = 1.02101761242 cm - p_motor_speed->current_speed_cms = (float) (1.02101761242f / + p_motor->speed.current_speed_cms = (float) (1.02101761242f / (elapsed_time / 1000000.f)); - p_motor_speed->distance += 1.02101761242f; + p_motor->speed.distance_cm += 1.02101761242f; // printf("speed: %f cm/s\n", p_motor_speed->current_speed_cms); } else { - p_motor_speed->current_speed_cms = 0.f; + p_motor->speed.current_speed_cms = 0.f; // printf("stopped\n"); } diff --git a/frtos/motor/motor_test.c b/frtos/motor/motor_test.c index 0e9268b..5644eab 100644 --- a/frtos/motor/motor_test.c +++ b/frtos/motor/motor_test.c @@ -11,18 +11,34 @@ test_speed_change_task(void *p_param) { for (;;) { - g_motor_speed_left.target_speed_cms = 15.0f; - g_motor_speed_right.target_speed_cms = 15.0f; + g_motor_left.speed.target_speed_cms = 30.0f; + g_motor_right.speed.target_speed_cms = 30.0f; vTaskDelay(pdMS_TO_TICKS(5000)); -// g_motor_speed_left.target_speed_cms = 20.0f; -// g_motor_speed_right.target_speed_cms = 20.0f; -// vTaskDelay(pdMS_TO_TICKS(5000)); - - g_motor_speed_left.target_speed_cms = 0.0f; - g_motor_speed_right.target_speed_cms = 0.0f; + g_motor_left.speed.target_speed_cms = 20.0f; + g_motor_right.speed.target_speed_cms = 20.0f; vTaskDelay(pdMS_TO_TICKS(5000)); + g_motor_left.speed.target_speed_cms = 0.0f; + g_motor_right.speed.target_speed_cms = 0.0f; + vTaskDelay(pdMS_TO_TICKS(5000)); + + set_wheel_direction(DIRECTION_LEFT_BACKWARD | DIRECTION_RIGHT_BACKWARD); + + g_motor_left.speed.target_speed_cms = 30.0f; + g_motor_right.speed.target_speed_cms = 30.0f; + vTaskDelay(pdMS_TO_TICKS(5000)); + + g_motor_left.speed.target_speed_cms = 20.0f; + g_motor_right.speed.target_speed_cms = 20.0f; + vTaskDelay(pdMS_TO_TICKS(5000)); + + g_motor_left.speed.target_speed_cms = 0.0f; + g_motor_right.speed.target_speed_cms = 0.0f; + vTaskDelay(pdMS_TO_TICKS(5000)); + + set_wheel_direction(DIRECTION_LEFT_FORWARD | DIRECTION_RIGHT_FORWARD); + } } @@ -41,21 +57,21 @@ launch() // Left wheel // -// TaskHandle_t h_monitor_left_wheel_speed_task_handle = NULL; -// xTaskCreate(monitor_wheel_speed_task, -// "monitor_left_wheel_speed_task", -// configMINIMAL_STACK_SIZE, -// (void *)&g_motor_speed_left, -// WHEEL_SPEED_PRIO, -// &h_monitor_left_wheel_speed_task_handle); + TaskHandle_t h_monitor_left_wheel_speed_task_handle = NULL; + xTaskCreate(monitor_wheel_speed_task, + "monitor_left_wheel_speed_task", + configMINIMAL_STACK_SIZE, + (void *)&g_motor_left, + WHEEL_SPEED_PRIO, + &h_monitor_left_wheel_speed_task_handle); -// TaskHandle_t h_motor_pid_left_task_handle = NULL; -// xTaskCreate(motor_pid_task, -// "motor_pid_task", -// configMINIMAL_STACK_SIZE, -// (void *)&g_motor_speed_left, -// WHEEL_SPEED_PRIO, -// &h_motor_pid_left_task_handle); + TaskHandle_t h_motor_pid_left_task_handle = NULL; + xTaskCreate(motor_pid_task, + "motor_pid_task", + configMINIMAL_STACK_SIZE, + (void *)&g_motor_left, + WHEEL_SPEED_PRIO, + &h_motor_pid_left_task_handle); // Right wheel // @@ -63,7 +79,7 @@ launch() xTaskCreate(monitor_wheel_speed_task, "monitor_wheel_speed_task", configMINIMAL_STACK_SIZE, - (void *)&g_motor_speed_right, + (void *)&g_motor_right, WHEEL_SPEED_PRIO, &h_monitor_right_wheel_speed_task_handle); @@ -71,7 +87,7 @@ launch() xTaskCreate(motor_pid_task, "motor_pid_task", configMINIMAL_STACK_SIZE, - (void *)&g_motor_speed_right, + (void *)&g_motor_right, WHEEL_SPEED_PRIO, &h_motor_pid_right_task_handle); From 8cd0cd95716fe5038ab75a160db270c7c653166a Mon Sep 17 00:00:00 2001 From: Richie <2837357W@student.gla.ac.uk> Date: Fri, 27 Oct 2023 13:11:41 +0800 Subject: [PATCH 04/11] update struct names --- frtos/config/motor_config.h | 14 +++++++------- frtos/motor/motor_init.h | 20 ++++++++++---------- frtos/motor/motor_pid.h | 32 ++++++++++++++++---------------- frtos/motor/motor_speed.h | 9 ++++----- frtos/motor/motor_test.c | 24 ++++++++++++------------ 5 files changed, 49 insertions(+), 50 deletions(-) diff --git a/frtos/config/motor_config.h b/frtos/config/motor_config.h index 1c8d231..86b995d 100644 --- a/frtos/config/motor_config.h +++ b/frtos/config/motor_config.h @@ -34,8 +34,8 @@ * @param distance_cm Distance travelled in cm */ typedef struct { - float target_speed_cms; - float current_speed_cms; + float target_cms; + float current_cms; float distance_cm; } motor_speed_t; @@ -47,8 +47,8 @@ typedef struct { */ typedef struct { uint slice_num; - uint pwm_channel; - uint16_t pwm_level; + uint channel; + uint16_t level; } motor_pwm_t; /*! @@ -58,9 +58,9 @@ typedef struct { * @param pid_kd Derivative gain */ typedef struct { - float pid_kp; - float pid_ki; - float pid_kd; + float kp_value; + float ki_value; + float kd_value; } motor_pid_t; /*! diff --git a/frtos/motor/motor_init.h b/frtos/motor/motor_init.h index 576e08f..da9b684 100644 --- a/frtos/motor/motor_init.h +++ b/frtos/motor/motor_init.h @@ -27,19 +27,19 @@ * Ki = Kp / Tu = 0.021 * Kd = Kp * Tu / 8 = 42 */ -motor_t g_motor_left = { .pwm.pwm_level = 0u, - .pwm.pwm_channel = PWM_CHAN_A, +motor_t g_motor_left = { .pwm.level = 0u, + .pwm.channel = PWM_CHAN_A, .speed.distance_cm = 0.0f, - .pid.pid_kp = 8.4f, - .pid.pid_ki = 0.021f, - .pid.pid_kd = 42.f,}; + .pid.kp_value = 8.4f, + .pid.ki_value = 0.021f, + .pid.kd_value = 42.f,}; -motor_t g_motor_right = { .pwm.pwm_level = 0u, - .pwm.pwm_channel = PWM_CHAN_B, +motor_t g_motor_right = { .pwm.level = 0u, + .pwm.channel = PWM_CHAN_B, .speed.distance_cm = 0.0f, - .pid.pid_kp = 0.0f, - .pid.pid_ki = 0.0f, - .pid.pid_kd = 0.0f,}; + .pid.kp_value = 0.0f, + .pid.ki_value = 0.0f, + .pid.kd_value = 0.0f,}; void motor_init(void) diff --git a/frtos/motor/motor_pid.h b/frtos/motor/motor_pid.h index 74e0b06..2eedd86 100644 --- a/frtos/motor/motor_pid.h +++ b/frtos/motor/motor_pid.h @@ -20,16 +20,16 @@ compute_pid(const volatile motor_t *p_motor, float *integral, float *prev_error) { - float error = p_motor->speed.target_speed_cms - p_motor->speed.current_speed_cms; + float error = p_motor->speed.target_cms - p_motor->speed.current_cms; *integral += error; float derivative = error - *prev_error; float control_signal - = p_motor->pid.pid_kp * error + - p_motor->pid.pid_ki * (*integral) + - p_motor->pid.pid_kd * derivative; + = p_motor->pid.kp_value * error + + p_motor->pid.ki_value * (*integral) + + p_motor->pid.kd_value * derivative; *prev_error = error; @@ -45,37 +45,37 @@ motor_pid_task(void *p_param) for (;;) { - if (p_motor->speed.target_speed_cms == 0.0f) + if (p_motor->speed.target_cms == 0.0f) { - p_motor->pwm.pwm_level = 0; + p_motor->pwm.level = 0; pwm_set_chan_level(p_motor->pwm.slice_num, - p_motor->pwm.pwm_channel, - p_motor->pwm.pwm_level); + p_motor->pwm.channel, + p_motor->pwm.level); vTaskDelay(pdMS_TO_TICKS(50)); continue; } float control_signal = compute_pid(p_motor, &integral, &prev_error); - if (p_motor->pwm.pwm_level + control_signal > MAX_SPEED) + if (p_motor->pwm.level + control_signal > MAX_SPEED) { - p_motor->pwm.pwm_level = MAX_SPEED; + p_motor->pwm.level = MAX_SPEED; } - else if (p_motor->pwm.pwm_level + control_signal < MIN_SPEED) + else if (p_motor->pwm.level + control_signal < MIN_SPEED) { - p_motor->pwm.pwm_level = MIN_SPEED; + p_motor->pwm.level = MIN_SPEED; } else { - p_motor->pwm.pwm_level = p_motor->pwm.pwm_level + control_signal; + p_motor->pwm.level = p_motor->pwm.level + control_signal; } // printf("control signal: %f\n", control_signal); - // printf("new pwm: %hu\n\n", p_motor_speed->pwm_level); + // printf("new pwm: %hu\n\n", p_motor_speed->level); pwm_set_chan_level(p_motor->pwm.slice_num, - p_motor->pwm.pwm_channel, - p_motor->pwm.pwm_level); + p_motor->pwm.channel, + p_motor->pwm.level); vTaskDelay(pdMS_TO_TICKS(50)); } diff --git a/frtos/motor/motor_speed.h b/frtos/motor/motor_speed.h index 81b89d1..f53dcdd 100644 --- a/frtos/motor/motor_speed.h +++ b/frtos/motor/motor_speed.h @@ -62,17 +62,16 @@ monitor_wheel_speed_task(void *pvParameters) // distance = circumference / 20 // circumference = 2 * pi * 3.25 cm = 20.4203522483 cm // distance = 20.4203522483 cm / 20 = 1.02101761242 cm - p_motor->speed.current_speed_cms = (float) (1.02101761242f / - (elapsed_time / - 1000000.f)); + p_motor->speed.current_cms + = (float) (1.02101761242f / (elapsed_time / 1000000.f)); p_motor->speed.distance_cm += 1.02101761242f; - // printf("speed: %f cm/s\n", p_motor_speed->current_speed_cms); + // printf("speed: %f cm/s\n", p_motor_speed->current_cms); } else { - p_motor->speed.current_speed_cms = 0.f; + p_motor->speed.current_cms = 0.f; // printf("stopped\n"); } diff --git a/frtos/motor/motor_test.c b/frtos/motor/motor_test.c index 5644eab..c248610 100644 --- a/frtos/motor/motor_test.c +++ b/frtos/motor/motor_test.c @@ -11,30 +11,30 @@ test_speed_change_task(void *p_param) { for (;;) { - g_motor_left.speed.target_speed_cms = 30.0f; - g_motor_right.speed.target_speed_cms = 30.0f; + g_motor_left.speed.target_cms = 30.0f; + g_motor_right.speed.target_cms = 30.0f; vTaskDelay(pdMS_TO_TICKS(5000)); - g_motor_left.speed.target_speed_cms = 20.0f; - g_motor_right.speed.target_speed_cms = 20.0f; + g_motor_left.speed.target_cms = 20.0f; + g_motor_right.speed.target_cms = 20.0f; vTaskDelay(pdMS_TO_TICKS(5000)); - g_motor_left.speed.target_speed_cms = 0.0f; - g_motor_right.speed.target_speed_cms = 0.0f; + g_motor_left.speed.target_cms = 0.0f; + g_motor_right.speed.target_cms = 0.0f; vTaskDelay(pdMS_TO_TICKS(5000)); set_wheel_direction(DIRECTION_LEFT_BACKWARD | DIRECTION_RIGHT_BACKWARD); - g_motor_left.speed.target_speed_cms = 30.0f; - g_motor_right.speed.target_speed_cms = 30.0f; + g_motor_left.speed.target_cms = 30.0f; + g_motor_right.speed.target_cms = 30.0f; vTaskDelay(pdMS_TO_TICKS(5000)); - g_motor_left.speed.target_speed_cms = 20.0f; - g_motor_right.speed.target_speed_cms = 20.0f; + g_motor_left.speed.target_cms = 20.0f; + g_motor_right.speed.target_cms = 20.0f; vTaskDelay(pdMS_TO_TICKS(5000)); - g_motor_left.speed.target_speed_cms = 0.0f; - g_motor_right.speed.target_speed_cms = 0.0f; + g_motor_left.speed.target_cms = 0.0f; + g_motor_right.speed.target_cms = 0.0f; vTaskDelay(pdMS_TO_TICKS(5000)); set_wheel_direction(DIRECTION_LEFT_FORWARD | DIRECTION_RIGHT_FORWARD); From 9ae974deab83fdb712d30423eb9c2832e68897ee Mon Sep 17 00:00:00 2001 From: Devoalda Date: Fri, 27 Oct 2023 19:54:22 +0800 Subject: [PATCH 05/11] fix(Magnetometer): Fixed Yaw Calculation --- frtos/config/magnetometer_config.h | 12 ++++++++++++ frtos/magnetometer/LSM303DLHC_register.h | 8 -------- frtos/magnetometer/magnetometer_test.c | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frtos/config/magnetometer_config.h b/frtos/config/magnetometer_config.h index 1eed5f3..2996d79 100644 --- a/frtos/config/magnetometer_config.h +++ b/frtos/config/magnetometer_config.h @@ -10,6 +10,18 @@ #define ALPHA ( 0.01f ) // Complementary // Filter Constant +// LSM303DLHC temperature compensation coefficients +#define SCALE_Z ( 1.0f ) // Scale for Z-axis +#define OFFSET_Z ( 3.0f ) // Offset for Z-axis + +#define TEMPERATURE_OFFSET ( 25.0f ) // Reference + // temperature for + // calibration + +#define TEMPERATURE_COEFFICIENT_Z ( 0.33f ) // Temperature + // coefficient for + // Z-axis + /** * @brief The orientation of the car */ diff --git a/frtos/magnetometer/LSM303DLHC_register.h b/frtos/magnetometer/LSM303DLHC_register.h index 5b5940f..8b24e6b 100644 --- a/frtos/magnetometer/LSM303DLHC_register.h +++ b/frtos/magnetometer/LSM303DLHC_register.h @@ -28,15 +28,7 @@ #define LSM303_TEMP_OUT_H_M 0x31 #define LSM303_TEMP_OUT_L_M 0x32 -// LSM303DLHC temperature compensation coefficients -#define SCALE_Z 0.9 // Scale factor for Z-axis -#define OFFSET_Z 5.0 // Offset for Z-axis -#define TEMPERATURE_OFFSET 25.0 // Reference temperature for calibration -#define TEMPERATURE_COEFFICIENT_Z 0.33 - -#define OFFSET_Z 5.0 -#define SCALE_Z 0.9 #define ACCEL_ADDR 0x19 #define MAG_ADDR 0x1E diff --git a/frtos/magnetometer/magnetometer_test.c b/frtos/magnetometer/magnetometer_test.c index 18af527..b509d42 100644 --- a/frtos/magnetometer/magnetometer_test.c +++ b/frtos/magnetometer/magnetometer_test.c @@ -30,9 +30,9 @@ main (void) { stdio_usb_init(); - sleep_ms(2000); +// sleep_ms(2000); - printf("Test started!\n"); +// printf("Test started!\n"); magnetometer_init(); launch(); From 460394720d8d8e5eabbd4c80ada2d689e066339f Mon Sep 17 00:00:00 2001 From: xypoon <2837264P@student.gla.ac.uk> Date: Sat, 28 Oct 2023 14:04:07 +0800 Subject: [PATCH 06/11] Ultrasonic added Callibration --- frtos/ultrasonic_sensor/ultrasonic_sensor.c | 90 ++------------------- frtos/ultrasonic_sensor/ultrasonic_sensor.h | 69 +++++++++++----- 2 files changed, 58 insertions(+), 101 deletions(-) diff --git a/frtos/ultrasonic_sensor/ultrasonic_sensor.c b/frtos/ultrasonic_sensor/ultrasonic_sensor.c index 1c89e06..c5e7f6f 100644 --- a/frtos/ultrasonic_sensor/ultrasonic_sensor.c +++ b/frtos/ultrasonic_sensor/ultrasonic_sensor.c @@ -3,89 +3,15 @@ #include "task.h" #include -#define TRIG_PIN 0 -#define ECHO_PIN 1 - -int16_t counter = 0; - -void -init_ultrasonic(void) -{ - // Set up the echo pin - gpio_init(ECHO_PIN); - gpio_set_dir(ECHO_PIN, GPIO_IN); - - // Set up the trigger pin - gpio_init(TRIG_PIN); - gpio_set_dir(TRIG_PIN, GPIO_OUT); -} - -float -KalmanFilter(float U) -{ - static float R = 10; // noise convariance can be 10, higher better smooth - static float H = 1; // Measurement Map scalar - static float Q = 10; // initial estimated convariance - static float P = 0; // initial error covariance - static float U_hat = 0; // initial estimated state - static float K = 0; // initial Kalman gain - - // Predict - // - K = P * H / (H * P * H + R); // Update Kalman gain - U_hat = U_hat + K * (U - H * U_hat); // Update estimated state - - // Update error covariance - // - P = (1 - K * H) * P + Q; - - return U_hat; -} - -void -distance_task(__unused void *params) -{ - while (true) - { - vTaskDelay(1000); - - // Trigger the ultrasonic sensor - gpio_put(TRIG_PIN, 1); - sleep_us(10); // Keep the trigger on for 10 microseconds - gpio_put(TRIG_PIN, 0); - - // Wait for the echo pulse to start - while (gpio_get(ECHO_PIN) == 0) - tight_loop_contents(); - - // Measure the pulse width (time taken for the echo to return) - uint32_t start_time = time_us_32(); - while (gpio_get(ECHO_PIN) == 1) - tight_loop_contents(); - uint32_t end_time = time_us_32(); - - // Calculate the distance (in centimeters) - uint32_t pulse_duration = end_time - start_time; - float distance = pulse_duration * 0.017; // Speed of sound at ~343 m/s - - // printf("Distance: %.2f cm\n", distance); - // printf("Kalman Filtered Distance: %.2f cm\n", KalmanFilter(distance)); - printf("%d,%.2f,%.2f\n", counter++, distance, KalmanFilter(distance)); - - // If gonna bang wall - // - if (distance < 5) - { - printf("Collision Imminent!\n"); - // Proc stop - // - } - } -} +#include "ultrasonic_sensor.h" void vLaunch(void) -{ +{ + gpio_set_irq_enabled_with_callback(ECHO_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, echo_handler); + + irq_set_enabled(IO_IRQ_BANK0, true); + TaskHandle_t disttask; xTaskCreate(distance_task, "TestDistThread", @@ -94,8 +20,6 @@ vLaunch(void) 1, &disttask); - // Start the tasks and timer running. - // vTaskStartScheduler(); } @@ -104,7 +28,7 @@ main(void) { stdio_init_all(); init_ultrasonic(); - sleep_ms(3000); + sleep_ms(1000); vLaunch(); return 0; diff --git a/frtos/ultrasonic_sensor/ultrasonic_sensor.h b/frtos/ultrasonic_sensor/ultrasonic_sensor.h index b61fc76..f5f1899 100644 --- a/frtos/ultrasonic_sensor/ultrasonic_sensor.h +++ b/frtos/ultrasonic_sensor/ultrasonic_sensor.h @@ -9,40 +9,73 @@ #include "ultrasonic_init.h" -void distance_task(__unused void *params) +volatile uint32_t start_time; +volatile uint32_t end_time; +volatile bool echo_rising = false; + +float +KalmanFilter(float U) +{ + static float R = 10; // noise convariance can be 10, higher better smooth + static float H = 1; // Measurement Map scalar + static float Q = 10; // initial estimated convariance + static float P = 0; // initial error covariance + static float U_hat = 0; // initial estimated state + static float K = 0; // initial Kalman gain + + // Predict + // + K = P * H / (H * P * H + R); // Update Kalman gain + U_hat = U_hat + K * (U - H * U_hat); // Update estimated state + + // Update error covariance + // + P = (1 - K * H) * P + Q; + + return U_hat; +} + +void +echo_handler() +{ + if (gpio_get(ECHO_PIN)) + { + start_time = time_us_32(); + echo_rising = true; + } + else + { + end_time = time_us_32(); + echo_rising = false; + } +} + +void +distance_task(__unused void *params) { while (true) { - vTaskDelay(500); + vTaskDelay(1000); - // Trigger the ultrasonic sensor gpio_put(TRIG_PIN, 1); - sleep_us(10); // Keep the trigger on for 10 microseconds + sleep_us(10); gpio_put(TRIG_PIN, 0); - // Wait for the echo pulse to start - while (gpio_get(ECHO_PIN) == 0) + while (echo_rising) + { tight_loop_contents(); - - // Measure the pulse width (time taken for the echo to return) - uint32_t start_time = time_us_32(); - while (gpio_get(ECHO_PIN) == 1) - tight_loop_contents(); - uint32_t end_time = time_us_32(); + } // Calculate the distance (in centimeters) uint32_t pulse_duration = end_time - start_time; - float distance = pulse_duration * 0.017; // Speed of sound at ~343 m/s + float distance = (pulse_duration * 0.034 / 2) * 1.125; // Speed of sound in cm/us - printf("Distance: %.2f cm\n", distance); + // printf("Distance: %.2f cm\n", distance); + printf("Kalman Filtered Distance: %.2f cm\n", KalmanFilter(distance)); - // If gonna bang wall - // if (distance < 5) { printf("Collision Imminent!\n"); - // Proc stop - // } } } From e8f8d31b8565d0d600a74d3cb6bfece30996bc32 Mon Sep 17 00:00:00 2001 From: Richie <2837357W@student.gla.ac.uk> Date: Sun, 29 Oct 2023 17:54:33 +0800 Subject: [PATCH 07/11] pending PID tuning; now car moves in S shape --- frtos/config/motor_config.h | 1 - frtos/motor/motor_direction.h | 26 ++++++++++++--- frtos/motor/motor_init.h | 14 ++------ frtos/motor/motor_pid.h | 63 +++++++++++++++++------------------ frtos/motor/motor_speed.h | 7 +--- frtos/motor/motor_test.c | 55 ++---------------------------- 6 files changed, 58 insertions(+), 108 deletions(-) diff --git a/frtos/config/motor_config.h b/frtos/config/motor_config.h index 86b995d..267d672 100644 --- a/frtos/config/motor_config.h +++ b/frtos/config/motor_config.h @@ -34,7 +34,6 @@ * @param distance_cm Distance travelled in cm */ typedef struct { - float target_cms; float current_cms; float distance_cm; } motor_speed_t; diff --git a/frtos/motor/motor_direction.h b/frtos/motor/motor_direction.h index 1fe604c..f81273f 100644 --- a/frtos/motor/motor_direction.h +++ b/frtos/motor/motor_direction.h @@ -17,14 +17,30 @@ * @param right_speed The speed of the right motor, from 0.0 to 1.0 */ void -set_wheel_direction (uint32_t direction) +set_wheel_direction(uint32_t direction) { - static const uint32_t mask = DIRECTION_LEFT_FORWARD | - DIRECTION_LEFT_BACKWARD | - DIRECTION_RIGHT_FORWARD | - DIRECTION_RIGHT_BACKWARD; + static const uint32_t mask + = DIRECTION_LEFT_FORWARD | DIRECTION_LEFT_BACKWARD + | DIRECTION_RIGHT_FORWARD | DIRECTION_RIGHT_BACKWARD; gpio_put_masked(mask, 0U); gpio_set_mask(direction); } +/*! + * @brief Set the speed of the wheels + * @param speed The speed of the wheels, from 0 to 5000 + */ +void +set_wheel_speed(uint32_t speed) +{ + g_motor_right.pwm.level = speed; + g_motor_left.pwm.level = speed; + + pwm_set_chan_level(g_motor_right.pwm.slice_num, + g_motor_right.pwm.channel, + g_motor_right.pwm.level); + pwm_set_chan_level(g_motor_left.pwm.slice_num, + g_motor_left.pwm.channel, + g_motor_left.pwm.level); +} \ No newline at end of file diff --git a/frtos/motor/motor_init.h b/frtos/motor/motor_init.h index da9b684..5a4f2ed 100644 --- a/frtos/motor/motor_init.h +++ b/frtos/motor/motor_init.h @@ -19,14 +19,6 @@ #include "motor_config.h" -// TODO: tune pid for both wheels again -/* - * ultimate gain Ku about 14, ultimate period Tu about 8 * 50 = 400ms - * Ku = 14, Tu = 400ms, - * Kp = 0.6 * Ku = 8.4 - * Ki = Kp / Tu = 0.021 - * Kd = Kp * Tu / 8 = 42 - */ motor_t g_motor_left = { .pwm.level = 0u, .pwm.channel = PWM_CHAN_A, .speed.distance_cm = 0.0f, @@ -37,9 +29,9 @@ motor_t g_motor_left = { .pwm.level = 0u, motor_t g_motor_right = { .pwm.level = 0u, .pwm.channel = PWM_CHAN_B, .speed.distance_cm = 0.0f, - .pid.kp_value = 0.0f, - .pid.ki_value = 0.0f, - .pid.kd_value = 0.0f,}; + .pid.kp_value = 8.4f, + .pid.ki_value = 0.021f, + .pid.kd_value = 42.0f,}; void motor_init(void) diff --git a/frtos/motor/motor_pid.h b/frtos/motor/motor_pid.h index 2eedd86..af112da 100644 --- a/frtos/motor/motor_pid.h +++ b/frtos/motor/motor_pid.h @@ -16,20 +16,20 @@ * @return The control signal */ float -compute_pid(const volatile motor_t *p_motor, - float *integral, - float *prev_error) +compute_pid(float *integral, float *prev_error) { - float error = p_motor->speed.target_cms - p_motor->speed.current_cms; + float error + = g_motor_left.speed.distance_cm - g_motor_right.speed.distance_cm; + + printf("error: %f\n", error); *integral += error; float derivative = error - *prev_error; - float control_signal - = p_motor->pid.kp_value * error + - p_motor->pid.ki_value * (*integral) + - p_motor->pid.kd_value * derivative; + float control_signal = g_motor_right.pid.kp_value * error + + g_motor_right.pid.ki_value * (*integral) + + g_motor_right.pid.kd_value * derivative; *prev_error = error; @@ -37,45 +37,42 @@ compute_pid(const volatile motor_t *p_motor, } void -motor_pid_task(void *p_param) +motor_pid_task(__unused void *p_param) { - motor_t *p_motor = p_param; - float integral = 0.0f; - float prev_error = 0.0f; + float integral = 0.0f; + float prev_error = 0.0f; for (;;) { - if (p_motor->speed.target_cms == 0.0f) + if (g_motor_left.pwm.level == 0u) { - p_motor->pwm.level = 0; - pwm_set_chan_level(p_motor->pwm.slice_num, - p_motor->pwm.channel, - p_motor->pwm.level); + g_motor_right.pwm.level = 0; + pwm_set_chan_level(g_motor_right.pwm.slice_num, + g_motor_right.pwm.channel, + g_motor_right.pwm.level); vTaskDelay(pdMS_TO_TICKS(50)); continue; } - float control_signal = compute_pid(p_motor, &integral, &prev_error); + float control_signal = compute_pid(&integral, &prev_error); - if (p_motor->pwm.level + control_signal > MAX_SPEED) + float temp = (float) g_motor_right.pwm.level + control_signal; + + if (temp > MAX_SPEED) { - p_motor->pwm.level = MAX_SPEED; - } - else if (p_motor->pwm.level + control_signal < MIN_SPEED) - { - p_motor->pwm.level = MIN_SPEED; - } - else - { - p_motor->pwm.level = p_motor->pwm.level + control_signal; + temp = MAX_SPEED; } - // printf("control signal: %f\n", control_signal); - // printf("new pwm: %hu\n\n", p_motor_speed->level); + if (temp < MIN_SPEED) + { + temp = MIN_SPEED; + } - pwm_set_chan_level(p_motor->pwm.slice_num, - p_motor->pwm.channel, - p_motor->pwm.level); + g_motor_right.pwm.level = (uint16_t) temp; + + pwm_set_chan_level(g_motor_right.pwm.slice_num, + g_motor_right.pwm.channel, + g_motor_right.pwm.level); vTaskDelay(pdMS_TO_TICKS(50)); } diff --git a/frtos/motor/motor_speed.h b/frtos/motor/motor_speed.h index f53dcdd..520fbee 100644 --- a/frtos/motor/motor_speed.h +++ b/frtos/motor/motor_speed.h @@ -63,18 +63,13 @@ monitor_wheel_speed_task(void *pvParameters) // circumference = 2 * pi * 3.25 cm = 20.4203522483 cm // distance = 20.4203522483 cm / 20 = 1.02101761242 cm p_motor->speed.current_cms - = (float) (1.02101761242f / (elapsed_time / 1000000.f)); + = (float) (1021017.61242f / elapsed_time); p_motor->speed.distance_cm += 1.02101761242f; - - // printf("speed: %f cm/s\n", p_motor_speed->current_cms); } else { p_motor->speed.current_cms = 0.f; - // printf("stopped\n"); } - - // printf("distance: %f cm\n", p_motor_speed->distance); } } \ No newline at end of file diff --git a/frtos/motor/motor_test.c b/frtos/motor/motor_test.c index c248610..4da2f21 100644 --- a/frtos/motor/motor_test.c +++ b/frtos/motor/motor_test.c @@ -6,42 +6,6 @@ #define WHEEL_SPEED_PRIO (tskIDLE_PRIORITY + 1UL) -void -test_speed_change_task(void *p_param) -{ - for (;;) - { - g_motor_left.speed.target_cms = 30.0f; - g_motor_right.speed.target_cms = 30.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); - - g_motor_left.speed.target_cms = 20.0f; - g_motor_right.speed.target_cms = 20.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); - - g_motor_left.speed.target_cms = 0.0f; - g_motor_right.speed.target_cms = 0.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); - - set_wheel_direction(DIRECTION_LEFT_BACKWARD | DIRECTION_RIGHT_BACKWARD); - - g_motor_left.speed.target_cms = 30.0f; - g_motor_right.speed.target_cms = 30.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); - - g_motor_left.speed.target_cms = 20.0f; - g_motor_right.speed.target_cms = 20.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); - - g_motor_left.speed.target_cms = 0.0f; - g_motor_right.speed.target_cms = 0.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); - - set_wheel_direction(DIRECTION_LEFT_FORWARD | DIRECTION_RIGHT_FORWARD); - - } -} - void launch() { @@ -55,6 +19,9 @@ launch() irq_set_enabled(IO_IRQ_BANK0, true); + // Set wheel speed + set_wheel_speed(3000); + // Left wheel // TaskHandle_t h_monitor_left_wheel_speed_task_handle = NULL; @@ -65,13 +32,6 @@ launch() WHEEL_SPEED_PRIO, &h_monitor_left_wheel_speed_task_handle); - TaskHandle_t h_motor_pid_left_task_handle = NULL; - xTaskCreate(motor_pid_task, - "motor_pid_task", - configMINIMAL_STACK_SIZE, - (void *)&g_motor_left, - WHEEL_SPEED_PRIO, - &h_motor_pid_left_task_handle); // Right wheel // @@ -91,15 +51,6 @@ launch() WHEEL_SPEED_PRIO, &h_motor_pid_right_task_handle); - // Test speed change - // - TaskHandle_t h_test_speed_change_task_handle = NULL; - xTaskCreate(test_speed_change_task, - "test_speed_change_task", - configMINIMAL_STACK_SIZE, - NULL, - WHEEL_SPEED_PRIO, - &h_test_speed_change_task_handle); vTaskStartScheduler(); } From f176192dce2e5ab78669ef4e5ac0555072c6ac11 Mon Sep 17 00:00:00 2001 From: Richie <2837357W@student.gla.ac.uk> Date: Sun, 29 Oct 2023 20:26:13 +0800 Subject: [PATCH 08/11] PID 99%, suspect wheel issue as if I swap the wheels the car slants opposite direction, for a bit; but generally straight for about 1m, should be enough for the map --- frtos/motor/motor_init.h | 11 ++++------- frtos/motor/motor_pid.h | 4 +--- frtos/motor/motor_test.c | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/frtos/motor/motor_init.h b/frtos/motor/motor_init.h index 5a4f2ed..56cb390 100644 --- a/frtos/motor/motor_init.h +++ b/frtos/motor/motor_init.h @@ -21,17 +21,14 @@ motor_t g_motor_left = { .pwm.level = 0u, .pwm.channel = PWM_CHAN_A, - .speed.distance_cm = 0.0f, - .pid.kp_value = 8.4f, - .pid.ki_value = 0.021f, - .pid.kd_value = 42.f,}; + .speed.distance_cm = 0.0f }; motor_t g_motor_right = { .pwm.level = 0u, .pwm.channel = PWM_CHAN_B, .speed.distance_cm = 0.0f, - .pid.kp_value = 8.4f, - .pid.ki_value = 0.021f, - .pid.kd_value = 42.0f,}; + .pid.kp_value = 1000.f, + .pid.ki_value = 0.0f, + .pid.kd_value = 10000.0f,}; void motor_init(void) diff --git a/frtos/motor/motor_pid.h b/frtos/motor/motor_pid.h index af112da..c741ef6 100644 --- a/frtos/motor/motor_pid.h +++ b/frtos/motor/motor_pid.h @@ -21,8 +21,6 @@ compute_pid(float *integral, float *prev_error) float error = g_motor_left.speed.distance_cm - g_motor_right.speed.distance_cm; - printf("error: %f\n", error); - *integral += error; float derivative = error - *prev_error; @@ -56,7 +54,7 @@ motor_pid_task(__unused void *p_param) float control_signal = compute_pid(&integral, &prev_error); - float temp = (float) g_motor_right.pwm.level + control_signal; + float temp = (float) g_motor_right.pwm.level + control_signal * 0.05f; if (temp > MAX_SPEED) { diff --git a/frtos/motor/motor_test.c b/frtos/motor/motor_test.c index 4da2f21..a5f16ff 100644 --- a/frtos/motor/motor_test.c +++ b/frtos/motor/motor_test.c @@ -20,7 +20,7 @@ launch() irq_set_enabled(IO_IRQ_BANK0, true); // Set wheel speed - set_wheel_speed(3000); + set_wheel_speed(3500); // Left wheel // @@ -60,7 +60,7 @@ main(void) { stdio_usb_init(); - sleep_ms(2000); + sleep_ms(4000); printf("Test started!\n"); motor_init(); From 5cb4fae0c96a8e8ba7bde4e2006d8f4d4ffab8f6 Mon Sep 17 00:00:00 2001 From: OldManSteve Date: Mon, 30 Oct 2023 11:03:55 +0800 Subject: [PATCH 09/11] frontend first push --- frtos/CMakeLists.txt | 2 + frtos/frontend/CMakeLists.txt | 39 +++ frtos/frontend/FreeRTOSConfig.h | 143 ++++++++++ frtos/frontend/cgi.h | 60 ++++ frtos/frontend/frontend.c | 56 ++++ frtos/frontend/html_files/index.shtml | 129 +++++++++ frtos/frontend/htmldata.c | 384 ++++++++++++++++++++++++++ frtos/frontend/lwipopts.h | 93 +++++++ frtos/frontend/makefsdata.c | 129 +++++++++ frtos/frontend/makefsdata.exe | Bin 0 -> 265302 bytes frtos/frontend/makefsdata.py | 106 +++++++ frtos/frontend/ssi.h | 56 ++++ 12 files changed, 1197 insertions(+) create mode 100644 frtos/frontend/CMakeLists.txt create mode 100644 frtos/frontend/FreeRTOSConfig.h create mode 100644 frtos/frontend/cgi.h create mode 100644 frtos/frontend/frontend.c create mode 100644 frtos/frontend/html_files/index.shtml create mode 100644 frtos/frontend/htmldata.c create mode 100644 frtos/frontend/lwipopts.h create mode 100644 frtos/frontend/makefsdata.c create mode 100644 frtos/frontend/makefsdata.exe create mode 100644 frtos/frontend/makefsdata.py create mode 100644 frtos/frontend/ssi.h diff --git a/frtos/CMakeLists.txt b/frtos/CMakeLists.txt index be93bcf..6aa36ca 100644 --- a/frtos/CMakeLists.txt +++ b/frtos/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(line_sensor) add_subdirectory(car) add_subdirectory(ultrasonic_sensor) add_subdirectory(magnetometer) +add_subdirectory(frontend) add_executable(rtos_car rtos_car.c) @@ -20,6 +21,7 @@ target_include_directories(rtos_car PRIVATE ${CMAKE_CURRENT_LIST_DIR}/motor ${CMAKE_CURRENT_LIST_DIR}/line_sensor ${CMAKE_CURRENT_LIST_DIR}/ultrasonic_sensor + ${CMAKE_CURRENT_LIST_DIR}/frontend ) target_link_libraries(rtos_car pico_cyw43_arch_lwip_sys_freertos diff --git a/frtos/frontend/CMakeLists.txt b/frtos/frontend/CMakeLists.txt new file mode 100644 index 0000000..5edf451 --- /dev/null +++ b/frtos/frontend/CMakeLists.txt @@ -0,0 +1,39 @@ +add_executable( + frontend + frontend.c + ) + +#message("Running makefsdata python script") +#execute_process(COMMAND +# py makefsdata.py +# WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +#) + +message("Running makefsdata C script") +execute_process(COMMAND + gcc makefsdata.c -o makefsdata.exe + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +) +message("Generating htmldata.c") +execute_process(COMMAND + ./makefsdata.exe + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +) + +target_link_libraries( + frontend + hardware_adc + pico_stdlib + #FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap + pico_cyw43_arch_lwip_threadsafe_background + pico_lwip_http + ) + +target_include_directories(frontend PRIVATE + #../config + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}../config + ) + +pico_enable_stdio_usb(frontend 1) +pico_add_extra_outputs(frontend) \ No newline at end of file diff --git a/frtos/frontend/FreeRTOSConfig.h b/frtos/frontend/FreeRTOSConfig.h new file mode 100644 index 0000000..f715e60 --- /dev/null +++ b/frtos/frontend/FreeRTOSConfig.h @@ -0,0 +1,143 @@ +/* + * FreeRTOS V202111.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* Scheduler Related */ +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 +#define configUSE_16_BIT_TICKS 0 + +#define configIDLE_SHOULD_YIELD 1 + +/* Synchronization Related */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 0 +// todo need this for lwip FreeRTOS sys_arch to compile +#define configENABLE_BACKWARD_COMPATIBILITY 1 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 + +/* System */ +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Memory allocation related definitions. */ +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (128*1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Hook function related definitions. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine related definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +/* Interrupt nesting behaviour configuration. */ +/* +#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] +#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] +#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] +*/ + +#if FREE_RTOS_KERNEL_SMP // set by the RP2040 SMP port of FreeRTOS +/* SMP port only */ +#define configNUM_CORES 1 +#define configTICK_CORE 0 +#define configRUN_MULTIPLE_PRIORITIES 1 +#define configUSE_CORE_AFFINITY 0 +#endif + +/* RP2040 specific */ +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include +/* Define to trap errors during development. */ +#define configASSERT(x) assert(x) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + +/* A header file that defines trace macro can be included here. */ + +#endif /* FREERTOS_CONFIG_H */ + diff --git a/frtos/frontend/cgi.h b/frtos/frontend/cgi.h new file mode 100644 index 0000000..39a9f99 --- /dev/null +++ b/frtos/frontend/cgi.h @@ -0,0 +1,60 @@ +#include "lwip/apps/httpd.h" +#include "pico/cyw43_arch.h" +#include "stdio.h" + +// CGI handler for start/stop car +const char * cgi_status_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) +{ + // Check if an request for LED has been made (/led.cgi?led=x) + // check if start/stop car button + if (strcmp(pcParam[0] , "status") == 0){ + // Look at the argument to check if LED is to be turned on (x=1) or off (x=0) + if(strcmp(pcValue[0], "0") == 0){ + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); + printf("CAR STOP"); // call car stop func + }else if(strcmp(pcValue[0], "1") == 0){ + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); + printf("CAR START"); // call car start func + } + } + + // Send the index page back to the user + return "/index.shtml"; +} + +// CGI handler for speed control +const char * cgi_speed_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) +{ + // Check if an request for LED has been made (/led.cgi?led=x) + // check for speed increment/decrement control + if (strcmp(pcParam[0] , "speed") == 0){ + if(strcmp(pcValue[0], "0") == 0){ + printf("SPEED Decrease"); // call speed decrement + }else if(strcmp(pcValue[0], "1") == 0){ + printf("Speed Increase"); // call speed increment + } + } + + // Send the index page back to the user + return "/index.shtml"; +} + + + +// tCGI Struct +// Fill this with all of the CGI requests and their respective handlers +static const tCGI cgi_handlers[] = { + { + // Html request for "/status.cgi" triggers cgi_handler + "/status.cgi", cgi_status_handler + }, + { + // Html request for "/speed.cgi" triggers cgi_handler + "/speed.cgi", cgi_speed_handler + }, +}; + +void cgi_init(void) +{ + http_set_cgi_handlers(cgi_handlers, 2); +} \ No newline at end of file diff --git a/frtos/frontend/frontend.c b/frtos/frontend/frontend.c new file mode 100644 index 0000000..10ffb7f --- /dev/null +++ b/frtos/frontend/frontend.c @@ -0,0 +1,56 @@ +#include "lwip/apps/httpd.h" +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "lwipopts.h" +#include "ssi.h" +#include "cgi.h" +#include "lwip/inet.h" +//#include "FreeRTOS.h" +//#include "task.h" +#include "lwip/sockets.h" + +// WIFI Credentials - take care if pushing to GitHub! +const char WIFI_SSID[] = "XXX"; +const char WIFI_PASSWORD[] = "XXX"; + +void print_ip_address() { + struct netif *netif = netif_list; + while (netif != NULL) { + if (netif_is_up(netif)) { + printf("IP Address: %s\n", ipaddr_ntoa(&(netif->ip_addr))); + } + netif = netif->next; + } +} + +int main() { + stdio_init_all(); + + cyw43_arch_init(); + + cyw43_arch_enable_sta_mode(); + + // Connect to the WiFI network - loop until connected + while(cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000) != 0){ + printf("Attempting to connect...\n"); + } + // Print a success message once connected + printf("Connected! \n"); + + // Print the assigned IP address + print_ip_address(); + + // Initialize web server + httpd_init(); + printf("Http server initialized\n"); + + // Configure SSI and CGI handler + ssi_init(); + printf("SSI Handler initialized\n"); + cgi_init(); + printf("CGI Handler initialized\n"); + + // Infinite loop + while(1); + +} \ No newline at end of file diff --git a/frtos/frontend/html_files/index.shtml b/frtos/frontend/html_files/index.shtml new file mode 100644 index 0000000..a30f9a3 --- /dev/null +++ b/frtos/frontend/html_files/index.shtml @@ -0,0 +1,129 @@ + + + + + + +

Car Control Panel

+
+
+

0 seconds

+
+
+ + + + +
+
+

Speed:

+
+ + +
+
+
+

Barcode Scanner Output:

+
+
+

PID Controller Output:

+
+
+

Orientation:

+
+
+ +
+
+ + + diff --git a/frtos/frontend/htmldata.c b/frtos/frontend/htmldata.c new file mode 100644 index 0000000..8faf14a --- /dev/null +++ b/frtos/frontend/htmldata.c @@ -0,0 +1,384 @@ +static const unsigned char data_index_shtml[] = { + /* ./index.shtml */ + 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x73, 0x68, 0x74, 0x6D, 0x6C, 0x00, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x3A, 0x20, 0x6C, 0x77, 0x49, 0x50, 0x2F, + 0x70, 0x72, 0x65, 0x2D, 0x30, 0x2E, 0x36, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, + 0x73, 0x69, 0x63, 0x73, 0x2E, 0x73, 0x65, 0x2F, 0x7E, 0x61, + 0x64, 0x61, 0x6D, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x2F, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x0D, 0x0A, 0x0D, 0x0A, + 0x3C, 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, + 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0D, 0x0A, 0x3C, 0x68, 0x74, + 0x6D, 0x6C, 0x3E, 0x0D, 0x0A, 0x3C, 0x68, 0x65, 0x61, 0x64, + 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x74, + 0x79, 0x6C, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x62, 0x6F, 0x64, 0x79, 0x20, 0x7B, + 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x66, + 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x20, 0x41, 0x72, 0x69, + 0x61, 0x6C, 0x2C, 0x20, 0x73, 0x61, 0x6E, 0x73, 0x2D, 0x73, + 0x65, 0x72, 0x69, 0x66, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, + 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0D, + 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x68, 0x31, 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x33, 0x33, 0x33, + 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2E, 0x63, 0x6F, 0x6E, 0x74, 0x61, + 0x69, 0x6E, 0x65, 0x72, 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x20, 0x66, + 0x6C, 0x65, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6C, + 0x65, 0x78, 0x2D, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6F, 0x6E, 0x3A, 0x20, 0x63, 0x6F, 0x6C, 0x75, 0x6D, 0x6E, + 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E, + 0x2D, 0x69, 0x74, 0x65, 0x6D, 0x73, 0x3A, 0x20, 0x63, 0x65, + 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2E, 0x62, + 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x73, 0x20, 0x7B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, + 0x20, 0x66, 0x6C, 0x65, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6A, 0x75, 0x73, 0x74, 0x69, 0x66, 0x79, 0x2D, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3A, 0x20, 0x63, 0x65, 0x6E, + 0x74, 0x65, 0x72, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x61, + 0x70, 0x3A, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2E, 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x20, + 0x7B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6E, 0x67, 0x3A, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x32, + 0x30, 0x70, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x38, 0x70, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x30, 0x30, 0x37, + 0x34, 0x64, 0x39, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, + 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6F, 0x72, 0x64, 0x65, + 0x72, 0x3A, 0x20, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6F, 0x72, 0x3A, 0x20, + 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0D, + 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2E, 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x7B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, + 0x7A, 0x65, 0x3A, 0x20, 0x32, 0x34, 0x70, 0x78, 0x3B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, + 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, + 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2E, 0x73, 0x70, 0x65, 0x65, 0x64, 0x2D, + 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x20, 0x7B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, + 0x3A, 0x20, 0x66, 0x6C, 0x65, 0x78, 0x3B, 0x0D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x2D, 0x69, 0x74, 0x65, + 0x6D, 0x73, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, + 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6A, 0x75, 0x73, 0x74, 0x69, + 0x66, 0x79, 0x2D, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, + 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2E, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, + 0x7B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x32, 0x34, 0x70, 0x78, + 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x30, 0x70, + 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2E, 0x70, 0x69, 0x64, 0x2D, + 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x7B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x32, 0x34, 0x70, 0x78, 0x3B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2E, 0x6F, 0x72, 0x69, 0x65, 0x6E, 0x74, 0x61, + 0x74, 0x69, 0x6F, 0x6E, 0x2D, 0x6F, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x32, 0x34, + 0x70, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x32, + 0x30, 0x70, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2E, 0x6D, 0x61, + 0x7A, 0x65, 0x2D, 0x6D, 0x61, 0x70, 0x20, 0x7B, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, + 0x32, 0x30, 0x30, 0x70, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x32, 0x30, + 0x30, 0x70, 0x78, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6F, + 0x72, 0x64, 0x65, 0x72, 0x3A, 0x20, 0x31, 0x70, 0x78, 0x20, + 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, 0x33, 0x33, 0x33, + 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, + 0x73, 0x74, 0x79, 0x6C, 0x65, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, + 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0D, 0x0A, 0x3C, 0x62, 0x6F, + 0x64, 0x79, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, + 0x68, 0x31, 0x3E, 0x43, 0x61, 0x72, 0x20, 0x43, 0x6F, 0x6E, + 0x74, 0x72, 0x6F, 0x6C, 0x20, 0x50, 0x61, 0x6E, 0x65, 0x6C, + 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, + 0x73, 0x3D, 0x22, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, + 0x65, 0x72, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x74, 0x69, 0x6D, 0x65, + 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x70, 0x20, 0x69, + 0x64, 0x3D, 0x22, 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, + 0x2D, 0x74, 0x69, 0x6D, 0x65, 0x22, 0x3E, 0x30, 0x20, 0x73, + 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x3C, 0x2F, 0x70, 0x3E, + 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, + 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x62, 0x75, + 0x74, 0x74, 0x6F, 0x6E, 0x73, 0x22, 0x3E, 0x0D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, + 0x2F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2E, 0x63, 0x67, + 0x69, 0x3F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3D, 0x31, + 0x22, 0x20, 0x3E, 0x3C, 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, + 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x62, 0x75, + 0x74, 0x74, 0x6F, 0x6E, 0x22, 0x20, 0x3E, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x3C, 0x2F, 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, + 0x3E, 0x3C, 0x2F, 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, + 0x3D, 0x22, 0x2F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2E, + 0x63, 0x67, 0x69, 0x3F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x3D, 0x30, 0x22, 0x20, 0x3E, 0x3C, 0x62, 0x75, 0x74, 0x74, + 0x6F, 0x6E, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x22, 0x20, 0x3E, 0x53, + 0x74, 0x6F, 0x70, 0x3C, 0x2F, 0x62, 0x75, 0x74, 0x74, 0x6F, + 0x6E, 0x3E, 0x3C, 0x2F, 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, + 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, + 0x73, 0x73, 0x3D, 0x22, 0x73, 0x70, 0x65, 0x65, 0x64, 0x22, + 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x70, 0x3E, 0x53, 0x70, + 0x65, 0x65, 0x64, 0x3A, 0x20, 0x3C, 0x21, 0x2D, 0x2D, 0x23, + 0x73, 0x70, 0x65, 0x65, 0x64, 0x2D, 0x2D, 0x3E, 0x3C, 0x2F, + 0x73, 0x70, 0x61, 0x6E, 0x3E, 0x3C, 0x2F, 0x70, 0x3E, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, + 0x61, 0x73, 0x73, 0x3D, 0x22, 0x73, 0x70, 0x65, 0x65, 0x64, + 0x2D, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x22, 0x3E, + 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x61, + 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, 0x2F, 0x73, 0x70, + 0x65, 0x65, 0x64, 0x2E, 0x63, 0x67, 0x69, 0x3F, 0x73, 0x70, + 0x65, 0x65, 0x64, 0x3D, 0x30, 0x22, 0x20, 0x3E, 0x3C, 0x62, + 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x20, 0x63, 0x6C, 0x61, 0x73, + 0x73, 0x3D, 0x22, 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x22, + 0x20, 0x3E, 0x2D, 0x3C, 0x2F, 0x62, 0x75, 0x74, 0x74, 0x6F, + 0x6E, 0x3E, 0x3C, 0x2F, 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x2F, 0x73, 0x70, 0x65, 0x65, 0x64, 0x2E, + 0x63, 0x67, 0x69, 0x3F, 0x73, 0x70, 0x65, 0x65, 0x64, 0x3D, + 0x31, 0x22, 0x20, 0x3E, 0x3C, 0x62, 0x75, 0x74, 0x74, 0x6F, + 0x6E, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x62, + 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x22, 0x20, 0x3E, 0x2B, 0x3C, + 0x2F, 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x3E, 0x3C, 0x2F, + 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, + 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, + 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x3E, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3C, 0x70, 0x3E, 0x42, 0x61, 0x72, 0x63, 0x6F, + 0x64, 0x65, 0x20, 0x53, 0x63, 0x61, 0x6E, 0x6E, 0x65, 0x72, + 0x20, 0x4F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3A, 0x20, 0x3C, + 0x21, 0x2D, 0x2D, 0x23, 0x62, 0x61, 0x72, 0x63, 0x6F, 0x64, + 0x65, 0x2D, 0x2D, 0x3E, 0x3C, 0x2F, 0x73, 0x70, 0x61, 0x6E, + 0x3E, 0x3C, 0x2F, 0x70, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x76, + 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, + 0x73, 0x3D, 0x22, 0x70, 0x69, 0x64, 0x2D, 0x6F, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, + 0x70, 0x3E, 0x50, 0x49, 0x44, 0x20, 0x43, 0x6F, 0x6E, 0x74, + 0x72, 0x6F, 0x6C, 0x6C, 0x65, 0x72, 0x20, 0x4F, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x3A, 0x20, 0x3C, 0x21, 0x2D, 0x2D, 0x23, + 0x70, 0x69, 0x64, 0x2D, 0x2D, 0x3E, 0x3C, 0x2F, 0x70, 0x3E, + 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, + 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x6F, 0x72, + 0x69, 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2D, + 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x3E, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3C, 0x70, 0x3E, 0x4F, 0x72, 0x69, 0x65, 0x6E, + 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x3C, 0x21, + 0x2D, 0x2D, 0x23, 0x6F, 0x72, 0x69, 0x65, 0x6E, 0x74, 0x2D, + 0x2D, 0x3E, 0x3C, 0x2F, 0x70, 0x3E, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, + 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, + 0x73, 0x73, 0x3D, 0x22, 0x6D, 0x61, 0x7A, 0x65, 0x2D, 0x6D, + 0x61, 0x70, 0x22, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x6D, 0x61, + 0x7A, 0x65, 0x2D, 0x6D, 0x61, 0x70, 0x22, 0x3E, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3C, 0x21, 0x2D, 0x2D, 0x20, 0x6D, 0x61, 0x7A, + 0x65, 0x20, 0x6D, 0x61, 0x70, 0x20, 0x70, 0x61, 0x72, 0x74, + 0x20, 0x2D, 0x2D, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, + 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, + 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x46, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, + 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6E, + 0x64, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x20, + 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, 0x20, 0x74, 0x69, + 0x6D, 0x65, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6C, 0x61, + 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6D, 0x65, 0x28, 0x29, + 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x43, + 0x68, 0x65, 0x63, 0x6B, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x69, + 0x6D, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x6F, 0x72, + 0x65, 0x64, 0x20, 0x69, 0x6E, 0x20, 0x6C, 0x6F, 0x63, 0x61, + 0x6C, 0x53, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6C, 0x65, 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x54, 0x69, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x6C, 0x6F, + 0x63, 0x61, 0x6C, 0x53, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, + 0x2E, 0x67, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6D, 0x28, 0x27, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x27, + 0x29, 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x21, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x66, 0x20, 0x6E, + 0x6F, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x69, + 0x6D, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x6F, 0x72, + 0x65, 0x64, 0x2C, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x20, + 0x74, 0x69, 0x6D, 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x69, + 0x6D, 0x65, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x20, + 0x3D, 0x20, 0x44, 0x61, 0x74, 0x65, 0x2E, 0x6E, 0x6F, 0x77, + 0x28, 0x29, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x53, 0x74, 0x6F, 0x72, + 0x61, 0x67, 0x65, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x74, 0x65, + 0x6D, 0x28, 0x27, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6D, 0x65, 0x27, 0x2C, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x54, 0x69, 0x6D, 0x65, 0x29, 0x3B, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x43, 0x61, 0x6C, 0x63, 0x75, 0x6C, 0x61, 0x74, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, + 0x64, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x69, 0x6E, 0x20, + 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x0D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6E, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x20, 0x3D, + 0x20, 0x44, 0x61, 0x74, 0x65, 0x2E, 0x6E, 0x6F, 0x77, 0x28, + 0x29, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, + 0x74, 0x20, 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, + 0x69, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x4D, 0x61, 0x74, 0x68, + 0x2E, 0x66, 0x6C, 0x6F, 0x6F, 0x72, 0x28, 0x28, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6E, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x20, + 0x2D, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6D, + 0x65, 0x29, 0x20, 0x2F, 0x20, 0x31, 0x30, 0x30, 0x30, 0x29, + 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x64, + 0x20, 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, 0x20, 0x74, + 0x69, 0x6D, 0x65, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6F, 0x63, + 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, + 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, + 0x28, 0x27, 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, 0x2D, + 0x74, 0x69, 0x6D, 0x65, 0x27, 0x29, 0x2E, 0x74, 0x65, 0x78, + 0x74, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x20, 0x3D, + 0x20, 0x60, 0x24, 0x7B, 0x65, 0x6C, 0x61, 0x70, 0x73, 0x65, + 0x64, 0x54, 0x69, 0x6D, 0x65, 0x7D, 0x20, 0x73, 0x65, 0x63, + 0x6F, 0x6E, 0x64, 0x73, 0x60, 0x3B, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, + 0x2F, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6C, 0x61, + 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6D, 0x65, 0x20, 0x66, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x77, 0x68, + 0x65, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x67, + 0x65, 0x20, 0x6C, 0x6F, 0x61, 0x64, 0x73, 0x0D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x6E, + 0x64, 0x6F, 0x77, 0x2E, 0x6F, 0x6E, 0x6C, 0x6F, 0x61, 0x64, + 0x20, 0x3D, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, + 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6D, 0x65, + 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6C, 0x61, + 0x70, 0x73, 0x65, 0x64, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, + 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x73, 0x65, 0x63, 0x6F, + 0x6E, 0x64, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6E, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6C, 0x28, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x45, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6D, + 0x65, 0x2C, 0x20, 0x31, 0x30, 0x30, 0x30, 0x29, 0x3B, 0x0D, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, + 0x2F, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, 0x0D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, + 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x68, + 0x74, 0x6D, 0x6C, 0x3E, 0x0D, 0x0A, }; + +const struct fsdata_file file_index_shtml[] = {{ NULL, data_index_shtml, data_index_shtml + 13, sizeof(data_index_shtml) - 13, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT }}; + +#define FS_ROOT file_index_shtml +#define FS_NUMFILES 1 diff --git a/frtos/frontend/lwipopts.h b/frtos/frontend/lwipopts.h new file mode 100644 index 0000000..8b4e785 --- /dev/null +++ b/frtos/frontend/lwipopts.h @@ -0,0 +1,93 @@ +// Common settings used in most of the pico_w examples +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)] + +// allow override in some examples +#ifndef NO_SYS +#define NO_SYS 1 +#endif +// allow override in some examples +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif +#if PICO_CYW43_ARCH_POLL +#define MEM_LIBC_MALLOC 1 +#else +// MEM_LIBC_MALLOC is incompatible with non polling versions +#define MEM_LIBC_MALLOC 0 +#endif +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define MEMP_NUM_ARP_QUEUE 10 +#define PBUF_POOL_SIZE 24 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 +#define LWIP_RAW 1 +#define TCP_WND (8 * TCP_MSS) +#define TCP_MSS 1460 +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETCONN 0 +#define MEM_STATS 0 +#define SYS_STATS 0 +#define MEMP_STATS 0 +#define LINK_STATS 0 +// #define ETH_PAD_SIZE 2 +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_DHCP 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DNS 1 +#define LWIP_TCP_KEEPALIVE 1 +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 + +#ifndef NDEBUG +#define LWIP_DEBUG 1 +#define LWIP_STATS 1 +#define LWIP_STATS_DISPLAY 1 +#endif + +#define ETHARP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define INET_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define RAW_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define SYS_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define PPP_DEBUG LWIP_DBG_OFF +#define SLIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF + +// This section enables HTTPD server with SSI, SGI +// and tells server which converted HTML files to use +#define LWIP_HTTPD 1 +#define LWIP_HTTPD_SSI 1 +#define LWIP_HTTPD_CGI 1 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 +#define HTTPD_FSDATA_FILE "htmldata.c" + diff --git a/frtos/frontend/makefsdata.c b/frtos/frontend/makefsdata.c new file mode 100644 index 0000000..31cb6e3 --- /dev/null +++ b/frtos/frontend/makefsdata.c @@ -0,0 +1,129 @@ +#include +#include +#include + +// Function to encode a string to hexadecimal +void stringToHex(const char* input, char* file, int length) { + for (int i = 0; i < length; i++) { + sprintf(file + 2 * i, "%02X", input[i]); + } +} + +int main() { + FILE *file; + file = fopen("htmldata.c", "w"); + const char *folder[] = { + //Add your file path here + "./html_files/index.shtml" + }; + const char *files[] = { + "./index.shtml" + }; + char *varnames[100] = { + + }; + + int fileCount = sizeof(files)/sizeof(files[0]); + for(int i = 0; i< fileCount ;i++){ + char header[1024]; + char fvar[256]; + if (strstr(files[i], "404")) { + snprintf(header, sizeof(header), "HTTP/1.0 404 File not found\r\n"); + } else { + snprintf(header, sizeof(header), "HTTP/1.0 200 OK\r\n"); + } + + snprintf(header + strlen(header), sizeof(header) - strlen(header), + "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n"); + + // Add content-type based on files extension + if (strstr(files[i], ".html") || strstr(files[i], ".shtml")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: text/html\r\n"); + } else if (strstr(files[i], ".jpg")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: image/jpeg\r\n"); + } else if (strstr(files[i], ".gif")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: image/gif\r\n"); + } else if (strstr(files[i], ".png")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: image/png\r\n"); + } else if (strstr(files[i], ".class")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: application/octet-stream\r\n"); + } else if (strstr(files[i], ".js")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: text/javascript\r\n"); + } else if (strstr(files[i], ".css")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: text/css\r\n"); + } else if (strstr(files[i], ".svg")) { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: image/svg+xml\r\n"); + } else { + snprintf(header + strlen(header), sizeof(header) - strlen(header), "Content-type: text/plain\r\n"); + } + + snprintf(header + strlen(header), sizeof(header) - strlen(header), "\r\n"); + + // Create a variable name for the files[i] + strcpy(fvar, files[i] + 1); // Remove the leading dot in the filename + for (int j = 0; fvar[j]; j++) { + if (fvar[j] == '/' || fvar[j] == '\\') { + fvar[j] = '_'; + } else if (fvar[j] == '.') { + fvar[j] = '_'; + } + } + + fprintf(file, "static const unsigned char data%s[] = {\n", fvar); + fprintf(file, "\t/* %s */\n\t", files[i]); + + // Encode the filename as hexadecimal + char hexFileName[2 * strlen(files[i]) + 1]; + stringToHex(files[i] + 1, hexFileName, strlen(files[i]) - 1); + int count = 0; + for (int j = 0; hexFileName[j]; j++) { + fprintf(file, "0x%c%c, ", hexFileName[j], hexFileName[j + 1]); + j++; + count++; + } + fprintf(file, "0x00,\n\t"); + + // Encode the HTTP header as hexadecimal + char hexHeader[2 * strlen(header) + 1]; + stringToHex(header, hexHeader, strlen(header)); + count = 0; + for (int j = 0; hexHeader[j]; j++) { + fprintf(file, "0x%c%c, ", hexHeader[j], hexHeader[j + 1]); + j++; + count++; + if (count == 10) { + fprintf(file, "\n\t"); + count = 0; + } + } + fprintf(file, "\n\t"); + // Encode the file content as hexadecimal + FILE* filePtr = fopen(folder[i], "rb"); + count = 0; + if (filePtr) { + int ch; + while ((ch = fgetc(filePtr)) != EOF) { + fprintf(file, "0x%02X, ", ch); + count++; + if (count == 10) { + fprintf(file, "\n\t"); + count = 0; + } + } + fclose(filePtr); + } + + fprintf(file, "};\n\n"); + varnames[i] = malloc(strlen(fvar) + 1); + strcpy(varnames[i], fvar); + + + fprintf(file, "const struct fsdata_file file%s[] = {{ %s, data%s, data%s + %d, sizeof(data%s) - %d, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT }};\n", + fvar,"NULL", fvar, fvar, strlen(files[i]) , fvar, strlen(files[i])); + } + + fprintf(file, "\n#define FS_ROOT file%s\n",varnames[fileCount - 1] ); + fprintf(file, "#define FS_NUMFILES %d\n", fileCount); + + fclose(file); +} diff --git a/frtos/frontend/makefsdata.exe b/frtos/frontend/makefsdata.exe new file mode 100644 index 0000000000000000000000000000000000000000..50286c449a54dfdb1d3763436a4079f500b20f12 GIT binary patch literal 265302 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 0): + prevfile = "file" + varnames[i-1] + + output.write("const struct fsdata_file file{0}[] = {{{{ {1}, data{2}, ".format(varnames[i], prevfile, varnames[i])) + output.write("data{} + {}, ".format(varnames[i], len(filenames[i]) + 1)) + output.write("sizeof(data{}) - {}, ".format(varnames[i], len(filenames[i]) + 1)) + output.write("FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}};\n") + +output.write("\n#define FS_ROOT file{}\n".format(varnames[-1])) +output.write("#define FS_NUMFILES {}\n".format(len(filenames))) \ No newline at end of file diff --git a/frtos/frontend/ssi.h b/frtos/frontend/ssi.h new file mode 100644 index 0000000..df1e5b0 --- /dev/null +++ b/frtos/frontend/ssi.h @@ -0,0 +1,56 @@ +#include "lwip/apps/httpd.h" +#include "pico/cyw43_arch.h" +#include "hardware/adc.h" +#include "pico/stdlib.h" + +// SSI tags - tag length limited to 8 bytes by default +const char * ssi_tags[] = {"speed","barcode","pid","orient"}; + +u16_t ssi_handler(int iIndex, char *pcInsert, int iInsertLen) { + size_t printed; + switch (iIndex) { + case 0: // Speed + { + // call getSpeed() function + const float Speed = adc_read() * 3.3f / (1 << 12); + printed = snprintf(pcInsert, iInsertLen, "%f ",Speed); + } + break; + case 1: // barcode + { + // call getBarcodeOutput() function + const char* barC = "36" ; + printed = snprintf(pcInsert, iInsertLen, "%s", barC); + } + break; + case 2: //PID + { + // whatever to display for PID + const char* PID = "54" ; + printed = snprintf(pcInsert, iInsertLen, "%s", PID); + } + break; + case 3: //Orientation + { + // call getOrientation() function + const char* orien = "South" ; + printed = snprintf(pcInsert, iInsertLen, "%s", orien); + } + break; + default: + printed = 0; + break; + } + + return (u16_t)printed; +} + +// Initialise the SSI handler +void ssi_init() { + // Initialise ADC (internal pin) + adc_init(); + adc_set_temp_sensor_enabled(true); + adc_select_input(4); + + http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags)); +} From ed516b5ce4be8ad677d65b9e71a9d1ae92e6f947 Mon Sep 17 00:00:00 2001 From: Richie <2837357W@student.gla.ac.uk> Date: Mon, 30 Oct 2023 23:20:15 +0800 Subject: [PATCH 10/11] add direction control --- frtos/motor/motor_direction.h | 49 +++++++++++++++++++++++++++-------- frtos/motor/motor_test.c | 48 +++++++++++++++++++++++++++++----- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/frtos/motor/motor_direction.h b/frtos/motor/motor_direction.h index f81273f..51389fe 100644 --- a/frtos/motor/motor_direction.h +++ b/frtos/motor/motor_direction.h @@ -28,19 +28,46 @@ set_wheel_direction(uint32_t direction) } /*! - * @brief Set the speed of the wheels - * @param speed The speed of the wheels, from 0 to 5000 + * @brief Turn the wheel, must set the priority higher than the motor PID task + * @param direction The direction of the wheel + * @param direction_after The direction of the wheel after turning + * @param speed_after The speed of the wheel after turning */ void -set_wheel_speed(uint32_t speed) +turn_wheel(uint32_t direction) { - g_motor_right.pwm.level = speed; - g_motor_left.pwm.level = speed; + set_wheel_speed(0u); + vTaskDelay(pdMS_TO_TICKS(1000)); + float initial_right = g_motor_right.speed.distance_cm; + float initial_left = g_motor_left.speed.distance_cm; - pwm_set_chan_level(g_motor_right.pwm.slice_num, - g_motor_right.pwm.channel, - g_motor_right.pwm.level); - pwm_set_chan_level(g_motor_left.pwm.slice_num, - g_motor_left.pwm.channel, - g_motor_left.pwm.level); + set_wheel_direction(direction); + set_wheel_speed(3500u); + + for (;;) + { + // gap between wheels = 11.3cm, to turn 90 degrees, need to travel + // 11.3 * pi / 4 = 8.9cm + if (g_motor_left.speed.distance_cm - initial_left >= 6.8f) + { + g_motor_left.pwm.level = 0; + pwm_set_chan_level(g_motor_left.pwm.slice_num, + g_motor_left.pwm.channel, + g_motor_left.pwm.level); + } + + if (g_motor_right.speed.distance_cm - initial_right >= 6.8f) + { + g_motor_right.pwm.level = 0; + pwm_set_chan_level(g_motor_right.pwm.slice_num, + g_motor_right.pwm.channel, + g_motor_right.pwm.level); + } + + if (g_motor_left.pwm.level == 0u && g_motor_right.pwm.level == 0u) + { + break; + } + } + vTaskDelay(pdMS_TO_TICKS(1000)); } \ No newline at end of file diff --git a/frtos/motor/motor_test.c b/frtos/motor/motor_test.c index a5f16ff..440972b 100644 --- a/frtos/motor/motor_test.c +++ b/frtos/motor/motor_test.c @@ -4,7 +4,37 @@ #include "motor_direction.h" #include "motor_pid.h" -#define WHEEL_SPEED_PRIO (tskIDLE_PRIORITY + 1UL) +#define WHEEL_SPEED_PRIO (tskIDLE_PRIORITY + 2UL) +#define WHEEL_CONTROL_PRIO (tskIDLE_PRIORITY + 2UL) +#define WHEEL_PID_PRIO (tskIDLE_PRIORITY + 2UL) + +static void +motor_control_task(__unused void *p_param) +{ + for (;;) + { + set_wheel_direction(DIRECTION_FORWARD); + set_wheel_speed(3000u); + distance_to_stop(30); + + set_wheel_direction(DIRECTION_BACKWARD); + set_wheel_speed(3000u); + distance_to_stop(30); + + turn_wheel(DIRECTION_LEFT); + set_wheel_direction(DIRECTION_FORWARD); + set_wheel_speed(3000u); + distance_to_stop(30); + + turn_wheel(DIRECTION_RIGHT); + set_wheel_direction(DIRECTION_FORWARD); + set_wheel_speed(3000u); + distance_to_stop(30); + + set_wheel_speed(0u); + vTaskDelay(pdMS_TO_TICKS(3000)); + } +} void launch() @@ -19,8 +49,8 @@ launch() irq_set_enabled(IO_IRQ_BANK0, true); - // Set wheel speed - set_wheel_speed(3500); +// set_wheel_direction(DIRECTION_FORWARD); +// set_wheel_speed(3000); // Left wheel // @@ -32,7 +62,6 @@ launch() WHEEL_SPEED_PRIO, &h_monitor_left_wheel_speed_task_handle); - // Right wheel // TaskHandle_t h_monitor_right_wheel_speed_task_handle = NULL; @@ -48,9 +77,17 @@ launch() "motor_pid_task", configMINIMAL_STACK_SIZE, (void *)&g_motor_right, - WHEEL_SPEED_PRIO, + WHEEL_PID_PRIO, &h_motor_pid_right_task_handle); + // control task + TaskHandle_t h_motor_turning_task_handle = NULL; + xTaskCreate(motor_control_task, + "motor_turning_task", + configMINIMAL_STACK_SIZE, + NULL, + WHEEL_CONTROL_PRIO, + &h_motor_turning_task_handle); vTaskStartScheduler(); } @@ -64,7 +101,6 @@ main(void) printf("Test started!\n"); motor_init(); - set_wheel_direction(DIRECTION_LEFT_FORWARD | DIRECTION_RIGHT_FORWARD); launch(); From ed3c440ebc75ec94c5ad7f7a0314c07860f48d6f Mon Sep 17 00:00:00 2001 From: Richie <2837357W@student.gla.ac.uk> Date: Mon, 30 Oct 2023 23:24:58 +0800 Subject: [PATCH 11/11] add direction control --- frtos/config/motor_config.h | 5 +++++ frtos/motor/motor_speed.h | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/frtos/config/motor_config.h b/frtos/config/motor_config.h index 267d672..6d2cf0e 100644 --- a/frtos/config/motor_config.h +++ b/frtos/config/motor_config.h @@ -18,6 +18,11 @@ #define DIRECTION_LEFT_FORWARD (1U << DIRECTION_PIN_LEFT_IN4) #define DIRECTION_LEFT_BACKWARD (1U << DIRECTION_PIN_LEFT_IN3) +#define DIRECTION_FORWARD (DIRECTION_RIGHT_FORWARD | DIRECTION_LEFT_FORWARD) +#define DIRECTION_BACKWARD (DIRECTION_RIGHT_BACKWARD | DIRECTION_LEFT_BACKWARD) +#define DIRECTION_LEFT (DIRECTION_RIGHT_FORWARD | DIRECTION_LEFT_BACKWARD) +#define DIRECTION_RIGHT (DIRECTION_RIGHT_BACKWARD | DIRECTION_LEFT_FORWARD) + #define SPEED_PIN_RIGHT 15U #define SPEED_PIN_LEFT 16U diff --git a/frtos/motor/motor_speed.h b/frtos/motor/motor_speed.h index 520fbee..d573276 100644 --- a/frtos/motor/motor_speed.h +++ b/frtos/motor/motor_speed.h @@ -72,4 +72,46 @@ monitor_wheel_speed_task(void *pvParameters) p_motor->speed.current_cms = 0.f; } } +} + + +/*! + * @brief Set the distance to travel before stopping, must be called after + * setting the speed and direction. + * @param distance_cm distance to travel in cm + */ +void +distance_to_stop(float distance_cm) +{ + float initial = g_motor_right.speed.distance_cm; + printf("initial: %f\n", initial); + + for (;;) + { + if (g_motor_right.speed.distance_cm - initial >= distance_cm) + { + g_motor_right.pwm.level = 0; + g_motor_left.pwm.level = 0; + break; + } + // vTaskDelay(pdMS_TO_TICKS(50)); + } +} + +/*! + * @brief Set the speed of the wheels + * @param pwm_level The pwm_level of the wheels, from 0 to 5000 + */ +void +set_wheel_speed(uint32_t pwm_level) +{ + g_motor_right.pwm.level = pwm_level; + g_motor_left.pwm.level = pwm_level; + + pwm_set_chan_level(g_motor_right.pwm.slice_num, + g_motor_right.pwm.channel, + g_motor_right.pwm.level); + pwm_set_chan_level(g_motor_left.pwm.slice_num, + g_motor_left.pwm.channel, + g_motor_left.pwm.level); } \ No newline at end of file