Commit b0b9a13a by tatsukiishikawa

Changing name

parent 8fc87403
Showing with 4908 additions and 0 deletions
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
set(USERHOME $ENV{USERPROFILE})
else()
set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.2.0)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.2.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico_w)
set(PROGRAM_NAME main)
cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(${PROGRAM_NAME} C CXX ASM)
set(PICO_CYW43_ARCH lwip_sys_freertos CACHE STRING "" FORCE)
pico_sdk_init()
add_subdirectory(lib/config)
add_subdirectory(lib/lcd)
add_subdirectory(lib/font)
add_subdirectory(lib/context)
add_subdirectory(lib/http_client)
add_subdirectory(lib/model)
add_subdirectory(lib/pico-tflmicro)
add_subdirectory(lib/sd_card_driver)
include_directories(./lib/config)
include_directories(./lib/lcd)
include_directories(./lib/font)
include_directories(./lib/context)
include_directories(./lib/http_client)
include_directories(./lib/model)
include_directories(./lib/pico-tflmicro)
include_directories(./lib/sd_card_driver)
add_library(sd_card_wrapper
src/sd_card_wrapper.cpp
config/hw_config.c
)
target_include_directories(sd_card_wrapper PUBLIC
${CMAKE_CURRENT_LIST_DIR}/lib/sd_card_driver
)
target_link_libraries(sd_card_wrapper
pico_stdlib
sd_custom_driver
)
add_executable(application1 application1.cpp) # to test hand written digit recognition with static test data.
add_executable(application2 application2.cpp) # to test hand written letter recognition with static test data.
add_executable(tensor_arena_test test/arena_size_test.cpp) # to check arena size for a given model.
add_executable(application1_test test/application1_test.cpp) # to test hand written digit recognition with static test data.
add_executable(application2_test test/application2_test.cpp) # to test hand written letter recognition with static test data.
target_include_directories(application1
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
target_include_directories(application2
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
${CMAKE_CURRENT_LIST_DIR}/src
)
target_include_directories(tensor_arena_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
target_include_directories(application1_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
target_include_directories(application2_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
${CMAKE_CURRENT_LIST_DIR}/src
)
set_target_properties(application1
PROPERTIES
COMPILE_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics -nostdlib"
)
set_target_properties(application2
PROPERTIES
COMPILE_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics -nostdlib"
)
set_target_properties(tensor_arena_test
PROPERTIES
COMPILE_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics -nostdlib"
)
set_target_properties(application1_test
PROPERTIES
COMPILE_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics -nostdlib"
)
set_target_properties(application2_test
PROPERTIES
COMPILE_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics -nostdlib"
)
pico_set_linker_script(application1 ${CMAKE_CURRENT_SOURCE_DIR}/ldscripts/application1_memmap.ld)
pico_set_linker_script(application2 ${CMAKE_CURRENT_SOURCE_DIR}/ldscripts/application2_memmap.ld)
target_link_libraries(
application1
config
model
context
pico_stdlib
hardware_spi
hardware_pwm
pico_multicore
hardware_adc
pico-tflmicro
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_mbedtls
pico_mbedtls
)
target_link_libraries(
application2
lcd
font
config
context
http_client
pico_stdlib
hardware_spi
hardware_pwm
pico_multicore
hardware_adc
sd_card_wrapper
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_http
pico_lwip_mbedtls
pico_mbedtls
pico_lwip_sntp
)
target_link_libraries(
tensor_arena_test
config
model
pico_stdlib
hardware_spi
hardware_pwm
pico_multicore
hardware_adc
pico-tflmicro
)
target_link_libraries(
application1_test
config
model
pico_stdlib
hardware_spi
hardware_pwm
pico_multicore
hardware_adc
pico-tflmicro
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_mbedtls
pico_mbedtls
)
target_link_libraries(
application2_test
lcd
font
config
http_client
pico_stdlib
hardware_spi
hardware_pwm
pico_multicore
hardware_adc
sd_card_wrapper
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_http
pico_lwip_mbedtls
pico_mbedtls
pico_lwip_sntp
)
set_property(TARGET application1 APPEND_STRING PROPERTY LINK_FLAGS
"-Wl,--print-memory-usage"
)
set_property(TARGET application2 APPEND_STRING PROPERTY LINK_FLAGS
"-Wl,--print-memory-usage"
)
set_property(TARGET tensor_arena_test APPEND_STRING PROPERTY LINK_FLAGS
"-Wl,--print-memory-usage"
)
set_property(TARGET application1_test APPEND_STRING PROPERTY LINK_FLAGS
"-Wl,--print-memory-usage"
)
set_property(TARGET application2_test APPEND_STRING PROPERTY LINK_FLAGS
"-Wl,--print-memory-usage"
)
# enable usb output, disable uart output
pico_enable_stdio_usb(application1 1)
pico_enable_stdio_uart(application1 0)
pico_enable_stdio_usb(application2 1)
pico_enable_stdio_uart(application2 0)
pico_enable_stdio_usb(tensor_arena_test 1)
pico_enable_stdio_uart(tensor_arena_test 1)
pico_enable_stdio_usb(application1_test 1)
pico_enable_stdio_uart(application1_test 0)
pico_enable_stdio_usb(application2_test 1)
pico_enable_stdio_uart(application2_test 0)
# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(application1)
pico_add_extra_outputs(application2)
pico_add_extra_outputs(tensor_arena_test)
pico_add_extra_outputs(application1_test)
pico_add_extra_outputs(application2_test)
\ No newline at end of file
#include <cmath>
#include<iostream>
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "pico/sync.h"
#include "hardware/watchdog.h"
#include "hardware/sync.h"
extern "C" {
#include "DEV_Config.h"
}
#include "model.h"
#include "context.h"
#include "model_settings.h"
using namespace std;
#define HALT_CORE_1() while (1) { tight_loop_contents(); }
#define ONE_SECOND_IN_US 1000000
#define CORE1_EXIT_FLAG 0xDEADBEEF
Model ml_model;
mutex_t mutex; // Declare a mutex
// run core0 loop that displays UI and handle user interaction
void core1_entry() {
// TODO: Implement your own Application 1 data acquisition logic here
for (int count = 0; count < 10; count++) {
sleep_ms(1000);
}
multicore_fifo_push_blocking(CORE1_EXIT_FLAG);
sleep_ms(1000);
printf("Core1 done, halting...\n");
HALT_CORE_1();
}
int main(void)
{
System_Init();
mutex_init(&mutex); // Initialize the mutex
sleep_ms(3000);
// run core1 loop that handles user interface
multicore_launch_core1(core1_entry);
// initialize ML model
if (!ml_model.setup()) {
printf("Failed to initialize ML model!\n");
HALT_CORE_1();
}
printf("Model initialized\n");
uint8_t* test_image_input = ml_model.input_data();
if (test_image_input == nullptr) {
printf("Cannot set input\n");
HALT_CORE_1();
}
int byte_size = ml_model.byte_size();
if (!byte_size) {
printf("Byte size not found\n");
HALT_CORE_1();
}
while (true) {
// Block the process until data being filled
uint32_t g = multicore_fifo_pop_blocking();
if (g == CORE1_EXIT_FLAG) {
break; // Exit the loop if the exit flag is received
}
// TODO: Implement your own Application 1 logic for data processing, run inference, success handling here.
// Acquire the mutex (blocking)
mutex_enter_blocking(&mutex);
inference.state = PROCESSING;
memcpy(test_image_input, NULL, byte_size);
absolute_time_t inference_start = get_absolute_time();
int result = ml_model.predict();
int64_t inference_time_us = absolute_time_diff_us(inference_start, get_absolute_time());
float inference_time_ms = inference_time_us / 1000.0f;
if (result == -1) {
printf("Failed to run inference\n");
} else {
printf("Predicted: %d\n", result);
printf("Inference time: %.2f ms\n", inference_time_ms);
// Check if prediction is correct
// If correct, release the mutex and break the loop to proceed to app2
// If incorrect, continue the loop after releasing the mutex
}
sleep_ms(200);
printf("Login process finished.\n");
//Return the resource
mutex_exit(&mutex);
}
printf("Successfully logged in!\n");
mutex_exit(&mutex);
multicore_reset_core1();
sleep_ms(1000);
printf("Jumping to the app2...\n");
jump_to_image(APP2_OFFSET);
return 0;
}
#include <stdio.h>
#include <vector>
#include <cstring>
#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "pico/sync.h"
#include "hardware/watchdog.h"
#include "hardware/sync.h"
extern "C" {
#include "LCD_Driver.h"
#include "LCD_Touch.h"
#include "LCD_GUI.h"
#include "DEV_Config.h"
}
#include "LCD_letter.h"
#include "context.h"
#include "request.hpp"
#include "sd_card_wrapper.hpp"
#define SD_CARD_FILENAME "large_request_data_test.bin"
#define REQUEST_CHUNK_SIZE 1024
#define REQUEST_ID "id_00002"
mutex_t mutex;
OCR_Inference inference;
static inline void debug_unpause_timer(void) {
timer_hw->dbgpause = 0;
}
// run core0 loop that displays UI and handle user interaction
void core1_entry() {
while(true) {
LCD_SetBackLight(1000);
TP_DrawBoard();
}
}
int write_data_to_sd_card(SD_CardWrapper &sd_card, uint8_t* data) {
// Save large data to SD card for later use
if (!sd_card.isMounted()) {
printf("[Writer] SD card not mounted, cannot write data\n");
return -1;
}
// filename to store large request data
size_t offset = 0;
size_t total_packets_count = (DATA_SIZE + REQUEST_CHUNK_SIZE - 1) / REQUEST_CHUNK_SIZE;
// create a file in SD card
if (!sd_card.createFile(SD_CARD_FILENAME)) {
printf("[Writer] Failed to create file on SD card\n");
return -1;
}
sleep_ms(100); // optional flush delay
// erase the content if any
if (!sd_card.clearFile(SD_CARD_FILENAME)) {
printf("[Writer] Failed to clear existing file content on SD card\n");
return -1;
}
// write data in chunks
bool success = true;
while (offset < DATA_SIZE) {
size_t remaining = DATA_SIZE - offset;
size_t chunk_elems = (remaining > REQUEST_CHUNK_SIZE) ? REQUEST_CHUNK_SIZE : remaining;
if (!sd_card.appendToFile(SD_CARD_FILENAME, data + offset, chunk_elems)) {
printf("[Writer] Failed to append data chunk to SD card\n");
success = false;
break;
}
offset += chunk_elems;
sleep_ms(100); // optional flush delay
}
if (!success) {
printf("[Writer] Data write to SD card failed.\n");
return -1;
}
printf("[Writer] Large data written to SD card successfully\n");
size_t file_size = sd_card.getFileSize(SD_CARD_FILENAME);
printf("Written file size: %zu bytes\n", file_size);
if (file_size != DATA_SIZE) {
printf("File size mismatched. Check the file content.\n");
}
printf("[Writer] Data write to SD card completed.\n");
return 0;
}
int read_data_from_sd_card_and_post_request(SD_CardWrapper &sd_card, HttpRequest *request) {
if (!sd_card.isMounted()) {
printf("SD card not mounted, cannot read data\n");
return -1;
}
printf("[Reader] SD card mounted successfully for reading data.\n");
sleep_ms(100); // optional flush delay
// get total file size from SD card
size_t file_size = sd_card.getFileSize(SD_CARD_FILENAME);
printf("[Reader] Reading file of size: %zu bytes\n", file_size);
size_t offset = 0;
size_t total_packets_count = (DATA_SIZE + REQUEST_CHUNK_SIZE - 1) / REQUEST_CHUNK_SIZE;
std::vector<uint8_t> stored_data;
// Pre-allocate buffer for JSON to avoid repeated allocations
std::string json_str;
json_str.reserve(REQUEST_CHUNK_SIZE * 5); // Reserve enough space
for (int packet_index = 0; packet_index < total_packets_count; packet_index++) {
size_t remaining = file_size - offset;
size_t chunk_elems = (remaining > REQUEST_CHUNK_SIZE) ? REQUEST_CHUNK_SIZE : remaining;
stored_data.clear();
stored_data.resize(chunk_elems);
// read chunked data from SD card
if (!sd_card.readChunkedFile(SD_CARD_FILENAME, stored_data, chunk_elems, offset)) {
printf("[Reader] Failed to read data chunk from SD card\n");
return -1;
}
printf("[Reader] Read chunk of size %zu bytes from offset %zu\n", stored_data.size(), offset);
// Build JSON string efficiently without ostringstream
json_str.clear(); // Reuse the same string buffer
json_str.append("{\"x\": \"");
// Manually build the comma-separated values without ostringstream
char buffer[16];
for (size_t i = 0; i < stored_data.size(); i++) {
snprintf(buffer, sizeof(buffer), "%u", static_cast<unsigned>(stored_data[i]));
json_str.append(buffer);
if ((REQUEST_CHUNK_SIZE * packet_index) + i < DATA_SIZE - 1) {
json_str.append(",");
}
}
json_str.append("\", \"request_id\": \"");
json_str.append(REQUEST_ID);
json_str.append("\", \"packet_index\": ");
snprintf(buffer, sizeof(buffer), "%d", packet_index);
json_str.append(buffer);
json_str.append(", \"total_packets\": ");
snprintf(buffer, sizeof(buffer), "%zu", total_packets_count);
json_str.append(buffer);
json_str.append("}");
request->set_body_raw(json_str.c_str(), json_str.size());
ResponseDataStr response = request->post();
if (!response.status_ok) {
printf("[Reader] Chunk send failed at packet index %d.\n", packet_index);
// Free response memory before continuing
if (response.y) {
free(response.y);
response.y = NULL;
}
sleep_ms(100);
continue;
}
if (packet_index == total_packets_count - 1) {
printf("[Reader] Sent last packet %d/%zu\n", packet_index + 1, total_packets_count);
printf("[Reader] Response: %s\n", response.y ? response.y : "No response");
// Free old response before allocating new one
if (inference.response) {
free(inference.response);
inference.response = NULL;
}
// Allocate new response only if we have data
if (response.y) {
size_t response_length = strlen(response.y);
inference.response = (char*)malloc(response_length + 1);
if (inference.response) {
strncpy(inference.response, response.y, response_length);
inference.response[response_length] = '\0';
}
}
} else {
printf("[Reader] Sent packet %d/%zu\n", packet_index + 1, total_packets_count);
}
// Free response memory BEFORE reinitializing request
if (response.y) {
free(response.y);
response.y = NULL;
}
request->reinitialize();
sleep_ms(300);
offset += chunk_elems;
}
// Clear vectors to free memory
stored_data.clear();
stored_data.shrink_to_fit(); // Release the memory back to system
json_str.clear();
json_str.shrink_to_fit();
return 0;
}
int main(void)
{
System_Init();
debug_unpause_timer();
mutex_init(&mutex); // Initialize the mutex
sleep_ms(5000);
if (cyw43_arch_init()) {
printf("Wi-Fi chip init failed\n");
return -1;
}
printf("Wi-Fi chip initialized successfully\n");
cyw43_arch_enable_sta_mode();
// Connect to the WiFI network - loop until connected
printf("Start connecting wifi.\n");
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("WiFi connected successfully! \n");
LCD_SCAN_DIR lcd_scan_dir = SCAN_DIR_DFT;
Letter_Init(lcd_scan_dir, &inference);
// run core1 loop that handles user interface
multicore_launch_core1(core1_entry);
HttpRequest request(URL_REQUEST);
SD_CardWrapper sd_card;
while(1) {
uint32_t g = multicore_fifo_pop_blocking();
if (g == CORE1_EXIT_FLAG) {
break; // Exit the loop if the exit flag is received
}
// Acquire the mutex (blocking)
mutex_enter_blocking(&mutex);
// Retrieve data from inference
inference.state = PROCESSING;
// Store data on SD card
printf("Writing data to SD card...\n");
if (write_data_to_sd_card(sd_card, inference.data)) {
printf("Failed to write data to SD card.\n");
// Release the mutex
inference.state = DEFAULT;
mutex_exit(&mutex);
continue;
}
printf("Data written to SD card.\n");
// Retrieve data from SD card and send to server chunk by chunk
printf("Sending a request...\n");
if (read_data_from_sd_card_and_post_request(sd_card, &request)) {
printf("Failed to send POST request with chunked body from SD card.\n");
// Release the mutex
} else {
printf("POST request with chunked body from SD card sent successfully.\n");
}
inference.state = DEFAULT;
//Return the resource
mutex_exit(&mutex);
}
printf("Logging out...\n");
mutex_exit(&mutex);
multicore_reset_core1();
sleep_ms(1000);
printf("Jumping to the app1...\n");
jump_to_image(APP1_OFFSET);
return 0;
}
\ No newline at end of file
/* hw_config.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
/*
This file should be tailored to match the hardware design.
There should be one element of the spi[] array for each RP2040 hardware SPI used.
There should be one element of the spi_ifs[] array for each SPI interface object.
* Each element of spi_ifs[] must point to an spi_t instance with member "spi".
There should be one element of the sdio_ifs[] array for each SDIO interface object.
There should be one element of the sd_cards[] array for each SD card slot.
* Each element of sd_cards[] must point to its interface with spi_if_p or sdio_if_p.
*/
/* Hardware configuration for Pico SD Card Development Board
See https://oshwlab.com/carlk3/rp2040-sd-card-dev
See https://docs.google.com/spreadsheets/d/1BrzLWTyifongf_VQCc2IpJqXWtsrjmG7KnIbSBy-CPU/edit?usp=sharing,
tab "Dev Brd", for pin assignments assumed in this configuration file.
*/
#include <assert.h>
//
#include "hw_config.h"
// Hardware Configuration of SPI "objects"
// Note: multiple SD cards can be driven by one SPI if they use different slave
// selects (or "chip selects").
// Using SPI1 to avoid conflicts with CYW43439 WiFi chip (which uses SPI0 internally on Pico W)
static spi_t spis[] = {
{ // SPI1 for SD card (avoiding CYW43439 conflict)
.hw_inst = spi1, // Use SPI1 instead of SPI0
.sck_gpio = 10, // SPI1 SCK (GP10)
.mosi_gpio = 11, // SPI1 MOSI (GP11) - TX
.miso_gpio = 12, // SPI1 MISO (GP12) - RX
.set_drive_strength = true,
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
.no_miso_gpio_pull_up = true,
.baud_rate = 125 * 1000 * 1000 / 64 // ~2 MHz startup
}
};
/* SPI Interfaces */
static sd_spi_if_t spi_ifs[] = {
{
.spi = &spis[0],
.ss_gpio = 22 // CS (Chip Select) pin for SD card (matches DEV_Config.h SD_CS_PIN)
}
};
/* SDIO Interfaces */
/*
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
which is -2 in mod32 arithmetic, so:
CLK_gpio = D0_gpio -2.
D1_gpio = D0_gpio + 1;
D2_gpio = D0_gpio + 2;
D3_gpio = D0_gpio + 3;
*/
static sd_sdio_if_t sdio_ifs[] = {
{
.CMD_gpio = 18,
.D0_gpio = 19,
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.SDIO_PIO = pio0, // Use PIO0 (CYW43 uses PIO internally, but PIO0 is safer for SDIO)
.DMA_IRQ_num = DMA_IRQ_1, // Use DMA_IRQ_1 to avoid conflict with WiFi (which may use DMA_IRQ_0)
.baud_rate = 125 * 1000 * 1000 / 7
}
};
/* Hardware Configuration of the SD Card "objects"
These correspond to SD card sockets
*/
static sd_card_t sd_cards[] = {
{ // sd_cards[0]: Socket sd0 using SDIO interface
.type = SD_IF_SDIO,
.spi_if_p = &sdio_ifs[0],
.use_card_detect = false,
.card_detect_gpio = 9,
.card_detected_true = 0, // What the GPIO read returns when a card is
// present.
.card_detect_use_pull = true,
.card_detect_pull_hi = true
}
};
/* ********************************************************************** */
size_t sd_get_num() { return count_of(sd_cards); }
sd_card_t *sd_get_by_num(size_t num) {
assert(num < sd_get_num());
if (num < sd_get_num()) {
return &sd_cards[num];
} else {
return NULL;
}
}
/* [] END OF FILE */
#!/bin/bash
picotool load build_digit_recognition/digit_recognition.uf2
picotool load build_letter_recognition/letter_recognition.bin --offset 0x10140000
\ No newline at end of file
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 512k
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM AT> FLASH
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
.bss : {
. = ALIGN(4);
__bss_start__ = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
__HeapLimit = .;
} > RAM
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
PROVIDE(__flash_binary_end = .);
} > FLASH
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
FLASH(rx) : ORIGIN = 0x10140000, LENGTH = 1024k
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
/* .boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")*/
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM AT> FLASH
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
.bss : {
. = ALIGN(4);
__bss_start__ = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
__HeapLimit = .;
} > RAM
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
PROVIDE(__flash_binary_end = .);
} > FLASH
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_Config_SRCS 变量
aux_source_directory(. DIR_CONFIG_SRCS)
# 生成链接库
add_library(config ${DIR_CONFIG_SRCS})
target_link_libraries(config PUBLIC pico_stdlib hardware_spi hardware_pwm)
/*****************************************************************************
* | File : DEV_Config.c
* | Author : Waveshare team
* | Function : Show SDcard BMP picto LCD
* | Info :
* Provide the hardware underlying interface
*----------------
* | This version: V1.0
* | Date : 2018-01-11
* | Info : Basic version
*
******************************************************************************/
#include "DEV_Config.h"
#include "pico/stdlib.h"
void DEV_Digital_Write(UWORD Pin, UBYTE Value)
{
gpio_put(Pin, Value);
}
UBYTE DEV_Digital_Read(UWORD Pin)
{
return gpio_get(Pin);
}
/**
* GPIO Mode
**/
void DEV_GPIO_Mode(UWORD Pin, UWORD Mode)
{
gpio_init(Pin);
if(Mode == 0 || Mode == GPIO_IN) {
gpio_set_dir(Pin, GPIO_IN);
} else {
gpio_set_dir(Pin, GPIO_OUT);
}
}
void DEV_GPIO_Init(void)
{
DEV_GPIO_Mode(LCD_RST_PIN,GPIO_OUT);
DEV_GPIO_Mode(LCD_DC_PIN, GPIO_OUT);
//DEV_GPIO_Mode(LCD_BKL_PIN, GPIO_OUT);
DEV_GPIO_Mode(LCD_CS_PIN, GPIO_OUT);
DEV_GPIO_Mode(TP_CS_PIN,GPIO_OUT);
DEV_GPIO_Mode(TP_IRQ_PIN,GPIO_IN);
DEV_GPIO_Mode(SD_CS_PIN,GPIO_OUT);
//gpio_set_pulls(TP_IRQ_PIN,true,false);
DEV_Digital_Write(TP_CS_PIN, 1);
DEV_Digital_Write(LCD_CS_PIN, 1);
//DEV_Digital_Write(LCD_BKL_PIN, 0);
DEV_Digital_Write(SD_CS_PIN, 1);
gpio_set_function(LCD_BKL_PIN, GPIO_FUNC_PWM);
}
/********************************************************************************
function: System Init
note:
Initialize the communication method
********************************************************************************/
uint8_t System_Init(void)
{
stdio_init_all();
DEV_GPIO_Init();
spi_init(SPI_PORT,5000000);
gpio_set_function(LCD_CLK_PIN,GPIO_FUNC_SPI);
gpio_set_function(LCD_MOSI_PIN,GPIO_FUNC_SPI);
gpio_set_function(LCD_MISO_PIN,GPIO_FUNC_SPI);
return 0;
}
void System_Exit(void)
{
}
/*********************************************
function: Hardware interface
note:
SPI4W_Write_Byte(value) :
Register hardware SPI
*********************************************/
uint8_t SPI4W_Write_Byte(uint8_t value)
{
uint8_t rxDat;
spi_write_read_blocking(spi1,&value,&rxDat,1);
return rxDat;
}
uint8_t SPI4W_Read_Byte(uint8_t value)
{
return SPI4W_Write_Byte(value);
}
/********************************************************************************
function: Delay function
note:
Driver_Delay_ms(xms) : Delay x ms
Driver_Delay_us(xus) : Delay x us
********************************************************************************/
void Driver_Delay_ms(uint32_t xms)
{
sleep_ms(xms);
}
void Driver_Delay_us(uint32_t xus)
{
int j;
for(j=xus; j > 0; j--);
}
/*****************************************************************************
* | File : DEV_Config.c
* | Author : Waveshare team
* | Function : GPIO Function
* | Info :
* Provide the hardware underlying interface
*----------------
* | This version: V1.0
* | Date : 2018-01-11
* | Info : Basic version
*
******************************************************************************/
#ifndef _DEV_CONFIG_H_
#define _DEV_CONFIG_H_
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/pwm.h"
#include "stdio.h"
#define UBYTE uint8_t
#define UWORD uint16_t
#define UDOUBLE uint32_t
#define LCD_RST_PIN 15
#define LCD_DC_PIN 8
#define LCD_CS_PIN 9
#define LCD_CLK_PIN 10
#define LCD_BKL_PIN 13 // LCD Backlight (PWM) - now safe, SD card uses GPIO 22
#define LCD_MOSI_PIN 11
#define LCD_MISO_PIN 12
#define TP_CS_PIN 16
#define TP_IRQ_PIN 17
#define SD_CS_PIN 22 // SD Card CS - separated from LCD_BKL_PIN
#define SPI_PORT spi1
#define MAX_BMP_FILES 25
/*------------------------------------------------------------------------------------------------------*/
void DEV_Digital_Write(UWORD Pin, UBYTE Value);
UBYTE DEV_Digital_Read(UWORD Pin);
void DEV_GPIO_Mode(UWORD Pin, UWORD Mode);
void DEV_GPIO_Init(void);
uint8_t System_Init(void);
void System_Exit(void);
uint8_t SPI4W_Write_Byte(uint8_t value);
uint8_t SPI4W_Read_Byte(uint8_t value);
void Driver_Delay_ms(uint32_t xms);
void Driver_Delay_us(uint32_t xus);
#endif
aux_source_directory(. DIR_context)
add_library(context context.c)
target_include_directories(context PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(context pico_stdlib cmsis_core hardware_flash)
\ No newline at end of file
#include "pico/stdlib.h"
#include "context.h"
#include "hardware/watchdog.h"
#include "pico/platform.h" // __disable_irq()
#include "hardware/regs/addressmap.h"
#include "hardware/regs/m0plus.h"
#include "hardware/structs/scb.h"
typedef void (*entry_t)(void);
__attribute__((noreturn, noinline))
void jump_to_image(uint32_t offset)
{
uint32_t app_base = XIP_BASE + offset;
printf("Jumping to image at address 0x%X\n", app_base);
__asm volatile ("cpsid i" ::: "memory");
// Disable SysTick (no CMSIS needed)
*(volatile uint32_t *)(PPB_BASE + M0PLUS_SYST_CSR_OFFSET) = 0;
*(volatile uint32_t *)(PPB_BASE + M0PLUS_SYST_CVR_OFFSET) = 0;
// Clear & disable NVIC interrupts
for (int i = 0; i < 8; ++i) {
*(volatile uint32_t *)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET + i*4) = 0xFFFFFFFFu;
*(volatile uint32_t *)(PPB_BASE + M0PLUS_NVIC_ICPR_OFFSET + i*4) = 0xFFFFFFFFu;
}
const uint32_t *vt = (const uint32_t *)app_base;
uint32_t msp = vt[0];
uint32_t reset = vt[1];
__asm volatile("dsb 0xF; isb 0xF" ::: "memory");
scb_hw->vtor = app_base; // VTOR without CMSIS
__asm volatile("dsb 0xF; isb 0xF" ::: "memory");
__asm volatile("msr msp, %0" :: "r"(msp) : "memory");
__asm volatile ("cpsie i" ::: "memory");
((entry_t)reset)();
__builtin_unreachable();
}
\ No newline at end of file
#ifndef CONTEXT_H
#define CONTEXT_H
#define CONTEXT_SWITCHER_OFFSET 0x100u
#define APP1_OFFSET 0x100u
#define APP2_OFFSET 0x140000u
#ifdef __cplusplus
extern "C" {
#endif
#include "pico/stdlib.h"
#include "hardware/structs/scb.h" // scb_hw
#include "hardware/regs/xip.h" // XIP_BASE
#include <stdint.h>
typedef void (*app_entry_t)(void);
void jump_to_image(uint32_t offset);
#ifdef __cplusplus
}
#endif
#endif // CONTEXT_H
\ No newline at end of file
aux_source_directory(. DIR_font_SRCS)
add_library(font ${DIR_font_SRCS})
target_link_libraries(font PUBLIC)
/**
******************************************************************************
* @file fonts.h
* @author MCD Application Team
* @version V1.0.0
* @date 18-February-2014
* @brief Header for fonts.c file
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FONTS_H
#define __FONTS_H
/* Max size of bitmap will based on a font24 (17x24) */
#define MAX_HEIGHT_FONT 24
#define MAX_WIDTH_FONT 17
#define OFFSET_BITMAP 54
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
typedef struct _tFont
{
const uint8_t *table;
uint16_t Width;
uint16_t Height;
} sFONT;
extern sFONT Font24;
extern sFONT Font20;
extern sFONT Font16;
extern sFONT Font12;
extern sFONT Font8;
#ifdef __cplusplus
}
#endif
#endif /* __FONTS_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
aux_source_directory(. DIR_HTTP_CLIENT_SRCS)
add_library(http_client ${DIR_HTTP_CLIENT_SRCS})
target_include_directories(http_client PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/.. ${CMAKE_SOURCE_DIR})
target_link_libraries(http_client PUBLIC
pico_stdlib
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_http
pico_lwip_mbedtls
)
# Ensure mbedtls headers are available while building this library
target_link_libraries(http_client PRIVATE pico_mbedtls)
# Use our mbedtls config
target_compile_definitions(http_client PUBLIC PICO_MBEDTLS_CONFIG_FILE="lib/http_client/mbedtls_config.h")
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include "pico/async_context.h"
#include "lwip/altcp.h"
#include "lwip/altcp_tls.h"
#if LWIP_ALTCP && LWIP_ALTCP_TLS
#include "mbedtls/ssl.h"
#endif
#include "http_client_util.h"
#ifndef HTTP_INFO
#define HTTP_INFO printf
#endif
#ifndef HTTP_INFOC
#define HTTP_INFOC putchar
#endif
#ifndef HTTP_INFOC
#define HTTP_INFOC putchar
#endif
#ifndef HTTP_DEBUG
#ifdef NDEBUG
#define HTTP_DEBUG
#else
#define HTTP_DEBUG printf
#endif
#endif
#ifndef HTTP_ERROR
#define HTTP_ERROR printf
#endif
// Print headers to stdout
err_t http_client_header_print_fn(__unused httpc_state_t *connection, __unused void *arg, struct pbuf *hdr, u16_t hdr_len, __unused u32_t content_len) {
HTTP_INFO("\nheaders %u\n", hdr_len);
u16_t offset = 0;
while (offset < hdr->tot_len && offset < hdr_len) {
char c = (char)pbuf_get_at(hdr, offset++);
HTTP_INFOC(c);
}
return ERR_OK;
}
// Print body to stdout
err_t http_client_receive_print_fn(__unused void *arg, __unused struct altcp_pcb *conn, struct pbuf *p, err_t err) {
printf("recv cb: err=%d\n", err);
HTTP_INFO("\ncontent err %d\n", err);
u16_t offset = 0;
while (offset < p->tot_len) {
char c = (char)pbuf_get_at(p, offset++);
// printf("%c", c);
HTTP_INFOC(c);
}
return ERR_OK;
}
static err_t internal_header_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len) {
assert(arg);
HTTP_REQUEST_T *req = (HTTP_REQUEST_T*)arg;
if (req->headers_fn) {
return req->headers_fn(connection, req->callback_arg, hdr, hdr_len, content_len);
}
return ERR_OK;
}
static err_t internal_recv_fn(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err) {
assert(arg);
HTTP_REQUEST_T *req = (HTTP_REQUEST_T*)arg;
if (req->recv_fn) {
return req->recv_fn(req->callback_arg, conn, p, err);
}
return ERR_OK;
}
static void internal_result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err) {
assert(arg);
HTTP_REQUEST_T *req = (HTTP_REQUEST_T*)arg;
HTTP_DEBUG("result %d len %u server_response %u err %d\n", httpc_result, rx_content_len, srv_res, err);
req->complete = true;
req->result = httpc_result;
if (req->result_fn) {
req->result_fn(req->callback_arg, httpc_result, rx_content_len, srv_res, err);
}
}
// Override altcp_tls_alloc to set sni
#if LWIP_ALTCP && LWIP_ALTCP_TLS
static struct altcp_pcb *altcp_tls_alloc_sni(void *arg, u8_t ip_type) {
assert(arg);
HTTP_REQUEST_T *req = (HTTP_REQUEST_T*)arg;
struct altcp_pcb *pcb = altcp_tls_alloc(req->tls_config, ip_type);
if (!pcb) {
HTTP_ERROR("Failed to allocate PCB\n");
return NULL;
}
mbedtls_ssl_set_hostname(altcp_tls_context(pcb), req->hostname);
return pcb;
}
#endif
// Make a http request, complete when req->complete returns true
int http_client_request_async(async_context_t *context, HTTP_REQUEST_T *req) {
const uint16_t default_port = 80;
#if LWIP_ALTCP && LWIP_ALTCP_TLS
if (req->tls_config) {
if (!req->tls_allocator.alloc) {
req->tls_allocator.alloc = altcp_tls_alloc_sni;
req->tls_allocator.arg = req;
}
req->settings.altcp_allocator = &req->tls_allocator;
}
#endif
req->complete = false;
req->settings.headers_done_fn = req->headers_fn ? internal_header_fn : NULL;
req->settings.result_fn = internal_result_fn;
async_context_acquire_lock_blocking(context);
err_t ret = httpc_get_file_dns(req->hostname, req->port ? req->port : default_port, req->url, &req->settings, internal_recv_fn, req, NULL);
async_context_release_lock(context);
if (ret != ERR_OK) {
HTTP_ERROR("http request failed: %d", ret);
}
return ret;
}
// Make a http request and only return when it has completed. Returns true on success
int http_client_request_sync(async_context_t *context, HTTP_REQUEST_T *req) {
assert(req);
int ret = http_client_request_async(context, req);
if (ret != 0) {
printf("http_client_request_async failed: %d\n", ret);
return ret;
}
while(!req->complete) {
async_context_poll(context);
async_context_wait_for_work_ms(context, 1000);
}
printf("http_client_request_sync completed\n");
printf("http_client_request_sync result: %d\n", req->result);
return req->result;
}
\ No newline at end of file
/**
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef HTTP_CLIENT_UTIL_H
#define HTTP_CLIENT_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include "lwipopts.h"
#include "lwip/apps/http_client.h"
#include "lwip/altcp.h"
#include "lwip/altcp_tls.h"
/*! \brief Parameters used to make HTTP request
* \ingroup pico_lwip
*/
typedef struct HTTP_REQUEST {
/*!
* The name of the host, e.g. www.raspberrypi.com
*/
const char *hostname;
/*!
* The url to request, e.g. /favicon.ico
*/
const char *url;
/*!
* Function to callback with headers, can be null
* @see httpc_headers_done_fn
*/
httpc_headers_done_fn headers_fn;
/*!
* Function to callback with results from the server, can be null
* @see altcp_recv_fn
*/
altcp_recv_fn recv_fn;
/*!
* Function to callback with final results of the request, can be null
* @see httpc_result_fn
*/
httpc_result_fn result_fn;
/*!
* Callback to pass to calback functions
*/
void *callback_arg;
/*!
* The port to use. A default port is chosen if this is set to zero
*/
uint16_t port;
#if LWIP_ALTCP && LWIP_ALTCP_TLS
/* TLS configuration, can be null or set to a correctly configured tls configuration.
e.g altcp_tls_create_config_client(NULL, 0) would use https without a certificate */
struct altcp_tls_config *tls_config;
/* TLS allocator, used internally for setting TLS server name indication */
altcp_allocator_t tls_allocator;
#endif
/*!
* LwIP HTTP client settings
*/
httpc_connection_t settings;
/* Optional POST body; if set, a POST request is sent instead of GET */
const void *post_data;
size_t post_len;
const char *post_content_type; /* e.g. "application/json"; optional */
/*!
* Flag to indicate when the request is complete
*/
int complete;
/*!
* Overall result of http request, only valid when complete is set
*/
httpc_result_t result;
} HTTP_REQUEST_T;
struct async_context;
/*! \brief Perform a http request asynchronously
* \ingroup pico_lwip
*
* Perform the http request asynchronously
*
* @param context async context
* @param req HTTP request parameters. As a minimum this should be initialised to zero with hostname and url set to valid values
* @return If zero is returned the request has been made and is complete when \em req->complete is true or the result callback has been called.
* A non-zero return value indicates an error.
*
* @see async_context
*/
int http_client_request_async(struct async_context *context, HTTP_REQUEST_T *req);
/*! \brief Perform a http request synchronously
* \ingroup pico_lwip
*
* Perform the http request synchronously
*
* @param context async context
* @param req HTTP request parameters. As a minimum this should be initialised to zero with hostname and url set to valid values
* @param result Returns the overall result of the http request when complete. Zero indicates success.
*/
int http_client_request_sync(struct async_context *context, HTTP_REQUEST_T *req);
/*! \brief A http header callback that can be passed to \em http_client_init or \em http_client_init_secure
* \ingroup pico_http_client
*
* An implementation of the http header callback which just prints headers to stdout
*
* @param arg argument specified on initialisation
* @param hdr header pbuf(s) (may contain data also)
* @param hdr_len length of the headers in 'hdr'
* @param content_len content length as received in the headers (-1 if not received)
* @return if != zero is returned, the connection is aborted
*/
err_t http_client_header_print_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len);
/*! \brief A http recv callback that can be passed to http_client_init or http_client_init_secure
* \ingroup pico_http_client
*
* An implementation of the http recv callback which just prints the http body to stdout
*
* @param arg argument specified on initialisation
* @param conn http client connection
* @param p body pbuf(s)
* @param err Error code in the case of an error
* @return if != zero is returned, the connection is aborted
*/
err_t http_client_receive_print_fn(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err);
#ifdef __cplusplus
}
#endif
#endif
\ No newline at end of file
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
#define _LWIPOPTS_EXAMPLE_COMMONH_H
// 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)
// Pico SDK uses NO_SYS=1 even for the threadsafe background arch
// 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
#ifndef MEM_SIZE
#define MEM_SIZE 40000
#endif
#ifndef MEMP_NUM_SYS_TIMEOUT
#define MEMP_NUM_SYS_TIMEOUT 64
#endif
#ifndef LWIP_TIMERS
#define LWIP_TIMERS 1
#endif
#ifndef SYS_LIGHTWEIGHT_PROT
#define SYS_LIGHTWEIGHT_PROT 1
#endif
#define MEMP_NUM_TCP_SEG 64
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 48
#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 (6 * 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_SNTP 1
#define SNTP_SERVER_DNS 1
#define LWIP_TCP_KEEPALIVE 1
#define LWIP_NETIF_TX_SINGLE_PBUF 0
// Core locking not used with NO_SYS=1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#define LWIP_HTTP_CLIENT 1
#define LWIP_ALTCP 1
#define LWIP_ALTCP_TLS 1
#define LWIP_ALTCP_TLS_MBEDTLS 1
#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_ON
#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_ON
#define MEMP_DEBUG LWIP_DBG_ON
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_ON
#define TCP_INPUT_DEBUG LWIP_DBG_ON
#define TCP_OUTPUT_DEBUG LWIP_DBG_ON
#define TCP_RTO_DEBUG LWIP_DBG_ON
#define TCP_CWND_DEBUG LWIP_DBG_ON
#define TCP_WND_DEBUG LWIP_DBG_ON
#define TCP_FR_DEBUG LWIP_DBG_ON
#define TCP_QLEN_DEBUG LWIP_DBG_ON
#define TCP_RST_DEBUG LWIP_DBG_ON
#define UDP_DEBUG LWIP_DBG_OFF
#define TCPIP_DEBUG LWIP_DBG_OFF
#define DNS_DEBUG LWIP_DBG_ON
#define HTTPC_DEBUG LWIP_DBG_ON
#define ALTCP_DEBUG LWIP_DBG_ON
#define ALTCP_TLS_DEBUG LWIP_DBG_ON
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
#endif /* __LWIPOPTS_H__ */
\ No newline at end of file
#ifndef MBEDTLS_CONFIG_EXAMPLES_COMMON_H
#define MBEDTLS_CONFIG_EXAMPLES_COMMON_H
/* Workaround for some mbedtls source files using INT_MAX without including limits.h */
#include <limits.h>
#define MBEDTLS_NO_PLATFORM_ENTROPY
#define MBEDTLS_ENTROPY_HARDWARE_ALT
#define MBEDTLS_SSL_OUT_CONTENT_LEN 4096
#define MBEDTLS_SSL_IN_CONTENT_LEN 4096
#define MBEDTLS_SSL_MAX_CONTENT_LEN 4096
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
/* Enable time support required by lwIP TLS shim; we can still skip strict validation at runtime if needed */
#define MBEDTLS_HAVE_TIME
#define MBEDTLS_PLATFORM_MS_TIME_ALT
#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
#define MBEDTLS_PKCS1_V15
#define MBEDTLS_SHA256_SMALLER
#define MBEDTLS_SSL_SERVER_NAME_INDICATION
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_ERROR_C
#define MBEDTLS_MD_C
#define MBEDTLS_MD5_C
#define MBEDTLS_OID_C
#define MBEDTLS_PKCS5_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_RSA_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_SHA224_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA512_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_AES_FEWER_TABLES
/* TLS 1.2 */
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_GCM_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ASN1_WRITE_C
// The following is needed to parse a certificate
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_BASE64_C
// The following significantly speeds up mbedtls due to NIST optimizations.
#define MBEDTLS_ECP_NIST_OPTIM
#endif
\ No newline at end of file
#pragma once
static const uint8_t CA_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDizCCAxGgAwIBAgISBo7pPZX5qXHJVynVmTNh5wrPMAoGCCqGSM49BAMDMDIx\n"
"CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF\n"
"NzAeFw0yNTEwMTMwMDAyMDRaFw0yNjAxMTEwMDAyMDNaMBUxEzARBgNVBAMMCiou\n"
"bmdyb2suaW8wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASjMPsGJiCZFmSB+AqR\n"
"S7/Bt9Cz7gvHkbdEp1s9BbljSd3rKraJY20qhjVrsHNGw0/FPAaW5/8TqAqrYsvf\n"
"1PB8o4ICIjCCAh4wDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMB\n"
"BggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTNPeRrYAgk/VkKAxtt\n"
"bw9laTbwxjAfBgNVHSMEGDAWgBSuSJ7chx1EoG/aouVgdAR4wpwAgDAyBggrBgEF\n"
"BQcBAQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly9lNy5pLmxlbmNyLm9yZy8wHwYD\n"
"VR0RBBgwFoIKKi5uZ3Jvay5pb4IIbmdyb2suaW8wEwYDVR0gBAwwCjAIBgZngQwB\n"
"AgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U3LmMubGVuY3Iub3JnLzgwLmNy\n"
"bDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AGQRxGykEuyniRyiAi4AvKtPKAfU\n"
"HjUnq+r+1QPJfc3wAAABmdsVXjMAAAQDAEcwRQIhAK83pbsmc+ZwMvWeuAX/Vtew\n"
"z1MJSOv0LRwgeWCDN+8MAiASipbiUZQRK/oxiKaHqg0+RO56+cmTaTFCvZIbCDfF\n"
"EwB2AMs49xWJfIShRF9bwd37yW7ymlnNRwppBYWwyxTDFFjnAAABmdsVXksAAAQD\n"
"AEcwRQIgeOfexXV+F/m3ndy9AE6cSlQjnxiG7BSQ1n1q/cO3wCECIQCgcryB5kRo\n"
"qlOrfLAPXosftikpapfUfGNp8tGXLQnT7TAKBggqhkjOPQQDAwNoADBlAjEA7VMh\n"
"l8MMNvQa2v0BoiaYrDllqtMJ4REtTv2qhjS90DqGLyCLQfriyK2chVFNvhxsAjAD\n"
"nyOR7vZSI3OwcveQFEiFluSeMVITKd9u78BqTL3A0Ce+VmW+h2RBvPJPk33A14o=\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIEVzCCAj+gAwIBAgIRAKp18eYrjwoiCWbTi7/UuqEwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n"
"WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n"
"RW5jcnlwdDELMAkGA1UEAxMCRTcwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARB6AST\n"
"CFh/vjcwDMCgQer+VtqEkz7JANurZxLP+U9TCeioL6sp5Z8VRvRbYk4P1INBmbef\n"
"QHJFHCxcSjKmwtvGBWpl/9ra8HW0QDsUaJW2qOJqceJ0ZVFT3hbUHifBM/2jgfgw\n"
"gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n"
"ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSuSJ7chx1EoG/aouVgdAR4\n"
"wpwAgDAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n"
"AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n"
"BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n"
"Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAjx66fDdLk5ywFn3CzA1w1qfylHUD\n"
"aEf0QZpXcJseddJGSfbUUOvbNR9N/QQ16K1lXl4VFyhmGXDT5Kdfcr0RvIIVrNxF\n"
"h4lqHtRRCP6RBRstqbZ2zURgqakn/Xip0iaQL0IdfHBZr396FgknniRYFckKORPG\n"
"yM3QKnd66gtMst8I5nkRQlAg/Jb+Gc3egIvuGKWboE1G89NTsN9LTDD3PLj0dUMr\n"
"OIuqVjLB8pEC6yk9enrlrqjXQgkLEYhXzq7dLafv5Vkig6Gl0nuuqjqfp0Q1bi1o\n"
"yVNAlXe6aUXw92CcghC9bNsKEO1+M52YY5+ofIXlS/SEQbvVYYBLZ5yeiglV6t3S\n"
"M6H+vTG0aP9YHzLn/KVOHzGQfXDP7qM5tkf+7diZe7o2fw6O7IvN6fsQXEQQj8TJ\n"
"UXJxv2/uJhcuy/tSDgXwHM8Uk34WNbRT7zGTGkQRX0gsbjAea/jYAoWv0ZvQRwpq\n"
"Pe79D/i7Cep8qWnA+7AE/3B3S/3dEEYmc0lpe1366A/6GEgk3ktr9PEoQrLChs6I\n"
"tu3wnNLB2euC8IKGLQFpGtOO/2/hiAKjyajaBP25w1jF0Wl8Bbqne3uZ2q1GyPFJ\n"
"YRmT7/OXpmOH/FVLtwS+8ng1cAmpCujPwteJZNcDG0sF2n/sc0+SQf49fdyUK0ty\n"
"+VUwFj9tmWxyR/M=\n"
"-----END CERTIFICATE-----\n"
;
#ifndef HTTP_CLIENT_REQUEST_HPP
#define HTTP_CLIENT_REQUEST_HPP
#include <string>
#include <map>
#include <memory>
#include <vector>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "pico/async_context.h"
#include "lwip/apps/httpd.h"
#include "lwip/altcp_tls.h"
#include "lwip/apps/sntp.h"
#include "lwip/dns.h"
#include "lwip/ip_addr.h"
#include "lwip/altcp.h"
#include "lwip/pbuf.h"
const char HOST[] = "taltech-mles-app.ngrok.io";
const char URL_REQUEST[] = "/api/v1/ocr_chunked";
const int PORT = 80;
const char WIFI_SSID[] = "";
const char WIFI_PASSWORD[] = "";
const char TEST_DATA[] =
"0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.16862746,0.18431373,0.18431373,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03529412,0.42352942,0.9764706,0.99215686,0.99215686,0.8156863,0.8117647,0.8117647,0.8117647,0.58431375,0.25490198,0.050980393,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03529412,0.72156864,0.99607843,0.99215686,0.99215686,0.99215686,0.99607843,0.99215686,0.99215686,0.99215686,0.99607843,0.99215686,0.8352941,0.09803922,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.21568628,0.79607844,0.99607843,0.99607843,0.78039217,0.49803922,0.49803922,0.23529412,0.3647059,0.32941177,0.26666668,0.5921569,0.87058824,0.99607843,0.6313726,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5411765,0.99215686,0.99215686,0.78039217,0.07450981,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.60784316,0.99215686,0.827451,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5411765,0.99215686,0.99215686,0.06666667,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.2901961,0.94509804,0.99215686,0.827451,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.4117647,0.99215686,0.99215686,0.4,0.0,0.0,0.0,0.0,0.0,0.0,0.13333334,0.8980392,0.99215686,0.99215686,0.627451,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.58431375,0.99607843,0.8980392,0.15686275,0.0,0.0,0.0,0.14901961,0.6,0.99607843,0.99607843,0.99607843,0.7058824,0.09803922,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.07450981,0.7764706,0.99607843,0.8117647,0.03529412,0.13333334,0.28235295,0.92156863,0.99215686,0.99215686,0.8784314,0.54509807,0.050980393,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.06666667,0.827451,0.99215686,0.84313726,0.9411765,0.99607843,0.99215686,0.91764706,0.5019608,0.06666667,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.4,0.8980392,0.99215686,0.99215686,0.99215686,0.89411765,0.3019608,0.050980393,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.27450982,0.6666667,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.46666667,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.101960786,0.50980395,0.9019608,0.99607843,0.99215686,0.99215686,0.7254902,0.4509804,0.2509804,0.827451,0.99215686,0.972549,0.08235294,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.6509804,0.9098039,0.99215686,0.99215686,0.96862745,0.63529414,0.18039216,0.050980393,0.02745098,0.35686275,0.9607843,0.99215686,0.99607843,0.21960784,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5019608,0.99215686,0.99215686,0.99215686,0.8235294,0.3647059,0.49803922,0.62352943,0.8,0.99215686,0.99215686,0.99215686,0.89411765,0.05882353,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5254902,0.94509804,0.99607843,1.0,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.89411765,0.13333334,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.105882354,0.4509804,0.54901963,0.80784315,0.80784315,0.80784315,0.8117647,0.80784315,0.48235294,0.05882353,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0";
typedef struct ResponseCtx {
char body[1024];
size_t len;
bool ok;
} ResponseCtx;
typedef struct ResponseDataStr {
char *y;
bool status_ok;
} ResponseDataStr;
typedef struct {
const char *url;
const char *host;
const char *ctype;
const char *body;
size_t body_len;
size_t sent_offset; // how many body bytes have been queued
bool header_queued; // header was successfully queued
// tcp
struct altcp_pcb *pcb;
// result/flow
volatile bool complete;
volatile bool resolved;
volatile bool connected;
err_t result;
ip_addr_t addr;
uint16_t port;
ResponseCtx *resp;
} PostState;
class HttpRequest {
public:
HttpRequest(const std::string& url);
~HttpRequest();
void set_body(const uint8_t* data, size_t len);
// void set_body_binary(const std::vector<uint8_t>& data);
void set_body_binary(const uint8_t* data, size_t len);
void set_body_raw(const char* data, size_t len);
void set_dummy_mnist_data();
void set_dummy_image_data();
void set_dummy_imu_data();
void reinitialize();
std::string get_body() const;
ResponseDataStr post();
ResponseDataStr get();
private:
// Backing storage that will stay alive
std::string url_;
std::string host_;
std::string ctype_;
std::string body_; // holds JSON payload
std::vector<uint8_t> body_bin_; // holds binary payload
std::vector<uint8_t> raw_body_;
ResponseCtx ctx_{}; // persistent response buffer
PostState st_{}; // persistent state
async_context_t* ctx = nullptr;
void parse_response_data(const char *json, size_t len, ResponseDataStr *out);
};
#endif // HTTP_CLIENT_REQUEST_HPP
\ No newline at end of file
#ifndef SAMPLE_DATA_H
#define SAMPLE_DATA_H
static const char MNIST_SAMPLE_DATA[] = "0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.16862746,0.18431373,0.18431373,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03529412,0.42352942,0.9764706,0.99215686,0.99215686,0.8156863,0.8117647,0.8117647,0.8117647,0.58431375,0.25490198,0.050980393,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03529412,0.72156864,0.99607843,0.99215686,0.99215686,0.99215686,0.99607843,0.99215686,0.99215686,0.99215686,0.99607843,0.99215686,0.8352941,0.09803922,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.21568628,0.79607844,0.99607843,0.99607843,0.78039217,0.49803922,0.49803922,0.23529412,0.3647059,0.32941177,0.26666668,0.5921569,0.87058824,0.99607843,0.6313726,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5411765,0.99215686,0.99215686,0.78039217,0.07450981,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.60784316,0.99215686,0.827451,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5411765,0.99215686,0.99215686,0.06666667,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.2901961,0.94509804,0.99215686,0.827451,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.4117647,0.99215686,0.99215686,0.4,0.0,0.0,0.0,0.0,0.0,0.0,0.13333334,0.8980392,0.99215686,0.99215686,0.627451,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.58431375,0.99607843,0.8980392,0.15686275,0.0,0.0,0.0,0.14901961,0.6,0.99607843,0.99607843,0.99607843,0.7058824,0.09803922,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.07450981,0.7764706,0.99607843,0.8117647,0.03529412,0.13333334,0.28235295,0.92156863,0.99215686,0.99215686,0.8784314,0.54509807,0.050980393,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.06666667,0.827451,0.99215686,0.84313726,0.9411765,0.99607843,0.99215686,0.91764706,0.5019608,0.06666667,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.4,0.8980392,0.99215686,0.99215686,0.99215686,0.89411765,0.3019608,0.050980393,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.27450982,0.6666667,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.46666667,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.101960786,0.50980395,0.9019608,0.99607843,0.99215686,0.99215686,0.7254902,0.4509804,0.2509804,0.827451,0.99215686,0.972549,0.08235294,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.6509804,0.9098039,0.99215686,0.99215686,0.96862745,0.63529414,0.18039216,0.050980393,0.02745098,0.35686275,0.9607843,0.99215686,0.99607843,0.21960784,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5019608,0.99215686,0.99215686,0.99215686,0.8235294,0.3647059,0.49803922,0.62352943,0.8,0.99215686,0.99215686,0.99215686,0.89411765,0.05882353,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.5254902,0.94509804,0.99607843,1.0,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.99607843,0.89411765,0.13333334,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.105882354,0.4509804,0.54901963,0.80784315,0.80784315,0.80784315,0.8117647,0.80784315,0.48235294,0.05882353,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0";
static const char AUDIO_SAMPLE_DATA[] = "";
static const char IMU_SAMPLE_DATA[] = "";
static const char IMAGE_SAMPLE_DATA[] = "";
#endif // SAMPLE_DATA_H
\ No newline at end of file
#ifndef TLS_CERT_H_
#define TLS_CERT_H_
static const uint8_t CA_ISRG_ROOT_X1_PEM[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDjzCCAxWgAwIBAgISBWVQkNbmc4/C4tqFJ8f8pbGSMAoGCCqGSM49BAMDMDIx\n"
"CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF\n"
"NjAeFw0yNTA4MDExODAyMjdaFw0yNTEwMzAxODAyMjZaMBYxFDASBgNVBAMMCyou\n"
"bmdyb2suYXBwMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4dI2kjmWuqdPrKED\n"
"M0iqt6YsMWIkhwBxWEm+wYO7z5UzAvF8svnKpcY10y43yAWhMQwhehvKDX+7hQpH\n"
"g0CRxaOCAiUwggIhMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcD\n"
"AQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUzmHvugQvL94QZDrf\n"
"nQz6nGU7ixIwHwYDVR0jBBgwFoAUkydGmAOpUWiOmNbEQkjbI79YlNIwMgYIKwYB\n"
"BQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRwOi8vZTYuaS5sZW5jci5vcmcvMCEG\n"
"A1UdEQQaMBiCCyoubmdyb2suYXBwggluZ3Jvay5hcHAwEwYDVR0gBAwwCjAIBgZn\n"
"gQwBAgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U2LmMubGVuY3Iub3JnLzE5\n"
"LmNybDCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3AKRCxQZJYGFUjw/U6pz7ei0m\n"
"RU2HqX8v30VZ9idPOoRUAAABmGcCQNEAAAQDAEgwRgIhAMBBMFvWxN1QbBNdDuTz\n"
"y1XTep6SB/1I5xvNvleIVJC/AiEAnn9kHokOiSP7WqRFikqMcFWhhVnd4a5H5yK+\n"
"vLvURkAAdgDd3Mo0ldfhFgXnlTL6x5/4PRxQ39sAOhQSdgosrLvIKgAAAZhnAkFA\n"
"AAAEAwBHMEUCIBpHq1s8tNgLcyRwkoUUBcO8BVWKjExh1jP5OP22qMZ7AiEA2j5B\n"
"ZD/ktUVDC2jNNwYoO3EtXOn9EbnGVRfjhAn3fmUwCgYIKoZIzj0EAwMDaAAwZQIw\n"
"OHnBxPMq4qJA6jOUvG6J3u4WtxixxjCmuk5qi6EEvcbx2buXwiCyo2Qly+V6P+xJ\n"
"AjEAjbxl8zPMaf1M0jfN/V4KPkKfG31GwYlRwG6iAR7siabPOLCve2lxAmqG51/i\n"
"2JyZ\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n"
"WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n"
"RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\n"
"h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n"
"6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\n"
"gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n"
"ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\n"
"v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n"
"AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n"
"BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n"
"Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\n"
"MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\n"
"pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\n"
"eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\n"
"pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\n"
"s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\n"
"h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\n"
"YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\n"
"ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\n"
"LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\n"
"EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\n"
"Ig46v9mFmBvyH04=\n"
"-----END CERTIFICATE-----\n"
;
#endif
\ No newline at end of file
aux_source_directory(. DIR_LCD_SRCS)
include_directories(../config)
include_directories(../font)
add_library(lcd ${DIR_LCD_SRCS})
target_link_libraries(lcd PUBLIC config font pico_stdlib)
/*****************************************************************************
* | File : LCD_Driver.h
* | Author : Waveshare team
* | Function : ILI9486 Drive function
* | Info :
* Image scanning:
* Please use progressive scanning to generate images or fonts
*----------------
* | This version: V1.0
* | Date : 2018-01-11
* | Info : Basic version
*
******************************************************************************/
/**************************Intermediate driver layer**************************/
#ifndef __LCD_DRIVER_H
#define __LCD_DRIVER_H
#include "DEV_Config.h"
#define LCD_2_8 0x52
#define LCD_3_5 0x00
#define COLOR uint16_t //The variable type of the color (unsigned short)
#define POINT uint16_t //The type of coordinate (unsigned short)
#define LENGTH uint16_t //The type of coordinate (unsigned short)
/********************************************************************************
function:
Define the full screen height length of the display
********************************************************************************/
#define LCD_X_MAXPIXEL 480 //LCD width maximum memory
#define LCD_Y_MAXPIXEL 320 //LCD height maximum memory
#define LCD_X 0
#define LCD_Y 0
#define LCD_3_5_WIDTH (LCD_X_MAXPIXEL - 2 * LCD_X) //LCD width
#define LCD_3_5_HEIGHT LCD_Y_MAXPIXEL //LCD height
#define LCD_2_8_WIDTH 240 //LCD width
#define LCD_2_8_HEIGHT 320
/********************************************************************************
function:
scanning method
********************************************************************************/
typedef enum {
L2R_U2D = 0, //The display interface is displayed , left to right, up to down
L2R_D2U ,
R2L_U2D ,
R2L_D2U ,
U2D_L2R ,
U2D_R2L ,
D2U_L2R ,
D2U_R2L ,
} LCD_SCAN_DIR;
#define SCAN_DIR_DFT D2U_L2R //Default scan direction = L2R_U2D
/********************************************************************************
function:
Defines the total number of rows in the display area
********************************************************************************/
typedef struct {
LENGTH LCD_Dis_Column; //COLUMN
LENGTH LCD_Dis_Page; //PAGE
LCD_SCAN_DIR LCD_Scan_Dir;
POINT LCD_X_Adjust; //LCD x actual display position calibration
POINT LCD_Y_Adjust; //LCD y actual display position calibration
} LCD_DIS;
/********************************************************************************
function:
Macro definition variable name
********************************************************************************/
void LCD_Init(LCD_SCAN_DIR LCD_ScanDir, uint16_t LCD_BLval);
void LCD_SetGramScanWay(LCD_SCAN_DIR Scan_dir);
void LCD_WriteReg(uint8_t Reg);
void LCD_WriteData(uint16_t Data);
void LCD_SetWindow(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend);
void LCD_SetCursor(POINT Xpoint, POINT Ypoint);
void LCD_SetColor(COLOR Color ,POINT Xpoint, POINT Ypoint);
void LCD_SetPointlColor(POINT Xpoint, POINT Ypoint, COLOR Color);
void LCD_SetArealColor(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend,COLOR Color);
void LCD_Clear(COLOR Color);
uint8_t LCD_Read_Id(void);
void LCD_SetBackLight(uint16_t value);
#endif
/*****************************************************************************
* | File : LCD_GUI.h
* | Author : Waveshare team
* | Function : Achieve drawing: draw points, lines, boxes, circles and
* their size, solid dotted line, solid rectangle hollow
* rectangle, solid circle hollow circle.
* | Info :
* Achieve display characters: Display a single character, string, number
* Achieve time display: adaptive size display time minutes and seconds
*----------------
* | This version: V1.0
* | Date : 2017-08-16
* | Info : Basic version
*
******************************************************************************/
/****************************Upper application layer**************************/
#ifndef __LCD_GUI_H
#define __LCD_GUI_H
#include "LCD_Driver.h"
#include "fonts.h"
#define LOW_Speed_Show 0
#define HIGH_Speed_Show 1
/********************************************************************************
function:
dot pixel
********************************************************************************/
typedef enum {
DOT_PIXEL_1X1 = 1, // dot pixel 1 x 1
DOT_PIXEL_2X2 , // dot pixel 2 X 2
DOT_PIXEL_3X3 , // dot pixel 3 X 3
DOT_PIXEL_4X4 , // dot pixel 4 X 4
DOT_PIXEL_5X5 , // dot pixel 5 X 5
DOT_PIXEL_6X6 , // dot pixel 6 X 6
DOT_PIXEL_7X7 , // dot pixel 7 X 7
DOT_PIXEL_8X8 , // dot pixel 8 X 8
} DOT_PIXEL;
#define DOT_PIXEL_DFT DOT_PIXEL_1X1 //Default dot pilex
/********************************************************************************
function:
dot Fill style
********************************************************************************/
typedef enum {
DOT_FILL_AROUND = 1, // dot pixel 1 x 1
DOT_FILL_RIGHTUP , // dot pixel 2 X 2
} DOT_STYLE;
#define DOT_STYLE_DFT DOT_FILL_AROUND //Default dot pilex
/********************************************************************************
function:
solid line and dotted line
********************************************************************************/
typedef enum {
LINE_SOLID = 0,
LINE_DOTTED,
} LINE_STYLE;
/********************************************************************************
function:
DRAW Internal fill
********************************************************************************/
typedef enum {
DRAW_EMPTY = 0,
DRAW_FULL,
} DRAW_FILL;
/********************************************************************************
function:
time
********************************************************************************/
typedef struct {
uint16_t Year; //0000
uint8_t Month; //1 - 12
uint8_t Day; //1 - 30
uint8_t Hour; //0 - 23
uint8_t Min; //0 - 59
uint8_t Sec; //0 - 59
} DEV_TIME;
extern DEV_TIME sDev_time;
/********************************************************************************
function:
Defines commonly used colors for the display
********************************************************************************/
#define LCD_BACKGROUND WHITE //Default background color
#define FONT_BACKGROUND WHITE //Default font background color
#define FONT_FOREGROUND GRED //Default font foreground color
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40
#define BRRED 0XFC07
#define GRAY 0X8430
/********************************************************************************
function:
Macro definition variable name
********************************************************************************/
//Clear
void GUI_Clear(COLOR Color);
//Drawing
void GUI_DrawPoint(POINT Xpoint, POINT Ypoint, COLOR Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_FillWay);
void GUI_DrawLine(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend, COLOR Color, LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel);
void GUI_DrawRectangle(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend, COLOR Color, DRAW_FILL Filled , DOT_PIXEL Dot_Pixel );
void GUI_DrawCircle(POINT X_Center, POINT Y_Center, LENGTH Radius, COLOR Color, DRAW_FILL Draw_Fill , DOT_PIXEL Dot_Pixel );
//pic
void GUI_Disbitmap(POINT Xpoint, POINT Ypoint, const unsigned char *pMap, POINT Width, POINT Height);
void GUI_DisGrayMap(POINT Xpoint, POINT Ypoint, const unsigned char *pBmp);
//Display string
void GUI_DisChar(POINT Xstart, POINT Ystart, const char Acsii_Char, sFONT* Font, COLOR Color_Background, COLOR Color_Foreground);
void GUI_DisString_EN(POINT Xstart, POINT Ystart, const char * pString, sFONT* Font, COLOR Color_Background, COLOR Color_Foreground );
void GUI_DisNum(POINT Xpoint, POINT Ypoint, int32_t Nummber, sFONT* Font, COLOR Color_Background, COLOR Color_Foreground );
void GUI_Showtime(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend, DEV_TIME *pTime, COLOR Color);
//show
void GUI_Show(void);
#endif
#include "LCD_Touch.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
TP_DEV* pTP_DEV;
TP_DRAW* pTP_Draw;
static void TP_DumpBitmapToSerial(int h, int w, const uint8_t bmp[h][w])
{
// Build and print one line at a time (faster than putchar per pixel)
char line[w + 2]; // + '\n' + '\0'
line[w] = '\n';
line[w+1] = '\0';
for (uint16_t y = 0; y < h; ++y) {
for (uint16_t x = 0; x < w; ++x) {
line[x] = bmp[y][x] ? '1' : '0';
}
printf("%s", line); // prints 280 chars of 0/1, then newline
}
}
static uint16_t TP_Read_ADC(uint8_t CMD)
{
uint16_t Data = 0;
//A cycle of at least 400ns.
DEV_Digital_Write(TP_CS_PIN, 0);
SPI4W_Write_Byte(CMD);
Driver_Delay_us(200);
// dont write 0xff, it will block xpt2046
//Data = SPI4W_Read_Byte(0Xff);
Data = SPI4W_Read_Byte(0X00);
Data <<= 8; //7bit
Data |= SPI4W_Read_Byte(0X00);
//Data = SPI4W_Read_Byte(0Xff);
Data >>= 3; //5bit
DEV_Digital_Write(TP_CS_PIN, 1);
return Data;
}
#define READ_TIMES 5 //Number of readings
#define LOST_NUM 1 //Discard value
static uint16_t
TP_Read_ADC_Average(uint8_t Channel_Cmd)
{
uint8_t i, j;
uint16_t Read_Buff[READ_TIMES];
uint16_t Read_Sum = 0, Read_Temp = 0;
//LCD SPI speed = 3 MHz
spi_set_baudrate(SPI_PORT, 3000000);
//Read and save multiple samples
for (i = 0; i < READ_TIMES; i++)
{
Read_Buff[i] = TP_Read_ADC(Channel_Cmd);
Driver_Delay_us(200);
}
//LCD SPI speed = 18 MHz
spi_set_baudrate(SPI_PORT, 18000000);
//Sort from small to large
for (i = 0; i < READ_TIMES - 1; i++)
{
for (j = i + 1; j < READ_TIMES; j++)
{
if (Read_Buff[i] > Read_Buff[j])
{
Read_Temp = Read_Buff[i];
Read_Buff[i] = Read_Buff[j];
Read_Buff[j] = Read_Temp;
}
}
}
//Exclude the largest and the smallest
for (i = LOST_NUM; i < READ_TIMES - LOST_NUM; i++)
Read_Sum += Read_Buff[i];
//Averaging
Read_Temp = Read_Sum / (READ_TIMES - 2 * LOST_NUM);
return Read_Temp;
}
static void TP_Read_ADC_XY(uint16_t *pXCh_Adc, uint16_t *pYCh_Adc)
{
*pXCh_Adc = TP_Read_ADC_Average(0xD0);
*pYCh_Adc = TP_Read_ADC_Average(0x90);
}
#define ERR_RANGE 50 //tolerance scope
static bool TP_Read_TwiceADC(uint16_t *pXCh_Adc, uint16_t *pYCh_Adc)
{
uint16_t XCh_Adc1, YCh_Adc1, XCh_Adc2, YCh_Adc2;
//Read the ADC values Read the ADC values twice
TP_Read_ADC_XY(&XCh_Adc1, &YCh_Adc1);
Driver_Delay_us(10);
TP_Read_ADC_XY(&XCh_Adc2, &YCh_Adc2);
Driver_Delay_us(10);
//The ADC error used twice is greater than ERR_RANGE to take the average
if (((XCh_Adc2 <= XCh_Adc1 && XCh_Adc1 < XCh_Adc2 + ERR_RANGE) ||
(XCh_Adc1 <= XCh_Adc2 && XCh_Adc2 < XCh_Adc1 + ERR_RANGE)) &&
((YCh_Adc2 <= YCh_Adc1 && YCh_Adc1 < YCh_Adc2 + ERR_RANGE) ||
(YCh_Adc1 <= YCh_Adc2 && YCh_Adc2 < YCh_Adc1 + ERR_RANGE)))
{
*pXCh_Adc = (XCh_Adc1 + XCh_Adc2) / 2;
*pYCh_Adc = (YCh_Adc1 + YCh_Adc2) / 2;
return true;
}
//The ADC error used twice is less than ERR_RANGE returns failed
return false;
}
uint8_t TP_Scan(uint8_t chCoordType)
{
//In X, Y coordinate measurement, IRQ is disabled and output is low
if (!DEV_Digital_Read(TP_IRQ_PIN))
{ //Press the button to press
//Read the physical coordinates
if (chCoordType)
{
TP_Read_TwiceADC(&pTP_DEV->Xpoint, &pTP_DEV->Ypoint);
//Read the screen coordinates
}
else if (TP_Read_TwiceADC(&pTP_DEV->Xpoint, &pTP_DEV->Ypoint))
{
if (LCD_2_8 == id)
{
pTP_Draw->Xpoint = sLCD_DIS.LCD_Dis_Column -
pTP_DEV->fXfac * pTP_DEV->Xpoint -
pTP_DEV->iXoff;
pTP_Draw->Ypoint = sLCD_DIS.LCD_Dis_Page -
pTP_DEV->fYfac * pTP_DEV->Ypoint -
pTP_DEV->iYoff;
}
else
{
//DEBUG("(Xad,Yad) = %d,%d\r\n",pTP_DEV->Xpoint,pTP_DEV->Ypoint);
if (pTP_DEV->TP_Scan_Dir == R2L_D2U)
{ //Converts the result to screen coordinates
pTP_Draw->Xpoint = pTP_DEV->fXfac * pTP_DEV->Xpoint +
pTP_DEV->iXoff;
pTP_Draw->Ypoint = pTP_DEV->fYfac * pTP_DEV->Ypoint +
pTP_DEV->iYoff;
}
else if (pTP_DEV->TP_Scan_Dir == L2R_U2D)
{
pTP_Draw->Xpoint = sLCD_DIS.LCD_Dis_Column -
pTP_DEV->fXfac * pTP_DEV->Xpoint -
pTP_DEV->iXoff;
pTP_Draw->Ypoint = sLCD_DIS.LCD_Dis_Page -
pTP_DEV->fYfac * pTP_DEV->Ypoint -
pTP_DEV->iYoff;
}
else if (pTP_DEV->TP_Scan_Dir == U2D_R2L)
{
pTP_Draw->Xpoint = pTP_DEV->fXfac * pTP_DEV->Ypoint +
pTP_DEV->iXoff;
pTP_Draw->Ypoint = pTP_DEV->fYfac * pTP_DEV->Xpoint +
pTP_DEV->iYoff;
}
else
{
pTP_Draw->Xpoint = sLCD_DIS.LCD_Dis_Column -
pTP_DEV->fXfac * pTP_DEV->Ypoint -
pTP_DEV->iXoff;
pTP_Draw->Ypoint = sLCD_DIS.LCD_Dis_Page -
pTP_DEV->fYfac * pTP_DEV->Xpoint -
pTP_DEV->iYoff;
}
// DEBUG("( x , y ) = %d,%d\r\n",pTP_Draw->Xpoint,pTP_Draw->Ypoint);
}
}
if (0 == (pTP_DEV->chStatus & TP_PRESS_DOWN))
{ //Not being pressed
pTP_DEV->chStatus = TP_PRESS_DOWN | TP_PRESSED;
pTP_DEV->Xpoint0 = pTP_DEV->Xpoint;
pTP_DEV->Ypoint0 = pTP_DEV->Ypoint;
}
}
else
{
if (pTP_DEV->chStatus & TP_PRESS_DOWN)
{ //0x80
pTP_DEV->chStatus &= ~(1 << 7); //0x00
}
else
{
pTP_DEV->Xpoint0 = 0;
pTP_DEV->Ypoint0 = 0;
pTP_DEV->Xpoint = 0xffff;
pTP_DEV->Ypoint = 0xffff;
}
}
return (pTP_DEV->chStatus & TP_PRESS_DOWN);
}
void TP_GetAdFac(void)
{
if (LCD_2_8 == id)
{
pTP_DEV->fXfac = 0.066626;
pTP_DEV->fYfac = 0.089779;
pTP_DEV->iXoff = -20;
pTP_DEV->iYoff = -34;
}
else
{
if (pTP_DEV->TP_Scan_Dir == D2U_L2R)
{ //SCAN_DIR_DFT = D2U_L2R
pTP_DEV->fXfac = -0.132443;
pTP_DEV->fYfac = 0.089997;
pTP_DEV->iXoff = 516;
pTP_DEV->iYoff = -22;
}
else if (pTP_DEV->TP_Scan_Dir == L2R_U2D)
{
pTP_DEV->fXfac = 0.089697;
pTP_DEV->fYfac = 0.134792;
pTP_DEV->iXoff = -21;
pTP_DEV->iYoff = -39;
}
else if (pTP_DEV->TP_Scan_Dir == R2L_D2U)
{
pTP_DEV->fXfac = 0.089915;
pTP_DEV->fYfac = 0.133178;
pTP_DEV->iXoff = -22;
pTP_DEV->iYoff = -38;
}
else if (pTP_DEV->TP_Scan_Dir == U2D_R2L)
{
pTP_DEV->fXfac = -0.132906;
pTP_DEV->fYfac = 0.087964;
pTP_DEV->iXoff = 517;
pTP_DEV->iYoff = -20;
}
else
{
LCD_Clear(LCD_BACKGROUND);
GUI_DisString_EN(0, 60, "Does not support touch-screen \
calibration in this direction",
&Font16, FONT_BACKGROUND, RED);
}
}
}
static int count_digits(int number)
{
if (number == 0)
return 1; // 0 has one digit
number = abs(number); // make positive
int count = 0;
while (number > 0) {
number /= 10;
count++;
}
return count;
}
void TP_display_input(int h, int w, const uint8_t* src)
{
for (int i=0; i<h; i++) {
for (int j=0; j<w; j++) {
uint8_t num = src[i * w + j];
int space = 3 - count_digits(num);
for (int i = 0; i < space; ++i) {
printf("\xE2\x80\x8A");
}
printf("%d", num);
}
printf("\n");
}
}
void TP_DrawHeader(void)
{
GUI_DisString_EN(100, 20, "MACHINE LEARNING", &Font24, WHITE, RED);
GUI_DisString_EN(80, 45, "FOR EMBEDDED SYSTEM", &Font24, WHITE, RED);
}
void TP_Init(LCD_SCAN_DIR Lcd_ScanDir, TP_DEV* tp_dev, TP_DRAW* tp_draw)
{
DEV_Digital_Write(TP_CS_PIN, 1);
pTP_DEV = tp_dev;
pTP_Draw = tp_draw;
pTP_DEV->TP_Scan_Dir = Lcd_ScanDir;
TP_Read_ADC_XY(&pTP_DEV->Xpoint, &pTP_DEV->Ypoint);
}
#ifndef __LCD_TOUCH_H_
#define __LCD_TOUCH_H_
#include <math.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/float.h"
#include "DEV_Config.h"
#include "LCD_Driver.h"
#include "LCD_GUI.h"
#define TP_PRESS_DOWN 0x80
#define TP_PRESSED 0x40
// ---- Capture area (the black box) ------------------------------------------
// 480×320
#define LCD_W 480
#define LCD_H 320
#define POINT_SPACE 2
#define IGNORE_INTERVAL_MS 200
#ifdef __cplusplus
extern "C" {
#endif
extern LCD_DIS sLCD_DIS;
extern uint8_t id;
enum State {
DEFAULT,
PROCESSING,
SUCCESS
};
//Touch screen structure
typedef struct {
POINT Xpoint0;
POINT Ypoint0;
POINT Xpoint;
POINT Ypoint;
uint8_t chStatus;
uint8_t chType;
int16_t iXoff;
int16_t iYoff;
float fXfac;
float fYfac;
//Select the coordinates of the XPT2046 touch \
screen relative to what scan direction
LCD_SCAN_DIR TP_Scan_Dir;
}TP_DEV;
//Brush structure
typedef struct{
POINT Xpoint;
POINT Ypoint;
COLOR Color;
DOT_PIXEL DotPixel;
}TP_DRAW;
void TP_GetAdFac(void);
void TP_Adjust(void);
void TP_Dialog(void);
void TP_Save(void);
void TP_DrawBoard(void);
uint8_t TP_Scan(uint8_t tp);
void TP_Init( LCD_SCAN_DIR Lcd_ScanDir, TP_DEV* tp_dev, TP_DRAW* tp_draw);
void TP_display_input(int h, int w, const uint8_t* src);
void TP_DrawHeader(void);
#ifdef __cplusplus
}
#endif
#endif
#include <string.h>
#include <stdio.h>
#include "pico/multicore.h"
#include "LCD_digit.h"
#define KERNEL_SIZE 3
static DigitInference_t* inference;
static TextField text_fields[DIGIT_INPUT_COUNT];
static Button* login_button;
static Button* clear_button;
TP_DEV sTP_DEV;
TP_DRAW sTP_Draw;
static const uint8_t sKernel[KERNEL_SIZE*KERNEL_SIZE] = {
51, 25, 51,
25, 76, 25,
51, 25, 51
};
static bool prevent_interaction(void)
{
return (inference != NULL && (inference->state == PROCESSING || inference->state == SUCCESS));
}
void TP_DrawPredictedResult(void)
{
if (prevent_interaction()) return;
// Show result
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
if (inference->UserInputs[i].PredictedDigit >= 0) {
char result[25] = "Predicted: ";
char temp[2];
if (inference->UserInputs[i].PredictedDigit == UNKNOWN_PREDICTION) {
sprintf(temp, "%s", "-");
} else {
sprintf(temp, "%d", inference->UserInputs[i].PredictedDigit);
}
strcat(result, temp);
GUI_DisString_EN(text_fields[i].get_x() + 2, text_fields[i].get_y() + BOX_SIZE + 12, result, &Font12, WHITE, BLACK);
}
}
}
void draw_expected_input(void)
{
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
char expected[25] = "Expected: ";
char temp[2];
sprintf(temp, "%c", inference->UserInputs[i].ExpectedDigit);
strcat(expected, temp);
GUI_DisString_EN(text_fields[i].get_x() + 2, text_fields[i].get_y() + BOX_SIZE + 2, expected, &Font12, WHITE, BLACK);
}
}
void reset_user_input(UserInput_t* input, int index)
{
if (input != NULL) {
memset(input->InputData, 0, sizeof(input->InputData));
input->PredictedDigit = -1;
char digit = FIRST_FOUR_DIGIT_STUDENT_CODE[index];
input->ExpectedDigit = digit; // set expected digit
}
}
static void apply_blur(const uint8_t* src,
uint8_t* dst,
const uint8_t* kernel)
{
const int r = KERNEL_SIZE / 2;
uint8_t temp[BOX_SIZE][BOX_SIZE] = {0};
// Convolution-like accumulation (binary foreground)
for (int y = 0; y < BOX_SIZE; ++y) {
for (int x = 0; x < BOX_SIZE; ++x) {
if (src[y * BOX_SIZE + x] == 0) continue;
for (int j = y - r; j <= y + r; ++j) {
if ((unsigned)j >= BOX_SIZE) continue;
int ky = j - (y - r);
for (int i = x - r; i <= x + r; ++i) {
if ((unsigned)i >= BOX_SIZE) continue;
int kx = i - (x - r);
unsigned int sum = temp[j][i] + kernel[ky * KERNEL_SIZE + kx];
temp[j][i] = (uint8_t)(sum > 255u ? 255u : sum);
}
}
}
}
// 3x3 average downsample → write into flat dst
for (int j = 0; j < INPUT_IMAGE_SIZE; ++j) {
for (int i = 0; i < INPUT_IMAGE_SIZE; ++i) {
unsigned int s = 0;
s += temp[j*3 + 0][i*3 + 0];
s += temp[j*3 + 0][i*3 + 1];
s += temp[j*3 + 1][i*3 + 0];
s += temp[j*3 + 1][i*3 + 1];
s += temp[j*3 + 1][i*3 + 2];
s += temp[j*3 + 2][i*3 + 0];
s += temp[j*3 + 2][i*3 + 1];
s += temp[j*3 + 2][i*3 + 2];
s /= 9;
if (s > 255u) s = 255u;
dst[j * INPUT_IMAGE_SIZE + i] = (uint8_t)s;
}
}
}
void on_login_button_pressed(void)
{
if (prevent_interaction()) return;
printf("Start processing.\r\n");
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
printf("Input %d:\n", i);
// text_fields[i].print_data();
apply_blur(text_fields[i].data(), inference->UserInputs[i].InputData, sKernel);
printf("Blur applied for Input %d.\r\n", i);
TP_display_input(INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE, inference->UserInputs[i].InputData);
}
// Send a http request
multicore_fifo_push_blocking(MULTICORE_RUN_INFERENCE_FLAG);
}
void reset_display(void) {
LCD_Clear(LCD_BACKGROUND);
TP_DrawHeader();
login_button->draw();
clear_button->draw();
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
text_fields[i].draw_box();
}
draw_expected_input();
}
void on_clear_button_pressed(void)
{
if (prevent_interaction()) return;
// NEW: also clear the shadow buffer that mirrors what's drawn
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
reset_user_input(&inference->UserInputs[i], i);
text_fields[i].clear_data();
}
inference->state = DEFAULT;
reset_display();
}
void reset_inference(DigitInference_t* _inference)
{
if (_inference != NULL) {
inference = _inference;
}
inference->state = DEFAULT;
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
reset_user_input(&inference->UserInputs[i], i);
}
}
void TP_DrawDigitBoard(void)
{
if (prevent_interaction()) return;
TP_Scan(0);
spi_init(SPI_PORT, 10000000);
// printf("horizontal x:%d,y:%d\n", sTP_Draw.Xpoint, sTP_Draw.Ypoint);
if (sTP_DEV.chStatus & TP_PRESS_DOWN) {
sTP_Draw.Color = BLACK;
if (login_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Login button pressed.\n");
login_button->call_cb();
}
else if (clear_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Clear button pressed.\n");
clear_button->call_cb();
}
else if (sTP_Draw.Ypoint > BOX_START_Y && sTP_Draw.Ypoint < BOX_START_Y + BOX_SIZE)
{
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
if (text_fields[i].is_box_touched(sTP_Draw.Xpoint, sTP_Draw.Ypoint)) {
// register in bitmapped array
text_fields[i].set_pixel(sTP_Draw.Xpoint, sTP_Draw.Ypoint);
// draw on LCD
GUI_DrawPoint(sTP_Draw.Xpoint, sTP_Draw.Ypoint, BLACK, DOT_PIXEL_5X5, DOT_FILL_RIGHTUP);
break;
}
}
}
spi_init(SPI_PORT, 5000000);
}
SPI4W_Write_Byte(0xFF);
}
void TP_DrawSuccess(void)
{
LCD_Clear(LCD_BACKGROUND);
TP_DrawHeader();
int screen_center_x = LCD_W / 2;
int screen_center_y = LCD_H / 2;
GUI_DisString_EN(screen_center_x - (screen_center_x / 2), screen_center_y,
"Login Success!", &Font24, WHITE, GREEN);
GUI_DisString_EN(screen_center_x - (screen_center_x / 2), screen_center_y + 30,
"Redirecting in 3 seconds...", &Font16, WHITE, BLACK);
GUI_DisString_EN(screen_center_x - (screen_center_x / 2), screen_center_y + 45,
"Stay connected.", &Font16, WHITE, BLACK);
}
void Digit_Init(LCD_SCAN_DIR lcd_scan_dir, DigitInference_t* _inference)
{
LCD_Init(lcd_scan_dir, 1000);
TP_Init(lcd_scan_dir, &sTP_DEV, &sTP_Draw);
TP_GetAdFac();
inference = _inference;
inference->state = DEFAULT;
TP_DrawHeader();
login_button = new Button(LOGIN_BUTTON_X0, LOGIN_BUTTON_Y0, BUTTON_W, BUTTON_H, "LOGIN", DOT_PIXEL_2X2, BLUE, WHITE, 24, 15);
login_button->set_callback(on_login_button_pressed);
clear_button = new Button(CLEAR_BUTTON_X0, CLEAR_BUTTON_Y0, BUTTON_W, BUTTON_H, "CLEAR", DOT_PIXEL_2X2, RED, WHITE, 24, 15);
clear_button->set_callback(on_clear_button_pressed);
uint16_t box_start_x = (LCD_W - BOX_TOTAL_W) / 2;
for (int i=0; i<DIGIT_INPUT_COUNT; i++) {
uint16_t x = box_start_x + i * (BOX_SIZE + 2 * BORDER_THICKNESS + SPACE_BETWEEN_BOXES) - BORDER_THICKNESS;
uint16_t y = BOX_START_Y - BORDER_THICKNESS;
text_fields[i] = TextField(
x,
y,
BOX_SIZE,
BOX_SIZE
);
reset_user_input(&inference->UserInputs[i], i);
}
reset_display();
}
#ifndef __LCD_DIGIT_H_
#define __LCD_DIGIT_H_
#include "LCD_utils.hpp"
#include "DEV_Config.h"
#include "LCD_Touch.h"
#define IGNORE_INTERVAL_MS 200
#define DIGIT_INPUT_COUNT 4
#define SPACE_BETWEEN_BOXES 10
#define BORDER_THICKNESS 2
#define BOX_SIZE 84
#define BOX_TOTAL_W (DIGIT_INPUT_COUNT * (BOX_SIZE + 2 * BORDER_THICKNESS) + (DIGIT_INPUT_COUNT-1) * SPACE_BETWEEN_BOXES)
#define BOX_START_Y 120 // Starting Y position for the boxes
#define BOX_PADDING 4
#define WINDOW_SIZE 3
#define INPUT_IMAGE_SIZE 28
#define UNKNOWN_PREDICTION 100
#define BUTTON_W 120
#define BUTTON_H 40
#define CLEAR_BUTTON_X0 (LCD_W / 2 - BUTTON_W - 20)
#define CLEAR_BUTTON_Y0 (LCD_H - BUTTON_H - 10)
#define LOGIN_BUTTON_X0 (LCD_W / 2 + 20)
#define LOGIN_BUTTON_Y0 (LCD_H - BUTTON_H - 10)
#define CORE1_EXIT_FLAG 0xDEAD
#define MULTICORE_RUN_INFERENCE_FLAG 123
#define FIRST_FOUR_DIGIT_STUDENT_CODE "2514"
typedef struct{
uint8_t InputData[INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE]; // 784 uint8_t array
int8_t PredictedDigit;
char ExpectedDigit; // student code
} UserInput_t;
typedef struct{
State state;
UserInput_t UserInputs[DIGIT_INPUT_COUNT]; // 4 inputs
} DigitInference_t;
void TP_DrawDigitBoard(void);
void TP_DrawSuccess(void);
void TP_DrawPredictedResult(void);
void Digit_Init(LCD_SCAN_DIR Lcd_ScanDir, DigitInference_t* _inference);
#endif
\ No newline at end of file
#include <string.h>
#include <stdio.h>
#include <vector>
#include <cstdint>
#include "pico/multicore.h"
#include "LCD_letter.h"
static OCR_Inference* inference;
static TextField* text_field;
static Button* send_button;
static Button* clear_button;
static Button* menu_button;
static Button* back_button;
static Button* logout_button;
static bool is_menu_open = false;
TP_DEV sTP_DEV;
TP_DRAW sTP_Draw;
static const uint8_t sKernel[KERNEL_SIZE*KERNEL_SIZE] = {
58, 44, 58,
44, 76, 44,
58, 44, 58
};
static bool prevent_interaction(void)
{
return (inference != NULL && (inference->state == PROCESSING || inference->state == SUCCESS));
}
void draw_ocr_prediction(void)
{
if (inference != NULL && inference->response != NULL)
{
int result_y0 = text_field->get_y() + BOX_H + 8;
GUI_DrawRectangle(text_field->get_x(), result_y0,
text_field->get_x() + BOX_W, result_y0 + 36,
GREEN, DRAW_EMPTY, DOT_PIXEL_2X2);
size_t response_length = strlen(inference->response);
size_t prefix_length = strlen("Predicted: ");
char* dynamic_display_str = (char*)malloc(response_length + prefix_length + 1);
if (dynamic_display_str != NULL) {
snprintf(dynamic_display_str, response_length + prefix_length + 1, "Predicted: %s", inference->response);
GUI_DisString_EN(text_field->get_x() + 20, result_y0 + 8,
dynamic_display_str, &Font20, WHITE, BLACK);
free(dynamic_display_str);
}
free(inference->response);
inference->response = NULL;
}
}
static void apply_blur(const uint8_t* src,
uint8_t* dst,
const uint8_t* kernel)
{
const int r = KERNEL_SIZE / 2;
uint8_t temp[DATA_SIZE_H][DATA_SIZE_W] = {0};
// Convolution-like accumulation (binary foreground)
for (int y = 0; y < DATA_SIZE_H; ++y) {
for (int x = 0; x < DATA_SIZE_W; ++x) {
if (src[(y + MARGIN) * BOX_W + (x + MARGIN)] == 0) continue;
for (int j = y - r; j <= y + r; ++j) {
if ((unsigned)j >= (unsigned)DATA_SIZE_H) continue;
int ky = j - (y - r);
for (int i = x - r; i <= x + r; ++i) {
if ((unsigned)i >= (unsigned)DATA_SIZE_W) continue;
int kx = i - (x - r);
unsigned int sum = temp[j][i] + kernel[ky * KERNEL_SIZE + kx];
temp[j][i] = (uint8_t)(sum > 255u ? 255u : sum);
}
}
}
}
// 2×2 average downsample
for (int j = 0; j < RESULT_H; ++j) {
for (int i = 0; i < RESULT_W; ++i) {
unsigned int s = 0;
s += temp[j * 2 + 0][i * 2 + 0];
s += temp[j * 2 + 0][i * 2 + 1];
s += temp[j * 2 + 1][i * 2 + 0];
s += temp[j * 2 + 1][i * 2 + 1];
s >>= 2;
if (s > 255u) s = 255u;
dst[j * RESULT_W + i] = (uint8_t)s;
}
}
}
void on_send_button_pressed(void)
{
if (prevent_interaction()) return;
// text_field->print_data();
printf("start processing.\r\n");
apply_blur(text_field->data(), inference->data, sKernel);
printf("blur applied.\r\n");
TP_display_input(RESULT_H, RESULT_W, inference->data);
// Trigger a http request
multicore_fifo_push_blocking(OCR_MULTICORE_RUN_INFERENCE_FLAG);
}
void reset_user_input(void)
{
if (inference != NULL) {
memset(inference->data, 0, sizeof(inference->data));
free(inference->response);
inference->response = NULL;
inference->state = DEFAULT;
}
text_field->clear_data();
}
void reset_display(void) {
LCD_Clear(LCD_BACKGROUND);
TP_DrawHeader();
menu_button->draw();
send_button->draw();
clear_button->draw();
text_field->draw_box();
}
void on_clear_button_pressed(void)
{
if (prevent_interaction()) return;
reset_display();
reset_user_input();
}
void on_menu_button_pressed(void)
{
if (prevent_interaction()) return;
printf("Menu button pressed. (Not implemented)\n");
reset_user_input();
LCD_Clear(LCD_BACKGROUND);
back_button->draw();
logout_button->draw();
is_menu_open = true;
}
void on_back_button_pressed(void)
{
if (prevent_interaction()) return;
printf("Back button pressed.\n");
reset_display();
is_menu_open = false;
}
void on_logout_button_pressed(void)
{
if (prevent_interaction()) return;
printf("Logging out...\n");
LCD_Clear(LCD_BACKGROUND);
int screen_center_x = LCD_W / 2;
int screen_center_y = LCD_H / 2;
GUI_DisString_EN(screen_center_x - (screen_center_x / 2), screen_center_y, "Logged out!", &Font24, WHITE, BLACK);
GUI_DisString_EN(screen_center_x - (screen_center_x / 2), screen_center_y + 26, "Goodbye!", &Font20, WHITE, BLACK);
sleep_ms(1000);
multicore_fifo_push_blocking(CORE1_EXIT_FLAG);
}
void TP_DrawBoard(void)
{
if (prevent_interaction()) return;
draw_ocr_prediction();
TP_Scan(0);
if (sTP_DEV.chStatus & TP_PRESS_DOWN)
{
spi_init(SPI_PORT, 10000000);
sTP_Draw.Color = BLACK;
// printf("horizontal x:%d,y:%d\n", sTP_Draw.Xpoint, sTP_Draw.Ypoint);
if (is_menu_open) {
if (back_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Back button pressed\n");
back_button->call_cb();
}
else if (logout_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Logout button pressed\n");
logout_button->call_cb();
}
}
else
{
if (clear_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Clear button pressed\n");
clear_button->call_cb();
}
else if (send_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Send button pressed\n");
send_button->call_cb();
}
else if (menu_button->is_pressed(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
printf("Menu button pressed\n");
menu_button->call_cb();
}
else if (text_field->is_box_touched(sTP_Draw.Xpoint, sTP_Draw.Ypoint))
{
text_field->set_pixel(sTP_Draw.Xpoint, sTP_Draw.Ypoint);
GUI_DrawPoint(sTP_Draw.Xpoint, sTP_Draw.Ypoint, BLACK, DOT_PIXEL_5X5, DOT_FILL_RIGHTUP);
}
}
spi_init(SPI_PORT, 5000000);
}
SPI4W_Write_Byte(0xFF);
}
void Letter_Init(LCD_SCAN_DIR lcd_scan_dir, OCR_Inference* _inference)
{
LCD_Init(lcd_scan_dir, 1000);
TP_Init(lcd_scan_dir, &sTP_DEV, &sTP_Draw);
TP_GetAdFac();
inference = _inference;
inference->state = DEFAULT;
TP_DrawHeader();
send_button = new Button(SEND_X0, SEND_Y0, BUTTON_W, BUTTON_H, "SEND", DOT_PIXEL_2X2, BLUE, WHITE, 22, 13);
send_button->set_callback(on_send_button_pressed);
clear_button = new Button(CLEAR_X0, CLEAR_Y0, BUTTON_W, BUTTON_H, "CLEAR", DOT_PIXEL_2X2, RED, WHITE, 18, 13);
clear_button->set_callback(on_clear_button_pressed);
menu_button = new Button(MENU_X0, MENU_Y0, 60, BUTTON_H, "MENU", DOT_PIXEL_2X2, GREEN, WHITE, 4, 13);
menu_button->set_callback(on_menu_button_pressed);
back_button = new Button(BACK_X0, BACK_Y0, MENU_BUTTON_W, BUTTON_H, "BACK", DOT_PIXEL_2X2, BLACK, WHITE, 30, 13);
back_button->set_callback(on_back_button_pressed);
logout_button = new Button(LOGOUT_X0, LOGOUT_Y0, MENU_BUTTON_W, BUTTON_H, "LOGOUT", DOT_PIXEL_2X2, RED, WHITE, 24, 13);
logout_button->set_callback(on_logout_button_pressed);
text_field = new TextField(BOX_X0, BOX_Y0, BOX_W, BOX_H);
reset_display();
}
#ifndef __LCD_LETTER_H_
#define __LCD_LETTER_H_
#include "LCD_utils.hpp"
#include "DEV_Config.h"
#include "LCD_Touch.h"
#define BUTTON_W 100
#define BUTTON_H 40
#define RESULT_TEXT_W 360
#define RESULT_TEXT_H 50
#define MARGIN 10
#define MARGIN_2 2*MARGIN
#define BOX_X0 60
#define BOX_Y0 80
#define BOX_X1 420 // right edge (exclusive in our math)
#define BOX_Y1 200 // bottom edge (exclusive in our math)
#define DATA_SIZE_W (BOX_X1 - BOX_X0) // 360
#define DATA_SIZE_H (BOX_Y1 - BOX_Y0) // 120
#define BOX_W (MARGIN_2 + DATA_SIZE_W) // 360 + 20
#define BOX_H (MARGIN_2 + DATA_SIZE_H) // 120 + 20
#define CLEAR_X1 (LCD_W / 2) - MARGIN
#define CLEAR_Y1 LCD_H - MARGIN
#define CLEAR_X0 CLEAR_X1 - BUTTON_W
#define CLEAR_Y0 CLEAR_Y1 - BUTTON_H
#define SEND_X0 ((LCD_W / 2) + MARGIN)
#define SEND_X1 (SEND_X0 + BUTTON_W)
#define SEND_Y0 CLEAR_Y0
#define SEND_Y1 CLEAR_Y1
#define MENU_X0 10
#define MENU_Y0 10
#define MENU_W 30
#define MENU_H 30
#define MENU_BUTTON_W (BUTTON_W + 30)
#define BACK_X0 (LCD_W / 2 - (MENU_BUTTON_W / 2))
#define BACK_Y0 (LCD_H / 2 - (BUTTON_H / 2) - 30)
#define LOGOUT_X0 BACK_X0
#define LOGOUT_Y0 (LCD_H / 2 - (BUTTON_H / 2) + 30)
#define RESULT_W 180
#define RESULT_H 60
#define DATA_SIZE (RESULT_W*RESULT_H)
#define KERNEL_SIZE 3
#define CORE1_EXIT_FLAG 0xDEAD
#define OCR_MULTICORE_RUN_INFERENCE_FLAG 111
typedef struct {
State state;
uint8_t data[DATA_SIZE];
char* response;
} OCR_Inference;
void Letter_Init(LCD_SCAN_DIR lcd_scan_dir, OCR_Inference* _inference);
#endif
\ No newline at end of file
#include <string.h>
#include <stdio.h>
#include <cstring>
#include "LCD_utils.hpp"
Button::Button(int x, int y, int w, int h, const char* label,
DOT_PIXEL font_size,
hex_color_t bg_color,
hex_color_t fg_color,
uint8_t letter_offset_x, uint8_t letter_offset_y)
: x_(x), y_(y), w_(w), h_(h),
label_(label), font_size_(font_size),
bg_color_(bg_color), fg_color_(fg_color),
letter_offset_x_(letter_offset_x), letter_offset_y_(letter_offset_y)
{
}
Button::~Button() {
callback_ = nullptr;
}
void Button::set_callback(void (*cb)()) {
callback_ = cb;
}
bool Button::is_pressed(int tx, int ty) {
bool is_pressed = (tx >= x_ && tx < (x_ + w_) &&
ty >= y_ && ty < (y_ + h_));
if (!is_pressed) return false;
uint32_t current_time = to_ms_since_boot(get_absolute_time());
if (current_time - button_pressed_at_ < IGNORE_INTERVAL_MS) {
// prevent debounce
return false;
}
button_pressed_at_ = current_time;
return true;
}
void Button::draw() {
GUI_DrawRectangle(x_, y_,
x_ + w_, y_ + h_,
bg_color_, DRAW_EMPTY, font_size_);
GUI_DisString_EN(x_ + letter_offset_x_, y_ + letter_offset_y_,
label_, &Font20, fg_color_, bg_color_);
}
void Button::call_cb() {
if (callback_) {
callback_();
}
}
void TextField::clear_data(void) {
std::fill(binary_data_.begin(), binary_data_.end(), 0);
}
void TextField::draw_box(void) {
GUI_DrawRectangle(x0_, y0_,
x1_, y1_,
BLACK, DRAW_EMPTY, DOT_PIXEL_2X2);
}
void TextField::set_pixel(POINT tx, POINT ty) {
if (binary_data_.empty()) return;
int x = tx - x0_;
int y = ty - y0_;
if (x >= 0 && x < box_w_ && y >= 0 && y < box_h_) {
binary_data_[y * box_w_ + x] = 1;
}
}
bool TextField::is_box_touched(int tx, int ty) {
return (tx >= x0_ && tx < x1_ &&
ty >= y0_ && ty < y1_);
}
void TextField::print_data(void) {
for (int i=0; i<box_h_; i++) {
for (int j=0; j<box_w_; j++) {
printf("%d ", static_cast<int>(binary_data_[i * box_w_ + j]));
}
printf("\n");
}
}
\ No newline at end of file
#ifndef HTTP_CLIENT_REQUEST_HPP_
#define HTTP_CLIENT_REQUEST_HPP_
#include <vector>
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif
#include "LCD_Touch.h"
#include "LCD_GUI.h"
#ifdef __cplusplus
}
#endif
typedef uint32_t hex_color_t;
class Button {
public:
Button(int x, int y, int w, int h, const char* label, DOT_PIXEL font_size= DOT_PIXEL_2X2,
hex_color_t bg_color=BLUE, hex_color_t fg_color=WHITE, uint8_t letter_offset_x=0, uint8_t letter_offset_y=0);
~Button();
void set_callback(void (*cb)());
bool is_pressed(int tx, int ty);
void draw();
void call_cb();
private:
int x_;
int y_;
int w_;
int h_;
hex_color_t bg_color_;
hex_color_t fg_color_;
DOT_PIXEL font_size_;
const char* label_;
uint8_t letter_offset_x_ = 0;
uint8_t letter_offset_y_ = 0;
uint32_t button_pressed_at_ = 0;
void (*callback_)() = nullptr;
};
class TextField {
public:
TextField()
: x0_(0), y0_(0), box_w_(0), box_h_(0), x1_(0), y1_(0) {}
TextField(uint16_t x0, uint16_t y0, uint16_t box_w, uint16_t box_h)
: x0_(x0), y0_(y0), box_w_(box_w), box_h_(box_h) {
x1_ = x0_ + box_w_;
y1_ = y0_ + box_h_;
binary_data_.assign(box_w_ * box_h_, 0);
clear_data();
}
~TextField() {
binary_data_.clear();
}
void clear_data(void);
void draw_box(void);
bool is_box_touched(int tx, int ty);
void set_pixel(POINT tx, POINT ty);
void print_data(void);
const uint8_t* data() const { return binary_data_.data(); }
const uint16_t get_x() const { return x0_; }
const uint16_t get_y() const { return y0_; }
private:
uint16_t x0_;
uint16_t y0_;
uint16_t x1_;
uint16_t y1_;
uint16_t box_w_;
uint16_t box_h_;
std::vector<uint8_t> binary_data_;
};
#endif // HTTP_CLIENT_REQUEST_HPP_
\ No newline at end of file
aux_source_directory(. DIR_SRC_SRCS)
include_directories(../lib/pico-tflmicro)
add_library(model ${DIR_SRC_SRCS})
target_link_libraries(
model
PUBLIC
pico_stdlib
pico-tflmicro
)
#ifndef TFLITE_INFERENCE_TEST_MNIST_IMAGE_DATA_H_
#define TFLITE_INFERENCE_TEST_MNIST_IMAGE_DATA_H_
#include <cstdint>
extern const uint8_t mnist_image_data_0[];
extern const uint8_t mnist_image_data_1[];
extern const uint8_t mnist_image_data_2[];
extern const uint8_t mnist_image_data_3[];
extern const uint8_t mnist_image_data_4[];
extern const uint8_t mnist_image_data_5[];
extern const uint8_t mnist_image_data_6[];
extern const uint8_t mnist_image_data_7[];
extern const uint8_t mnist_image_data_8[];
extern const uint8_t mnist_image_data_9[];
#endif // TFLITE_INFERENCE_TEST_MNIST_IMAGE_DATA_H_
#include "mnist_model_data.h"
alignas(8) const unsigned char mnist_model_data[] = {};
const int mnist_model_data_len = 0;
\ No newline at end of file
#ifndef TFLITE_INFERENCE_TEST_MNIST_MODEL_DATA_H_
#define TFLITE_INFERENCE_TEST_MNIST_MODEL_DATA_H_
#include <cstdint>
extern const unsigned char mnist_model_data[];
extern const int mnist_model_data_len;
#endif // TFLITE_INFERENCE_TEST_MNIST_MODEL_DATA_H_
#include "tensorflow/lite/micro/tflite_bridge/micro_error_reporter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "model.h"
#include "model_settings.h"
// TODO: 4. Import your model data
Model::Model() :
model(nullptr),
interpreter(nullptr),
input(nullptr),
error_reporter(nullptr)
{
}
Model::~Model()
{
if (interpreter != NULL) {
delete interpreter;
interpreter = NULL;
}
if (model != NULL) {
delete model;
model = NULL;
}
if (input != NULL) {
delete input;
input = NULL;
}
if (error_reporter != NULL) {
delete error_reporter;
error_reporter = NULL;
}
}
int Model::setup()
{
static tflite::MicroErrorReporter micro_error_reporter;
error_reporter = &micro_error_reporter;
model = tflite::GetModel(mnist_model_data);
if (model->version() != TFLITE_SCHEMA_VERSION) {
TF_LITE_REPORT_ERROR(error_reporter,
"Model provided is schema version %d not equal "
"to supported version %d.",
model->version(), TFLITE_SCHEMA_VERSION);
return 0;
}
static tflite::MicroMutableOpResolver</* TODO: Define number of kernel operations*/> micro_op_resolver;
// TODO: 7. Add operations according to your model.
static uint8_t tensor_arena[arena_size];
// Build an interpreter to run the model with.
// NOLINTNEXTLINE(runtime-global-variables)
static tflite::MicroInterpreter static_interpreter(
model, micro_op_resolver, tensor_arena, arena_size);
interpreter = &static_interpreter;
TfLiteStatus allocate_status = interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
return 0;
}
// Get information about the memory area to use for the model's input.
input = interpreter->input(0);
return 1;
}
uint8_t* Model::input_data() {
if (input == nullptr) {
return nullptr;
}
return input->data.uint8;
}
int Model::byte_size() {
if (input == nullptr) {
return 0;
}
return input->bytes;
}
int Model::predict()
{
printf("Invocation started\n");
// TODO: 9. Run invoke inference, if error, return -1
if (interpreter->Invoke() != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed.");
return -1;
}
printf("Invocation finished\n");
TfLiteTensor* output = interpreter->output(0);
int result = -1;
// TODO: 10. Return an index of the output neuron, which has maximum probability.
return result;
}
\ No newline at end of file
#ifndef TFLITE_INFERENCE_TEST_MODEL_H_
#define TFLITE_INFERENCE_TEST_MODEL_H_
#include "tensorflow/lite/micro/tflite_bridge/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/schema/schema_generated.h"
class Model {
public:
Model();
virtual ~Model();
int setup();
int predict();
uint8_t* input_data();
int byte_size();
const tflite::Model* model = nullptr;
TfLiteTensor* input = nullptr;
tflite::MicroInterpreter* interpreter = nullptr;
tflite::ErrorReporter* error_reporter = nullptr;
private:
};
#endif // TFLITE_INFERENCE_TEST_MODEL_H_
\ No newline at end of file
#ifndef TFLITE_INFERENCE_TEST_MODEL_SETTINGS_H_
#define TFLITE_INFERENCE_TEST_MODEL_SETTINGS_H_
constexpr int kNumCols = 96;
constexpr int kNumRows = 96;
constexpr int kNumChannels = 1;
constexpr int kMaxImageSize = kNumCols * kNumRows * kNumChannels;
constexpr int kCategoryCount = 2;
constexpr int kPersonIndex = 1;
constexpr int kNotAPersonIndex = 0;
extern const char* kCategoryLabels[kCategoryCount];
constexpr int image_col_size = 28;
constexpr int image_row_size = 28;
constexpr int arena_size = 12 * 1024; // TODO: 3. Edit this for your own model.
#endif // TFLITE_INFERENCE_TEST_MODEL_SETTINGS_H_
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
set(USERHOME $ENV{USERPROFILE})
else()
set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.1.0)
set(toolchainVersion 13_3_Rel1)
set(picotoolVersion 2.1.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")
cmake_minimum_required(VERSION 3.12)
# Pull in PICO SDK (must be before project)
include(pico_sdk_import.cmake)
project(pico-tflmicro C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_library(pico-tflmicro STATIC)
target_include_directories(pico-tflmicro
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/
${CMAKE_CURRENT_LIST_DIR}/src/third_party/ruy
${CMAKE_CURRENT_LIST_DIR}/src/third_party/gemmlowp
${CMAKE_CURRENT_LIST_DIR}/src/third_party/kissfft
${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers
${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis/CMSIS/Core/Include
${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers/include
${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis_nn/Include
)
target_compile_definitions(
pico-tflmicro
PUBLIC
COMPILE_DEFINITIONS TF_LITE_DISABLE_X86_NEON=1
COMPILE_DEFINITIONS TF_LITE_STATIC_MEMORY=1
COMPILE_DEFINITIONS TF_LITE_USE_CTIME=1
COMPILE_DEFINITIONS CMSIS_NN=1
COMPILE_DEFINITIONS ARDUINO=1
COMPILE_DEFINITIONS TFLITE_USE_CTIME=1
)
set_target_properties(
pico-tflmicro
PROPERTIES
COMPILE_FLAGS -Os
COMPILE_FLAGS -fno-rtti
COMPILE_FLAGS -fno-exceptions
COMPILE_FLAGS -fno-threadsafe-statics
COMPILE_FLAGS -nostdlib
)
target_link_libraries(
pico-tflmicro
pico_stdlib
pico_multicore
)
target_sources(pico-tflmicro
PRIVATE
{{LIBRARY_SOURCES}}
)
add_library(pico-tflmicro_test STATIC)
target_include_directories(pico-tflmicro_test
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/
${CMAKE_CURRENT_LIST_DIR}/src/third_party/ruy
${CMAKE_CURRENT_LIST_DIR}/src/third_party/gemmlowp
${CMAKE_CURRENT_LIST_DIR}/src/third_party/kissfft
${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers
${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis/CMSIS/Core/Include
${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers/include
${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis_nn/Include
)
target_compile_definitions(
pico-tflmicro_test
PUBLIC
COMPILE_DEFINITIONS TF_LITE_DISABLE_X86_NEON=1
COMPILE_DEFINITIONS TF_LITE_STATIC_MEMORY=1
COMPILE_DEFINITIONS CMSIS_NN=1
)
set_target_properties(
pico-tflmicro_test
PROPERTIES
COMPILE_FLAGS -fno-rtti
COMPILE_FLAGS -fno-exceptions
COMPILE_FLAGS -fno-threadsafe-statics
COMPILE_FLAGS -nostdlib
)
target_link_libraries(
pico-tflmicro_test
pico_stdlib
pico_multicore
)
add_subdirectory("examples/hello_world")
add_subdirectory("examples/person_detection")
{{TEST_FOLDERS}}
# TensorFlow Lite Micro
An Open Source Machine Learning Framework for Everyone.
## Introduction
This is a version of the [TensorFlow Lite Micro library](https://www.tensorflow.org/lite/microcontrollers)
for the Raspberry Pi Pico microcontroller. It allows you to run machine
learning models to do things like voice recognition, detect people in images,
recognize gestures from an accelerometer, and other sensor analysis tasks.
This version has scripts to upstream changes from the Google codebase. It also
takes advantage of the RP2040's dual cores for increased speed on some
operations.
## Getting Started
First you'll need to follow the Pico setup instructions to initialize the
development environment on your machine. Once that is done, make sure that the
`PICO_SDK_PATH` environment variable has been set to the location of the Pico
SDK, either in the shell you're building in, or the CMake configure environment
variable setting of the extension if you're using VS Code.
You should then be able to build the library, tests, and examples. The easiest
way to build is using VS Code's CMake integration, by loading the project and
choosing the build option at the bottom of the window.
Alternatively you can build the entire project, including tests, by running the
following commands from a terminal once you're in this repo's directory:
```bash
mkdir build
cd build
cmake ..
make
```
## What's Included
There are several example applications included. The simplest one to begin with
is the hello_world project. This demonstrates the fundamentals of deploying an
ML model on a device, driving the Pico's LED in a learned sine-wave pattern.
Once you have built the project, a UF2 file you can copy to the Pico should be
present at `build/examples/hello_world/hello_world.uf2`.
Another example is the person detector, but since the Pico doesn't come with
image inputs you'll need to write some code to hook up your own sensor. You can
find a fork of TFLM for the Arducam Pico4ML that does this at [arducam.com/pico4ml-an-rp2040-based-platform-for-tiny-machine-learning/](https://www.arducam.com/pico4ml-an-rp2040-based-platform-for-tiny-machine-learning/).
## Contributing
This repository (https://github.com/raspberrypi/pico-tflmicro) is read-only,
because it has been automatically generated from the master TensorFlow
repository at https://github.com/tensorflow/tensorflow. It's maintained by
@petewarden on a best effort basis, so bugs and PRs may not get addressed. You
can generate an updated version of this generated project by running the command:
```
sync/sync_with_upstream.sh
```
This should create a Pico-compatible project from the latest version of the
TensorFlow repository.
## Learning More
The [TensorFlow website](https://www.tensorflow.org/lite/microcontrollers) has
information on training, tutorials, and other resources.
The [TinyML Book](https://tinymlbook.com) is a guide to using TensorFlow Lite Micro
across a variety of different systems.
[TensorFlowLite Micro: Embedded Machine Learning on TinyML Systems](https://arxiv.org/pdf/2010.08678.pdf)
has more details on the design and implementation of the framework.
## Licensing
The TensorFlow source code is covered by the Apache 2 license described in
src/tensorflow/LICENSE, components from other libraries have the appropriate
licenses included in their third_party folders.
\ No newline at end of file
Results of running person_detection_benchmark with and without multicore optimizations.
To reproduce these, run `make person_detection_benchmark`, with and without the
TF_LITE_PICO_MULTICORE macro defined at the top of src/third_party/cmsis_nn/Source/NNSup
portFunctions/arm_nn_mat_mult_nt_t_s8.c
Without multicore CONV2D optimizations:
NoPersonDataIterations(1) took 823658 ticks (823 ms)
DEPTHWISE_CONV_2D took 34553 ticks (34 ms).
DEPTHWISE_CONV_2D took 60260 ticks (60 ms).
CONV_2D took 47509 ticks (47 ms).
DEPTHWISE_CONV_2D took 29581 ticks (29 ms).
CONV_2D took 32941 ticks (32 ms).
DEPTHWISE_CONV_2D took 57434 ticks (57 ms).
CONV_2D took 51301 ticks (51 ms).
DEPTHWISE_CONV_2D took 14411 ticks (14 ms).
CONV_2D took 26003 ticks (26 ms).
DEPTHWISE_CONV_2D took 27689 ticks (27 ms).
CONV_2D took 44571 ticks (44 ms).
DEPTHWISE_CONV_2D took 7025 ticks (7 ms).
CONV_2D took 23344 ticks (23 ms).
DEPTHWISE_CONV_2D took 13935 ticks (13 ms).
CONV_2D took 43007 ticks (43 ms).
DEPTHWISE_CONV_2D took 12996 ticks (12 ms).
CONV_2D took 42947 ticks (42 ms).
DEPTHWISE_CONV_2D took 12983 ticks (12 ms).
CONV_2D took 42953 ticks (42 ms).
DEPTHWISE_CONV_2D took 13023 ticks (13 ms).
CONV_2D took 42979 ticks (42 ms).
DEPTHWISE_CONV_2D took 13015 ticks (13 ms).
CONV_2D took 42951 ticks (42 ms).
DEPTHWISE_CONV_2D took 3522 ticks (3 ms).
CONV_2D took 25795 ticks (25 ms).
DEPTHWISE_CONV_2D took 6016 ticks (6 ms).
CONV_2D took 49461 ticks (49 ms).
AVERAGE_POOL_2D took 874 ticks (0 ms).
CONV_2D took 220 ticks (0 ms).
RESHAPE took 21 ticks (0 ms).
SOFTMAX took 338 ticks (0 ms).
Multi-core CONV2D and Depthwise Conv:
NoPersonDataIterations(1) took 587400 ticks (587 ms)
DEPTHWISE_CONV_2D took 34550 ticks (34 ms).
DEPTHWISE_CONV_2D took 31942 ticks (31 ms).
CONV_2D took 29140 ticks (29 ms).
DEPTHWISE_CONV_2D took 15765 ticks (15 ms).
CONV_2D took 21402 ticks (21 ms).
DEPTHWISE_CONV_2D took 30346 ticks (30 ms).
CONV_2D took 35317 ticks (35 ms).
DEPTHWISE_CONV_2D took 7792 ticks (7 ms).
CONV_2D took 17922 ticks (17 ms).
DEPTHWISE_CONV_2D took 14706 ticks (14 ms).
CONV_2D took 32168 ticks (32 ms).
DEPTHWISE_CONV_2D took 4780 ticks (4 ms).
CONV_2D took 16981 ticks (16 ms).
DEPTHWISE_CONV_2D took 9800 ticks (9 ms).
CONV_2D took 36303 ticks (36 ms).
DEPTHWISE_CONV_2D took 7141 ticks (7 ms).
CONV_2D took 36236 ticks (36 ms).
DEPTHWISE_CONV_2D took 7137 ticks (7 ms).
CONV_2D took 36343 ticks (36 ms).
DEPTHWISE_CONV_2D took 7166 ticks (7 ms).
CONV_2D took 36217 ticks (36 ms).
DEPTHWISE_CONV_2D took 7148 ticks (7 ms).
CONV_2D took 36216 ticks (36 ms).
DEPTHWISE_CONV_2D took 3624 ticks (3 ms).
CONV_2D took 22197 ticks (22 ms).
DEPTHWISE_CONV_2D took 4526 ticks (4 ms).
CONV_2D took 43024 ticks (43 ms).
AVERAGE_POOL_2D took 876 ticks (0 ms).
CONV_2D took 275 ticks (0 ms).
RESHAPE took 20 ticks (0 ms).
SOFTMAX took 340 ticks (0 ms).
\ No newline at end of file
cmake_minimum_required(VERSION 3.12)
project(hello_world C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
add_executable(hello_world_test "")
target_include_directories(hello_world_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
set_target_properties(
hello_world_test
PROPERTIES
COMPILE_FLAGS -fno-rtti
COMPILE_FLAGS -fno-exceptions
COMPILE_FLAGS -fno-threadsafe-statics
COMPILE_FLAGS -nostdlib
)
target_sources(hello_world_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/hello_world_float_model_data.cpp
${CMAKE_CURRENT_LIST_DIR}/hello_world_int8_model_data.cpp
${CMAKE_CURRENT_LIST_DIR}/hello_world_test.cpp
)
target_link_libraries(
hello_world_test
pico-tflmicro
hardware_pwm
pico-tflmicro_test
)
pico_enable_stdio_usb(hello_world_test 1)
pico_enable_stdio_uart(hello_world_test 0)
pico_add_extra_outputs(hello_world_test)
add_executable(hello_world "")
target_include_directories(hello_world
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
set_target_properties(
hello_world
PROPERTIES
COMPILE_FLAGS -fno-rtti
COMPILE_FLAGS -fno-exceptions
COMPILE_FLAGS -fno-threadsafe-statics
COMPILE_FLAGS -nostdlib
)
target_sources(hello_world
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/constants.cpp
${CMAKE_CURRENT_LIST_DIR}/hello_world_float_model_data.cpp
${CMAKE_CURRENT_LIST_DIR}/hello_world_int8_model_data.cpp
${CMAKE_CURRENT_LIST_DIR}/main.cpp
${CMAKE_CURRENT_LIST_DIR}/main_functions.cpp
${CMAKE_CURRENT_LIST_DIR}/rp2/output_handler.cpp
${CMAKE_CURRENT_LIST_DIR}/constants.h
${CMAKE_CURRENT_LIST_DIR}/main_functions.h
${CMAKE_CURRENT_LIST_DIR}/output_handler.h
)
target_link_libraries(
hello_world
pico-tflmicro
hardware_pwm
)
pico_enable_stdio_usb(hello_world 1)
pico_enable_stdio_uart(hello_world 0)
pico_add_extra_outputs(hello_world)
add_executable(output_handler_test "")
target_include_directories(output_handler_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
set_target_properties(
output_handler_test
PROPERTIES
COMPILE_FLAGS -fno-rtti
COMPILE_FLAGS -fno-exceptions
COMPILE_FLAGS -fno-threadsafe-statics
COMPILE_FLAGS -nostdlib
)
target_sources(output_handler_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/output_handler_test.cpp
${CMAKE_CURRENT_LIST_DIR}/rp2/output_handler.cpp
${CMAKE_CURRENT_LIST_DIR}/constants.h
${CMAKE_CURRENT_LIST_DIR}/output_handler.h
)
target_link_libraries(
output_handler_test
pico-tflmicro
hardware_pwm
pico-tflmicro_test
)
pico_add_extra_outputs(output_handler_test)
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "constants.h"
// This is a small number so that it's easy to read the logs
const int kInferencesPerCycle = 20;
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_CONSTANTS_H_
#define TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_CONSTANTS_H_
// This constant represents the range of x values our model was trained on,
// which is from 0 to (2 * Pi). We approximate Pi to avoid requiring additional
// libraries.
const float kXrange = 2.f * 3.14159265359f;
// This constant determines the number of inferences to perform across the range
// of x values defined above. Since each inference takes time, the higher this
// number, the more time it will take to run through the entire range. The value
// of this constant can be tuned so that one full cycle takes a desired amount
// of time. Since different devices take different amounts of time to perform
// inference, this value should be defined per-device.
extern const int kInferencesPerCycle;
#endif // TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_CONSTANTS_H_
#include <cstdint>
extern const unsigned int g_hello_world_float_model_data_size;
extern const unsigned char g_hello_world_float_model_data[];
#include <cstdint>
extern const unsigned int g_hello_world_int8_model_data_size;
extern const unsigned char g_hello_world_int8_model_data[];
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <math.h>
#include "tensorflow/lite/core/c/common.h"
#include "hello_world_float_model_data.h"
#include "hello_world_int8_model_data.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_profiler.h"
#include "tensorflow/lite/micro/recording_micro_interpreter.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace {
using HelloWorldOpResolver = tflite::MicroMutableOpResolver<1>;
TfLiteStatus RegisterOps(HelloWorldOpResolver& op_resolver) {
TF_LITE_ENSURE_STATUS(op_resolver.AddFullyConnected());
return kTfLiteOk;
}
} // namespace
TfLiteStatus ProfileMemoryAndLatency() {
tflite::MicroProfiler profiler;
HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));
// Arena size just a round number. The exact arena usage can be determined
// using the RecordingMicroInterpreter.
constexpr int kTensorArenaSize = 3000;
uint8_t tensor_arena[kTensorArenaSize];
constexpr int kNumResourceVariables = 24;
tflite::RecordingMicroAllocator* allocator(
tflite::RecordingMicroAllocator::Create(tensor_arena, kTensorArenaSize));
tflite::RecordingMicroInterpreter interpreter(
tflite::GetModel(g_hello_world_float_model_data), op_resolver, allocator,
tflite::MicroResourceVariables::Create(allocator, kNumResourceVariables),
&profiler);
TF_LITE_ENSURE_STATUS(interpreter.AllocateTensors());
TFLITE_CHECK_EQ(interpreter.inputs_size(), 1);
interpreter.input(0)->data.f[0] = 1.f;
TF_LITE_ENSURE_STATUS(interpreter.Invoke());
MicroPrintf(""); // Print an empty new line
profiler.LogTicksPerTagCsv();
MicroPrintf(""); // Print an empty new line
interpreter.GetMicroAllocator().PrintAllocations();
return kTfLiteOk;
}
TfLiteStatus LoadFloatModelAndPerformInference() {
const tflite::Model* model =
::tflite::GetModel(g_hello_world_float_model_data);
TFLITE_CHECK_EQ(model->version(), TFLITE_SCHEMA_VERSION);
HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));
// Arena size just a round number. The exact arena usage can be determined
// using the RecordingMicroInterpreter.
constexpr int kTensorArenaSize = 3000;
uint8_t tensor_arena[kTensorArenaSize];
tflite::MicroInterpreter interpreter(model, op_resolver, tensor_arena,
kTensorArenaSize);
TF_LITE_ENSURE_STATUS(interpreter.AllocateTensors());
// Check if the predicted output is within a small range of the
// expected output
float epsilon = 0.05f;
constexpr int kNumTestValues = 4;
float golden_inputs[kNumTestValues] = {0.f, 1.f, 3.f, 5.f};
for (int i = 0; i < kNumTestValues; ++i) {
interpreter.input(0)->data.f[0] = golden_inputs[i];
TF_LITE_ENSURE_STATUS(interpreter.Invoke());
float y_pred = interpreter.output(0)->data.f[0];
TFLITE_CHECK_LE(abs(sin(golden_inputs[i]) - y_pred), epsilon);
}
return kTfLiteOk;
}
TfLiteStatus LoadQuantModelAndPerformInference() {
// Map the model into a usable data structure. This doesn't involve any
// copying or parsing, it's a very lightweight operation.
const tflite::Model* model =
::tflite::GetModel(g_hello_world_int8_model_data);
TFLITE_CHECK_EQ(model->version(), TFLITE_SCHEMA_VERSION);
HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));
// Arena size just a round number. The exact arena usage can be determined
// using the RecordingMicroInterpreter.
constexpr int kTensorArenaSize = 3000;
uint8_t tensor_arena[kTensorArenaSize];
tflite::MicroInterpreter interpreter(model, op_resolver, tensor_arena,
kTensorArenaSize);
TF_LITE_ENSURE_STATUS(interpreter.AllocateTensors());
TfLiteTensor* input = interpreter.input(0);
TFLITE_CHECK_NE(input, nullptr);
TfLiteTensor* output = interpreter.output(0);
TFLITE_CHECK_NE(output, nullptr);
float output_scale = output->params.scale;
int output_zero_point = output->params.zero_point;
// Check if the predicted output is within a small range of the
// expected output
float epsilon = 0.05;
constexpr int kNumTestValues = 4;
float golden_inputs_float[kNumTestValues] = {0.77, 1.57, 2.3, 3.14};
// The int8 values are calculated using the following formula
// (golden_inputs_float[i] / input->params.scale + input->params.scale)
int8_t golden_inputs_int8[kNumTestValues] = {-96, -63, -34, 0};
for (int i = 0; i < kNumTestValues; ++i) {
input->data.int8[0] = golden_inputs_int8[i];
TF_LITE_ENSURE_STATUS(interpreter.Invoke());
float y_pred = (output->data.int8[0] - output_zero_point) * output_scale;
TFLITE_CHECK_LE(abs(sin(golden_inputs_float[i]) - y_pred), epsilon);
}
return kTfLiteOk;
}
int main(int argc, char* argv[]) {
tflite::InitializeTarget();
TF_LITE_ENSURE_STATUS(ProfileMemoryAndLatency());
TF_LITE_ENSURE_STATUS(LoadFloatModelAndPerformInference());
TF_LITE_ENSURE_STATUS(LoadQuantModelAndPerformInference());
MicroPrintf("~~~ALL TESTS PASSED~~~\n");
return kTfLiteOk;
}
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "main_functions.h"
// This is the default main used on systems that have the standard C entry
// point. Other devices (for example FreeRTOS or ESP32) that have different
// requirements for entry code (like an app_main function) should specialize
// this main.cc file in a target-specific subfolder.
int main(int argc, char* argv[]) {
setup();
while (true) {
loop();
}
}
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "constants.h"
#include "hello_world_float_model_data.h"
#include "main_functions.h"
#include "output_handler.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"
// Globals, used for compatibility with Arduino-style sketches.
namespace {
const tflite::Model* model = nullptr;
tflite::MicroInterpreter* interpreter = nullptr;
TfLiteTensor* input = nullptr;
TfLiteTensor* output = nullptr;
int inference_count = 0;
constexpr int kTensorArenaSize = 2000;
uint8_t tensor_arena[kTensorArenaSize];
} // namespace
// The name of this function is important for Arduino compatibility.
void setup() {
tflite::InitializeTarget();
// Map the model into a usable data structure. This doesn't involve any
// copying or parsing, it's a very lightweight operation.
model = tflite::GetModel(g_hello_world_float_model_data);
if (model->version() != TFLITE_SCHEMA_VERSION) {
MicroPrintf(
"Model provided is schema version %d not equal "
"to supported version %d.",
model->version(), TFLITE_SCHEMA_VERSION);
return;
}
// This pulls in all the operation implementations we need.
// NOLINTNEXTLINE(runtime-global-variables)
static tflite::MicroMutableOpResolver<1> resolver;
TfLiteStatus resolve_status = resolver.AddFullyConnected();
if (resolve_status != kTfLiteOk) {
MicroPrintf("Op resolution failed");
return;
}
// Build an interpreter to run the model with.
static tflite::MicroInterpreter static_interpreter(
model, resolver, tensor_arena, kTensorArenaSize);
interpreter = &static_interpreter;
// Allocate memory from the tensor_arena for the model's tensors.
TfLiteStatus allocate_status = interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
MicroPrintf("AllocateTensors() failed");
return;
}
// Obtain pointers to the model's input and output tensors.
input = interpreter->input(0);
output = interpreter->output(0);
// Keep track of how many inferences we have performed.
inference_count = 0;
}
// The name of this function is important for Arduino compatibility.
void loop() {
// Calculate an x value to feed into the model. We compare the current
// inference_count to the number of inferences per cycle to determine
// our position within the range of possible x values the model was
// trained on, and use this to calculate a value.
float position = static_cast<float>(inference_count) /
static_cast<float>(kInferencesPerCycle);
float x = position * kXrange;
// Quantize the input from floating-point to integer
int8_t x_quantized = x / input->params.scale + input->params.zero_point;
// Place the quantized input in the model's input tensor
input->data.int8[0] = x_quantized;
// Run inference, and report any error
TfLiteStatus invoke_status = interpreter->Invoke();
if (invoke_status != kTfLiteOk) {
MicroPrintf("Invoke failed on x: %f\n", static_cast<double>(x));
return;
}
// Obtain the quantized output from model's output tensor
int8_t y_quantized = output->data.int8[0];
// Dequantize the output from integer to floating-point
float y = (y_quantized - output->params.zero_point) * output->params.scale;
// Output the results. A custom HandleOutput function can be implemented
// for each supported hardware target.
HandleOutput(x, y);
// Increment the inference_counter, and reset it if we have reached
// the total number per cycle
inference_count += 1;
if (inference_count >= kInferencesPerCycle) inference_count = 0;
}
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_MAIN_FUNCTIONS_H_
#define TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_MAIN_FUNCTIONS_H_
// Expose a C friendly interface for main functions.
#ifdef __cplusplus
extern "C" {
#endif
// Initializes all data needed for the example. The name is important, and needs
// to be setup() for Arduino compatibility.
void setup();
// Runs one iteration of data gathering and inference. This should be called
// repeatedly from the application code. The name needs to be loop() for Arduino
// compatibility.
void loop();
#ifdef __cplusplus
}
#endif
#endif // TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_MAIN_FUNCTIONS_H_
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_OUTPUT_HANDLER_H_
#define TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_OUTPUT_HANDLER_H_
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/micro/micro_log.h"
// Called by the main loop to produce some output based on the x and y values
void HandleOutput(float x_value, float y_value);
#endif // TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_OUTPUT_HANDLER_H_
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "output_handler.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(TestCallability) {
// This will have external side-effects (like printing to the debug console
// or lighting an LED) that are hard to observe, so the most we can do is
// make sure the call doesn't crash.
HandleOutput(0, 0);
}
TF_LITE_MICRO_TESTS_END
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "output_handler.h"
#include "pico/stdlib.h"
#include "pico/time.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
#include "hardware/pwm.h"
#include "constants.h"
namespace {
int g_led_brightness = 0;
// For details on what this code is doing, see
// https://github.com/raspberrypi/pico-examples/blob/master/pwm/led_fade
extern "C" void on_pwm_wrap() {
// Clear the interrupt flag that brought us here
pwm_clear_irq(pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN));
// Square the value to make the LED's brightness appear more linear
// Note this range matches with the wrap value
pwm_set_gpio_level(PICO_DEFAULT_LED_PIN, g_led_brightness * g_led_brightness);
}
void init_pwm_fade() {
// Tell the LED pin that the PWM is in charge of its value.
gpio_set_function(PICO_DEFAULT_LED_PIN, GPIO_FUNC_PWM);
// Figure out which slice we just connected to the LED pin
uint slice_num = pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN);
// Mask our slice's IRQ output into the PWM block's single interrupt line,
// and register our interrupt handler
pwm_clear_irq(slice_num);
pwm_set_irq_enabled(slice_num, true);
irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
irq_set_enabled(PWM_IRQ_WRAP, true);
// Get some sensible defaults for the slice configuration. By default, the
// counter is allowed to wrap over its maximum range (0 to 2**16-1)
pwm_config config = pwm_get_default_config();
// Set divider, reduces counter clock to sysclock/this value
pwm_config_set_clkdiv(&config, 4.f);
// Load the configuration into our PWM slice, and set it running.
pwm_init(slice_num, &config, true);
}
} // namespace
void HandleOutput(float x_value, float y_value) {
// Do this only once
static bool is_initialized = false;
if (!is_initialized) {
init_pwm_fade();
is_initialized = true;
}
// Calculate the brightness of the LED such that y=-1 is fully off
// and y=1 is fully on. The LED's brightness can range from 0-255.
g_led_brightness = (int)(127.5f * (y_value + 1));
// Log the current brightness value for display in the console.
MicroPrintf("%d\n", g_led_brightness);
// By default the sine wave is too fast to see in the LED, so slow
// down the whole program deliberately so it's more visible.
sleep_ms(10);
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment