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/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/config/motor_config.h b/frtos/config/motor_config.h index 4219df3..6d2cf0e 100644 --- a/frtos/config/motor_config.h +++ b/frtos/config/motor_config.h @@ -18,42 +18,67 @@ #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 #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 current_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 channel; + uint16_t 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 kp_value; + float ki_value; + float kd_value; +} 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/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 0000000..50286c4 Binary files /dev/null and b/frtos/frontend/makefsdata.exe differ diff --git a/frtos/frontend/makefsdata.py b/frtos/frontend/makefsdata.py new file mode 100644 index 0000000..6d2f98f --- /dev/null +++ b/frtos/frontend/makefsdata.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 + +# This script is by @rspeir on GitHub: +# https://github.com/krzmaz/pico-w-webserver-example/pull/1/files/4b3e78351dd236f213da9bebbb20df690d470476#diff-e675c4a367e382db6f9ba61833a58c62029d8c71c3156a9f238b612b69de279d +# Renamed output to avoid linking incorrect file + +import os +import binascii + +#Create file to write output into +output = open('htmldata.c', 'w') + +#Traverse directory, generate list of files +files = list() +os.chdir('./html_files') +for(dirpath, dirnames, filenames) in os.walk('.'): + files += [os.path.join(dirpath, file) for file in filenames] + +filenames = list() +varnames = list() + +#Generate appropriate HTTP headers +for file in files: + + if '404' in file: + header = "HTTP/1.0 404 File not found\r\n" + else: + header = "HTTP/1.0 200 OK\r\n" + + header += "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n" + + if '.html' in file: + header += "Content-type: text/html\r\n" + elif '.shtml' in file: + header += "Content-type: text/html\r\n" + elif '.jpg' in file: + header += "Content-type: image/jpeg\r\n" + elif '.gif' in file: + header += "Content-type: image/gif\r\n" + elif '.png' in file: + header += "Content-type: image/png\r\n" + elif '.class' in file: + header += "Content-type: application/octet-stream\r\n" + elif '.js' in file: + header += "Content-type: text/javascript\r\n" + elif '.css' in file: + header += "Content-type: text/css\r\n" + elif '.svg' in file: + header += "Content-type: image/svg+xml\r\n" + else: + header += "Content-type: text/plain\r\n" + + header += "\r\n" + + fvar = file[1:] #remove leading dot in filename + fvar = fvar.replace('/', '_') #replace *nix path separator with underscore + fvar = fvar.replace('\\', '_') #replace DOS path separator with underscore + fvar = fvar.replace('.', '_') #replace file extension dot with underscore + + output.write("static const unsigned char data{}[] = {{\n".format(fvar)) + output.write("\t/* {} */\n\t".format(file)) + + #first set of hex data encodes the filename + b = bytes(file[1:].replace('\\', '/'), 'utf-8') #change DOS path separator to forward slash + for byte in binascii.hexlify(b, b' ', 1).split(): + output.write("0x{}, ".format(byte.decode())) + output.write("0,\n\t") + + #second set of hex data is the HTTP header/mime type we generated above + b = bytes(header, 'utf-8') + count = 0 + for byte in binascii.hexlify(b, b' ', 1).split(): + output.write("0x{}, ".format(byte.decode())) + count = count + 1 + if(count == 10): + output.write("\n\t") + count = 0 + output.write("\n\t") + + #finally, dump raw hex data from files + with open(file, 'rb') as f: + count = 0 + while(byte := f.read(1)): + byte = binascii.hexlify(byte) + output.write("0x{}, ".format(byte.decode())) + count = count + 1 + if(count == 10): + output.write("\n\t") + count = 0 + output.write("};\n\n") + + filenames.append(file[1:]) + varnames.append(fvar) + +for i in range(len(filenames)): + prevfile = "NULL" + if(i > 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)); +} diff --git a/frtos/line_sensor/line_sensor.h b/frtos/line_sensor/line_sensor.h index 11c6429..9aa8970 100644 --- a/frtos/line_sensor/line_sensor.h +++ b/frtos/line_sensor/line_sensor.h @@ -15,14 +15,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); @@ -31,9 +52,6 @@ monitor_left_sensor_task(__unused void *params) { &state, sizeof(state_t), 0); - // Reset the flag - left_sensor_triggered = pdFALSE; - } } } } @@ -46,12 +64,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); @@ -60,9 +98,6 @@ monitor_right_sensor_task(void *params) { &state, sizeof(state_t), 0); - // Reset the flag - right_sensor_triggered = pdFALSE; - } } } } diff --git a/frtos/line_sensor/line_sensor_test.c b/frtos/line_sensor/line_sensor_test.c index 054384a..a60e625 100644 --- a/frtos/line_sensor/line_sensor_test.c +++ b/frtos/line_sensor/line_sensor_test.c @@ -24,17 +24,17 @@ launch() 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); + 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/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_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 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(); diff --git a/frtos/motor/motor_direction.h b/frtos/motor/motor_direction.h index 1fe604c..51389fe 100644 --- a/frtos/motor/motor_direction.h +++ b/frtos/motor/motor_direction.h @@ -17,14 +17,57 @@ * @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 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 +turn_wheel(uint32_t direction) +{ + 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; + + 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_init.h b/frtos/motor/motor_init.h index 970b12e..56cb390 100644 --- a/frtos/motor/motor_init.h +++ b/frtos/motor/motor_init.h @@ -19,20 +19,23 @@ #include "motor_config.h" -motor_speed_t g_motor_speed_left = { .pwm_level = 2500u, - .pwm_channel = PWM_CHAN_A, - .distance = 0.0f,}; +motor_t g_motor_left = { .pwm.level = 0u, + .pwm.channel = PWM_CHAN_A, + .speed.distance_cm = 0.0f }; -motor_speed_t g_motor_speed_right = { .pwm_level = 2500u, - .pwm_channel = PWM_CHAN_B, - .distance = 0.0f,}; +motor_t g_motor_right = { .pwm.level = 0u, + .pwm.channel = PWM_CHAN_B, + .speed.distance_cm = 0.0f, + .pid.kp_value = 1000.f, + .pid.ki_value = 0.0f, + .pid.kd_value = 10000.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 +57,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..c741ef6 100644 --- a/frtos/motor/motor_pid.h +++ b/frtos/motor/motor_pid.h @@ -16,18 +16,18 @@ * @return The control signal */ float -compute_pid(const volatile float *target_speed, - const volatile float *current_speed, - float *integral, - float *prev_error) +compute_pid(float *integral, float *prev_error) { - float error = *target_speed - *current_speed; + float error + = g_motor_left.speed.distance_cm - g_motor_right.speed.distance_cm; + *integral += error; float derivative = error - *prev_error; - float control_signal - = PID_KP * error + PID_KI * (*integral) + PID_KD * 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; @@ -35,37 +35,42 @@ compute_pid(const volatile float *target_speed, } void -motor_pid_task(void *p_param) +motor_pid_task(__unused void *p_param) { - motor_speed_t *p_motor_speed = p_param; - float integral = 0.0f; - float prev_error = 0.0f; + 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 (g_motor_left.pwm.level == 0u) { - p_motor_speed->pwm_level = MAX_SPEED; - } - else if (p_motor_speed->pwm_level + control_signal < MIN_SPEED) - { - p_motor_speed->pwm_level = MIN_SPEED; - } - else - { - p_motor_speed->pwm_level = p_motor_speed->pwm_level + control_signal; + 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; } - // printf("control signal: %f\n", control_signal); - // printf("new pwm: %hu\n\n", p_motor_speed->pwm_level); + float control_signal = compute_pid(&integral, &prev_error); - pwm_set_chan_level(p_motor_speed->slice_num, - p_motor_speed->pwm_channel, - p_motor_speed->pwm_level); + float temp = (float) g_motor_right.pwm.level + control_signal * 0.05f; + + if (temp > MAX_SPEED) + { + temp = MAX_SPEED; + } + + if (temp < MIN_SPEED) + { + temp = MIN_SPEED; + } + + 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 525601f..d573276 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,20 +62,56 @@ 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) (1021017.61242f / elapsed_time); - p_motor_speed->distance += 1.02101761242f; - - // printf("speed: %f cm/s\n", p_motor_speed->current_speed_cms); + p_motor->speed.distance_cm += 1.02101761242f; } else { - p_motor_speed->current_speed_cms = 0.f; - // printf("stopped\n"); + p_motor->speed.current_cms = 0.f; } - - // printf("distance: %f cm\n", p_motor_speed->distance); } +} + + +/*! + * @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 diff --git a/frtos/motor/motor_test.c b/frtos/motor/motor_test.c index 0e9268b..440972b 100644 --- a/frtos/motor/motor_test.c +++ b/frtos/motor/motor_test.c @@ -4,25 +4,35 @@ #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) -void -test_speed_change_task(void *p_param) +static void +motor_control_task(__unused void *p_param) { for (;;) { - g_motor_speed_left.target_speed_cms = 15.0f; - g_motor_speed_right.target_speed_cms = 15.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); + set_wheel_direction(DIRECTION_FORWARD); + set_wheel_speed(3000u); + distance_to_stop(30); -// g_motor_speed_left.target_speed_cms = 20.0f; -// g_motor_speed_right.target_speed_cms = 20.0f; -// vTaskDelay(pdMS_TO_TICKS(5000)); + set_wheel_direction(DIRECTION_BACKWARD); + set_wheel_speed(3000u); + distance_to_stop(30); - g_motor_speed_left.target_speed_cms = 0.0f; - g_motor_speed_right.target_speed_cms = 0.0f; - vTaskDelay(pdMS_TO_TICKS(5000)); + 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)); } } @@ -39,23 +49,18 @@ launch() irq_set_enabled(IO_IRQ_BANK0, true); +// set_wheel_direction(DIRECTION_FORWARD); +// set_wheel_speed(3000); + // 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_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_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); // Right wheel // @@ -63,7 +68,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,19 +76,18 @@ launch() xTaskCreate(motor_pid_task, "motor_pid_task", configMINIMAL_STACK_SIZE, - (void *)&g_motor_speed_right, - WHEEL_SPEED_PRIO, + (void *)&g_motor_right, + WHEEL_PID_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", + // control task + TaskHandle_t h_motor_turning_task_handle = NULL; + xTaskCreate(motor_control_task, + "motor_turning_task", configMINIMAL_STACK_SIZE, NULL, - WHEEL_SPEED_PRIO, - &h_test_speed_change_task_handle); + WHEEL_CONTROL_PRIO, + &h_motor_turning_task_handle); vTaskStartScheduler(); } @@ -93,11 +97,10 @@ main(void) { stdio_usb_init(); - sleep_ms(2000); + sleep_ms(4000); printf("Test started!\n"); motor_init(); - set_wheel_direction(DIRECTION_LEFT_FORWARD | DIRECTION_RIGHT_FORWARD); launch(); diff --git a/frtos/rtos_car.c b/frtos/rtos_car.c index 50df6a6..3cbd440 100644 --- a/frtos/rtos_car.c +++ b/frtos/rtos_car.c @@ -109,6 +109,8 @@ main (void) line_sensor_setup(); + init_ultrasonic(); + initialize_car_state(); // TODO: Could be common functionality, To confirm // during Integration launch(); 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 - // } } }