MORE DEMO APP
8
Makefile
|
@ -4,7 +4,7 @@ IDF_PATH ?= $(shell pwd)/esp-idf
|
|||
IDF_EXPORT_QUIET ?= 0
|
||||
SHELL := /usr/bin/env bash
|
||||
|
||||
.PHONY: prepare clean build flash erase monitor menuconfig image qemu install
|
||||
.PHONY: prepare clean build flash erase monitor menuconfig image
|
||||
|
||||
all: prepare build flash
|
||||
|
||||
|
@ -35,9 +35,3 @@ image:
|
|||
cd "$(BUILDDIR)"; dd if=bootloader/bootloader.bin bs=1 seek=4096 of=flash.bin conv=notrunc
|
||||
cd "$(BUILDDIR)"; dd if=partition_table/partition-table.bin bs=1 seek=36864 of=flash.bin conv=notrunc
|
||||
cd "$(BUILDDIR)"; dd if=main.bin bs=1 seek=65536 of=flash.bin conv=notrunc
|
||||
|
||||
qemu: image
|
||||
cd "$(BUILDDIR)"; qemu-system-xtensa -nographic -machine esp32 -drive 'file=flash.bin,if=mtd,format=raw'
|
||||
|
||||
install: flash
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# MCH2022 ESP32 firmware: Launcher
|
||||
# MCH2022 template app
|
||||
|
||||
This repository contains the ESP32 part of the firmware for the MCH2022 badge. This firmware allows for device testing, setup, OTA updates and of course launching apps.
|
||||
This repository contains a template app for the MCH2022 badge.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../components/appfs
|
|
@ -1 +0,0 @@
|
|||
../components/appfs/bootloader_main
|
|
@ -1,72 +1,6 @@
|
|||
idf_component_register(
|
||||
SRCS "main.c"
|
||||
"appfs_wrapper.c"
|
||||
"fpga_test.c"
|
||||
"graphics_wrapper.c"
|
||||
"menu.c"
|
||||
"rp2040_updater.c"
|
||||
"settings.c"
|
||||
"system_wrapper.c"
|
||||
"wifi_connection.c"
|
||||
"wifi_connect.c"
|
||||
"wifi_ota.c"
|
||||
"fpga_download.c"
|
||||
"audio.c"
|
||||
"bootscreen.c"
|
||||
"menus/launcher.c"
|
||||
"menus/settings.c"
|
||||
"menus/start.c"
|
||||
"menus/dev.c"
|
||||
"menus/wifi.c"
|
||||
"uninstall.c"
|
||||
"file_browser.c"
|
||||
"test_common.c"
|
||||
"factory_test.c"
|
||||
"animation.c"
|
||||
"button_test.c"
|
||||
"adc_test.c"
|
||||
"webusb.c"
|
||||
INCLUDE_DIRS "."
|
||||
"include"
|
||||
"menus"
|
||||
EMBED_TXTFILES ${project_dir}/resources/isrgrootx1.pem
|
||||
EMBED_FILES ${project_dir}/resources/wallpaper.png
|
||||
${project_dir}/resources/fpga_selftest.bin
|
||||
${project_dir}/resources/rp2040_firmware.bin
|
||||
${project_dir}/resources/boot.snd
|
||||
${project_dir}/resources/mch2022_logo.png
|
||||
${project_dir}/resources/logo_screen.png
|
||||
${project_dir}/resources/icons/dev.png
|
||||
${project_dir}/resources/icons/home.png
|
||||
${project_dir}/resources/icons/settings.png
|
||||
${project_dir}/resources/icons/apps.png
|
||||
${project_dir}/resources/icons/hatchery.png
|
||||
${project_dir}/resources/animation/animation_frame_1.png
|
||||
${project_dir}/resources/animation/animation_frame_2.png
|
||||
${project_dir}/resources/animation/animation_frame_3.png
|
||||
${project_dir}/resources/animation/animation_frame_4.png
|
||||
${project_dir}/resources/animation/animation_frame_5.png
|
||||
${project_dir}/resources/animation/animation_frame_6.png
|
||||
${project_dir}/resources/animation/animation_frame_7.png
|
||||
${project_dir}/resources/animation/animation_frame_8.png
|
||||
${project_dir}/resources/animation/animation_frame_9.png
|
||||
${project_dir}/resources/animation/animation_frame_10.png
|
||||
${project_dir}/resources/animation/animation_frame_11.png
|
||||
${project_dir}/resources/animation/animation_frame_12.png
|
||||
${project_dir}/resources/animation/animation_frame_13.png
|
||||
${project_dir}/resources/animation/animation_frame_14.png
|
||||
${project_dir}/resources/animation/animation_frame_15.png
|
||||
${project_dir}/resources/animation/animation_frame_16.png
|
||||
${project_dir}/resources/animation/animation_frame_17.png
|
||||
${project_dir}/resources/animation/animation_frame_18.png
|
||||
${project_dir}/resources/animation/animation_frame_19.png
|
||||
${project_dir}/resources/animation/animation_frame_20.png
|
||||
${project_dir}/resources/animation/animation_frame_21.png
|
||||
${project_dir}/resources/animation/animation_frame_22.png
|
||||
${project_dir}/resources/animation/animation_frame_23.png
|
||||
${project_dir}/resources/animation/animation_frame_24.png
|
||||
${project_dir}/resources/animation/animation_frame_25.png
|
||||
${project_dir}/resources/animation/animation_frame_26.png
|
||||
${project_dir}/resources/animation/animation_frame_27.png
|
||||
${project_dir}/resources/animation/animation_frame_28.png
|
||||
SRCS
|
||||
"main.c"
|
||||
INCLUDE_DIRS
|
||||
"." "include"
|
||||
)
|
|
@ -1,76 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "rp2040.h"
|
||||
#include "hardware.h"
|
||||
|
||||
void test_adc(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
bool quit = false;
|
||||
|
||||
RP2040* rp2040 = get_rp2040();
|
||||
const pax_font_t* font = pax_get_font("saira regular");
|
||||
|
||||
while (!quit) {
|
||||
bool error = false;
|
||||
|
||||
float vbat = 0;
|
||||
if (rp2040_read_vbat(rp2040, &vbat) != ESP_OK) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
float vusb = 0;
|
||||
if (rp2040_read_vusb(rp2040, &vusb) != ESP_OK) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
/*uint16_t raw_temperature = 0;
|
||||
if (rp2040_read_temp(rp2040, &raw_temperature) != ESP_OK) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
uint8_t charging = 0;
|
||||
if (rp2040_get_charging(rp2040, &charging) != ESP_OK) {
|
||||
error = true;
|
||||
}*/
|
||||
|
||||
//const float conversion_factor = 3.3f / (1 << 12); // 12-bit ADC with 3.3v vref
|
||||
//float vtemperature = raw_temperature * conversion_factor; // Inside of RP2040 chip
|
||||
//float temperature = 27 - (vtemperature - 0.706) / 0.001721; // From https://raspberrypi.github.io/pico-sdk-doxygen/group__hardware__adc.html
|
||||
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, font, 18, 0, 20*0, "Analog inputs");
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "Battery voltage %f v", vbat);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, font, 18, 0, 20*1, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "USB voltage %f v", vusb);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, font, 18, 0, 20*2, buffer);
|
||||
/*snprintf(buffer, sizeof(buffer), "Temperature %f *c", temperature);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, font, 18, 0, 20*3, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "Charging %02X", charging);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, font, 18, 0, 20*4, buffer);*/
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, font, 18, 0, 20*5, (error ? "ERROR" : ""));
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 250 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
if (value) {
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
quit = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
167
main/animation.c
|
@ -1,167 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "ili9341.h"
|
||||
#include "ws2812.h"
|
||||
#include "hardware.h"
|
||||
|
||||
extern const uint8_t animation_frame_1_start[] asm("_binary_animation_frame_1_png_start");
|
||||
extern const uint8_t animation_frame_1_end[] asm("_binary_animation_frame_1_png_end");
|
||||
extern const uint8_t animation_frame_2_start[] asm("_binary_animation_frame_2_png_start");
|
||||
extern const uint8_t animation_frame_2_end[] asm("_binary_animation_frame_2_png_end");
|
||||
extern const uint8_t animation_frame_3_start[] asm("_binary_animation_frame_3_png_start");
|
||||
extern const uint8_t animation_frame_3_end[] asm("_binary_animation_frame_3_png_end");
|
||||
extern const uint8_t animation_frame_4_start[] asm("_binary_animation_frame_4_png_start");
|
||||
extern const uint8_t animation_frame_4_end[] asm("_binary_animation_frame_4_png_end");
|
||||
extern const uint8_t animation_frame_5_start[] asm("_binary_animation_frame_5_png_start");
|
||||
extern const uint8_t animation_frame_5_end[] asm("_binary_animation_frame_5_png_end");
|
||||
extern const uint8_t animation_frame_6_start[] asm("_binary_animation_frame_6_png_start");
|
||||
extern const uint8_t animation_frame_6_end[] asm("_binary_animation_frame_6_png_end");
|
||||
extern const uint8_t animation_frame_7_start[] asm("_binary_animation_frame_7_png_start");
|
||||
extern const uint8_t animation_frame_7_end[] asm("_binary_animation_frame_7_png_end");
|
||||
extern const uint8_t animation_frame_8_start[] asm("_binary_animation_frame_8_png_start");
|
||||
extern const uint8_t animation_frame_8_end[] asm("_binary_animation_frame_8_png_end");
|
||||
extern const uint8_t animation_frame_9_start[] asm("_binary_animation_frame_9_png_start");
|
||||
extern const uint8_t animation_frame_9_end[] asm("_binary_animation_frame_9_png_end");
|
||||
extern const uint8_t animation_frame_10_start[] asm("_binary_animation_frame_10_png_start");
|
||||
extern const uint8_t animation_frame_10_end[] asm("_binary_animation_frame_10_png_end");
|
||||
extern const uint8_t animation_frame_11_start[] asm("_binary_animation_frame_11_png_start");
|
||||
extern const uint8_t animation_frame_11_end[] asm("_binary_animation_frame_11_png_end");
|
||||
extern const uint8_t animation_frame_12_start[] asm("_binary_animation_frame_12_png_start");
|
||||
extern const uint8_t animation_frame_12_end[] asm("_binary_animation_frame_12_png_end");
|
||||
extern const uint8_t animation_frame_13_start[] asm("_binary_animation_frame_13_png_start");
|
||||
extern const uint8_t animation_frame_13_end[] asm("_binary_animation_frame_13_png_end");
|
||||
extern const uint8_t animation_frame_14_start[] asm("_binary_animation_frame_14_png_start");
|
||||
extern const uint8_t animation_frame_14_end[] asm("_binary_animation_frame_14_png_end");
|
||||
extern const uint8_t animation_frame_15_start[] asm("_binary_animation_frame_15_png_start");
|
||||
extern const uint8_t animation_frame_15_end[] asm("_binary_animation_frame_15_png_end");
|
||||
extern const uint8_t animation_frame_16_start[] asm("_binary_animation_frame_16_png_start");
|
||||
extern const uint8_t animation_frame_16_end[] asm("_binary_animation_frame_16_png_end");
|
||||
extern const uint8_t animation_frame_17_start[] asm("_binary_animation_frame_17_png_start");
|
||||
extern const uint8_t animation_frame_17_end[] asm("_binary_animation_frame_17_png_end");
|
||||
extern const uint8_t animation_frame_18_start[] asm("_binary_animation_frame_18_png_start");
|
||||
extern const uint8_t animation_frame_18_end[] asm("_binary_animation_frame_18_png_end");
|
||||
extern const uint8_t animation_frame_19_start[] asm("_binary_animation_frame_19_png_start");
|
||||
extern const uint8_t animation_frame_19_end[] asm("_binary_animation_frame_19_png_end");
|
||||
extern const uint8_t animation_frame_20_start[] asm("_binary_animation_frame_20_png_start");
|
||||
extern const uint8_t animation_frame_20_end[] asm("_binary_animation_frame_20_png_end");
|
||||
extern const uint8_t animation_frame_21_start[] asm("_binary_animation_frame_21_png_start");
|
||||
extern const uint8_t animation_frame_21_end[] asm("_binary_animation_frame_21_png_end");
|
||||
extern const uint8_t animation_frame_22_start[] asm("_binary_animation_frame_22_png_start");
|
||||
extern const uint8_t animation_frame_22_end[] asm("_binary_animation_frame_22_png_end");
|
||||
extern const uint8_t animation_frame_23_start[] asm("_binary_animation_frame_23_png_start");
|
||||
extern const uint8_t animation_frame_23_end[] asm("_binary_animation_frame_23_png_end");
|
||||
extern const uint8_t animation_frame_24_start[] asm("_binary_animation_frame_24_png_start");
|
||||
extern const uint8_t animation_frame_24_end[] asm("_binary_animation_frame_24_png_end");
|
||||
extern const uint8_t animation_frame_25_start[] asm("_binary_animation_frame_25_png_start");
|
||||
extern const uint8_t animation_frame_25_end[] asm("_binary_animation_frame_25_png_end");
|
||||
extern const uint8_t animation_frame_26_start[] asm("_binary_animation_frame_26_png_start");
|
||||
extern const uint8_t animation_frame_26_end[] asm("_binary_animation_frame_26_png_end");
|
||||
extern const uint8_t animation_frame_27_start[] asm("_binary_animation_frame_27_png_start");
|
||||
extern const uint8_t animation_frame_27_end[] asm("_binary_animation_frame_27_png_end");
|
||||
extern const uint8_t animation_frame_28_start[] asm("_binary_animation_frame_28_png_start");
|
||||
extern const uint8_t animation_frame_28_end[] asm("_binary_animation_frame_28_png_end");
|
||||
|
||||
const uint8_t* animation_frames[] = {
|
||||
animation_frame_1_start,
|
||||
animation_frame_2_start,
|
||||
animation_frame_3_start,
|
||||
animation_frame_4_start,
|
||||
animation_frame_5_start,
|
||||
animation_frame_6_start,
|
||||
animation_frame_7_start,
|
||||
animation_frame_8_start,
|
||||
animation_frame_9_start,
|
||||
animation_frame_10_start,
|
||||
animation_frame_11_start,
|
||||
animation_frame_12_start,
|
||||
animation_frame_13_start,
|
||||
animation_frame_14_start,
|
||||
animation_frame_15_start,
|
||||
animation_frame_16_start,
|
||||
animation_frame_17_start,
|
||||
animation_frame_18_start,
|
||||
animation_frame_19_start,
|
||||
animation_frame_20_start,
|
||||
animation_frame_21_start,
|
||||
animation_frame_22_start,
|
||||
animation_frame_23_start,
|
||||
animation_frame_24_start,
|
||||
animation_frame_25_start,
|
||||
animation_frame_26_start,
|
||||
animation_frame_27_start,
|
||||
animation_frame_28_start
|
||||
};
|
||||
|
||||
const uint8_t* animation_frames_end[] = {
|
||||
animation_frame_1_end,
|
||||
animation_frame_2_end,
|
||||
animation_frame_3_end,
|
||||
animation_frame_4_end,
|
||||
animation_frame_5_end,
|
||||
animation_frame_6_end,
|
||||
animation_frame_7_end,
|
||||
animation_frame_8_end,
|
||||
animation_frame_9_end,
|
||||
animation_frame_10_end,
|
||||
animation_frame_11_end,
|
||||
animation_frame_12_end,
|
||||
animation_frame_13_end,
|
||||
animation_frame_14_end,
|
||||
animation_frame_15_end,
|
||||
animation_frame_16_end,
|
||||
animation_frame_17_end,
|
||||
animation_frame_18_end,
|
||||
animation_frame_19_end,
|
||||
animation_frame_20_end,
|
||||
animation_frame_21_end,
|
||||
animation_frame_22_end,
|
||||
animation_frame_23_end,
|
||||
animation_frame_24_end,
|
||||
animation_frame_25_end,
|
||||
animation_frame_26_end,
|
||||
animation_frame_27_end,
|
||||
animation_frame_28_end
|
||||
};
|
||||
|
||||
void display_animation(pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
|
||||
gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(GPIO_SD_PWR, 1);
|
||||
ws2812_init(GPIO_LED_DATA);
|
||||
uint8_t led_data[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
ws2812_send_data(led_data, sizeof(led_data));
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
|
||||
for (uint8_t frame = 0; frame < 28; frame++) {
|
||||
pax_buf_t image;
|
||||
pax_decode_png_buf(&image, (void*) animation_frames[frame], animation_frames_end[frame] - animation_frames[frame], PAX_BUF_16_565RGB, 0);
|
||||
pax_draw_image(pax_buffer, &image, 0, 0);
|
||||
pax_buf_destroy(&image);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
uint8_t brightness = (frame > 14) ? (frame - 14) : (0);
|
||||
led_data[1] = brightness;
|
||||
led_data[3] = brightness;
|
||||
led_data[8] = brightness;
|
||||
led_data[9] = brightness / 2;
|
||||
led_data[10] = brightness / 2;
|
||||
led_data[14] = brightness;
|
||||
ws2812_send_data(led_data, sizeof(led_data));
|
||||
}
|
||||
|
||||
for (uint8_t brightness = 14; brightness < 50; brightness++) {
|
||||
led_data[1] = brightness;
|
||||
led_data[3] = brightness;
|
||||
led_data[8] = brightness;
|
||||
led_data[9] = brightness / 2;
|
||||
led_data[10] = brightness / 2;
|
||||
led_data[14] = brightness;
|
||||
ws2812_send_data(led_data, sizeof(led_data));
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "appfs_wrapper.h"
|
||||
#include "hardware.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "bootscreen.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
|
||||
static const char *TAG = "appfs wrapper";
|
||||
|
||||
esp_err_t appfs_init(void) {
|
||||
return appfsInit(APPFS_PART_TYPE, APPFS_PART_SUBTYPE);
|
||||
}
|
||||
|
||||
uint8_t* load_file_to_ram(FILE* fd, size_t* fsize) {
|
||||
fseek(fd, 0, SEEK_END);
|
||||
*fsize = ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
uint8_t* file = malloc(*fsize);
|
||||
if (file == NULL) return NULL;
|
||||
fread(file, *fsize, 1, fd);
|
||||
return file;
|
||||
}
|
||||
|
||||
void appfs_boot_app(int fd) {
|
||||
if (fd<0 || fd>255) {
|
||||
REG_WRITE(RTC_CNTL_STORE0_REG, 0);
|
||||
} else {
|
||||
REG_WRITE(RTC_CNTL_STORE0_REG, 0xA5000000|fd);
|
||||
}
|
||||
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
||||
esp_sleep_enable_timer_wakeup(10);
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
void appfs_store_app(pax_buf_t* pax_buffer, ILI9341* ili9341, char* path, char* label) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Installing app...");
|
||||
esp_err_t res;
|
||||
appfs_handle_t handle;
|
||||
FILE* app_fd = fopen(path, "rb");
|
||||
if (app_fd == NULL) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to open file");
|
||||
ESP_LOGE(TAG, "Failed to open file");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
return;
|
||||
}
|
||||
size_t app_size;
|
||||
uint8_t* app = load_file_to_ram(app_fd, &app_size);
|
||||
if (app == NULL) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to load app to RAM");
|
||||
ESP_LOGE(TAG, "Failed to load application into RAM");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Application size %d", app_size);
|
||||
|
||||
res = appfsCreateFile(label, app_size, &handle);
|
||||
if (res != ESP_OK) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to create file");
|
||||
ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
free(app);
|
||||
return;
|
||||
}
|
||||
int roundedSize=(app_size+(SPI_FLASH_MMU_PAGE_SIZE-1))&(~(SPI_FLASH_MMU_PAGE_SIZE-1));
|
||||
res = appfsErase(handle, 0, roundedSize);
|
||||
if (res != ESP_OK) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to erase file");
|
||||
ESP_LOGE(TAG, "Failed to erase file on AppFS (%d)", res);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
free(app);
|
||||
return;
|
||||
}
|
||||
res = appfsWrite(handle, 0, app, app_size);
|
||||
if (res != ESP_OK) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to write file");
|
||||
ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
free(app);
|
||||
return;
|
||||
}
|
||||
free(app);
|
||||
ESP_LOGI(TAG, "Application is now stored in AppFS");
|
||||
display_boot_screen(pax_buffer, ili9341, "App installed!");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
return;
|
||||
}
|
92
main/audio.c
|
@ -1,92 +0,0 @@
|
|||
#include "audio.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include <freertos/task.h>
|
||||
#include "esp_system.h"
|
||||
#include "driver/i2s.h"
|
||||
#include "driver/rtc_io.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void _audio_init(int i2s_num) {
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
|
||||
.sample_rate = 8000,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 256,
|
||||
.intr_alloc_flags = 0,
|
||||
.use_apll = false,
|
||||
.bits_per_chan = I2S_BITS_PER_SAMPLE_16BIT
|
||||
};
|
||||
|
||||
i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.mck_io_num = 0,
|
||||
.bck_io_num = 4,
|
||||
.ws_io_num = 12,
|
||||
.data_out_num = 13,
|
||||
.data_in_num = I2S_PIN_NO_CHANGE
|
||||
};
|
||||
|
||||
i2s_set_pin(i2s_num, &pin_config);
|
||||
}
|
||||
|
||||
typedef struct _audio_player_cfg {
|
||||
uint8_t* buffer;
|
||||
size_t size;
|
||||
bool free_buffer;
|
||||
} audio_player_cfg_t;
|
||||
|
||||
void audio_player_task(void* arg) {
|
||||
audio_player_cfg_t* config = (audio_player_cfg_t*) arg;
|
||||
size_t sample_length = config->size;
|
||||
uint8_t* sample_buffer = config->buffer;
|
||||
|
||||
size_t count;
|
||||
size_t position = 0;
|
||||
|
||||
while (position < sample_length) {
|
||||
size_t length = sample_length - position;
|
||||
if (length > 256) length = 256;
|
||||
uint8_t buffer[256];
|
||||
memcpy(buffer, &sample_buffer[position], length);
|
||||
for (size_t l = 0; l < length; l+=2) {
|
||||
int16_t* sample = (int16_t*) &buffer[l];
|
||||
*sample *= 0.50;
|
||||
}
|
||||
i2s_write(0, buffer, length, &count, portMAX_DELAY);
|
||||
if (count != length) {
|
||||
printf("i2s_write_bytes: count (%d) != length (%d)\n", count, length);
|
||||
abort();
|
||||
}
|
||||
position += length;
|
||||
}
|
||||
|
||||
i2s_zero_dma_buffer(0); // Fill buffer with silence
|
||||
if (config->free_buffer) free(sample_buffer);
|
||||
vTaskDelete(NULL); // Tell FreeRTOS that the task is done
|
||||
}
|
||||
|
||||
void audio_init() {
|
||||
_audio_init(0);
|
||||
}
|
||||
|
||||
extern const uint8_t boot_snd_start[] asm("_binary_boot_snd_start");
|
||||
extern const uint8_t boot_snd_end[] asm("_binary_boot_snd_end");
|
||||
|
||||
audio_player_cfg_t bootsound;
|
||||
|
||||
void play_bootsound() {
|
||||
TaskHandle_t handle;
|
||||
|
||||
bootsound.buffer = boot_snd_start,
|
||||
bootsound.size = boot_snd_end - boot_snd_start;
|
||||
bootsound.free_buffer = false;
|
||||
|
||||
xTaskCreate(&audio_player_task, "Audio player", 4096, (void*) &bootsound, 10, &handle);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
extern const uint8_t mch2022_logo_png_start[] asm("_binary_mch2022_logo_png_start");
|
||||
extern const uint8_t mch2022_logo_png_end[] asm("_binary_mch2022_logo_png_end");
|
||||
|
||||
void display_boot_screen(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* text) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_buf_t logo;
|
||||
pax_decode_png_buf(&logo, (void*) mch2022_logo_png_start, mch2022_logo_png_end - mch2022_logo_png_start, PAX_BUF_16_565RGB, 0);
|
||||
pax_draw_image(pax_buffer, &logo, (320 / 2) - (212 / 2), ((240 - 32 - 10) / 2) - (160 / 2));
|
||||
pax_buf_destroy(&logo);
|
||||
|
||||
pax_vec1_t size = pax_text_size(font, 18, text);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, (320 / 2) - (size.x / 2), 240 - 32, text);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "rp2040.h"
|
||||
|
||||
void test_buttons(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
bool render = true;
|
||||
bool quit = false;
|
||||
|
||||
bool btn_joy_down = false;
|
||||
bool btn_joy_up = false;
|
||||
bool btn_joy_left = false;
|
||||
bool btn_joy_right = false;
|
||||
bool btn_joy_press = false;
|
||||
bool btn_home = false;
|
||||
bool btn_menu = false;
|
||||
bool btn_select = false;
|
||||
bool btn_start = false;
|
||||
bool btn_accept = false;
|
||||
bool btn_back = false;
|
||||
|
||||
bool btn_joy_down_green = false;
|
||||
bool btn_joy_up_green = false;
|
||||
bool btn_joy_left_green = false;
|
||||
bool btn_joy_right_green = false;
|
||||
bool btn_joy_press_green = false;
|
||||
bool btn_home_green = false;
|
||||
bool btn_menu_green = false;
|
||||
bool btn_select_green = false;
|
||||
bool btn_start_green = false;
|
||||
bool btn_accept_green = false;
|
||||
bool btn_back_green = false;
|
||||
|
||||
|
||||
while (!quit) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
render = true;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
btn_joy_down = value;
|
||||
if (value) btn_joy_down_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
btn_joy_up = value;
|
||||
if (value) btn_joy_up_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_LEFT:
|
||||
btn_joy_left = value;
|
||||
if (value) btn_joy_left_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_RIGHT:
|
||||
btn_joy_right = value;
|
||||
if (value) btn_joy_right_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
btn_joy_press = value;
|
||||
if (value) btn_joy_press_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
btn_home = value;
|
||||
if (value) btn_home_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_MENU:
|
||||
btn_menu = value;
|
||||
if (value) btn_menu_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
btn_select = value;
|
||||
if (value) btn_select_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
btn_start = value;
|
||||
if (value) btn_start_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
btn_accept = value;
|
||||
if (value) btn_accept_green = true;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
btn_back = value;
|
||||
if (value) btn_back_green = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "Press HOME + START to exit");
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "JOY DOWN %s", btn_joy_down ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_joy_down_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*1, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "JOY UP %s", btn_joy_up ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_joy_up_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*2, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "JOY LEFT %s", btn_joy_left ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_joy_right_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*3, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "JOY RIGHT %s", btn_joy_right ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_joy_left_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*4, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "JOY PRESS %s", btn_joy_press ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_joy_press_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*5, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "BTN HOME %s", btn_home ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_home_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*6, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "BTN MENU %s", btn_menu ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_menu_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*7, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "BTN SELECT %s", btn_select ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_select_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*8, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "BTN START %s", btn_start ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_start_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*9, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "BTN A %s", btn_accept ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_accept_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*10, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "BTN B %s", btn_back ? "PRESSED" : "released");
|
||||
pax_draw_text(pax_buffer, btn_back_green ? 0xFF00FF00 : 0xFFFFFFFF, NULL, 18, 0, 20*11, buffer);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (btn_home && btn_start) {
|
||||
quit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "hardware.h"
|
||||
#include "ili9341.h"
|
||||
#include "ice40.h"
|
||||
#include "rp2040.h"
|
||||
#include "fpga_test.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "test_common.h"
|
||||
#include "settings.h"
|
||||
#include "ws2812.h"
|
||||
#include "audio.h"
|
||||
|
||||
static const char *TAG = "factory";
|
||||
|
||||
/* Test routines */
|
||||
|
||||
bool test_rp2040_init(uint32_t* rc) {
|
||||
esp_err_t res = bsp_rp2040_init();
|
||||
*rc = (uint32_t) res;
|
||||
return (res == ESP_OK);
|
||||
}
|
||||
|
||||
bool test_ice40_init(uint32_t* rc) {
|
||||
esp_err_t res = bsp_ice40_init();
|
||||
*rc = (uint32_t) res;
|
||||
return (res == ESP_OK);
|
||||
}
|
||||
|
||||
bool test_bno055_init(uint32_t* rc) {
|
||||
esp_err_t res = bsp_bno055_init();
|
||||
*rc = (uint32_t) res;
|
||||
return (res == ESP_OK);
|
||||
}
|
||||
|
||||
bool test_bme680_init(uint32_t* rc) {
|
||||
esp_err_t res = bsp_bme680_init();
|
||||
*rc = (uint32_t) res;
|
||||
return (res == ESP_OK);
|
||||
}
|
||||
|
||||
bool test_stuck_buttons(uint32_t* rc) {
|
||||
RP2040* rp2040 = get_rp2040();
|
||||
uint16_t state;
|
||||
esp_err_t res = rp2040_read_buttons(rp2040, &state);
|
||||
if (res != ESP_OK) {
|
||||
*rc = 0xFFFFFFFF;
|
||||
return false;
|
||||
}
|
||||
|
||||
state &= ~(1 << RP2040_INPUT_FPGA_CDONE); // Ignore FPGA CDONE
|
||||
|
||||
*rc = state;
|
||||
|
||||
return (state == 0x0000);
|
||||
}
|
||||
|
||||
bool test_adc_vbat(uint32_t* rc) {
|
||||
float value = 0;
|
||||
esp_err_t res = rp2040_read_vbat(get_rp2040(), &value);
|
||||
*rc = value * 100;
|
||||
return ((res == ESP_OK) && (value < 4.3) && (value > 3.9));
|
||||
}
|
||||
|
||||
bool test_adc_vusb(uint32_t* rc) {
|
||||
float value = 0;
|
||||
esp_err_t res = rp2040_read_vusb(get_rp2040(), &value);
|
||||
*rc = value * 100;
|
||||
return ((res == ESP_OK) && (value > 4.5));
|
||||
}
|
||||
|
||||
bool test_sd_power(uint32_t* rc) {
|
||||
*rc = 0x00000000;
|
||||
// Init all GPIO pins for SD card and LED
|
||||
if (gpio_reset_pin(GPIO_SD_PWR) != ESP_OK) return false;
|
||||
if (gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_INPUT) != ESP_OK) return false;
|
||||
if (gpio_reset_pin(GPIO_SD_CMD) != ESP_OK) return false;
|
||||
if (gpio_set_direction(GPIO_SD_CMD, GPIO_MODE_INPUT) != ESP_OK) return false;
|
||||
if (gpio_reset_pin(GPIO_SD_CLK) != ESP_OK) return false;
|
||||
if (gpio_set_direction(GPIO_SD_CLK, GPIO_MODE_INPUT) != ESP_OK) return false;
|
||||
if (gpio_reset_pin(GPIO_SD_D0) != ESP_OK) return false;
|
||||
if (gpio_set_direction(GPIO_SD_D0, GPIO_MODE_INPUT) != ESP_OK) return false;
|
||||
if (gpio_reset_pin(GPIO_LED_DATA) != ESP_OK) return false;
|
||||
if (gpio_set_direction(GPIO_LED_DATA, GPIO_MODE_INPUT) != ESP_OK) return false;
|
||||
|
||||
if (gpio_get_level(GPIO_SD_PWR)) {*rc = 0x01; return false;} // Check that power enable is pulled low
|
||||
|
||||
if (gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT) != ESP_OK) return false;
|
||||
if (gpio_set_level(GPIO_SD_PWR, 1) != ESP_OK) return false;
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
// SD pins should be pulled high
|
||||
if (!gpio_get_level(GPIO_SD_CMD)) {*rc = 0x02; return false;}
|
||||
if (!gpio_get_level(GPIO_SD_CLK)) {*rc = 0x04; return false;}
|
||||
if (!gpio_get_level(GPIO_SD_D0)) {*rc = 0x08; return false;}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool run_basic_tests(pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
const pax_font_t *font;
|
||||
int line = 0;
|
||||
bool ok = true;
|
||||
|
||||
/* Screen init */
|
||||
font = pax_get_font("sky mono");
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x8060f0);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
/* Run mandatory tests */
|
||||
RUN_TEST_MANDATORY("RP2040", test_rp2040_init);
|
||||
RUN_TEST_MANDATORY("ICE40", test_ice40_init);
|
||||
RUN_TEST_MANDATORY("BNO055", test_bno055_init);
|
||||
RUN_TEST_MANDATORY("BME680", test_bme680_init);
|
||||
|
||||
/* Run tests */
|
||||
RUN_TEST("STUCK BUTTONS", test_stuck_buttons);
|
||||
RUN_TEST("SD/LED POWER", test_sd_power);
|
||||
RUN_TEST("Battery voltage", test_adc_vbat);
|
||||
RUN_TEST("USB voltage", test_adc_vusb);
|
||||
|
||||
|
||||
error:
|
||||
/* Fail result on screen */
|
||||
if (!ok) pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FAIL");
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
const uint8_t led_green[15] = {50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0};
|
||||
const uint8_t led_red[15] = {0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0};
|
||||
const uint8_t led_blue[15] = {0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50};
|
||||
|
||||
void factory_test(pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
uint8_t factory_test_done = nvs_get_u8_default("system", "factory_test", 0);
|
||||
if (!factory_test_done) {
|
||||
bool result;
|
||||
|
||||
ESP_LOGI(TAG, "Factory test start");
|
||||
|
||||
result = run_basic_tests(pax_buffer, ili9341);
|
||||
|
||||
gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(GPIO_SD_PWR, 1);
|
||||
ws2812_init(GPIO_LED_DATA);
|
||||
if (result) {
|
||||
ws2812_send_data(led_blue, sizeof(led_blue));
|
||||
} else {
|
||||
ws2812_send_data(led_red, sizeof(led_red));
|
||||
}
|
||||
|
||||
if (!result) goto test_end;
|
||||
|
||||
RP2040* rp2040 = get_rp2040();
|
||||
|
||||
result = run_fpga_tests(rp2040->queue, pax_buffer, ili9341);
|
||||
if (!result) {
|
||||
ws2812_send_data(led_red, sizeof(led_red));
|
||||
goto test_end;
|
||||
}
|
||||
|
||||
ws2812_send_data(led_green, sizeof(led_green));
|
||||
|
||||
// Wait for the operator to unplug the badge
|
||||
test_end:
|
||||
|
||||
if (result) {
|
||||
esp_err_t res = nvs_set_u8_fixed("system", "factory_test", 1);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to store test result %d\n", res);
|
||||
result = false;
|
||||
ws2812_send_data(led_red, sizeof(led_red));
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (result) play_bootsound();
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "appfs_wrapper.h"
|
||||
#include "bootscreen.h"
|
||||
|
||||
static const char *TAG = "file browser";
|
||||
|
||||
void list_files_in_folder(const char* path) {
|
||||
DIR* dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open directory %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
char type;
|
||||
char size[12];
|
||||
char tpath[255];
|
||||
char tbuffer[80];
|
||||
struct stat sb;
|
||||
struct tm *tm_info;
|
||||
char *lpath = NULL;
|
||||
int statok;
|
||||
|
||||
uint64_t total = 0;
|
||||
int nfiles = 0;
|
||||
printf("T Size Date/Time Name\n");
|
||||
printf("-----------------------------------\n");
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
sprintf(tpath, path);
|
||||
if (path[strlen(path)-1] != '/') {
|
||||
strcat(tpath,"/");
|
||||
}
|
||||
strcat(tpath,ent->d_name);
|
||||
tbuffer[0] = '\0';
|
||||
|
||||
// Get file stat
|
||||
statok = stat(tpath, &sb);
|
||||
|
||||
if (statok == 0) {
|
||||
tm_info = localtime(&sb.st_mtime);
|
||||
strftime(tbuffer, 80, "%d/%m/%Y %R", tm_info);
|
||||
} else {
|
||||
sprintf(tbuffer, " ");
|
||||
}
|
||||
|
||||
if (ent->d_type == DT_REG) {
|
||||
type = 'f';
|
||||
nfiles++;
|
||||
if (statok) {
|
||||
strcpy(size, " ?");
|
||||
} else {
|
||||
total += sb.st_size;
|
||||
if (sb.st_size < (1024*1024)) sprintf(size,"%8d", (int)sb.st_size);
|
||||
else if ((sb.st_size/1024) < (1024*1024)) sprintf(size,"%6dKB", (int)(sb.st_size / 1024));
|
||||
else sprintf(size,"%6dMB", (int)(sb.st_size / (1024 * 1024)));
|
||||
}
|
||||
} else {
|
||||
type = 'd';
|
||||
strcpy(size, " -");
|
||||
}
|
||||
|
||||
printf("%c %s %s %s\r\n", type, size, tbuffer, ent->d_name);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
if (total < (1024*1024)) printf(" %8d", (int)total);
|
||||
else if ((total/1024) < (1024*1024)) printf(" %6dKB", (int)(total / 1024));
|
||||
else printf(" %6dMB", (int)(total / (1024 * 1024)));
|
||||
printf(" in %d file(s)\n", nfiles);
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
closedir(dir);
|
||||
free(lpath);
|
||||
}
|
||||
|
||||
typedef struct _file_browser_menu_args {
|
||||
char type;
|
||||
char path[512];
|
||||
char label[512];
|
||||
} file_browser_menu_args_t;
|
||||
|
||||
void find_parent_dir(char* path, char* parent) {
|
||||
size_t last_separator = 0;
|
||||
for (size_t index = 0; index < strlen(path); index++) {
|
||||
if (path[index] == '/') last_separator = index;
|
||||
}
|
||||
|
||||
strcpy(parent, path);
|
||||
parent[last_separator] = '\0';
|
||||
}
|
||||
|
||||
void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* initial_path) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Please wait...");
|
||||
char path[512] = {0};
|
||||
strncpy(path, initial_path, sizeof(path));
|
||||
while (true) {
|
||||
menu_t* menu = menu_alloc(path, 20, 18);
|
||||
DIR* dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
if (path[0] != 0) {
|
||||
ESP_LOGE(TAG, "Failed to open directory %s", path);
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to open directory");
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
struct dirent *ent;
|
||||
file_browser_menu_args_t* pd_args = malloc(sizeof(file_browser_menu_args_t));
|
||||
pd_args->type = 'd';
|
||||
find_parent_dir(path, pd_args->path);
|
||||
menu_insert_item(menu, "../", NULL, pd_args, -1);
|
||||
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
file_browser_menu_args_t* args = malloc(sizeof(file_browser_menu_args_t));
|
||||
sprintf(args->path, path);
|
||||
if (path[strlen(path)-1] != '/') {
|
||||
strcat(args->path,"/");
|
||||
}
|
||||
strcat(args->path,ent->d_name);
|
||||
|
||||
if (ent->d_type == DT_REG) {
|
||||
args->type = 'f';
|
||||
} else {
|
||||
args->type = 'd';
|
||||
}
|
||||
|
||||
snprintf(args->label, sizeof(args->label), "%s%s", ent->d_name, (args->type == 'd') ? "/" : "");
|
||||
menu_insert_item(menu, args->label, NULL, args, -1);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
bool render = true;
|
||||
bool renderbg = true;
|
||||
bool exit = false;
|
||||
file_browser_menu_args_t* menuArgs = NULL;
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
menuArgs = pd_args;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
if (value) {
|
||||
menuArgs = menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
if (value) exit = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (renderbg) {
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] install [B] back");
|
||||
renderbg = false;
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF000000);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (menuArgs != NULL) {
|
||||
if (menuArgs->type == 'd') {
|
||||
strcpy(path, menuArgs->path);
|
||||
break;
|
||||
} else {
|
||||
printf("File selected: %s\n", menuArgs->path);
|
||||
appfs_store_app(pax_buffer, ili9341, menuArgs->path, menuArgs->label);
|
||||
}
|
||||
menuArgs = NULL;
|
||||
render = true;
|
||||
renderbg = true;
|
||||
}
|
||||
|
||||
if (exit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < menu_get_length(menu); index++) {
|
||||
free(menu_get_callback_args(menu, index));
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "driver/uart.h"
|
||||
#include "hardware.h"
|
||||
#include "managed_i2c.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "ice40.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "graphics_wrapper.h"
|
||||
#include "esp32/rom/crc.h"
|
||||
|
||||
void fpga_install_uart() {
|
||||
fflush(stdout);
|
||||
ESP_ERROR_CHECK(uart_driver_install(0, 2048, 0, 0, NULL, 0));
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 921600,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_APB,
|
||||
};
|
||||
ESP_ERROR_CHECK(uart_param_config(0, &uart_config));
|
||||
}
|
||||
|
||||
void fpga_uninstall_uart() {
|
||||
uart_driver_delete(0);
|
||||
}
|
||||
|
||||
bool fpga_read_stdin(uint8_t* buffer, uint32_t len, uint32_t timeout) {
|
||||
int read = uart_read_bytes(0, buffer, len, timeout / portTICK_PERIOD_MS);
|
||||
return (read == len);
|
||||
}
|
||||
|
||||
bool fpga_uart_sync(uint32_t* length, uint32_t* crc) {
|
||||
uint8_t data[256];
|
||||
uart_read_bytes(0, data, sizeof(data), 10 / portTICK_PERIOD_MS);
|
||||
char command[] = "FPGA";
|
||||
uart_write_bytes(0, command, 4);
|
||||
uint8_t rx_buffer[4 * 3];
|
||||
fpga_read_stdin(rx_buffer, sizeof(rx_buffer), 1000);
|
||||
if (memcmp(rx_buffer, "FPGA", 4) != 0) return false;
|
||||
memcpy((uint8_t*) length, &rx_buffer[4 * 1], 4);
|
||||
memcpy((uint8_t*) crc, &rx_buffer[4 * 2], 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fpga_uart_load(uint8_t* buffer, uint32_t length) {
|
||||
return fpga_read_stdin(buffer, length, 3000);
|
||||
}
|
||||
|
||||
void fpga_uart_mess(const char *mess) {
|
||||
uart_write_bytes(0, mess, strlen(mess));
|
||||
}
|
||||
|
||||
esp_err_t fpga_process_events(xQueueHandle buttonQueue, ICE40* ice40, uint16_t *key_state, uint16_t *idle_count)
|
||||
{
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
while (xQueueReceive(buttonQueue, &buttonMessage, 0) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
uint16_t key_mask = 0;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
key_mask = 1 << 0;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
key_mask = 1 << 1;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_LEFT:
|
||||
key_mask = 1 << 2;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_RIGHT:
|
||||
key_mask = 1 << 3;
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
key_mask = 1 << 4;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
key_mask = 1 << 5;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_MENU:
|
||||
key_mask = 1 << 6;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
key_mask = 1 << 7;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
key_mask = 1 << 8;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
key_mask = 1 << 9;
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
key_mask = 1 << 10;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (key_mask != 0)
|
||||
{
|
||||
if (value) {
|
||||
*key_state |= key_mask;
|
||||
}
|
||||
else {
|
||||
*key_state &= ~key_mask;
|
||||
}
|
||||
|
||||
uint8_t spi_message[5] = { 0xf4 };
|
||||
spi_message[1] = *key_state >> 8;
|
||||
spi_message[2] = *key_state & 0xff;
|
||||
spi_message[3] = key_mask >> 8;
|
||||
spi_message[4] = key_mask & 0xff;
|
||||
esp_err_t res = ice40_send(ice40, spi_message, 5);
|
||||
if (res != ESP_OK) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
*idle_count = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void fpga_download(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
char message[64];
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Preparing...");
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
fpga_install_uart();
|
||||
|
||||
ice40_disable(ice40);
|
||||
ili9341_init(ili9341);
|
||||
|
||||
uint8_t counter = 0;
|
||||
uint32_t length = 0;
|
||||
uint32_t crc = 0;
|
||||
while (!fpga_uart_sync(&length, &crc)) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
snprintf(message, sizeof(message), "Waiting for bitstream%s%s%s", (counter > 0) ? "." : " ", (counter > 1) ? "." : " ", (counter > 2) ? "." : " ");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
counter++;
|
||||
if (counter > 3) counter = 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Receiving bitstream...");
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
uint8_t* buffer = malloc(length);
|
||||
if (buffer == NULL) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Malloc failed");
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
fpga_uninstall_uart();
|
||||
return;
|
||||
}
|
||||
if (!fpga_uart_load(buffer, length)) {
|
||||
free(buffer);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Timeout while loading");
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
fpga_uninstall_uart();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t checkCrc = crc32_le(0, buffer, length);
|
||||
|
||||
if (checkCrc != crc) {
|
||||
free(buffer);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "CRC incorrect");
|
||||
snprintf(message, sizeof(message), "Provided CRC: %08X", crc);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*2, message);
|
||||
snprintf(message, sizeof(message), "Calculated CRC: %08X", checkCrc);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*3, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
snprintf(message, sizeof(message), "CRC incorrect %08X %08x\n", crc, checkCrc);
|
||||
fpga_uart_mess(message);
|
||||
fpga_uninstall_uart();
|
||||
return;
|
||||
}
|
||||
fpga_uart_mess("CRC correct\n");
|
||||
|
||||
ili9341_deinit(ili9341);
|
||||
ili9341_select(ili9341, false);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
ili9341_select(ili9341, true);
|
||||
|
||||
esp_err_t res = ice40_load_bitstream(ice40, buffer, length);
|
||||
free(buffer);
|
||||
|
||||
if (res != ESP_OK) {
|
||||
ice40_disable(ice40);
|
||||
ili9341_init(ili9341);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
snprintf(message, sizeof(message), "Upload failed: %d", res);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
snprintf(message, sizeof(message), "uploading bitstream failed with %d\n", res);
|
||||
fpga_uart_mess(message);
|
||||
fpga_uninstall_uart();
|
||||
return;
|
||||
}
|
||||
snprintf(message, sizeof(message), "bitstream has uploaded\n");
|
||||
fpga_uart_mess(message);
|
||||
|
||||
// Waiting for next download and sending key strokes to FPGA
|
||||
uint16_t key_state = 0;
|
||||
uint16_t idle_count = 0;
|
||||
while (true) {
|
||||
if (idle_count >= 200) {
|
||||
if (fpga_uart_sync(&length, &crc)) {
|
||||
break;
|
||||
}
|
||||
idle_count = 0;
|
||||
}
|
||||
esp_err_t res = fpga_process_events(buttonQueue, ice40, &key_state, &idle_count);
|
||||
if (res != ESP_OK) {
|
||||
ice40_disable(ice40);
|
||||
ili9341_init(ili9341);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode");
|
||||
snprintf(message, sizeof(message), "Error: %d", res);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
snprintf(message, sizeof(message), "processing events failed with %d\n", res);
|
||||
fpga_uart_mess(message);
|
||||
fpga_uninstall_uart();
|
||||
return;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
idle_count++;
|
||||
}
|
||||
ice40_disable(ice40);
|
||||
ili9341_init(ili9341);
|
||||
}
|
||||
}
|
509
main/fpga_test.c
|
@ -1,509 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "hardware.h"
|
||||
#include "ili9341.h"
|
||||
#include "ice40.h"
|
||||
#include "rp2040.h"
|
||||
#include "fpga_test.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "test_common.h"
|
||||
|
||||
extern const uint8_t fpga_selftest_bin_start[] asm("_binary_fpga_selftest_bin_start");
|
||||
extern const uint8_t fpga_selftest_bin_end[] asm("_binary_fpga_selftest_bin_end");
|
||||
|
||||
|
||||
static const char *TAG = "fpga_test";
|
||||
|
||||
/* SPI commands */
|
||||
#define SPI_CMD_NOP1 0x00
|
||||
#define SPI_CMD_SOC_MSG 0x10
|
||||
#define SPI_CMD_REG_ACCESS 0xf0
|
||||
#define SPI_CMD_LOOPBACK 0xf1
|
||||
#define SPI_CMD_LCD_PASSTHROUGH 0xf2
|
||||
#define SPI_CMD_BUTTON_REPORT 0xf4
|
||||
#define SPI_CMD_IRQ_ACK 0xfd
|
||||
#define SPI_CMD_RESP_ACK 0xfe
|
||||
#define SPI_CMD_NOP2 0xff
|
||||
|
||||
/* Messages to self-test SoC */
|
||||
#define SOC_CMD_PING 0x00
|
||||
#define SOC_CMD_PING_PARAM 0xc0ffee
|
||||
#define SOC_CMD_PING_RESP 0xcafebabe
|
||||
|
||||
#define SOC_CMD_RGB_STATE_SET 0x10
|
||||
#define SOC_CMD_IRQN_SET 0x11
|
||||
#define SOC_CMD_LCD_RGB_CYCLE_SET 0x12
|
||||
#define SOC_CMD_PMOD_CYCLE_SET 0x13
|
||||
#define SOC_CMD_LCD_PASSTHROUGH_SET 0x14
|
||||
|
||||
#define SOC_CMD_PSRAM_TEST 0x20
|
||||
#define SOC_CMD_UART_LOOPBACK_TEST 0x21
|
||||
#define SOC_CMD_PMOD_OPEN_TEST 0x22
|
||||
#define SOC_CMD_PMOD_PLUG_TEST 0x23
|
||||
#define SOC_CMD_LCD_INIT_TEST 0x24
|
||||
|
||||
#define SOC_CMD_LCD_CHECK_MODE 0x30
|
||||
|
||||
#define SOC_RESP_OK 0x00000000
|
||||
|
||||
|
||||
/* SoC commands */
|
||||
|
||||
static bool soc_message(ICE40* ice40, uint8_t cmd, uint32_t param, uint32_t *resp, TickType_t ticks_to_wait) {
|
||||
esp_err_t res;
|
||||
uint8_t data_tx[6];
|
||||
uint8_t data_rx[6];
|
||||
|
||||
/* Default delay */
|
||||
ticks_to_wait /= 10; /* We do 10 retries */
|
||||
if (!ticks_to_wait)
|
||||
ticks_to_wait = pdMS_TO_TICKS(50);
|
||||
|
||||
/* Prepare message */
|
||||
data_tx[0] = SPI_CMD_SOC_MSG;
|
||||
data_tx[1] = cmd;
|
||||
data_tx[2] = (param >> 16) & 0xff;
|
||||
data_tx[3] = (param >> 8) & 0xff;
|
||||
data_tx[4] = (param ) & 0xff;
|
||||
|
||||
/* Send message to PicoRV */
|
||||
res = ice40_send_turbo(ice40, data_tx, 5);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SoC message TX failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Poll until we get a response */
|
||||
data_tx[0] = SPI_CMD_RESP_ACK;
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
/* Poll */
|
||||
res = ice40_transaction(ice40, data_tx, 6, data_rx, 6);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SoC response RX failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Was response valid ? */
|
||||
if (data_rx[1] & 0x80)
|
||||
break;
|
||||
|
||||
/* Wait before retry */
|
||||
vTaskDelay(ticks_to_wait);
|
||||
}
|
||||
|
||||
if (!(data_rx[1] & 0x80)) {
|
||||
ESP_LOGE(TAG, "SoC response RX timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Report response */
|
||||
if (resp) {
|
||||
*resp = 0;
|
||||
for (int i=0; i<4; i++)
|
||||
*resp = (*resp << 8) | data_rx[2+i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Test routines */
|
||||
|
||||
static bool test_bitstream_load(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
esp_err_t res;
|
||||
|
||||
res = ice40_load_bitstream(ice40, fpga_selftest_bin_start, fpga_selftest_bin_end - fpga_selftest_bin_start);
|
||||
if (res != ESP_OK) {
|
||||
*rc = res;
|
||||
return false;
|
||||
}
|
||||
|
||||
*rc = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _test_spi_loopback_one(ICE40* ice40) {
|
||||
esp_err_t res;
|
||||
uint8_t data_tx[257];
|
||||
uint8_t data_rx[258];
|
||||
|
||||
/* Generate pseudo random sequence */
|
||||
data_tx[1] = 1;
|
||||
for (int i = 2; i < 257; i++)
|
||||
data_tx[i] = (data_tx[i-1] << 1) ^ ((data_tx[i-1] & 0x80) ? 0x1d : 0x00);
|
||||
|
||||
/* Send 256 bytes at high speed with echo command */
|
||||
data_tx[0] = SPI_CMD_LOOPBACK;
|
||||
|
||||
res = ice40_send_turbo(ice40, data_tx, 257);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 1 failed (Turbo TX)");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Execute full duplex transaction with next 128 bytes */
|
||||
res = ice40_transaction(ice40, data_tx, 257, data_rx, 257);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 2 failed (Full Duplex)");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate response present */
|
||||
if ((data_rx[1] & 0x80) == 0) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 2 reports no response available\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate RX data (only 254 byte got read) */
|
||||
if (memcmp(&data_rx[2], &data_tx[1], 254)) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 1->2 integrity fail:\n");
|
||||
for (int i = 0; i < 254; i++)
|
||||
printf("%02X%c", data_rx[i], ((i&0xf)==0xf) ? '\n' : ' ');
|
||||
printf("\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read two responses and ack them */
|
||||
for (int t = 0; t < 2; t++) {
|
||||
/* Receive half duplex */
|
||||
res = ice40_receive(ice40, data_rx, 258);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 3.%d failed (Half Duplex RX)", t);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Short acknowledge command */
|
||||
data_tx[0] = SPI_CMD_RESP_ACK;
|
||||
|
||||
res = ice40_send_turbo(ice40, data_tx, 1);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 4.%d failed (Turbo ACK)", t);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate response present */
|
||||
if ((data_rx[1] & 0x80) == 0) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 3.%d reports no response available\n", t);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate RX data (only 254 byte got read) */
|
||||
if (memcmp(&data_rx[2], &data_tx[1], 254)) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction %d->3.%d integrity fail:\n", 1+t, t);
|
||||
for (int i = 0; i < 254; i++)
|
||||
printf("%02X%c", data_rx[i], ((i&0xf)==0xf) ? '\n' : ' ');
|
||||
printf("\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check there is no more responses pending */
|
||||
data_tx[0] = SPI_CMD_NOP2;
|
||||
|
||||
res = ice40_transaction(ice40, data_tx, 2, data_rx, 2);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 5 failed (Full Duplex)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((data_rx[1] & 0x80) != 0) {
|
||||
ESP_LOGE(TAG, "SPI loopback transaction 5 reports response available\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_spi_loopback(uint32_t *rc) {
|
||||
int i;
|
||||
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
/* Run test 256 times */
|
||||
for (i=0; i<256; i++) {
|
||||
if (!_test_spi_loopback_one(ice40))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Failure ? */
|
||||
if (i != 256) {
|
||||
*rc = i + 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* OK ! */
|
||||
*rc = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_soc_loopback(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
/* Execute command */
|
||||
if (!soc_message(ice40, SOC_CMD_PING, SOC_CMD_PING_PARAM, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check response */
|
||||
if (*rc != SOC_CMD_PING_RESP)
|
||||
return false;
|
||||
|
||||
/* Success */
|
||||
*rc = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_uart_loopback(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
/* Enable loopback mode of RP2040 */
|
||||
rp2040_set_fpga_loopback(get_rp2040(), true, true);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
/* Execute command */
|
||||
if (!soc_message(ice40, SOC_CMD_UART_LOOPBACK_TEST, 0, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Disable loopback mode of RP2040 */
|
||||
rp2040_set_fpga_loopback(get_rp2040(), true, false);
|
||||
|
||||
/* Check response */
|
||||
return *rc == SOC_RESP_OK;
|
||||
}
|
||||
|
||||
static bool test_psram(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
/* Execute command */
|
||||
if (!soc_message(ice40, SOC_CMD_PSRAM_TEST, 0, rc, pdMS_TO_TICKS(1000))) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check response */
|
||||
return *rc == SOC_RESP_OK;
|
||||
}
|
||||
|
||||
static bool test_irq_n(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
esp_err_t res;
|
||||
|
||||
/* Set pin as input */
|
||||
res = gpio_set_direction(GPIO_INT_FPGA, GPIO_MODE_INPUT);
|
||||
if (res != ESP_OK) {
|
||||
*rc = 32;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Assert interrupt line */
|
||||
if (!soc_message(ice40, SOC_CMD_IRQN_SET, 1, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*rc != SOC_RESP_OK)
|
||||
return false;
|
||||
|
||||
/* Check level is 0 */
|
||||
if (gpio_get_level(GPIO_INT_FPGA) != 0) {
|
||||
*rc = 16;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Release interrupt line */
|
||||
if (!soc_message(ice40, SOC_CMD_IRQN_SET, 0, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*rc != SOC_RESP_OK)
|
||||
return false;
|
||||
|
||||
/* Check level is 1 */
|
||||
if (gpio_get_level(GPIO_INT_FPGA) != 1) {
|
||||
*rc = 16;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_lcd_mode(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
esp_err_t res;
|
||||
bool ok;
|
||||
|
||||
/* Defaults */
|
||||
ok = true;
|
||||
*rc = 0;
|
||||
|
||||
/* Check state is 0 */
|
||||
if (!soc_message(ice40, SOC_CMD_LCD_CHECK_MODE, 0, rc, 0)) {
|
||||
*rc = 16;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*rc != SOC_RESP_OK)
|
||||
return false;
|
||||
|
||||
/* Set LCD mode to 1 */
|
||||
res = gpio_set_level(GPIO_LCD_MODE, 1);
|
||||
if (res != ESP_OK) {
|
||||
*rc = 32;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check state is 1 */
|
||||
if (!soc_message(ice40, SOC_CMD_LCD_CHECK_MODE, 1, rc, 0)) {
|
||||
*rc = 17;
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (*rc != SOC_RESP_OK)
|
||||
ok = false;
|
||||
|
||||
/* Set LCD mode back to 0 */
|
||||
res = gpio_set_level(GPIO_LCD_MODE, 0);
|
||||
if (res != ESP_OK) {
|
||||
*rc = 33;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* All good */
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool test_pmod_open(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
/* Execute command */
|
||||
if (!soc_message(ice40, SOC_CMD_PMOD_OPEN_TEST, 0, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check response */
|
||||
return *rc == SOC_RESP_OK;
|
||||
}
|
||||
|
||||
static bool test_pmod_plug(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
/* Execute command */
|
||||
if (!soc_message(ice40, SOC_CMD_PMOD_PLUG_TEST, 0, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check response */
|
||||
return *rc == SOC_RESP_OK;
|
||||
}
|
||||
|
||||
static bool test_lcd_init(uint32_t *rc) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
|
||||
/* Execute command */
|
||||
if (!soc_message(ice40, SOC_CMD_LCD_INIT_TEST, 0, rc, 0)) {
|
||||
*rc = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check response */
|
||||
return *rc == SOC_RESP_OK;
|
||||
}
|
||||
|
||||
bool run_fpga_tests(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
ICE40* ice40 = get_ice40();
|
||||
const pax_font_t *font;
|
||||
int line = 0;
|
||||
bool ok = true;
|
||||
|
||||
/* Screen init */
|
||||
font = pax_get_font("sky mono");
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x8060f0);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
/* Run mandatory tests */
|
||||
RUN_TEST_MANDATORY("Bitstream load", test_bitstream_load);
|
||||
RUN_TEST_MANDATORY("SPI loopback", test_spi_loopback);
|
||||
RUN_TEST_MANDATORY("SoC loopback", test_soc_loopback);
|
||||
|
||||
/* Set indicator to "in-progress" */
|
||||
soc_message(ice40, SOC_CMD_RGB_STATE_SET, 1, NULL, 0);
|
||||
|
||||
/* Run non-interactive tests */
|
||||
RUN_TEST("UART loopback", test_uart_loopback);
|
||||
RUN_TEST("PSRAM", test_psram);
|
||||
RUN_TEST("IRQ_n signal", test_irq_n);
|
||||
RUN_TEST("LCD_MODE signal", test_lcd_mode);
|
||||
RUN_TEST("PMOD open", test_pmod_open);
|
||||
|
||||
/* Show instructions for interactive test */
|
||||
/*pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+ 0, "Insert PMOD plug");
|
||||
pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+10, "Then press button for interactive test");
|
||||
pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+20, " - Check LCD color bars");
|
||||
pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+30, " - Then LCD & RGB led color cycling");
|
||||
ili9341_write(ili9341, pax_buffer->buf);*/
|
||||
|
||||
/* Wait for button */
|
||||
//wait_button(buttonQueue);
|
||||
|
||||
/* Clear the instructions from buffer */
|
||||
//pax_draw_rect(pax_buffer, 0xff8060f0, 0, 20*line, 320, 240-20*line);
|
||||
|
||||
/* Handover LCD to FPGA */
|
||||
ili9341_deinit(ili9341);
|
||||
|
||||
/* Run interactive tests */
|
||||
//RUN_TEST("PMOD plug", test_pmod_plug);
|
||||
RUN_TEST("LCD init", test_lcd_init);
|
||||
|
||||
/* Wait a second (for user to see color bars) */
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
/* Start LCD / RGB cycling */
|
||||
soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 1, NULL, 0);
|
||||
|
||||
/* Wait for button */
|
||||
RUN_TEST("LCD control", test_wait_for_response);
|
||||
|
||||
/* Stop LCD / RGB cycling */
|
||||
soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 0, NULL, 0);
|
||||
|
||||
/* Take control of the LCD back and refresh screen */
|
||||
ili9341_init(ili9341);
|
||||
|
||||
error:
|
||||
/* Update indicator */
|
||||
soc_message(ice40, SOC_CMD_RGB_STATE_SET, ok ? 2 : 3, NULL, 0);
|
||||
|
||||
/* Pass / Fail result on screen */
|
||||
if (ok)
|
||||
pax_draw_text(pax_buffer, 0xff00ff00, font, 36, 0, 20*line, "PASS");
|
||||
else
|
||||
pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FAIL");
|
||||
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
/* Cleanup */
|
||||
ice40_disable(ice40);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void fpga_test(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
run_fpga_tests(buttonQueue, pax_buffer, ili9341);
|
||||
test_wait_for_response(NULL);
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
#include <string.h>
|
||||
#include "graphics_wrapper.h"
|
||||
#include "hardware.h"
|
||||
#include "pax_keyboard.h"
|
||||
#include "rp2040.h"
|
||||
|
||||
void render_message(pax_buf_t *pax_buffer, char* message, float aPosX, float aPosY, float aWidth, float aHeight) {
|
||||
pax_col_t fgColor = 0xFFFF0000;
|
||||
pax_col_t bgColor = 0xFFFFD4D4;
|
||||
pax_clip(pax_buffer, aPosX, aPosY, aWidth, aHeight);
|
||||
pax_simple_rect(pax_buffer, bgColor, aPosX, aPosY, aWidth, aHeight);
|
||||
pax_outline_rect(pax_buffer, fgColor, aPosX, aPosY, aWidth, aHeight);
|
||||
pax_clip(pax_buffer, aPosX + 1, aPosY + 1, aWidth - 2, aHeight - 2);
|
||||
pax_draw_text(pax_buffer, fgColor, NULL, 18, aPosX + 1, aPosY + 1, message);
|
||||
pax_noclip(pax_buffer);
|
||||
}
|
||||
|
||||
esp_err_t graphics_task(pax_buf_t* pax_buffer, ILI9341* ili9341, menu_t* menu, char* message) {
|
||||
pax_background(pax_buffer, 0xCCCCCC);
|
||||
if (menu != NULL) {
|
||||
menu_render(pax_buffer, menu, 10, 10, 320-20, 240-20, 0xFF000000);
|
||||
}
|
||||
|
||||
if (message != NULL) {
|
||||
render_message(pax_buffer, message, 20, 110, 320-40, 20);
|
||||
}
|
||||
|
||||
return ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
||||
|
||||
bool keyboard(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, float aPosX, float aPosY, float aWidth, float aHeight, const char* aTitle, const char* aHint, char* aOutput, size_t aOutputSize) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
bool accepted = false;
|
||||
pkb_ctx_t kb_ctx;
|
||||
pkb_init(pax_buffer, &kb_ctx, 1024);
|
||||
pkb_set_content(&kb_ctx, aOutput);
|
||||
kb_ctx.kb_font = font;
|
||||
kb_ctx.text_font = font;
|
||||
|
||||
pax_col_t fgColor = 0xFF000000;
|
||||
pax_col_t bgColor = 0xFFFFFFFF;
|
||||
pax_col_t shadowColor = 0xFFC0C3C8;
|
||||
pax_col_t borderColor = 0xFF0000AA;
|
||||
pax_col_t titleBgColor = 0xFF080764;
|
||||
pax_col_t titleColor = 0xFFFFFFFF;
|
||||
pax_col_t selColor = 0xff007fff;
|
||||
|
||||
kb_ctx.text_col = borderColor;
|
||||
kb_ctx.sel_text_col = bgColor;
|
||||
kb_ctx.sel_col = selColor;
|
||||
kb_ctx.bg_col = bgColor;
|
||||
|
||||
kb_ctx.kb_font_size = 18;
|
||||
|
||||
float titleHeight = 20;
|
||||
float hintHeight = 14;
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_simple_rect(pax_buffer, shadowColor, aPosX+5, aPosY+5, aWidth, aHeight);
|
||||
pax_simple_rect(pax_buffer, bgColor, aPosX, aPosY, aWidth, aHeight);
|
||||
pax_outline_rect(pax_buffer, borderColor, aPosX, aPosY, aWidth, aHeight);
|
||||
pax_simple_rect(pax_buffer, titleBgColor, aPosX, aPosY, aWidth, titleHeight);
|
||||
pax_simple_line(pax_buffer, titleColor, aPosX + 1, aPosY + titleHeight, aPosX + aWidth - 2, aPosY + titleHeight - 1);
|
||||
pax_clip(pax_buffer, aPosX + 1, aPosY + 1, aWidth - 2, titleHeight - 2);
|
||||
pax_draw_text(pax_buffer, titleColor, font, titleHeight - 2, aPosX + 1, aPosY + 1, aTitle);
|
||||
pax_clip(pax_buffer, aPosX + 1, aPosY + aHeight - hintHeight, aWidth - 2, hintHeight);
|
||||
pax_draw_text(pax_buffer, borderColor, font, hintHeight - 2, aPosX + 1, aPosY + aHeight - hintHeight, aHint);
|
||||
pax_noclip(pax_buffer);
|
||||
|
||||
kb_ctx.x = aPosX + 1;
|
||||
kb_ctx.y = aPosY + titleHeight + 1 ;
|
||||
kb_ctx.width = aWidth - 2;
|
||||
kb_ctx.height = aHeight - 3 - titleHeight - hintHeight;
|
||||
|
||||
bool running = true;
|
||||
while (running) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_DOWN);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_DOWN);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_UP);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_UP);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_LEFT:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_LEFT);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_LEFT);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_RIGHT:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_RIGHT);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_RIGHT);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_SHIFT);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_SHIFT);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_CHARSELECT);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_CHARSELECT);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_DELETE_BEFORE);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_DELETE_BEFORE);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
if (value) {
|
||||
pkb_press(&kb_ctx, PKB_MODESELECT);
|
||||
} else {
|
||||
pkb_release(&kb_ctx, PKB_MODESELECT);
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
if (value) {
|
||||
running = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pkb_loop(&kb_ctx);
|
||||
if (kb_ctx.dirty) {
|
||||
pkb_redraw(pax_buffer, &kb_ctx);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
||||
if (kb_ctx.input_accepted) {
|
||||
memset(aOutput, 0, aOutputSize);
|
||||
strncpy(aOutput, kb_ctx.content, aOutputSize - 1);
|
||||
running = false;
|
||||
accepted = true;
|
||||
}
|
||||
}
|
||||
pkb_destroy(&kb_ctx);
|
||||
return accepted;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
void test_adc(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,4 +0,0 @@
|
|||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void display_animation(pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
|
||||
esp_err_t appfs_init(void);
|
||||
uint8_t* load_file_to_ram(FILE* fd, size_t* fsize);
|
||||
void appfs_boot_app(int fd);
|
||||
void appfs_store_app(pax_buf_t* pax_buffer, ILI9341* ili9341, char* path, char* label);
|
|
@ -1,3 +0,0 @@
|
|||
#pragma once
|
||||
void audio_init();
|
||||
void play_bootsound();
|
|
@ -1,4 +0,0 @@
|
|||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void display_boot_screen(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* text);
|
|
@ -1,9 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
void test_buttons(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,10 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
void factory_test(pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,9 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
void list_files_in_folder(const char* path);
|
||||
void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* initial_path);
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "ice40.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
void fpga_download(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "hardware.h"
|
||||
#include "rp2040.h"
|
||||
#include "ili9341.h"
|
||||
#include "ice40.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
void fpga_test(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
||||
bool run_fpga_tests(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_system.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
#include "menu.h"
|
||||
|
||||
|
||||
esp_err_t graphics_task(pax_buf_t* pax_buffer, ILI9341* ili9341, menu_t* menu, char* message);
|
||||
bool keyboard(xQueueHandle buttonQueue, pax_buf_t* aBuffer, ILI9341* ili9341, float aPosX, float aPosY, float aWidth, float aHeight, const char* aTitle, const char* aHint, char* aOutput, size_t aOutputSize);
|
0
main/include/main.h
Normal file
|
@ -1,65 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "pax_gfx.h"
|
||||
|
||||
typedef bool (*menu_callback_t)();
|
||||
|
||||
typedef struct _menu_item {
|
||||
char* label;
|
||||
menu_callback_t callback;
|
||||
void* callbackArgs;
|
||||
|
||||
pax_buf_t* icon;
|
||||
|
||||
// Linked list
|
||||
struct _menu_item* previousItem;
|
||||
struct _menu_item* nextItem;
|
||||
} menu_item_t;
|
||||
|
||||
typedef struct menu {
|
||||
char* title;
|
||||
menu_item_t* firstItem;
|
||||
size_t length;
|
||||
size_t position;
|
||||
float entry_height;
|
||||
float text_height;
|
||||
pax_buf_t* icon;
|
||||
|
||||
pax_col_t fgColor;
|
||||
pax_col_t bgColor;
|
||||
pax_col_t selectedItemColor;
|
||||
pax_col_t bgTextColor;
|
||||
pax_col_t borderColor;
|
||||
pax_col_t titleColor;
|
||||
pax_col_t titleBgColor;
|
||||
pax_col_t scrollbarBgColor;
|
||||
pax_col_t scrollbarFgColor;
|
||||
|
||||
} menu_t;
|
||||
|
||||
menu_t* menu_alloc(const char* aTitle, float arg_entry_height, float arg_text_height);
|
||||
void menu_free(menu_t* aMenu);
|
||||
void menu_set_icon(menu_t* aMenu, pax_buf_t* icon);
|
||||
bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition);
|
||||
bool menu_insert_item_icon(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition, pax_buf_t* icon);
|
||||
bool menu_remove_item(menu_t* aMenu, size_t aPosition);
|
||||
bool menu_navigate_to(menu_t* aMenu, size_t aPosition);
|
||||
void menu_navigate_previous(menu_t* aMenu);
|
||||
void menu_navigate_next(menu_t* aMenu);
|
||||
size_t menu_get_position(menu_t* aMenu);
|
||||
size_t menu_get_length(menu_t* aMenu);
|
||||
void* menu_get_callback_args(menu_t* aMenu, size_t aPosition);
|
||||
void menu_debug(menu_t* aMenu);
|
||||
void menu_render(pax_buf_t *aBuffer, menu_t *aMenu, float aPosX, float aPosY, float aWidth, float aHeight, pax_col_t aColor);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_system.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
|
||||
void rp2040_updater(RP2040* rp2040, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "hardware.h"
|
||||
#include "rp2040.h"
|
||||
#include "ili9341.h"
|
||||
#include "ice40.h"
|
||||
|
||||
esp_err_t nvs_init();
|
||||
esp_err_t nvs_get_str_fixed(const char* nvs_namespace, const char* key, char* target, size_t target_size, size_t* size);
|
||||
uint8_t nvs_get_u8_default(const char* nvs_namespace, const char* key, uint8_t default_value);
|
||||
esp_err_t nvs_set_u8_fixed(const char* nvs_namespace, const char* key, uint8_t value);
|
|
@ -1,3 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
void restart();
|
|
@ -1,28 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
typedef bool (*test_fn)(uint32_t *rc);
|
||||
|
||||
bool test_wait_for_response(uint32_t *rc);
|
||||
bool run_test(pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, const char *test_name, test_fn fn);
|
||||
|
||||
#define RUN_TEST(name, fn) do {\
|
||||
ok &= run_test(pax_buffer, font, ili9341, line++, name, fn); \
|
||||
} while (0)
|
||||
|
||||
#define RUN_TEST_MANDATORY(name, fn) do {\
|
||||
if (!run_test(pax_buffer, font, ili9341, line++, name, fn)) { \
|
||||
pax_draw_text(pax_buffer, 0xffff0000, font, 18, 0, 20*line, "Aborted"); \
|
||||
ili9341_write(ili9341, pax_buffer->buf); \
|
||||
ok = false; \
|
||||
goto error; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RUN_TEST_BLIND(name, fn) do {\
|
||||
ok &= run_test(pax_buffer, font, NULL, line++, name, fn); \
|
||||
} while (0)
|
|
@ -1,8 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void uninstall_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "ice40.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
void webusb_main(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool wifi_connect_to_stored();
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_types.h"
|
||||
#include "esp_wpa2.h"
|
||||
|
||||
#define WIFI_MCH2022_SSID "MCH2022"
|
||||
#define WIFI_MCH2022_USER "mch2022"
|
||||
#define WIFI_MCH2022_IDENT "mch2022"
|
||||
#define WIFI_MCH2022_PASSWORD "mch2022"
|
||||
#define WIFI_MCH2022_AUTH WIFI_AUTH_WPA2_ENTERPRISE
|
||||
#define WIFI_MCH2022_PHASE2 ESP_EAP_TTLS_PHASE2_MSCHAPV2
|
||||
|
||||
// Simpler interpretation of WiFi signal strength.
|
||||
typedef enum {
|
||||
WIFI_STRENGTH_VERY_BAD,
|
||||
WIFI_STRENGTH_BAD,
|
||||
WIFI_STRENGTH_GOOD,
|
||||
WIFI_STRENGTH_VERY_GOOD,
|
||||
} wifi_strength_t;
|
||||
|
||||
// Thresholds for aforementioned signal strength definitions.
|
||||
#define WIFI_THRESH_BAD -80
|
||||
#define WIFI_THRESH_GOOD -70
|
||||
#define WIFI_THRESH_VERY_GOOD -67
|
||||
|
||||
// Firt time initialisation of the WiFi stack.
|
||||
void wifi_init();
|
||||
|
||||
// Connect to a traditional username/password WiFi network.
|
||||
bool wifi_connect(const char* aSsid, const char* aPassword, wifi_auth_mode_t aAuthmode, uint8_t aRetryMax);
|
||||
|
||||
// Connect to a WPA2 enterprise WiFi network.
|
||||
bool wifi_connect_ent(const char* aSsid, const char *aIdent, const char *aAnonIdent, const char* aPassword, esp_eap_ttls_phase2_types phase2, uint8_t aRetryMax);
|
||||
|
||||
// Scan for WiFi networks.
|
||||
// Updates the APs pointer if non-null.
|
||||
// Returns the number of APs found.
|
||||
size_t wifi_scan(wifi_ap_record_t **aps);
|
||||
|
||||
// Get the strength value for a given RSSI.
|
||||
wifi_strength_t wifi_rssi_to_strength(int8_t rssi);
|
|
@ -1,6 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341);
|
317
main/main.c
|
@ -1,295 +1,38 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include "driver/uart.h"
|
||||
|
||||
// This file contains a simple hello world app which you can base you own apps on.
|
||||
|
||||
#include "main.h"
|
||||
#include "hardware.h"
|
||||
#include "managed_i2c.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "sdcard.h"
|
||||
#include "appfs.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "rp2040.h"
|
||||
#include "rp2040bl.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "ili9341.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#include "fpga_test.h"
|
||||
pax_buf_t buf;
|
||||
xQueueHandle buttonQueue;
|
||||
|
||||
#include "menu.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "graphics_wrapper.h"
|
||||
#include "appfs_wrapper.h"
|
||||
#include "settings.h"
|
||||
#include "wifi_connection.h"
|
||||
#include "rp2040_updater.h"
|
||||
static const char *TAG = "mch2022-demo-app";
|
||||
|
||||
#include "ws2812.h"
|
||||
|
||||
#include "esp32/rom/crc.h"
|
||||
|
||||
#include "efuse.h"
|
||||
|
||||
#include "wifi_ota.h"
|
||||
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
|
||||
#include <pax_codecs.h>
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
#include "bootscreen.h"
|
||||
|
||||
#include "menus/start.h"
|
||||
|
||||
#include "factory_test.h"
|
||||
#include "fpga_download.h"
|
||||
#include "webusb.h"
|
||||
|
||||
extern const uint8_t wallpaper_png_start[] asm("_binary_wallpaper_png_start");
|
||||
extern const uint8_t wallpaper_png_end[] asm("_binary_wallpaper_png_end");
|
||||
|
||||
extern const uint8_t logo_screen_png_start[] asm("_binary_logo_screen_png_start");
|
||||
extern const uint8_t logo_screen_png_end[] asm("_binary_logo_screen_png_end");
|
||||
|
||||
|
||||
static const char *TAG = "main";
|
||||
|
||||
void display_fatal_error(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* line0, const char* line1, const char* line2, const char* line3) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
if (line0 != NULL) pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, line0);
|
||||
if (line1 != NULL) pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 12, 0, 20*1, line1);
|
||||
if (line2 != NULL) pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 12, 0, 20*2, line2);
|
||||
if (line3 != NULL) pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 12, 0, 20*3, line3);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
void disp_flush() {
|
||||
ili9341_write(get_ili9341(), buf.buf);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
ESP_LOGW(TAG, "*** HALTED ***");
|
||||
gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(GPIO_SD_PWR, 1);
|
||||
ws2812_init(GPIO_LED_DATA);
|
||||
uint8_t led_off[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t led_red[15] = {0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0};
|
||||
uint8_t led_red2[15] = {0, 0xFF, 0, 0, 0xFF, 0, 0, 0xFF, 0, 0, 0xFF, 0, 0, 0xFF, 0};
|
||||
while (true) {
|
||||
ws2812_send_data(led_red2, sizeof(led_red2));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
ws2812_send_data(led_red, sizeof(led_red));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
ws2812_send_data(led_off, sizeof(led_off));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void) {
|
||||
esp_err_t res;
|
||||
|
||||
audio_init();
|
||||
|
||||
const esp_app_desc_t *app_description = esp_ota_get_app_description();
|
||||
ESP_LOGI(TAG, "App version: %s", app_description->version);
|
||||
//ESP_LOGI(TAG, "Project name: %s", app_description->project_name);
|
||||
|
||||
/* Initialize memory */
|
||||
uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT);
|
||||
if (framebuffer == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate framebuffer");
|
||||
esp_restart();
|
||||
}
|
||||
memset(framebuffer, 0, ILI9341_BUFFER_SIZE);
|
||||
|
||||
pax_buf_t* pax_buffer = malloc(sizeof(pax_buf_t));
|
||||
if (framebuffer == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate buffer for PAX graphics library");
|
||||
esp_restart();
|
||||
}
|
||||
memset(pax_buffer, 0, sizeof(pax_buf_t));
|
||||
|
||||
pax_buf_init(pax_buffer, framebuffer, ILI9341_WIDTH, ILI9341_HEIGHT, PAX_BUF_16_565RGB);
|
||||
|
||||
/* Initialize hardware */
|
||||
|
||||
efuse_protect();
|
||||
|
||||
if (bsp_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize basic board support functions");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
ILI9341* ili9341 = get_ili9341();
|
||||
if (ili9341 == NULL) {
|
||||
ESP_LOGE(TAG, "ili9341 is NULL");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/* Start NVS */
|
||||
res = nvs_init();
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS init failed: %d", res);
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "NVS failed to initialize", "Flash may be corrupted", NULL);
|
||||
stop();
|
||||
}
|
||||
|
||||
display_boot_screen(pax_buffer, ili9341, "Starting...");
|
||||
|
||||
if (bsp_rp2040_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize the RP2040 co-processor");
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "RP2040 co-processor error", NULL, NULL);
|
||||
stop();
|
||||
}
|
||||
|
||||
RP2040* rp2040 = get_rp2040();
|
||||
if (rp2040 == NULL) {
|
||||
ESP_LOGE(TAG, "rp2040 is NULL");
|
||||
stop();
|
||||
}
|
||||
|
||||
rp2040_updater(rp2040, pax_buffer, ili9341); // Handle RP2040 firmware update & bootloader mode
|
||||
|
||||
factory_test(pax_buffer, ili9341);
|
||||
|
||||
/*uint8_t rp2040_uid[8];
|
||||
if (rp2040_get_uid(rp2040, rp2040_uid) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get RP2040 UID");
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "Failed to read UID", NULL, NULL);
|
||||
stop();
|
||||
}
|
||||
|
||||
printf("RP2040 UID: %02X%02X%02X%02X%02X%02X%02X%02X\n", rp2040_uid[0], rp2040_uid[1], rp2040_uid[2], rp2040_uid[3], rp2040_uid[4], rp2040_uid[5], rp2040_uid[6], rp2040_uid[7]);*/
|
||||
|
||||
if (bsp_ice40_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize the ICE40 FPGA");
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "ICE40 FPGA error", NULL, NULL);
|
||||
stop();
|
||||
}
|
||||
|
||||
ICE40* ice40 = get_ice40();
|
||||
if (ice40 == NULL) {
|
||||
ESP_LOGE(TAG, "ice40 is NULL");
|
||||
stop();
|
||||
}
|
||||
|
||||
/*display_boot_screen(pax_buffer, ili9341, "Initializing BNO055...");
|
||||
|
||||
if (bsp_bno055_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize the BNO055 position sensor");
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "BNO055 sensor error", "Check I2C bus", "Remove SAO and try again");
|
||||
stop();
|
||||
}
|
||||
|
||||
BNO055* bno055 = get_bno055();
|
||||
if (bno055 == NULL) {
|
||||
ESP_LOGE(TAG, "bno055 is NULL");
|
||||
stop();
|
||||
}*/
|
||||
|
||||
/*display_boot_screen(pax_buffer, ili9341, "Initializing BME680...");
|
||||
|
||||
if (bsp_bme680_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize the BME680 position sensor");
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "BME680 sensor error", "Check I2C bus", "Remove SAO and try again");
|
||||
stop();
|
||||
}
|
||||
|
||||
BME680* bme680 = get_bme680();
|
||||
if (bme680 == NULL) {
|
||||
ESP_LOGE(TAG, "bme680 is NULL");
|
||||
stop();
|
||||
}*/
|
||||
|
||||
//display_boot_screen(pax_buffer, ili9341, "Initializing AppFS...");
|
||||
|
||||
/* Start AppFS */
|
||||
res = appfs_init();
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "AppFS init failed: %d", res);
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "AppFS failed to initialize", "Flash may be corrupted", NULL);
|
||||
stop();
|
||||
}
|
||||
|
||||
//display_boot_screen(pax_buffer, ili9341, "Initializing filesystem...");
|
||||
|
||||
/* Start internal filesystem */
|
||||
const esp_partition_t* fs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "locfd");
|
||||
|
||||
wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
|
||||
|
||||
if (fs_partition != NULL) {
|
||||
const esp_vfs_fat_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 0,
|
||||
};
|
||||
esp_err_t res = esp_vfs_fat_spiflash_mount("/internal", "locfd", &mount_config, &s_wl_handle);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to mount locfd (%d)", res);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Internal filesystem mounted");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "locfd partition not found");
|
||||
}
|
||||
|
||||
/* Start SD card filesystem */
|
||||
res = mount_sd(GPIO_SD_CMD, GPIO_SD_CLK, GPIO_SD_D0, GPIO_SD_PWR, "/sd", false, 5);
|
||||
bool sdcard_ready = (res == ESP_OK);
|
||||
if (sdcard_ready) {
|
||||
ESP_LOGI(TAG, "SD card filesystem mounted");
|
||||
|
||||
/* LED power is on: start LED driver and turn LEDs off */
|
||||
ws2812_init(GPIO_LED_DATA);
|
||||
const uint8_t led_off[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
ws2812_send_data(led_off, sizeof(led_off));
|
||||
} else {
|
||||
gpio_set_level(GPIO_SD_PWR, 0); // Disable power to LEDs and SD card
|
||||
}
|
||||
|
||||
/* Start WiFi */
|
||||
wifi_init();
|
||||
|
||||
/* Check WebUSB mode */
|
||||
|
||||
uint8_t webusb_mode;
|
||||
res = rp2040_get_webusb_mode(rp2040, &webusb_mode);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read WebUSB mode: %d", res);
|
||||
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "Failed to read WebUSB mode", NULL, NULL);
|
||||
stop();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "WebUSB mode 0x%02X", webusb_mode);
|
||||
|
||||
if (webusb_mode == 0x00) { // Normal boot
|
||||
/* Rick that roll */
|
||||
play_bootsound();
|
||||
|
||||
/* Launcher menu */
|
||||
while (true) {
|
||||
menu_start(rp2040->queue, pax_buffer, ili9341, app_description->version);
|
||||
}
|
||||
} else if (webusb_mode == 0x01) {
|
||||
display_boot_screen(pax_buffer, ili9341, "WebUSB mode");
|
||||
while (true) {
|
||||
webusb_main(rp2040->queue, pax_buffer, ili9341);
|
||||
}
|
||||
} else if (webusb_mode == 0x02) {
|
||||
display_boot_screen(pax_buffer, ili9341, "FPGA download mode");
|
||||
while (true) {
|
||||
fpga_download(rp2040->queue, get_ice40(), pax_buffer, ili9341);
|
||||
}
|
||||
} else {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "Invalid mode 0x%02X", webusb_mode);
|
||||
display_boot_screen(pax_buffer, ili9341, buffer);
|
||||
}
|
||||
|
||||
free(framebuffer);
|
||||
void app_main() {
|
||||
// Init HW.
|
||||
bsp_init();
|
||||
bsp_rp2040_init();
|
||||
buttonQueue = get_rp2040()->queue;
|
||||
|
||||
// Init GFX.
|
||||
pax_buf_init(&buf, NULL, 320, 240, PAX_BUF_16_565RGB);
|
||||
|
||||
// Show some hello world.
|
||||
pax_background(&buf, 0xffff00ff);
|
||||
disp_flush();
|
||||
|
||||
// Just wait.
|
||||
while (1) vTaskDelay(10000);
|
||||
}
|
||||
|
|
296
main/menu.c
|
@ -1,296 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "menu.h"
|
||||
|
||||
menu_t* menu_alloc(const char* aTitle, float arg_entry_height, float arg_text_height) {
|
||||
if (aTitle == NULL) return NULL;
|
||||
menu_t* menu = malloc(sizeof(menu_t));
|
||||
if (menu == NULL) return NULL;
|
||||
size_t titleSize = strlen(aTitle) + 1;
|
||||
menu->title = malloc(titleSize);
|
||||
if (menu->title == NULL) {
|
||||
free(menu);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(menu->title, aTitle, titleSize);
|
||||
menu->firstItem = NULL;
|
||||
menu->length = 0;
|
||||
menu->position = 0;
|
||||
menu->entry_height = (arg_entry_height > 0) ? arg_entry_height : 20;
|
||||
menu->text_height = (arg_text_height > 0) ? arg_text_height : (arg_entry_height - 2);
|
||||
menu->icon = NULL;
|
||||
|
||||
menu->fgColor = 0xFF000000;
|
||||
menu->bgColor = 0xFFFFFFFF;
|
||||
menu->bgTextColor = 0xFFFFFFFF;
|
||||
menu->selectedItemColor = 0xFF000000;
|
||||
menu->borderColor = 0x88000000;
|
||||
menu->titleColor = 0xFFFFFFFF;
|
||||
menu->titleBgColor = 0xFF000000;
|
||||
menu->scrollbarBgColor = 0xFFCCCCCC;
|
||||
menu->scrollbarFgColor = 0xFF555555;
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
void _menu_free_item(menu_item_t* aMenuItem) {
|
||||
free(aMenuItem->label);
|
||||
free(aMenuItem);
|
||||
}
|
||||
|
||||
void menu_free(menu_t* aMenu) {
|
||||
if (aMenu == NULL) return;
|
||||
free(aMenu->title);
|
||||
menu_item_t* currentItem = aMenu->firstItem;
|
||||
while (currentItem != NULL) {
|
||||
menu_item_t* nextItem = currentItem->nextItem;
|
||||
_menu_free_item(currentItem);
|
||||
currentItem = nextItem;
|
||||
}
|
||||
free(aMenu);
|
||||
}
|
||||
|
||||
void menu_set_icon(menu_t* aMenu, pax_buf_t* icon) {
|
||||
aMenu->icon = icon;
|
||||
}
|
||||
|
||||
menu_item_t* _menu_find_item(menu_t* aMenu, size_t aPosition) {
|
||||
menu_item_t* currentItem = aMenu->firstItem;
|
||||
if (currentItem == NULL) return NULL;
|
||||
size_t index = 0;
|
||||
while (index < aPosition) {
|
||||
if (currentItem->nextItem == NULL) break;
|
||||
currentItem = currentItem->nextItem;
|
||||
index++;
|
||||
}
|
||||
return currentItem;
|
||||
}
|
||||
|
||||
menu_item_t* _menu_find_last_item(menu_t* aMenu) {
|
||||
menu_item_t* lastItem = aMenu->firstItem;
|
||||
if (lastItem == NULL) return NULL;
|
||||
while (lastItem->nextItem != NULL) {
|
||||
lastItem = lastItem->nextItem;
|
||||
}
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition) {
|
||||
if (aMenu == NULL) return false;
|
||||
menu_item_t* newItem = malloc(sizeof(menu_item_t));
|
||||
if (newItem == NULL) return false;
|
||||
size_t labelSize = strlen(aLabel) + 1;
|
||||
newItem->label = malloc(labelSize);
|
||||
if (newItem->label == NULL) {
|
||||
free(newItem);
|
||||
return false;
|
||||
}
|
||||
memcpy(newItem->label, aLabel, labelSize);
|
||||
newItem->callback = aCallback;
|
||||
newItem->callbackArgs = aCallbackArgs;
|
||||
newItem->icon = NULL;
|
||||
if (aMenu->firstItem == NULL) {
|
||||
newItem->nextItem = NULL;
|
||||
newItem->previousItem = NULL;
|
||||
aMenu->firstItem = newItem;
|
||||
} else {
|
||||
if (aPosition >= aMenu->length) {
|
||||
newItem->previousItem = _menu_find_last_item(aMenu);
|
||||
newItem->nextItem = NULL;
|
||||
newItem->previousItem->nextItem = newItem;
|
||||
} else {
|
||||
newItem->nextItem = _menu_find_item(aMenu, aPosition);
|
||||
newItem->previousItem = newItem->nextItem->previousItem; // Copy pointer to previous item to new item
|
||||
if (newItem->nextItem != NULL) newItem->nextItem->previousItem = newItem; // Replace pointer to previous item with new item
|
||||
if (newItem->previousItem != NULL) newItem->previousItem->nextItem = newItem; // Replace pointer to next item in previous item
|
||||
}
|
||||
}
|
||||
aMenu->length++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menu_insert_item_icon(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition, pax_buf_t* icon) {
|
||||
if (!menu_insert_item(aMenu, aLabel, aCallback, aCallbackArgs, aPosition)) {
|
||||
return false;
|
||||
}
|
||||
menu_item_t* item;
|
||||
if (aPosition >= aMenu->length - 1) {
|
||||
item = _menu_find_last_item(aMenu);
|
||||
} else {
|
||||
item = _menu_find_item(aMenu, aPosition);
|
||||
}
|
||||
|
||||
item->icon = icon;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menu_remove_item(menu_t* aMenu, size_t aPosition) {
|
||||
if (aMenu == NULL) return false; // Can't delete an item from a menu that doesn't exist
|
||||
if (aMenu->length <= aPosition) return false; // Can't delete an item that doesn't exist
|
||||
menu_item_t* item;
|
||||
|
||||
if (aPosition == 0) {
|
||||
item = aMenu->firstItem;
|
||||
if (item == NULL) return false; // Can't delete if no linked list is allocated
|
||||
if (item->nextItem != NULL) {
|
||||
aMenu->firstItem = item->nextItem;
|
||||
aMenu->firstItem->previousItem = NULL;
|
||||
} else {
|
||||
aMenu->firstItem = NULL;
|
||||
}
|
||||
} else {
|
||||
item = _menu_find_item(aMenu, aPosition);
|
||||
if (item == NULL) return false;
|
||||
if (item->previousItem != NULL) item->previousItem->nextItem = item->nextItem;
|
||||
if (item->nextItem != NULL) item->nextItem->previousItem = item->previousItem;
|
||||
}
|
||||
free(item->label);
|
||||
free(item);
|
||||
aMenu->length--;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menu_navigate_to(menu_t* aMenu, size_t aPosition) {
|
||||
if (aMenu == NULL) return false;
|
||||
if (aMenu->length < 1) return false;
|
||||
aMenu->position = aPosition;
|
||||
if (aMenu->position >= aMenu->length) aMenu->position = aMenu->length - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void menu_navigate_previous(menu_t* aMenu) {
|
||||
if (aMenu == NULL) return;
|
||||
if (aMenu->length < 1) return;
|
||||
aMenu->position--;
|
||||
if (aMenu->position > aMenu->length) {
|
||||
aMenu->position = aMenu->length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_navigate_next(menu_t* aMenu) {
|
||||
if (aMenu == NULL) return;
|
||||
if (aMenu->length < 1) return;
|
||||
aMenu->position = (aMenu->position + 1) % aMenu->length;
|
||||
}
|
||||
|
||||
size_t menu_get_position(menu_t* aMenu) {
|
||||
return aMenu->position;
|
||||
}
|
||||
|
||||
size_t menu_get_length(menu_t* aMenu) {
|
||||
return aMenu->length;
|
||||
}
|
||||
|
||||
void* menu_get_callback_args(menu_t* aMenu, size_t aPosition) {
|
||||
menu_item_t* item = _menu_find_item(aMenu, aPosition);
|
||||
if (item == NULL) return NULL;
|
||||
return item->callbackArgs;
|
||||
}
|
||||
|
||||
void menu_debug(menu_t* aMenu) {
|
||||
if (aMenu == NULL) {
|
||||
printf("Menu pointer is NULL\n");
|
||||
return;
|
||||
}
|
||||
printf("Title: %s\n", aMenu->title);
|
||||
printf("Length: %u\n", aMenu->length);
|
||||
printf("Position: %u\n", aMenu->position);
|
||||
menu_item_t* item = aMenu->firstItem;
|
||||
if (item == NULL) {
|
||||
printf("Menu contains no items\n");
|
||||
} else {
|
||||
while (item != NULL) {
|
||||
printf("> %s\n", item->label);
|
||||
item = item->nextItem;
|
||||
}
|
||||
}
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, float aWidth, float aHeight, pax_col_t aColor) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
|
||||
float entry_height = aMenu->entry_height;//18 + 2;
|
||||
float text_height = aMenu->text_height;
|
||||
float text_offset = ((entry_height - text_height) / 2) + 1;
|
||||
size_t maxItems = aHeight / entry_height;
|
||||
|
||||
float posY = aPosY;
|
||||
|
||||
pax_noclip(aBuffer);
|
||||
|
||||
if (maxItems > 1) {
|
||||
float offsetX = 0;
|
||||
if (aMenu->icon != NULL) {
|
||||
offsetX = aMenu->icon->width;
|
||||
}
|
||||
|
||||
maxItems--;
|
||||
pax_simple_rect(aBuffer, aMenu->titleBgColor, aPosX, posY, aWidth, entry_height);
|
||||
//pax_simple_line(aBuffer, aMenu->titleColor, aPosX + 1, aPosY + entry_height, aPosX + aWidth - 2, aPosY + entry_height - 1);
|
||||
pax_clip(aBuffer, aPosX + 1, posY + text_offset, aWidth - 2, text_height);
|
||||
pax_draw_text(aBuffer, aMenu->titleColor, font, text_height, aPosX + offsetX + 1, posY + text_offset, aMenu->title);
|
||||
pax_noclip(aBuffer);
|
||||
if (aMenu->icon != NULL) {
|
||||
pax_draw_image(aBuffer, aMenu->icon, aPosX, posY);
|
||||
}
|
||||
posY += entry_height;
|
||||
}
|
||||
|
||||
size_t itemOffset = 0;
|
||||
if (aMenu->position >= maxItems) {
|
||||
itemOffset = aMenu->position - maxItems + 1;
|
||||
}
|
||||
|
||||
pax_outline_rect(aBuffer, aMenu->borderColor, aPosX, aPosY, aWidth, aHeight);
|
||||
pax_simple_rect(aBuffer, aMenu->bgColor, aPosX, posY, aWidth, aHeight - posY + aPosY);
|
||||
|
||||
for (size_t index = itemOffset; (index < itemOffset + maxItems) && (index < aMenu->length); index++) {
|
||||
menu_item_t* item = _menu_find_item(aMenu, index);
|
||||
if (item == NULL) {
|
||||
printf("Render error: item is NULL at %u\n", index);
|
||||
break;
|
||||
}
|
||||
|
||||
float iconWidth = 0;
|
||||
if (item->icon != NULL) {
|
||||
iconWidth = item->icon->width + 1;
|
||||
}
|
||||
|
||||
if (index == aMenu->position) {
|
||||
pax_simple_rect(aBuffer, aMenu->selectedItemColor, aPosX + 1, posY, aWidth - 2, entry_height);
|
||||
pax_clip(aBuffer, aPosX + 1, posY + text_offset, aWidth - 4, text_height);
|
||||
pax_draw_text(aBuffer, aMenu->bgTextColor, font, text_height, aPosX + iconWidth + 1, posY + text_offset, item->label);
|
||||
pax_noclip(aBuffer);
|
||||
} else {
|
||||
pax_simple_rect(aBuffer, aMenu->bgColor, aPosX + 1, posY, aWidth - 2, entry_height);
|
||||
pax_clip(aBuffer, aPosX + 1, posY + text_offset, aWidth - 4, text_height);
|
||||
pax_draw_text(aBuffer, aMenu->fgColor, font, text_height, aPosX + iconWidth + 1, posY + text_offset, item->label);
|
||||
pax_noclip(aBuffer);
|
||||
}
|
||||
|
||||
if (item->icon != NULL) {
|
||||
pax_draw_image(aBuffer, item->icon, aPosX + 1, posY);
|
||||
}
|
||||
|
||||
posY += entry_height;
|
||||
}
|
||||
|
||||
pax_clip(aBuffer, aPosX + aWidth - 5, aPosY + entry_height, 4, aHeight - 1 - entry_height);
|
||||
|
||||
float fractionStart = itemOffset / (aMenu->length * 1.0);
|
||||
float fractionSelected = aMenu->position / (aMenu->length * 1.0);
|
||||
float fractionEnd = (itemOffset + maxItems) / (aMenu->length * 1.0);
|
||||
if (fractionEnd > 1.0) fractionEnd = 1.0;
|
||||
|
||||
float scrollbarHeight = aHeight - entry_height;
|
||||
float scrollbarStart = scrollbarHeight * fractionStart;
|
||||
float scrollbarEnd = scrollbarHeight * fractionEnd;
|
||||
|
||||
pax_simple_rect(aBuffer, aMenu->scrollbarBgColor, aPosX + aWidth - 5, aPosY + entry_height - 1, 4, scrollbarHeight);
|
||||
pax_simple_rect(aBuffer, aMenu->scrollbarFgColor, aPosX + aWidth - 5, aPosY + entry_height - 1 + scrollbarStart, 4, scrollbarEnd - scrollbarStart);
|
||||
|
||||
pax_noclip(aBuffer);
|
||||
}
|
150
main/menus/dev.c
|
@ -1,150 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "launcher.h"
|
||||
#include "settings.h"
|
||||
#include "dev.h"
|
||||
#include "fpga_download.h"
|
||||
#include "hardware.h"
|
||||
#include "file_browser.h"
|
||||
#include "fpga_test.h"
|
||||
#include "animation.h"
|
||||
#include "button_test.h"
|
||||
#include "adc_test.h"
|
||||
|
||||
extern const uint8_t dev_png_start[] asm("_binary_dev_png_start");
|
||||
extern const uint8_t dev_png_end[] asm("_binary_dev_png_end");
|
||||
|
||||
typedef enum action {
|
||||
ACTION_NONE,
|
||||
ACTION_BACK,
|
||||
ACTION_FPGA_DL,
|
||||
ACTION_FPGA_TEST,
|
||||
ACTION_FILE_BROWSER,
|
||||
ACTION_FILE_BROWSER_INT,
|
||||
ACTION_ANIMATION,
|
||||
ACTION_BUTTON_TEST,
|
||||
ACTION_ADC_TEST
|
||||
} menu_dev_action_t;
|
||||
|
||||
void render_dev_help(pax_buf_t* pax_buffer) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] accept [B] back");
|
||||
}
|
||||
|
||||
void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
menu_t* menu = menu_alloc("Development tools", 34, 18);
|
||||
|
||||
menu->fgColor = 0xFF000000;
|
||||
menu->bgColor = 0xFFFFFFFF;
|
||||
menu->bgTextColor = 0xFF000000;
|
||||
menu->selectedItemColor = 0xFFfec859;
|
||||
menu->borderColor = 0xFFfa448c;
|
||||
menu->titleColor = 0xFFfec859;
|
||||
menu->titleBgColor = 0xFFfa448c;
|
||||
menu->scrollbarBgColor = 0xFFCCCCCC;
|
||||
menu->scrollbarFgColor = 0xFF555555;
|
||||
|
||||
pax_buf_t icon_dev;
|
||||
pax_decode_png_buf(&icon_dev, (void*) dev_png_start, dev_png_end - dev_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
|
||||
menu_set_icon(menu, &icon_dev);
|
||||
|
||||
menu_insert_item(menu, "FPGA download mode", NULL, (void*) ACTION_FPGA_DL, -1);
|
||||
menu_insert_item(menu, "FPGA selftest", NULL, (void*) ACTION_FPGA_TEST, -1);
|
||||
menu_insert_item(menu, "File browser (SD card)", NULL, (void*) ACTION_FILE_BROWSER, -1);
|
||||
menu_insert_item(menu, "File browser (internal)", NULL, (void*) ACTION_FILE_BROWSER_INT, -1);
|
||||
menu_insert_item(menu, "Animation", NULL, (void*) ACTION_ANIMATION, -1);
|
||||
menu_insert_item(menu, "Button test", NULL, (void*) ACTION_BUTTON_TEST, -1);
|
||||
menu_insert_item(menu, "Analog inputs", NULL, (void*) ACTION_ADC_TEST, -1);
|
||||
|
||||
bool render = true;
|
||||
menu_dev_action_t action = ACTION_NONE;
|
||||
|
||||
render_dev_help(pax_buffer);
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
action = ACTION_BACK;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
action = (menu_dev_action_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
if (action == ACTION_FPGA_DL) {
|
||||
fpga_download(buttonQueue, get_ice40(), pax_buffer, ili9341);
|
||||
} else if (action == ACTION_FPGA_TEST) {
|
||||
fpga_test(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_FILE_BROWSER) {
|
||||
file_browser(buttonQueue, pax_buffer, ili9341, "/sd");
|
||||
} else if (action == ACTION_FILE_BROWSER_INT) {
|
||||
file_browser(buttonQueue, pax_buffer, ili9341, "/internal");
|
||||
} else if (action == ACTION_ANIMATION) {
|
||||
display_animation(pax_buffer, ili9341);
|
||||
} else if (action == ACTION_BUTTON_TEST) {
|
||||
test_buttons(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_ADC_TEST) {
|
||||
test_adc(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_BACK) {
|
||||
break;
|
||||
}
|
||||
action = ACTION_NONE;
|
||||
render = true;
|
||||
render_dev_help(pax_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
|
||||
pax_buf_destroy(&icon_dev);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,134 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "appfs_wrapper.h"
|
||||
|
||||
extern const uint8_t apps_png_start[] asm("_binary_apps_png_start");
|
||||
extern const uint8_t apps_png_end[] asm("_binary_apps_png_end");
|
||||
|
||||
typedef enum {
|
||||
ACTION_NONE,
|
||||
ACTION_APPFS,
|
||||
ACTION_BACK
|
||||
} menu_launcher_action_t;
|
||||
|
||||
typedef struct {
|
||||
appfs_handle_t fd;
|
||||
menu_launcher_action_t action;
|
||||
} menu_launcher_args_t;
|
||||
|
||||
void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
menu_t* menu = menu_alloc("Apps", 34, 18);
|
||||
|
||||
menu->fgColor = 0xFF000000;
|
||||
menu->bgColor = 0xFFFFFFFF;
|
||||
menu->bgTextColor = 0xFFFFFFFF;
|
||||
menu->selectedItemColor = 0xFFfa448c;
|
||||
menu->borderColor = 0xFF491d88;
|
||||
menu->titleColor = 0xFFfa448c;
|
||||
menu->titleBgColor = 0xFF491d88;
|
||||
menu->scrollbarBgColor = 0xFFCCCCCC;
|
||||
menu->scrollbarFgColor = 0xFF555555;
|
||||
|
||||
pax_buf_t icon_apps;
|
||||
pax_decode_png_buf(&icon_apps, (void*) apps_png_start, apps_png_end - apps_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
|
||||
menu_set_icon(menu, &icon_apps);
|
||||
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
|
||||
appfs_handle_t appfs_fd = APPFS_INVALID_FD;
|
||||
while (1) {
|
||||
appfs_fd = appfsNextEntry(appfs_fd);
|
||||
if (appfs_fd == APPFS_INVALID_FD) break;
|
||||
const char* name = NULL;
|
||||
appfsEntryInfo(appfs_fd, &name, NULL);
|
||||
menu_launcher_args_t* args = malloc(sizeof(menu_launcher_args_t));
|
||||
args->fd = appfs_fd;
|
||||
args->action = ACTION_APPFS;
|
||||
menu_insert_item(menu, name, NULL, (void*) args, -1);
|
||||
}
|
||||
|
||||
bool render = true;
|
||||
menu_launcher_args_t* menuArgs = NULL;
|
||||
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] start app [B] back");
|
||||
|
||||
bool quit = false;
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
quit = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
menuArgs = menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (menuArgs != NULL) {
|
||||
if (menuArgs->action == ACTION_APPFS) {
|
||||
appfs_boot_app(menuArgs->fd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < menu_get_length(menu); index++) {
|
||||
free(menu_get_callback_args(menu, index));
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
pax_buf_destroy(&icon_apps);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,144 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "appfs_wrapper.h"
|
||||
#include "hardware.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "bootscreen.h"
|
||||
#include "wifi_connect.h"
|
||||
#include "wifi_ota.h"
|
||||
#include "wifi.h"
|
||||
#include "uninstall.h"
|
||||
|
||||
extern const uint8_t settings_png_start[] asm("_binary_settings_png_start");
|
||||
extern const uint8_t settings_png_end[] asm("_binary_settings_png_end");
|
||||
|
||||
typedef enum action {
|
||||
ACTION_NONE,
|
||||
ACTION_BACK,
|
||||
ACTION_WIFI,
|
||||
ACTION_OTA,
|
||||
ACTION_RP2040_BL,
|
||||
ACTION_UNINSTALL
|
||||
} menu_settings_action_t;
|
||||
|
||||
void render_settings_help(pax_buf_t* pax_buffer) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] accept [B] back");
|
||||
}
|
||||
|
||||
void menu_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
menu_t* menu = menu_alloc("Settings", 34, 18);
|
||||
|
||||
menu->fgColor = 0xFF000000;
|
||||
menu->bgColor = 0xFFFFFFFF;
|
||||
menu->bgTextColor = 0xFFFFFFFF;
|
||||
menu->selectedItemColor = 0xFF491d88;
|
||||
menu->borderColor = 0xFF43b5a0;
|
||||
menu->titleColor = 0xFF491d88;
|
||||
menu->titleBgColor = 0xFF43b5a0;
|
||||
menu->scrollbarBgColor = 0xFFCCCCCC;
|
||||
menu->scrollbarFgColor = 0xFF555555;
|
||||
|
||||
pax_buf_t icon_settings;
|
||||
pax_decode_png_buf(&icon_settings, (void*) settings_png_start, settings_png_end - settings_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
|
||||
menu_set_icon(menu, &icon_settings);
|
||||
|
||||
menu_insert_item(menu, "WiFi configuration", NULL, (void*) ACTION_WIFI, -1);
|
||||
menu_insert_item(menu, "Firmware update", NULL, (void*) ACTION_OTA, -1);
|
||||
menu_insert_item(menu, "Flash RP2040 firmware", NULL, (void*) ACTION_RP2040_BL, -1);
|
||||
menu_insert_item(menu, "Uninstall app", NULL, (void*) ACTION_UNINSTALL, -1);
|
||||
|
||||
bool render = true;
|
||||
menu_settings_action_t action = ACTION_NONE;
|
||||
|
||||
render_settings_help(pax_buffer);
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
action = ACTION_BACK;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
action = (menu_settings_action_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
if (action == ACTION_RP2040_BL) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Please wait...");
|
||||
rp2040_reboot_to_bootloader(get_rp2040());
|
||||
esp_restart();
|
||||
} else if (action == ACTION_OTA) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Connecting to WiFi...");
|
||||
if (wifi_connect_to_stored()) {
|
||||
display_boot_screen(pax_buffer, ili9341, "Starting firmware update...");
|
||||
ota_update(pax_buffer, ili9341);
|
||||
} else {
|
||||
display_boot_screen(pax_buffer, ili9341, "Failed to connect to WiFi");
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
}
|
||||
} else if (action == ACTION_WIFI) {
|
||||
menu_wifi(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_UNINSTALL) {
|
||||
uninstall_browser(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_BACK) {
|
||||
break;
|
||||
}
|
||||
render = true;
|
||||
action = ACTION_NONE;
|
||||
render_settings_help(pax_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
pax_buf_destroy(&icon_settings);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void menu_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,186 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "pax_codecs.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "launcher.h"
|
||||
#include "settings.h"
|
||||
#include "dev.h"
|
||||
#include "bootscreen.h"
|
||||
#include "hardware.h"
|
||||
#include "math.h"
|
||||
|
||||
extern const uint8_t home_png_start[] asm("_binary_home_png_start");
|
||||
extern const uint8_t home_png_end[] asm("_binary_home_png_end");
|
||||
|
||||
extern const uint8_t apps_png_start[] asm("_binary_apps_png_start");
|
||||
extern const uint8_t apps_png_end[] asm("_binary_apps_png_end");
|
||||
|
||||
extern const uint8_t hatchery_png_start[] asm("_binary_hatchery_png_start");
|
||||
extern const uint8_t hatchery_png_end[] asm("_binary_hatchery_png_end");
|
||||
|
||||
extern const uint8_t dev_png_start[] asm("_binary_dev_png_start");
|
||||
extern const uint8_t dev_png_end[] asm("_binary_dev_png_end");
|
||||
|
||||
extern const uint8_t settings_png_start[] asm("_binary_settings_png_start");
|
||||
extern const uint8_t settings_png_end[] asm("_binary_settings_png_end");
|
||||
|
||||
typedef enum action {
|
||||
ACTION_NONE,
|
||||
ACTION_APPS,
|
||||
ACTION_HATCHERY,
|
||||
ACTION_DEV,
|
||||
ACTION_SETTINGS
|
||||
} menu_start_action_t;
|
||||
|
||||
void render_start_help(pax_buf_t* pax_buffer, const char* text) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_draw_text(pax_buffer, 0xFF491d88, font, 18, 5, 240 - 18, "[A] accept");
|
||||
pax_vec1_t version_size = pax_text_size(font, 18, text);
|
||||
pax_draw_text(pax_buffer, 0xFF491d88, font, 18, 320 - 5 - version_size.x, 240 - 18, text);
|
||||
}
|
||||
|
||||
void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* version) {
|
||||
menu_t* menu = menu_alloc("Main menu", 34, 18);
|
||||
|
||||
menu->fgColor = 0xFF000000;
|
||||
menu->bgColor = 0xFFFFFFFF;
|
||||
menu->bgTextColor = 0xFF000000;
|
||||
menu->selectedItemColor = 0xFFfec859;
|
||||
menu->borderColor = 0xFF491d88;
|
||||
menu->titleColor = 0xFFfec859;
|
||||
menu->titleBgColor = 0xFF491d88;
|
||||
menu->scrollbarBgColor = 0xFFCCCCCC;
|
||||
menu->scrollbarFgColor = 0xFF555555;
|
||||
|
||||
pax_buf_t icon_home;
|
||||
pax_decode_png_buf(&icon_home, (void*) home_png_start, home_png_end - home_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
pax_buf_t icon_apps;
|
||||
pax_decode_png_buf(&icon_apps, (void*) apps_png_start, apps_png_end - apps_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
pax_buf_t icon_hatchery;
|
||||
pax_decode_png_buf(&icon_hatchery, (void*) hatchery_png_start, hatchery_png_end - hatchery_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
pax_buf_t icon_dev;
|
||||
pax_decode_png_buf(&icon_dev, (void*) dev_png_start, dev_png_end - dev_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
pax_buf_t icon_settings;
|
||||
pax_decode_png_buf(&icon_settings, (void*) settings_png_start, settings_png_end - settings_png_start, PAX_BUF_32_8888ARGB, 0);
|
||||
|
||||
menu_set_icon(menu, &icon_home);
|
||||
|
||||
menu_insert_item_icon(menu, "Apps", NULL, (void*) ACTION_APPS, -1, &icon_apps);
|
||||
menu_insert_item_icon(menu, "Hatchery", NULL, (void*) ACTION_HATCHERY, -1, &icon_hatchery);
|
||||
menu_insert_item_icon(menu, "Development tools", NULL, (void*) ACTION_DEV, -1, &icon_dev);
|
||||
menu_insert_item_icon(menu, "Settings", NULL, (void*) ACTION_SETTINGS, -1, &icon_settings);
|
||||
|
||||
|
||||
bool render = true;
|
||||
menu_start_action_t action = ACTION_NONE;
|
||||
|
||||
uint8_t analogReadTimer = 0;
|
||||
float battery_voltage = 0;
|
||||
float usb_voltage = 0;
|
||||
//uint8_t rp2040_usb = 0;
|
||||
|
||||
// Calculated:
|
||||
uint8_t battery_percent = 0;
|
||||
bool battery_charging = false;
|
||||
|
||||
RP2040* rp2040 = get_rp2040();
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 100 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
action = (menu_start_action_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (analogReadTimer > 0) {
|
||||
analogReadTimer--;
|
||||
} else {
|
||||
analogReadTimer = 10; // No need to update these values really quickly
|
||||
if (rp2040_read_vbat(rp2040, &battery_voltage) != ESP_OK) {
|
||||
battery_voltage = 0;
|
||||
}
|
||||
if (rp2040_read_vusb(rp2040, &usb_voltage) != ESP_OK) {
|
||||
usb_voltage = 0;
|
||||
}
|
||||
|
||||
if (battery_voltage >= 3.6) {
|
||||
battery_percent = ((battery_voltage - 3.6) * 100) / (4.2 - 3.6);
|
||||
if (battery_percent > 100) battery_percent = 100;
|
||||
} else {
|
||||
battery_percent = 0;
|
||||
}
|
||||
|
||||
battery_charging = (usb_voltage > 4.0) && (battery_percent < 100);
|
||||
|
||||
render = true;
|
||||
}
|
||||
|
||||
if (render) {
|
||||
char textBuffer[64];
|
||||
snprintf(textBuffer, sizeof(textBuffer), "B%1.1fv U%1.1fv %03u%%%c v%s", battery_voltage, usb_voltage, battery_percent, battery_charging ? '+' : ' ', version);
|
||||
render_start_help(pax_buffer, textBuffer);
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
if (action == ACTION_APPS) {
|
||||
menu_launcher(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_HATCHERY) {
|
||||
// Not implemented
|
||||
display_boot_screen(pax_buffer, ili9341, "Not implemented");
|
||||
} else if (action == ACTION_SETTINGS) {
|
||||
menu_settings(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_DEV) {
|
||||
menu_dev(buttonQueue, pax_buffer, ili9341);
|
||||
}
|
||||
action = ACTION_NONE;
|
||||
render = true;
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
pax_buf_destroy(&icon_home);
|
||||
pax_buf_destroy(&icon_apps);
|
||||
pax_buf_destroy(&icon_hatchery);
|
||||
pax_buf_destroy(&icon_dev);
|
||||
pax_buf_destroy(&icon_settings);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* version);
|
|
@ -1,535 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "appfs_wrapper.h"
|
||||
#include "hardware.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "bootscreen.h"
|
||||
#include "wifi_connect.h"
|
||||
#include "wifi_connection.h"
|
||||
#include "wifi_ota.h"
|
||||
#include "graphics_wrapper.h"
|
||||
|
||||
static const char *TAG = "wifi menu";
|
||||
|
||||
typedef enum action {
|
||||
/* ==== GENERIC ACTIONS ==== */
|
||||
// Nothing happens.
|
||||
ACTION_NONE,
|
||||
// Go back to the parent menu.
|
||||
ACTION_BACK,
|
||||
|
||||
/* ==== MAIN MENU ACTIONS ==== */
|
||||
// Show the current WiFi settings.
|
||||
ACTION_SHOW,
|
||||
// Scan for networks and pick one to connect to.
|
||||
ACTION_SCAN,
|
||||
// Manually edit the current WiFi settings.
|
||||
ACTION_MANUAL,
|
||||
|
||||
/* ==== AUTH MODES ==== */
|
||||
ACTION_AUTH_OPEN,
|
||||
ACTION_AUTH_WEP,
|
||||
ACTION_AUTH_WPA_PSK,
|
||||
ACTION_AUTH_WPA2_PSK,
|
||||
ACTION_AUTH_WPA_WPA2_PSK,
|
||||
ACTION_AUTH_WPA2_ENTERPRISE,
|
||||
ACTION_AUTH_WPA3_PSK,
|
||||
ACTION_AUTH_WPA2_WPA3_PSK,
|
||||
ACTION_AUTH_WAPI_PSK,
|
||||
|
||||
/* ==== PHASE2 AUTH MODES ==== */
|
||||
ACTION_PHASE2_EAP,
|
||||
ACTION_PHASE2_MSCHAPV2,
|
||||
ACTION_PHASE2_MSCHAP,
|
||||
ACTION_PHASE2_PAP,
|
||||
ACTION_PHASE2_CHAP,
|
||||
} menu_wifi_action_t;
|
||||
|
||||
void render_wifi_help(pax_buf_t* pax_buffer) {
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] accept [B] back");
|
||||
}
|
||||
|
||||
void wifi_show(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
||||
void wifi_setup(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, bool scan);
|
||||
wifi_ap_record_t *wifi_scan_results(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, size_t num_aps, wifi_ap_record_t *aps);
|
||||
int wifi_auth_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, wifi_auth_mode_t default_mode);
|
||||
int wifi_phase2_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, esp_eap_ttls_phase2_types default_mode);
|
||||
|
||||
void menu_wifi(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
menu_t* menu = menu_alloc("WiFi configuration", 34, 18);
|
||||
menu_insert_item(menu, "Show current settings", NULL, (void*) ACTION_SHOW, -1);
|
||||
menu_insert_item(menu, "Scan for networks", NULL, (void*) ACTION_SCAN, -1);
|
||||
menu_insert_item(menu, "Configure manually", NULL, (void*) ACTION_MANUAL, -1);
|
||||
|
||||
bool render = true;
|
||||
menu_wifi_action_t action = ACTION_NONE;
|
||||
|
||||
render_wifi_help(pax_buffer);
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
action = ACTION_BACK;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
action = (menu_wifi_action_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
if (action == ACTION_SHOW) {
|
||||
wifi_show(buttonQueue, pax_buffer, ili9341);
|
||||
} else if (action == ACTION_SCAN) {
|
||||
wifi_setup(buttonQueue, pax_buffer, ili9341, true);
|
||||
} else if (action == ACTION_MANUAL) {
|
||||
wifi_setup(buttonQueue, pax_buffer, ili9341, false);
|
||||
} else if (action == ACTION_BACK) {
|
||||
break;
|
||||
}
|
||||
render = true;
|
||||
action = ACTION_NONE;
|
||||
render_wifi_help(pax_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
}
|
||||
|
||||
void wifi_show(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
nvs_handle_t handle;
|
||||
|
||||
nvs_open("system", NVS_READWRITE, &handle);
|
||||
char ssid[33] = "<not set>";
|
||||
char password[33] = "<not set>";
|
||||
size_t requiredSize;
|
||||
|
||||
esp_err_t res = nvs_get_str(handle, "wifi.ssid", NULL, &requiredSize);
|
||||
if ((res == ESP_OK) && (requiredSize < sizeof(ssid))) {
|
||||
res = nvs_get_str(handle, "wifi.ssid", ssid, &requiredSize);
|
||||
}
|
||||
|
||||
res = nvs_get_str(handle, "wifi.password", NULL, &requiredSize);
|
||||
if ((res == ESP_OK) && (requiredSize < sizeof(password))) {
|
||||
res = nvs_get_str(handle, "wifi.password", password, &requiredSize);
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
|
||||
char buffer[300];
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
snprintf(buffer, sizeof(buffer), "WiFi SSID:\n%s\nWiFi password:\n%s", ssid, password);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, NULL, 18, 0, 0, buffer);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
bool quit = false;
|
||||
while (!quit) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) quit = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wifi_ap_record_t *wifi_scan_results(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, size_t num_aps, wifi_ap_record_t *aps) {
|
||||
menu_t *menu = menu_alloc("Select network", 20, 18);
|
||||
wifi_ap_record_t *picked = NULL;
|
||||
|
||||
render_wifi_help(pax_buffer);
|
||||
|
||||
for (size_t i = 0; i < num_aps; i++) {
|
||||
menu_insert_item(menu, (const char*) aps[i].ssid, NULL, (void *) (i + 1), -1);
|
||||
}
|
||||
|
||||
bool render = true;
|
||||
size_t selection = 0;
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
selection = -1;
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
selection = 0;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
selection = (size_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF2f55a8);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (selection != (size_t) -1) {
|
||||
if (selection == 0) {
|
||||
break;
|
||||
} else {
|
||||
// You picked one, yay!
|
||||
picked = &aps[selection-1];
|
||||
break;
|
||||
}
|
||||
render = true;
|
||||
selection = -1;
|
||||
render_wifi_help(pax_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
return picked;
|
||||
}
|
||||
|
||||
int wifi_auth_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, wifi_auth_mode_t default_mode) {
|
||||
menu_t* menu = menu_alloc("Authentication mode", 20, 18);
|
||||
menu_insert_item(menu, "Insecure", NULL, (void*) ACTION_AUTH_OPEN, -1);
|
||||
menu_insert_item(menu, "WEP", NULL, (void*) ACTION_AUTH_WEP, -1);
|
||||
menu_insert_item(menu, "WPA PSK", NULL, (void*) ACTION_AUTH_WPA_PSK, -1);
|
||||
menu_insert_item(menu, "WPA2 PSK", NULL, (void*) ACTION_AUTH_WPA2_PSK, -1);
|
||||
// menu_insert_item(menu, "QQQQQQQQQQQQ", NULL, (void*) ACTION_AUTH_WPA_WPA2_PSK, -1);
|
||||
menu_insert_item(menu, "WPA2 Enterprise", NULL, (void*) ACTION_AUTH_WPA2_ENTERPRISE, -1);
|
||||
menu_insert_item(menu, "WPA3 PSK", NULL, (void*) ACTION_AUTH_WPA3_PSK, -1);
|
||||
// menu_insert_item(menu, "QQQQQQQQQQQQ", NULL, (void*) ACTION_AUTH_WPA2_WPA3_PSK, -1);
|
||||
// menu_insert_item(menu, "WAPI PSK", NULL, (void*) ACTION_AUTH_WAPI_PSK, -1);
|
||||
|
||||
bool render = true;
|
||||
menu_wifi_action_t action = ACTION_NONE;
|
||||
wifi_auth_mode_t pick = default_mode;
|
||||
|
||||
render_wifi_help(pax_buffer);
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
action = ACTION_BACK;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
action = (menu_wifi_action_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF2f55a8);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
if (action == ACTION_BACK) {
|
||||
break;
|
||||
} else {
|
||||
pick = (wifi_auth_mode_t) (action - ACTION_AUTH_OPEN);
|
||||
break;
|
||||
}
|
||||
render = true;
|
||||
action = ACTION_NONE;
|
||||
render_wifi_help(pax_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
return pick;
|
||||
}
|
||||
|
||||
int wifi_phase2_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, esp_eap_ttls_phase2_types default_mode) {
|
||||
menu_t* menu = menu_alloc("Phase 2 authentication mode", 20, 18);
|
||||
menu_insert_item(menu, "ESP", NULL, (void*) ACTION_PHASE2_EAP, -1);
|
||||
menu_insert_item(menu, "MSCHAPv2", NULL, (void*) ACTION_PHASE2_MSCHAPV2, -1);
|
||||
menu_insert_item(menu, "MSCHAP", NULL, (void*) ACTION_PHASE2_MSCHAP, -1);
|
||||
menu_insert_item(menu, "PAP", NULL, (void*) ACTION_PHASE2_PAP, -1);
|
||||
menu_insert_item(menu, "CHAP", NULL, (void*) ACTION_PHASE2_CHAP, -1);
|
||||
|
||||
bool render = true;
|
||||
menu_wifi_action_t action = ACTION_NONE;
|
||||
esp_eap_ttls_phase2_types pick = default_mode;
|
||||
|
||||
render_wifi_help(pax_buffer);
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
action = ACTION_BACK;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
action = (menu_wifi_action_t) menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF2f55a8);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
if (action == ACTION_BACK) {
|
||||
break;
|
||||
} else {
|
||||
pick = (wifi_auth_mode_t) (action - ACTION_PHASE2_EAP);
|
||||
break;
|
||||
}
|
||||
render = true;
|
||||
action = ACTION_NONE;
|
||||
render_wifi_help(pax_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
return pick;
|
||||
}
|
||||
|
||||
// Sorts WiFi APs by RSSI (best RSSI first in the list).
|
||||
static int wifi_ap_sorter(const void *a0, const void *b0) {
|
||||
const wifi_ap_record_t *a = a0;
|
||||
const wifi_ap_record_t *b = b0;
|
||||
return b->rssi - a->rssi;
|
||||
}
|
||||
|
||||
void wifi_setup(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, bool scan) {
|
||||
|
||||
char ssid[33] = {0};
|
||||
char username[33] = {0};
|
||||
char password[33] = {0};
|
||||
nvs_handle_t handle;
|
||||
nvs_open("system", NVS_READWRITE, &handle);
|
||||
bool accepted = true;
|
||||
wifi_auth_mode_t authmode = WIFI_AUTH_WPA2_PSK;
|
||||
esp_eap_ttls_phase2_types phase2 = ESP_EAP_TTLS_PHASE2_EAP;
|
||||
|
||||
/* ==== scanning phase ==== */
|
||||
if (scan) {
|
||||
// Show a little bit of text.
|
||||
display_boot_screen(pax_buffer, ili9341, "Scanning WiFi networks...");
|
||||
|
||||
// Scan for networks.
|
||||
wifi_ap_record_t *aps;
|
||||
size_t n_aps = wifi_scan(&aps);
|
||||
|
||||
// Sort them by RSSI.
|
||||
qsort(aps, n_aps, sizeof(wifi_ap_record_t), wifi_ap_sorter);
|
||||
|
||||
// Make a de-duplicated list.
|
||||
wifi_ap_record_t *dedup = malloc(sizeof(wifi_ap_record_t) * n_aps);
|
||||
size_t n_dedup = 0;
|
||||
for (size_t i = 0; i < n_aps; ) {
|
||||
for (size_t x = 0; x < n_dedup; x++) {
|
||||
if (!strcmp((const char *) aps[i].ssid, (const char *) dedup[x].ssid)) goto cont;
|
||||
}
|
||||
dedup[n_dedup] = aps[i];
|
||||
n_dedup ++;
|
||||
cont:
|
||||
i++;
|
||||
}
|
||||
|
||||
// Open a little menu for picking a network.
|
||||
wifi_ap_record_t *pick = wifi_scan_results(buttonQueue, pax_buffer, ili9341, n_dedup, dedup);
|
||||
if (!pick) {
|
||||
nvs_close(handle);
|
||||
return;
|
||||
}
|
||||
// Copy the SSID in.
|
||||
memcpy(ssid, pick->ssid, 33);
|
||||
authmode = pick->authmode;
|
||||
|
||||
// Free memories.
|
||||
free(aps);
|
||||
free(dedup);
|
||||
} else {
|
||||
size_t requiredSize;
|
||||
esp_err_t res = nvs_get_str(handle, "wifi.ssid", NULL, &requiredSize);
|
||||
if (res != ESP_OK) {
|
||||
strcpy(ssid, "");
|
||||
strcpy(password, "");
|
||||
} else if (requiredSize < sizeof(ssid)) {
|
||||
res = nvs_get_str(handle, "wifi.ssid", ssid, &requiredSize);
|
||||
if (res != ESP_OK) strcpy(ssid, "");
|
||||
res = nvs_get_str(handle, "wifi.password", NULL, &requiredSize);
|
||||
if (res != ESP_OK) {
|
||||
strcpy(password, "");
|
||||
} else if (requiredSize < sizeof(password)) {
|
||||
res = nvs_get_str(handle, "wifi.password", password, &requiredSize);
|
||||
if (res != ESP_OK) strcpy(password, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Select SSID.
|
||||
accepted = keyboard(buttonQueue, pax_buffer, ili9341, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi SSID", "Press HOME to cancel", ssid, sizeof(ssid));
|
||||
|
||||
// Select auth mode.
|
||||
if (accepted) {
|
||||
authmode = wifi_auth_menu(buttonQueue, pax_buffer, ili9341, -1);
|
||||
accepted = authmode != -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==== manual entering phase ==== */
|
||||
if (authmode == WIFI_AUTH_WPA2_ENTERPRISE) {
|
||||
if (accepted) {
|
||||
// Phase2 method.
|
||||
phase2 = wifi_phase2_menu(buttonQueue, pax_buffer, ili9341, -1);
|
||||
accepted = phase2 != -1;
|
||||
}
|
||||
if (accepted) {
|
||||
// Username.
|
||||
accepted = keyboard(buttonQueue, pax_buffer, ili9341, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi username", "Press HOME to cancel", username, sizeof(username));
|
||||
}
|
||||
}
|
||||
if (accepted) {
|
||||
// Password.
|
||||
accepted = keyboard(buttonQueue, pax_buffer, ili9341, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi password", "Press HOME to cancel", password, sizeof(password));
|
||||
}
|
||||
if (accepted) {
|
||||
nvs_set_str(handle, "wifi.ssid", ssid);
|
||||
nvs_set_str(handle, "wifi.password", password);
|
||||
nvs_set_u8 (handle, "wifi.authmode", authmode);
|
||||
if (authmode == WIFI_AUTH_WPA2_ENTERPRISE) {
|
||||
nvs_set_str(handle, "wifi.username", username);
|
||||
nvs_set_u8 (handle, "wifi.phase2", phase2);
|
||||
}
|
||||
display_boot_screen(pax_buffer, ili9341, "WiFi settings stored");
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
void menu_wifi(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
|
|
@ -1,219 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "driver/uart.h"
|
||||
#include "hardware.h"
|
||||
#include "managed_i2c.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "rp2040.h"
|
||||
#include "rp2040bl.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "graphics_wrapper.h"
|
||||
#include "esp32/rom/crc.h"
|
||||
|
||||
extern const uint8_t rp2040_firmware_bin_start[] asm("_binary_rp2040_firmware_bin_start");
|
||||
extern const uint8_t rp2040_firmware_bin_end[] asm("_binary_rp2040_firmware_bin_end");
|
||||
|
||||
void display_rp2040_update_state(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* text) {
|
||||
pax_noclip(pax_buffer);
|
||||
const pax_font_t* font = pax_get_font("sky mono");
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_vec1_t title_size = pax_text_size(font, 18, "Co-processor update");
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, (320 / 2) - (title_size.x / 2), 120 - 30, "Co-processor update");
|
||||
pax_vec1_t size = pax_text_size(font, 18, text);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, (320 / 2) - (size.x / 2), 120 + 10, text);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
||||
|
||||
void rp2040_updater(RP2040* rp2040, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
size_t firmware_size = rp2040_firmware_bin_end - rp2040_firmware_bin_start;
|
||||
char message[64];
|
||||
|
||||
uint8_t fw_version;
|
||||
if (rp2040_get_firmware_version(rp2040, &fw_version) != ESP_OK) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 error");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Failed to read firmware version");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
restart();
|
||||
}
|
||||
|
||||
if (fw_version < 0x03) { // Update required
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Starting bootloader...");
|
||||
rp2040_reboot_to_bootloader(rp2040);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
if (fw_version == 0xFF) { // RP2040 is in bootloader mode
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Starting update...");
|
||||
|
||||
uint8_t bl_version;
|
||||
if (rp2040_get_bootloader_version(rp2040, &bl_version) != ESP_OK) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 update failed");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Communication error (1)");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
restart();
|
||||
}
|
||||
if (bl_version != 0x01) {
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 update failed");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Unsupported bootloader version");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
restart();
|
||||
}
|
||||
|
||||
rp2040_bl_install_uart();
|
||||
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Preparing...");
|
||||
|
||||
while (true) {
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
uint8_t bl_state;
|
||||
if (rp2040_get_bootloader_state(rp2040, &bl_state) != ESP_OK) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 update failed");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Communication error (2)");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
restart();
|
||||
}
|
||||
if (bl_state == 0xB0) {
|
||||
break;
|
||||
}
|
||||
if (bl_state > 0xB0) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 update failed");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Unknown bootloader state");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
restart();
|
||||
}
|
||||
}
|
||||
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Synchronizing...");
|
||||
|
||||
while (true) {
|
||||
if (rp2040_bl_sync()) break;
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
uint32_t flash_start = 0, flash_size = 0, erase_size = 0, write_size = 0, max_data_len = 0;
|
||||
|
||||
bool success = rp2040_bl_get_info(&flash_start, &flash_size, &erase_size, &write_size, &max_data_len);
|
||||
|
||||
if (!success) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 update failed");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Failed to read information");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
restart();
|
||||
}
|
||||
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Erasing...");
|
||||
|
||||
uint32_t erase_length = firmware_size;
|
||||
erase_length = erase_length + erase_size - (erase_length % erase_size); // Round up to erase size
|
||||
|
||||
if (erase_length > flash_size - erase_size) {
|
||||
erase_length = flash_size - erase_size;
|
||||
}
|
||||
|
||||
bool eraseSuccess = rp2040_bl_erase(flash_start, flash_size - erase_size);//erase_length); < erase whole flash as workaround for a yet to be fixed bug in the calculation of erase_length
|
||||
|
||||
if (!eraseSuccess) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xa85a32);
|
||||
snprintf(message, sizeof(message) - 1, "RP2040 update failed");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, message);
|
||||
snprintf(message, sizeof(message) - 1, "Failed to erase flash");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 13, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
restart();
|
||||
}
|
||||
|
||||
uint32_t position = 0;
|
||||
uint32_t txSize = write_size;
|
||||
uint8_t* txBuffer = malloc(write_size);
|
||||
|
||||
uint32_t blockCrc = 0;
|
||||
uint32_t totalCrc = 0;
|
||||
uint32_t totalLength = 0;
|
||||
|
||||
while (true) {
|
||||
if ((firmware_size - position) < txSize) {
|
||||
txSize = firmware_size - position;
|
||||
}
|
||||
|
||||
if (txSize == 0) break;
|
||||
|
||||
uint8_t percentage = position * 100 / firmware_size;
|
||||
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
snprintf(message, sizeof(message) - 1, "Writing... %u%%", percentage);
|
||||
display_rp2040_update_state(pax_buffer, ili9341, message);
|
||||
|
||||
uint32_t checkCrc = 0;
|
||||
memset(txBuffer, 0, write_size);
|
||||
memcpy(txBuffer, &rp2040_firmware_bin_start[position], txSize);
|
||||
blockCrc = crc32_le(0, txBuffer, write_size);
|
||||
totalCrc = crc32_le(totalCrc, txBuffer, write_size);
|
||||
totalLength += write_size;
|
||||
bool writeSuccess = rp2040_bl_write(0x10010000 + position, write_size, txBuffer, &checkCrc);
|
||||
if (writeSuccess && (blockCrc == checkCrc)) {
|
||||
position += txSize;
|
||||
} else {
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "CRC mismatch");
|
||||
while (!rp2040_bl_sync()) {
|
||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(txBuffer);
|
||||
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Finalizing...");
|
||||
|
||||
bool sealRes = rp2040_bl_seal(0x10010000, 0x10010000, totalLength, totalCrc);
|
||||
|
||||
if (sealRes) {
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0xCCCCCC);
|
||||
memset(message, 0, sizeof(message));
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Update completed");
|
||||
rp2040_bl_go(0x10010000);
|
||||
} else {
|
||||
display_rp2040_update_state(pax_buffer, ili9341, "Update failed");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
restart();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include "ili9341.h"
|
||||
#include "ice40.h"
|
||||
#include "hardware.h"
|
||||
|
||||
static const char *TAG = "settings";
|
||||
|
||||
esp_err_t nvs_init() {
|
||||
const esp_partition_t * nvs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
|
||||
if (nvs_partition == NULL) return ESP_FAIL;
|
||||
esp_err_t res = nvs_flash_init();
|
||||
if (res != ESP_OK) {
|
||||
res = esp_partition_erase_range(nvs_partition, 0, nvs_partition->size);
|
||||
if (res != ESP_OK) return res;
|
||||
res = nvs_flash_init();
|
||||
if (res != ESP_OK) return res;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t nvs_get_str_fixed(const char* nvs_namespace, const char* key, char* target, size_t target_size, size_t* size) {
|
||||
nvs_handle_t handle;
|
||||
esp_err_t res;
|
||||
|
||||
res = nvs_open(nvs_namespace, NVS_READWRITE, &handle);
|
||||
if (res != ESP_OK) return res;
|
||||
|
||||
size_t required_size;
|
||||
res = nvs_get_str(handle, key, NULL, &required_size);
|
||||
if (res != ESP_OK) {
|
||||
nvs_close(handle);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (required_size > target_size) {
|
||||
nvs_close(handle);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
res = nvs_get_str(handle, key, target, &required_size);
|
||||
|
||||
if (size != NULL) *size = required_size;
|
||||
|
||||
nvs_close(handle);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t nvs_get_u8_default(const char* nvs_namespace, const char* key, uint8_t default_value) {
|
||||
nvs_handle_t handle;
|
||||
esp_err_t res;
|
||||
|
||||
res = nvs_open(nvs_namespace, NVS_READWRITE, &handle);
|
||||
if (res != ESP_OK) return default_value;
|
||||
|
||||
uint8_t target;
|
||||
res = nvs_get_u8(handle, key, &target);
|
||||
if (res != ESP_OK) {
|
||||
nvs_close(handle);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
return target;
|
||||
}
|
||||
|
||||
esp_err_t nvs_set_u8_fixed(const char* nvs_namespace, const char* key, uint8_t value) {
|
||||
nvs_handle_t handle;
|
||||
esp_err_t res;
|
||||
|
||||
res = nvs_open(nvs_namespace, NVS_READWRITE, &handle);
|
||||
if (res != ESP_OK) return res;
|
||||
|
||||
res = nvs_set_u8(handle, key, value);
|
||||
nvs_close(handle);
|
||||
|
||||
return res;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_system.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include "system_wrapper.h"
|
||||
|
||||
void restart() {
|
||||
/*for (int i = 3; i >= 0; i--) {
|
||||
printf("Restarting in %d seconds...\n", i);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
printf("Restarting now.\n");*/
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
fflush(stdout);
|
||||
esp_restart();
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "hardware.h"
|
||||
#include "ili9341.h"
|
||||
#include "ice40.h"
|
||||
#include "rp2040.h"
|
||||
#include "pax_gfx.h"
|
||||
|
||||
typedef bool (*test_fn)(uint32_t *rc);
|
||||
|
||||
bool test_wait_for_response(uint32_t *rc) {
|
||||
RP2040* rp2040 = get_rp2040();
|
||||
rp2040_input_message_t button_message = {0};
|
||||
if (rc != NULL) *rc = 0;
|
||||
while (1) {
|
||||
if (xQueueReceive(rp2040->queue, &button_message, portMAX_DELAY) == pdTRUE) {
|
||||
if (button_message.state) {
|
||||
switch(button_message.input) {
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_MENU:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
return false;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
if (rc != NULL) *rc = 1;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool run_test(pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, const char *test_name, test_fn fn) {
|
||||
bool test_result;
|
||||
uint32_t rc;
|
||||
|
||||
/* Test name */
|
||||
pax_draw_text(pax_buffer, 0xffffffff, font, 18, 0, 20*line, test_name);
|
||||
if (ili9341) ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
/* Run the test */
|
||||
test_result = fn(&rc);
|
||||
|
||||
/* Display result */
|
||||
if (!test_result) {
|
||||
/* Error */
|
||||
char buf[10];
|
||||
snprintf(buf, sizeof(buf), "%08x", rc);
|
||||
pax_draw_text(pax_buffer, 0xffff0000, font, 18, 200, 20*line, buf);
|
||||
} else {
|
||||
/* OK ! */
|
||||
pax_draw_text(pax_buffer, 0xff00ff00, font, 18, 200, 20*line, " OK");
|
||||
}
|
||||
|
||||
if (ili9341) ili9341_write(ili9341, pax_buffer->buf);
|
||||
|
||||
/* Pass through the 'OK' status */
|
||||
return test_result;
|
||||
}
|
120
main/uninstall.c
|
@ -1,120 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "appfs.h"
|
||||
#include "ili9341.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "menu.h"
|
||||
#include "rp2040.h"
|
||||
#include "appfs_wrapper.h"
|
||||
#include "hardware.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "bootscreen.h"
|
||||
#include "uninstall.h"
|
||||
|
||||
static const char *TAG = "uninstaller";
|
||||
|
||||
typedef struct _uninstall_menu_args {
|
||||
appfs_handle_t fd;
|
||||
char name[512];
|
||||
} uninstall_menu_args_t;
|
||||
|
||||
void uninstall_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
menu_t* menu = menu_alloc("Uninstall application", 20, 18);
|
||||
const pax_font_t *font = pax_get_font("saira regular");
|
||||
|
||||
appfs_handle_t appfs_fd = APPFS_INVALID_FD;
|
||||
while (1) {
|
||||
appfs_fd = appfsNextEntry(appfs_fd);
|
||||
if (appfs_fd == APPFS_INVALID_FD) break;
|
||||
const char* name = NULL;
|
||||
appfsEntryInfo(appfs_fd, &name, NULL);
|
||||
uninstall_menu_args_t* args = malloc(sizeof(uninstall_menu_args_t));
|
||||
if (args == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to malloc() menu args");
|
||||
return;
|
||||
}
|
||||
args->fd = appfs_fd;
|
||||
sprintf(args->name, name);
|
||||
menu_insert_item(menu, name, NULL, (void*) args, -1);
|
||||
}
|
||||
|
||||
bool render = true;
|
||||
uninstall_menu_args_t* menuArgs = NULL;
|
||||
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_noclip(pax_buffer);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] uninstall app [B] back");
|
||||
|
||||
bool quit = false;
|
||||
|
||||
while (1) {
|
||||
rp2040_input_message_t buttonMessage = {0};
|
||||
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
uint8_t pin = buttonMessage.input;
|
||||
bool value = buttonMessage.state;
|
||||
switch(pin) {
|
||||
case RP2040_INPUT_JOYSTICK_DOWN:
|
||||
if (value) {
|
||||
menu_navigate_next(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_JOYSTICK_UP:
|
||||
if (value) {
|
||||
menu_navigate_previous(menu);
|
||||
render = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_HOME:
|
||||
case RP2040_INPUT_BUTTON_BACK:
|
||||
if (value) {
|
||||
quit = true;
|
||||
}
|
||||
break;
|
||||
case RP2040_INPUT_BUTTON_ACCEPT:
|
||||
case RP2040_INPUT_JOYSTICK_PRESS:
|
||||
case RP2040_INPUT_BUTTON_SELECT:
|
||||
case RP2040_INPUT_BUTTON_START:
|
||||
if (value) {
|
||||
menuArgs = menu_get_callback_args(menu, menu_get_position(menu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (render) {
|
||||
menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF72008a);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
render = false;
|
||||
}
|
||||
|
||||
if (menuArgs != NULL) {
|
||||
char message[1024];
|
||||
sprintf(message, "Uninstalling %s...", menuArgs->name);
|
||||
printf("%s\n", message);
|
||||
display_boot_screen(pax_buffer, ili9341, message);
|
||||
appfsDeleteFile(menuArgs->name);
|
||||
menuArgs = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < menu_get_length(menu); index++) {
|
||||
free(menu_get_callback_args(menu, index));
|
||||
}
|
||||
|
||||
menu_free(menu);
|
||||
}
|
117
main/webusb.c
|
@ -1,117 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include "driver/uart.h"
|
||||
#include "hardware.h"
|
||||
#include "managed_i2c.h"
|
||||
#include "pax_gfx.h"
|
||||
#include "ice40.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "graphics_wrapper.h"
|
||||
#include "esp32/rom/crc.h"
|
||||
|
||||
void webusb_install_uart() {
|
||||
fflush(stdout);
|
||||
ESP_ERROR_CHECK(uart_driver_install(0, 2048, 0, 0, NULL, 0));
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 921600,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_APB,
|
||||
};
|
||||
ESP_ERROR_CHECK(uart_param_config(0, &uart_config));
|
||||
}
|
||||
|
||||
void webusb_uninstall_uart() {
|
||||
uart_driver_delete(0);
|
||||
}
|
||||
|
||||
bool webusb_read_stdin(uint8_t* buffer, uint32_t len, uint32_t timeout) {
|
||||
int read = uart_read_bytes(0, buffer, len, timeout / portTICK_PERIOD_MS);
|
||||
return (read == len);
|
||||
}
|
||||
|
||||
bool webusb_uart_sync(uint32_t* length, uint32_t* crc) {
|
||||
uint8_t rx_buffer[4*3];
|
||||
webusb_read_stdin(rx_buffer, sizeof(rx_buffer), 100);
|
||||
if (memcmp(rx_buffer, "WUSB", 4) != 0) return false;
|
||||
memcpy((uint8_t*) length, &rx_buffer[4 * 1], 4);
|
||||
memcpy((uint8_t*) crc, &rx_buffer[4 * 2], 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool webusb_uart_load(uint8_t* buffer, uint32_t length) {
|
||||
return webusb_read_stdin(buffer, length, 3000);
|
||||
}
|
||||
|
||||
void webusb_uart_mess(const char *mess) {
|
||||
uart_write_bytes(0, mess, strlen(mess));
|
||||
}
|
||||
|
||||
void webusb_print_status(pax_buf_t* pax_buffer, ILI9341* ili9341, char* message) {
|
||||
pax_noclip(pax_buffer);
|
||||
pax_background(pax_buffer, 0x325aa8);
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "WebUSB mode");
|
||||
pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, message);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
||||
|
||||
void webusb_main(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
webusb_install_uart();
|
||||
|
||||
while (true) {
|
||||
webusb_print_status(pax_buffer, ili9341, "Waiting...");
|
||||
|
||||
// 1) Wait for WUSB followed by data length as uint32 and CRC32 of the data as uint32
|
||||
uint32_t length, crc;
|
||||
while (!webusb_uart_sync(&length, &crc)) {
|
||||
webusb_uart_mess("WUSB");
|
||||
}
|
||||
|
||||
webusb_print_status(pax_buffer, ili9341, "Receiving...");
|
||||
|
||||
// 2) Allocate RAM for the data to be received
|
||||
uint8_t* buffer = malloc(length);
|
||||
if (buffer == NULL) {
|
||||
webusb_uart_mess("EMEM");
|
||||
webusb_print_status(pax_buffer, ili9341, "Error: malloc failed");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3) Receive data into the buffer
|
||||
if (!webusb_uart_load(buffer, length)) {
|
||||
free(buffer);
|
||||
webusb_uart_mess("ERCV");
|
||||
webusb_print_status(pax_buffer, ili9341, "Error: receive failed");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4) Check CRC
|
||||
uint32_t checkCrc = crc32_le(0, buffer, length);
|
||||
|
||||
if (checkCrc != crc) {
|
||||
free(buffer);
|
||||
webusb_uart_mess("ECRC");
|
||||
webusb_print_status(pax_buffer, ili9341, "Error: CRC invalid");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
webusb_uart_mess("OKOK");
|
||||
webusb_print_status(pax_buffer, ili9341, "Packet received");
|
||||
|
||||
// To-do: parse packet
|
||||
}
|
||||
|
||||
webusb_uninstall_uart();
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include "pax_gfx.h"
|
||||
#include "system_wrapper.h"
|
||||
#include "settings.h"
|
||||
#include "wifi_connection.h"
|
||||
|
||||
static const char *TAG = "wifi_connect";
|
||||
|
||||
bool wifi_connect_to_stored() {
|
||||
bool result = false;
|
||||
// Open NVS.
|
||||
nvs_handle_t handle;
|
||||
nvs_open("system", NVS_READWRITE, &handle);
|
||||
wifi_auth_mode_t authmode = 0;
|
||||
esp_eap_ttls_phase2_types phase2 = 0;
|
||||
char *ssid = NULL;
|
||||
char *ident = NULL;
|
||||
char *anon_ident = NULL;
|
||||
char *password = NULL;
|
||||
size_t len;
|
||||
|
||||
// Read NVS.
|
||||
esp_err_t res;
|
||||
// Read SSID.
|
||||
res = nvs_get_str(handle, "wifi.ssid", NULL, &len);
|
||||
if (res) goto errcheck;
|
||||
ssid = malloc(len);
|
||||
res = nvs_get_str(handle, "wifi.ssid", ssid, &len);
|
||||
if (res) goto errcheck;
|
||||
|
||||
// Check whether connection is enterprise.
|
||||
res = nvs_get_u8(handle, "wifi.authmode", &authmode);
|
||||
bool use_ent = authmode == WIFI_AUTH_WPA2_ENTERPRISE;
|
||||
if (res) goto errcheck;
|
||||
|
||||
if (use_ent) {
|
||||
// Read enterprise-specific parameters.
|
||||
|
||||
// Read phase2 mode.
|
||||
res = nvs_get_u8(handle, "wifi.phase2", &phase2);
|
||||
if (res) goto errcheck;
|
||||
|
||||
// Read identity.
|
||||
res = nvs_get_str(handle, "wifi.username", NULL, &len);
|
||||
if (res) goto errcheck;
|
||||
ident = malloc(len);
|
||||
res = nvs_get_str(handle, "wifi.username", ident, &len);
|
||||
|
||||
// Read anonymous identity.
|
||||
res = nvs_get_str(handle, "wifi.anon_ident", NULL, &len);
|
||||
if (res == ESP_ERR_NVS_NOT_FOUND) {
|
||||
// Default is use the same thing.
|
||||
anon_ident = strdup(ident);
|
||||
} else {
|
||||
if (res) goto errcheck;
|
||||
anon_ident = malloc(len);
|
||||
res = nvs_get_str(handle, "wifi.anon_ident", anon_ident, &len);
|
||||
if (res) goto errcheck;
|
||||
}
|
||||
}
|
||||
// Read password.
|
||||
res = nvs_get_str(handle, "wifi.password", NULL, &len);
|
||||
if (res) goto errcheck;
|
||||
password = malloc(len);
|
||||
res = nvs_get_str(handle, "wifi.password", password, &len);
|
||||
if (res) goto errcheck;
|
||||
|
||||
// Close NVS.
|
||||
nvs_close(handle);
|
||||
|
||||
// Open the appropriate connection.
|
||||
if (use_ent) {
|
||||
result = wifi_connect_ent(ssid, ident, anon_ident, password, phase2, 3);
|
||||
} else {
|
||||
result = wifi_connect(ssid, password, authmode, 3);
|
||||
}
|
||||
|
||||
errcheck:
|
||||
if (res == ESP_ERR_NVS_NOT_FOUND || res == ESP_ERR_NVS_NOT_INITIALIZED) {
|
||||
// When NVS is not initialised.
|
||||
ESP_LOGI(TAG, "WiFi settings not stored in NVS.");
|
||||
} else if (res) {
|
||||
// Other errors.
|
||||
ESP_LOGE(TAG, "Error connecting to WiFi: %s", esp_err_to_name(res));
|
||||
}
|
||||
|
||||
// Free memory.
|
||||
if (ssid) free(ssid);
|
||||
if (ident) free(ident);
|
||||
if (anon_ident) free(anon_ident);
|
||||
if (password) free(password);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,272 +0,0 @@
|
|||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "wifi_connection.h"
|
||||
|
||||
static const char *TAG = "wifi_connection";
|
||||
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
#define WIFI_STARTED_BIT BIT2
|
||||
|
||||
static EventGroupHandle_t wifiEventGroup;
|
||||
|
||||
static uint8_t retryCount = 0;
|
||||
static uint8_t maxRetries = 3;
|
||||
static bool isScanning = false;
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
xEventGroupSetBits(wifiEventGroup, WIFI_STARTED_BIT);
|
||||
if (!isScanning) {
|
||||
// Connect only if we're not scanning the WiFi.
|
||||
esp_wifi_connect();
|
||||
}
|
||||
ESP_LOGI(TAG, "WiFi station start.");
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) {
|
||||
xEventGroupClearBits(wifiEventGroup, WIFI_STARTED_BIT);
|
||||
ESP_LOGI(TAG, "WiFi station stop.");
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
if (retryCount < 3) {
|
||||
esp_wifi_connect();
|
||||
retryCount++;
|
||||
ESP_LOGI(TAG, "Retrying connection");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Connection failed");
|
||||
xEventGroupSetBits(wifiEventGroup, WIFI_FAIL_BIT);
|
||||
}
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "Got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
retryCount = 0;
|
||||
xEventGroupSetBits(wifiEventGroup, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_init() {
|
||||
// Create an event group for WiFi things.
|
||||
wifiEventGroup = xEventGroupCreate();
|
||||
|
||||
// Initialise WiFi stack.
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
// Register event handlers for WiFi.
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
|
||||
|
||||
// Turn off WiFi hardware.
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
}
|
||||
|
||||
bool wifi_connect(const char* aSsid, const char* aPassword, wifi_auth_mode_t aAuthmode, uint8_t aRetryMax) {
|
||||
// Set the retry counts.
|
||||
retryCount = 0;
|
||||
maxRetries = aRetryMax;
|
||||
|
||||
// Create a config.
|
||||
wifi_config_t wifi_config = {0};
|
||||
strcpy((char*) wifi_config.sta.ssid, aSsid);
|
||||
strcpy((char*) wifi_config.sta.password, aPassword);
|
||||
wifi_config.sta.threshold.authmode = aAuthmode;
|
||||
|
||||
// Set WiFi config.
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
// Disable 11b as NOC asked.
|
||||
esp_wifi_config_11b_rate(WIFI_IF_STA, true);
|
||||
// Start WiFi.
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "Connecting to WiFi...");
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(wifiEventGroup, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "Connected to WiFi");
|
||||
return true;
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGE(TAG, "Failed to connect");
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Unknown event received while waiting on connection");
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wifi_connect_ent(const char* aSsid, const char *aIdent, const char *aAnonIdent, const char* aPassword, esp_eap_ttls_phase2_types phase2, uint8_t aRetryMax) {
|
||||
retryCount = 0;
|
||||
maxRetries = aRetryMax;
|
||||
wifi_config_t wifi_config = {0};
|
||||
if (strlen(aSsid) > 32) {
|
||||
ESP_LOGE(TAG, "SSID is too long (%zu > 32)!", strlen(aSsid));
|
||||
return false;
|
||||
}
|
||||
strncpy((char*) wifi_config.sta.ssid, aSsid, 32);
|
||||
|
||||
// Set WiFi config.
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
|
||||
|
||||
// Set WPA2 ENT config.
|
||||
esp_wifi_sta_wpa2_ent_set_identity((const uint8_t *) aAnonIdent, strlen(aAnonIdent));
|
||||
esp_wifi_sta_wpa2_ent_set_username((const uint8_t *) aIdent, strlen(aIdent));
|
||||
esp_wifi_sta_wpa2_ent_set_password((const uint8_t *) aPassword, strlen(aPassword));
|
||||
esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(phase2);
|
||||
// Enable enterprise auth.
|
||||
esp_wifi_sta_wpa2_ent_enable();
|
||||
// Disable 11b as NOC asked.
|
||||
esp_wifi_config_11b_rate(WIFI_IF_STA, true);
|
||||
// Start the connection.
|
||||
esp_wifi_start();
|
||||
|
||||
ESP_LOGI(TAG, "Connecting to '%s' as '%s'/'%s': %s", aSsid, aIdent, aAnonIdent, aPassword);
|
||||
ESP_LOGI(TAG, "Phase2 mode: %d", phase2);
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(wifiEventGroup, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "Connected to WiFi");
|
||||
return true;
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGE(TAG, "Failed to connect");
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Unknown event received while waiting on connection");
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shows a nice info message describing an AP record.
|
||||
static inline void wifi_desc_record(wifi_ap_record_t *record) {
|
||||
// Make a string representation of BSSID.
|
||||
char *bssid_str = malloc(3*6);
|
||||
if (!bssid_str) return;
|
||||
snprintf(bssid_str, 3*6, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
record->bssid[0], record->bssid[1], record->bssid[2],
|
||||
record->bssid[3], record->bssid[4], record->bssid[5]
|
||||
);
|
||||
|
||||
// Make a string representation of 11b/g/n modes.
|
||||
char *phy_str = malloc(9);
|
||||
if (!phy_str) {
|
||||
free(bssid_str);
|
||||
return;
|
||||
}
|
||||
*phy_str = 0;
|
||||
if (record->phy_11b | record->phy_11g | record->phy_11n) {
|
||||
strcpy(phy_str, " 1");
|
||||
}
|
||||
if (record->phy_11b) {
|
||||
strcat(phy_str, "/b");
|
||||
}
|
||||
if (record->phy_11g) {
|
||||
strcat(phy_str, "/g");
|
||||
}
|
||||
if (record->phy_11n) {
|
||||
strcat(phy_str, "/n");
|
||||
}
|
||||
phy_str[2] = '1';
|
||||
|
||||
ESP_LOGI(TAG, "AP %s %s rssi=%hhd%s", bssid_str, record->ssid, record->rssi, phy_str);
|
||||
free(bssid_str);
|
||||
free(phy_str);
|
||||
}
|
||||
|
||||
// Scan for WiFi access points.
|
||||
size_t wifi_scan(wifi_ap_record_t **aps_out) {
|
||||
isScanning = true;
|
||||
// Scan for any non-hidden APs on all channels.
|
||||
wifi_scan_config_t cfg = {
|
||||
.ssid = NULL,
|
||||
.bssid = NULL,
|
||||
.channel = 0,
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
|
||||
.scan_time = { .active={ 0, 0 } },
|
||||
};
|
||||
|
||||
// Start the scan now.
|
||||
ESP_LOGI(TAG, "Starting scan...");
|
||||
esp_err_t res = esp_wifi_scan_start(&cfg, true);
|
||||
// Whether to call esp_wifi_stop() on finish.
|
||||
bool stopWhenDone = false;
|
||||
if (res == ESP_ERR_WIFI_NOT_STARTED) {
|
||||
// If it complains that the wifi wasn't started, then do so.
|
||||
ESP_LOGI(TAG, "Starting WiFi for scan");
|
||||
|
||||
// Set to station but don't connect.
|
||||
res = esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (res) goto ohno;
|
||||
|
||||
// Start WiFi.
|
||||
res = esp_wifi_start();
|
||||
if (res) goto ohno;
|
||||
stopWhenDone = true;
|
||||
|
||||
// Await the STA started bit.
|
||||
xEventGroupWaitBits(wifiEventGroup, WIFI_STARTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(2000));
|
||||
|
||||
// Try again.
|
||||
res = esp_wifi_scan_start(&cfg, true);
|
||||
}
|
||||
if (res) {
|
||||
ohno:
|
||||
ESP_LOGE(TAG, "Error in WiFi scan: %s", esp_err_to_name(res));
|
||||
isScanning = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory for AP list.
|
||||
uint16_t num_ap = 0;
|
||||
esp_wifi_scan_get_ap_num(&num_ap);
|
||||
wifi_ap_record_t *aps = malloc(sizeof(wifi_ap_record_t) * num_ap);
|
||||
|
||||
// Collect APs and report findings.
|
||||
esp_wifi_scan_get_ap_records(&num_ap, aps);
|
||||
for (uint16_t i = 0; i < num_ap; i++) {
|
||||
wifi_desc_record(&aps[i]);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
if (aps_out) {
|
||||
// Output pointer is non-null, return the APs list.
|
||||
*aps_out = aps;
|
||||
} else {
|
||||
// Output pointer is null, free the APs list.
|
||||
free(aps);
|
||||
}
|
||||
if (stopWhenDone) {
|
||||
// Stop WiFi because it was started only for this scan.
|
||||
esp_wifi_stop();
|
||||
}
|
||||
isScanning = false;
|
||||
return num_ap;
|
||||
}
|
||||
|
||||
// Get the strength value for a given RSSI.
|
||||
wifi_strength_t wifi_rssi_to_strength(int8_t rssi) {
|
||||
if (rssi > WIFI_THRESH_VERY_GOOD) return WIFI_STRENGTH_VERY_GOOD;
|
||||
else if (rssi > WIFI_THRESH_GOOD) return WIFI_STRENGTH_GOOD;
|
||||
else if (rssi > WIFI_THRESH_BAD) return WIFI_STRENGTH_BAD;
|
||||
else return WIFI_STRENGTH_VERY_BAD;
|
||||
}
|
227
main/wifi_ota.c
|
@ -1,227 +0,0 @@
|
|||
#include "wifi_ota.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "string.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include <sys/socket.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "bootscreen.h"
|
||||
|
||||
#define HASH_LEN 32
|
||||
|
||||
static const char *TAG = "OTA update";
|
||||
|
||||
extern const uint8_t server_cert_pem_start[] asm("_binary_isrgrootx1_pem_start");
|
||||
extern const uint8_t server_cert_pem_end[] asm("_binary_isrgrootx1_pem_end");
|
||||
|
||||
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch (evt->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_HEADERS_SENT:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_HEADERS_SENT");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t validate_image_header(esp_app_desc_t *new_app_info) {
|
||||
if (new_app_info == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_app_desc_t running_app_info;
|
||||
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
|
||||
}
|
||||
|
||||
/*
|
||||
if (memcmp(new_app_info->version, running_app_info.version, sizeof(new_app_info->version)) == 0) {
|
||||
ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
*/
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _http_client_init_cb(esp_http_client_handle_t http_client) {
|
||||
esp_err_t err = ESP_OK;
|
||||
/* Uncomment to add custom headers to HTTP request */
|
||||
// err = esp_http_client_set_header(http_client, "Custom-Header", "Value");
|
||||
return err;
|
||||
}
|
||||
|
||||
static void print_sha256(const uint8_t *image_hash, const char *label) {
|
||||
char hash_print[HASH_LEN * 2 + 1];
|
||||
hash_print[HASH_LEN * 2] = 0;
|
||||
for (int i = 0; i < HASH_LEN; ++i) {
|
||||
sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s %s", label, hash_print);
|
||||
}
|
||||
|
||||
static void get_sha256_of_partitions(void) {
|
||||
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||
esp_partition_t partition;
|
||||
|
||||
// get sha256 digest for bootloader
|
||||
partition.address = ESP_BOOTLOADER_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.type = ESP_PARTITION_TYPE_APP;
|
||||
esp_partition_get_sha256(&partition, sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for bootloader: ");
|
||||
|
||||
// get sha256 digest for running partition
|
||||
esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for current firmware: ");
|
||||
}
|
||||
|
||||
void display_ota_state(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* text) {
|
||||
pax_noclip(pax_buffer);
|
||||
const pax_font_t* font = pax_get_font("sky mono");
|
||||
pax_background(pax_buffer, 0xFFFFFF);
|
||||
pax_vec1_t title_size = pax_text_size(font, 18, "Firmware update");
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, (320 / 2) - (title_size.x / 2), 120 - 30, "Firmware update");
|
||||
pax_vec1_t size = pax_text_size(font, 18, text);
|
||||
pax_draw_text(pax_buffer, 0xFF000000, font, 18, (320 / 2) - (size.x / 2), 120 + 10, text);
|
||||
ili9341_write(ili9341, pax_buffer->buf);
|
||||
}
|
||||
|
||||
|
||||
void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) {
|
||||
esp_wifi_set_ps(WIFI_PS_NONE); // Disable any WiFi power save mode
|
||||
|
||||
ESP_LOGI(TAG, "Starting OTA update");
|
||||
|
||||
esp_http_client_config_t config = {
|
||||
.url = "https://ota.bodge.team/mch2022.bin",
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
.cert_pem = (char *)server_cert_pem_start,
|
||||
.event_handler = _http_event_handler,
|
||||
.keep_alive_enable = true
|
||||
};
|
||||
|
||||
esp_https_ota_config_t ota_config = {
|
||||
.http_config = &config,
|
||||
.http_client_init_cb = _http_client_init_cb, // Register a callback to be invoked after esp_http_client is initialized
|
||||
#ifdef CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD
|
||||
.partial_http_download = true,
|
||||
.max_http_request_size = CONFIG_EXAMPLE_HTTP_REQUEST_SIZE,
|
||||
#endif
|
||||
};
|
||||
|
||||
//config.skip_cert_common_name_check = true;
|
||||
|
||||
ESP_LOGI(TAG, "Attempting to download update from %s", config.url);
|
||||
|
||||
display_ota_state(pax_buffer, ili9341, "Starting download...");
|
||||
|
||||
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed");
|
||||
display_ota_state(pax_buffer, ili9341, "Failed to start download");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
esp_app_desc_t app_desc;
|
||||
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed");
|
||||
esp_https_ota_abort(https_ota_handle);
|
||||
display_ota_state(pax_buffer, ili9341, "Failed to read image desc");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
err = validate_image_header(&app_desc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "image header verification failed");
|
||||
esp_https_ota_abort(https_ota_handle);
|
||||
display_ota_state(pax_buffer, ili9341, "Image header verification failed");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
esp_err_t ota_finish_err = ESP_OK;
|
||||
int percent_shown = -1;
|
||||
while (1) {
|
||||
err = esp_https_ota_perform(https_ota_handle);
|
||||
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
int len_total = esp_https_ota_get_image_size(https_ota_handle);
|
||||
int len_read = esp_https_ota_get_image_len_read(https_ota_handle);
|
||||
int percent = (len_read * 100) / len_total;
|
||||
|
||||
if (percent != percent_shown) {
|
||||
ESP_LOGI(TAG, "Downloading %d / %d (%d%%)", len_read, len_total, percent);
|
||||
percent_shown = percent;
|
||||
char buffer[128];
|
||||
snprintf(buffer, sizeof(buffer), "Updating... %d%%", percent);
|
||||
display_ota_state(pax_buffer, ili9341, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
|
||||
// the OTA image was not completely received and user can customise the response to this situation.
|
||||
ESP_LOGE(TAG, "Complete data was not received.");
|
||||
display_ota_state(pax_buffer, ili9341, "Download failed");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
} else {
|
||||
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||
if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
|
||||
ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ...");
|
||||
display_ota_state(pax_buffer, ili9341, "Update installed");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
} else {
|
||||
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||
display_ota_state(pax_buffer, ili9341, "Image validation failed");
|
||||
} else {
|
||||
display_ota_state(pax_buffer, ili9341, "Update failed");
|
||||
}
|
||||
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err);
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
}
|
||||
|
||||
esp_https_ota_abort(https_ota_handle);
|
||||
esp_restart();
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 706 B |
Before Width: | Height: | Size: 468 B |
Before Width: | Height: | Size: 395 B |
Before Width: | Height: | Size: 164 B |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 700 B |
Before Width: | Height: | Size: 857 B |
Before Width: | Height: | Size: 725 B |
Before Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 1 KiB |
|
@ -1,31 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 186 KiB |