mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Merge pull request #446 from armel/feature_update_v4
🇺🇸 EN - Optimization of the build script - Code refactoring and memory usage improvements (thanks Reppad) - Integration of the k5viewer feature in Bandscope, Broadcast, and RescueOps editions - Development of the k5viewer Python script - Development of the k5viewer Web application - Improvement of the k5viewer Python script (thanks Michael) - Add Narrower feature in the Basic edition 🇫🇷 FR - Optimisation du script de compilation - Refactorisation du code et amélioration de la gestion mémoire (merci Reppad) - Intégration de la fonctionnalité k5viewer dans les éditions Bandscope, Broadcast et RescueOps - Développement du script Python k5viewer - Développement de l’application Web k5viewer - Amélioration du script Python k5viewer (merci Michael) - Ajout de la fonctionnalité Narrower dans l’édition Basic Bandscope edition, 391 bytes left. Broadcast edition, 1 311 bytes left. RescueOps edition, 3 559 bytes left. Basic edition, 51 bytes left. Game edition, 599 bytes left.
This commit is contained in:
29 files changed
+614
-111
No files matched your search
@@ -4,46 +4,56 @@
|
||||
# 1 = enable
|
||||
|
||||
# ---- STOCK QUANSHENG FEATURES ----
|
||||
ENABLE_FMRADIO ?= 0
|
||||
ENABLE_UART ?= 1
|
||||
ENABLE_AIRCOPY ?= 0
|
||||
ENABLE_NOAA ?= 0
|
||||
ENABLE_VOICE ?= 0
|
||||
ENABLE_VOX ?= 1
|
||||
ENABLE_ALARM ?= 0
|
||||
ENABLE_TX1750 ?= 1
|
||||
ENABLE_PWRON_PASSWORD ?= 0
|
||||
ENABLE_DTMF_CALLING ?= 0
|
||||
ENABLE_FLASHLIGHT ?= 1
|
||||
ENABLE_FMRADIO ?= 0
|
||||
ENABLE_UART ?= 1
|
||||
ENABLE_AIRCOPY ?= 0
|
||||
ENABLE_NOAA ?= 0
|
||||
ENABLE_VOICE ?= 0
|
||||
ENABLE_VOX ?= 1
|
||||
ENABLE_ALARM ?= 0
|
||||
ENABLE_TX1750 ?= 1
|
||||
ENABLE_PWRON_PASSWORD ?= 0
|
||||
ENABLE_DTMF_CALLING ?= 0
|
||||
ENABLE_FLASHLIGHT ?= 1
|
||||
|
||||
# ---- CUSTOM MODS ----
|
||||
ENABLE_SPECTRUM ?= 0
|
||||
ENABLE_BIG_FREQ ?= 1
|
||||
ENABLE_SMALL_BOLD ?= 1
|
||||
ENABLE_CUSTOM_MENU_LAYOUT ?= 1
|
||||
ENABLE_KEEP_MEM_NAME ?= 1
|
||||
ENABLE_WIDE_RX ?= 1
|
||||
ENABLE_TX_WHEN_AM ?= 0
|
||||
ENABLE_F_CAL_MENU ?= 0
|
||||
ENABLE_CTCSS_TAIL_PHASE_SHIFT ?= 0
|
||||
ENABLE_BOOT_BEEPS ?= 0
|
||||
ENABLE_SHOW_CHARGE_LEVEL ?= 0
|
||||
ENABLE_REVERSE_BAT_SYMBOL ?= 0
|
||||
ENABLE_NO_CODE_SCAN_TIMEOUT ?= 1
|
||||
ENABLE_AM_FIX ?= 1
|
||||
ENABLE_SQUELCH_MORE_SENSITIVE ?= 1
|
||||
ENABLE_FASTER_CHANNEL_SCAN ?= 1
|
||||
ENABLE_RSSI_BAR ?= 1
|
||||
ENABLE_AUDIO_BAR ?= 1
|
||||
ENABLE_COPY_CHAN_TO_VFO ?= 1
|
||||
ENABLE_REDUCE_LOW_MID_TX_POWER ?= 0
|
||||
ENABLE_BYP_RAW_DEMODULATORS ?= 0
|
||||
ENABLE_BLMIN_TMP_OFF ?= 0
|
||||
ENABLE_SCAN_RANGES ?= 1
|
||||
ENABLE_FEAT_F4HWN ?= 1
|
||||
ENABLE_FEAT_F4HWN_GAME ?= 0
|
||||
ENABLE_FEAT_F4HWN_SCREENSHOT ?= 0
|
||||
ENABLE_FEAT_F4HWN_SPECTRUM ?= 1
|
||||
ENABLE_SPECTRUM ?= 0
|
||||
ENABLE_BIG_FREQ ?= 1
|
||||
ENABLE_SMALL_BOLD ?= 1
|
||||
ENABLE_CUSTOM_MENU_LAYOUT ?= 1
|
||||
ENABLE_KEEP_MEM_NAME ?= 1
|
||||
ENABLE_WIDE_RX ?= 1
|
||||
ENABLE_TX_WHEN_AM ?= 0
|
||||
ENABLE_F_CAL_MENU ?= 0
|
||||
ENABLE_CTCSS_TAIL_PHASE_SHIFT ?= 0
|
||||
ENABLE_BOOT_BEEPS ?= 0
|
||||
ENABLE_SHOW_CHARGE_LEVEL ?= 0
|
||||
ENABLE_REVERSE_BAT_SYMBOL ?= 0
|
||||
ENABLE_NO_CODE_SCAN_TIMEOUT ?= 1
|
||||
ENABLE_AM_FIX ?= 1
|
||||
ENABLE_SQUELCH_MORE_SENSITIVE ?= 1
|
||||
ENABLE_FASTER_CHANNEL_SCAN ?= 1
|
||||
ENABLE_RSSI_BAR ?= 1
|
||||
ENABLE_AUDIO_BAR ?= 1
|
||||
ENABLE_COPY_CHAN_TO_VFO ?= 1
|
||||
ENABLE_REDUCE_LOW_MID_TX_POWER ?= 0
|
||||
ENABLE_BYP_RAW_DEMODULATORS ?= 0
|
||||
ENABLE_BLMIN_TMP_OFF ?= 0
|
||||
ENABLE_SCAN_RANGES ?= 1
|
||||
|
||||
# ---- CONTRIB MODS ----
|
||||
|
||||
# Thank you @markusb
|
||||
ENABLE_REGA ?= 0
|
||||
# Thank you @reppad
|
||||
ENABLE_EXTRA_UART_CMD ?= 0
|
||||
|
||||
# ---- F4HWN MODS ----
|
||||
|
||||
ENABLE_FEAT_F4HWN ?= 1
|
||||
ENABLE_FEAT_F4HWN_GAME ?= 0
|
||||
ENABLE_FEAT_F4HWN_SCREENSHOT ?= 0
|
||||
ENABLE_FEAT_F4HWN_SPECTRUM ?= 1
|
||||
ENABLE_FEAT_F4HWN_RX_TX_TIMER ?= 1
|
||||
ENABLE_FEAT_F4HWN_CHARGING_C ?= 0
|
||||
ENABLE_FEAT_F4HWN_SLEEP ?= 1
|
||||
@@ -56,20 +66,19 @@ ENABLE_FEAT_F4HWN_VOL ?= 0
|
||||
ENABLE_FEAT_F4HWN_RESET_CHANNEL ?= 0
|
||||
ENABLE_FEAT_F4HWN_PMR ?= 0
|
||||
ENABLE_FEAT_F4HWN_GMRS_FRS_MURS ?= 0
|
||||
ENABLE_FEAT_F4HWN_CA ?= 1
|
||||
ENABLE_FEAT_F4HWN_CA ?= 1
|
||||
ENABLE_FEAT_F4HWN_DEBUG ?= 0
|
||||
ENABLE_REGA ?= 0
|
||||
|
||||
# ---- DEBUGGING ----
|
||||
ENABLE_AM_FIX_SHOW_DATA ?= 0
|
||||
ENABLE_AGC_SHOW_DATA ?= 0
|
||||
ENABLE_UART_RW_BK_REGS ?= 0
|
||||
ENABLE_AM_FIX_SHOW_DATA ?= 0
|
||||
ENABLE_AGC_SHOW_DATA ?= 0
|
||||
ENABLE_UART_RW_BK_REGS ?= 0
|
||||
|
||||
# ---- COMPILER/LINKER OPTIONS ----
|
||||
ENABLE_CLANG ?= 0
|
||||
ENABLE_SWD ?= 0
|
||||
ENABLE_OVERLAY ?= 0
|
||||
ENABLE_LTO ?= 1
|
||||
ENABLE_CLANG ?= 0
|
||||
ENABLE_SWD ?= 0
|
||||
ENABLE_OVERLAY ?= 0
|
||||
ENABLE_LTO ?= 1
|
||||
|
||||
#############################################################
|
||||
|
||||
@@ -154,6 +163,9 @@ OBJS += app/menu.o
|
||||
ifeq ($(ENABLE_SPECTRUM), 1)
|
||||
OBJS += app/spectrum.o
|
||||
endif
|
||||
ifeq ($(ENABLE_FEAT_F4HWN_SCREENSHOT), 1)
|
||||
OBJS += screenshot.o
|
||||
endif
|
||||
ifeq ($(ENABLE_FEAT_F4HWN_GAME), 1)
|
||||
OBJS += app/breakout.o
|
||||
endif
|
||||
@@ -234,7 +246,7 @@ ifeq ($(ENABLE_FEAT_F4HWN),1)
|
||||
VERSION_STRING_1 ?= v0.22
|
||||
|
||||
AUTHOR_STRING_2 ?= F4HWN
|
||||
VERSION_STRING_2 ?= v4.1
|
||||
VERSION_STRING_2 ?= v4.2
|
||||
|
||||
EDITION_STRING ?= Custom
|
||||
|
||||
@@ -485,6 +497,9 @@ endif
|
||||
ifeq ($(ENABLE_FEAT_F4HWN_DEBUG),1)
|
||||
CFLAGS += -DENABLE_FEAT_F4HWN_DEBUG
|
||||
endif
|
||||
ifeq ($(ENABLE_EXTRA_UART_CMD),1)
|
||||
CFLAGS += -DENABLE_EXTRA_UART_CMD
|
||||
endif
|
||||
|
||||
LDFLAGS =
|
||||
LDFLAGS += -z noexecstack -mcpu=cortex-m0 -nostartfiles -Wl,-T,firmware.ld -Wl,--gc-sections
|
||||
|
||||
@@ -119,7 +119,7 @@ Special thanks to Jean-Cyrille F6IWW (2 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* scan list 2,
|
||||
* scan list 3,
|
||||
* scan lists [1, 2, 3],
|
||||
* scan all (all channels with ou without list),
|
||||
* scan all (all channels with or without list),
|
||||
* add scan list shortcuts,
|
||||
* add resume mode on startup (scan, bandscope and broadcast FM),
|
||||
* new actions:
|
||||
@@ -153,6 +153,7 @@ Special thanks to Jean-Cyrille F6IWW (2 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* remove 200Tx, 350Tx and 500Tx,
|
||||
* unlock TX on all bands needs only to be repeat 3 times,
|
||||
* code refactoring and many memory optimization,
|
||||
* displays the live screen of the Quansheng K5 on your computer via a USB-to-Serial cable,
|
||||
* and more...
|
||||
|
||||
## Main features from Egzumer:
|
||||
|
||||
@@ -46,10 +46,6 @@
|
||||
#include "app/rega.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FMRADIO)
|
||||
static void ACTION_Scan_FM(bool bRestart);
|
||||
#endif
|
||||
@@ -349,11 +345,6 @@ void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
funcShort = funcLong;
|
||||
|
||||
// For screenshot
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot();
|
||||
#endif
|
||||
|
||||
if (!bKeyPressed) //ignore release if held
|
||||
return;
|
||||
}
|
||||
|
||||
+5
-2
@@ -51,6 +51,9 @@ static void AIRCOPY_clear()
|
||||
{
|
||||
crc[i] = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AIRCOPY_SendMessage(void)
|
||||
@@ -78,7 +81,7 @@ bool AIRCOPY_SendMessage(void)
|
||||
if (++gAirCopyBlockNumber >= 0x78) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot();
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
//NVIC_SystemReset();
|
||||
}
|
||||
@@ -139,7 +142,7 @@ void AIRCOPY_StorePacket(void)
|
||||
if (Offset == 0x1E00) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot();
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@
|
||||
#include "ui/status.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#endif
|
||||
|
||||
static bool flagSaveVfo;
|
||||
static bool flagSaveSettings;
|
||||
static bool flagSaveChannel;
|
||||
@@ -1381,13 +1385,23 @@ void APP_TimeSlice10ms(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gUpdateDisplay) {
|
||||
bool gUpdateDisplayCurrent = gUpdateDisplay;
|
||||
bool gUpdateStatusCurrent = gUpdateStatus;
|
||||
|
||||
if (gUpdateDisplayCurrent) {
|
||||
gUpdateDisplay = false;
|
||||
GUI_DisplayScreen();
|
||||
}
|
||||
|
||||
if (gUpdateStatus)
|
||||
if (gUpdateStatusCurrent) {
|
||||
UI_DisplayStatus();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
if (gUpdateDisplayCurrent || gUpdateStatusCurrent) {
|
||||
getScreenShot(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Skipping authentic device checks
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
#include "app/breakout.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#endif
|
||||
|
||||
static uint32_t randSeed = 1;
|
||||
static uint8_t blockAnim = 0;
|
||||
|
||||
@@ -478,6 +482,11 @@ void APP_RunBreakout(void) {
|
||||
if(swap == 0)
|
||||
{
|
||||
blockAnim = (blockAnim + 1) % 4;
|
||||
|
||||
// For screenshot
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
swap = (swap + 1) % 4;
|
||||
|
||||
+4
-4
@@ -728,10 +728,6 @@ static void ToggleBacklight()
|
||||
{
|
||||
BACKLIGHT_TurnOff();
|
||||
}
|
||||
// For screenshot
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ToggleStepsCount()
|
||||
@@ -1645,6 +1641,10 @@ static void Tick()
|
||||
if (redrawScreen)
|
||||
{
|
||||
Render();
|
||||
// For screenshot
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
redrawScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -106,6 +106,7 @@ typedef struct {
|
||||
} Data;
|
||||
} REPLY_051D_t;
|
||||
|
||||
#ifdef ENABLE_EXTRA_UART_CMD
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
@@ -127,6 +128,7 @@ typedef struct {
|
||||
Header_t Header;
|
||||
uint32_t Response[4];
|
||||
} CMD_052D_t;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
@@ -136,10 +138,13 @@ typedef struct {
|
||||
} Data;
|
||||
} REPLY_052D_t;
|
||||
|
||||
|
||||
#ifdef ENABLE_EXTRA_UART_CMD
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
uint32_t Timestamp;
|
||||
} CMD_052F_t;
|
||||
#endif
|
||||
|
||||
static const uint8_t Obfuscation[16] =
|
||||
{
|
||||
@@ -329,6 +334,7 @@ static void CMD_051D(const uint8_t *pBuffer)
|
||||
SendReply(&Reply, sizeof(Reply));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EXTRA_UART_CMD
|
||||
// read RSSI
|
||||
static void CMD_0527(void)
|
||||
{
|
||||
@@ -438,6 +444,7 @@ static void CMD_052F(const uint8_t *pBuffer)
|
||||
|
||||
SendVersion();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_UART_RW_BK_REGS
|
||||
static void CMD_0601_ReadBK4819Reg(const uint8_t *pBuffer)
|
||||
@@ -591,7 +598,8 @@ void UART_HandleCommand(void)
|
||||
|
||||
case 0x0521: // Not implementing non-authentic command
|
||||
break;
|
||||
|
||||
|
||||
#ifdef ENABLE_EXTRA_UART_CMD
|
||||
case 0x0527:
|
||||
CMD_0527();
|
||||
break;
|
||||
@@ -609,6 +617,7 @@ void UART_HandleCommand(void)
|
||||
case 0x052F:
|
||||
CMD_052F(UART_Command.Buffer);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 0x05DD: // reset
|
||||
#if defined(ENABLE_OVERLAY)
|
||||
@@ -628,4 +637,7 @@ void UART_HandleCommand(void)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
gUART_LockScreenshot = 20; // lock screenshot
|
||||
#endif
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+26
-9
@@ -9,15 +9,28 @@ mkdir -p "$FIRMWARE_DIR"
|
||||
# Clean previously compiled firmware files
|
||||
rm -f "$FIRMWARE_DIR"/*
|
||||
|
||||
# Build image only if it doesn't already exist
|
||||
if ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then
|
||||
echo "Docker image '$IMAGE_NAME' not found, building..."
|
||||
if ! docker build --build-arg BUILDPLATFORM=linux/amd64 -t "$IMAGE_NAME" .; then
|
||||
echo "❌ Failed to build docker image"
|
||||
exit 1
|
||||
fi
|
||||
# Clean up old Docker artifacts
|
||||
echo "🧽 Cleaning up old Docker artifacts..."
|
||||
docker system prune -f --volumes >/dev/null 2>&1 || true
|
||||
|
||||
# Always rebuild the Docker image to ensure latest code changes
|
||||
echo "⚙️ Rebuilding Docker image '$IMAGE_NAME'..."
|
||||
docker rmi "$IMAGE_NAME" 2>/dev/null || true
|
||||
if ! docker build --build-arg BUILDPLATFORM=linux/amd64 -t "$IMAGE_NAME" .; then
|
||||
echo "❌ Failed to build docker image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------------------- CLEAN ALL ---------------------
|
||||
|
||||
clean() {
|
||||
echo "🧽 Cleaning all"
|
||||
docker rmi uvk5
|
||||
docker buildx prune -f
|
||||
docker buildx history ls | awk 'NR>1 {print $1}' | xargs docker buildx history rm
|
||||
make clean
|
||||
}
|
||||
|
||||
# ------------------ BUILD VARIANTS ------------------
|
||||
|
||||
custom() {
|
||||
@@ -50,6 +63,7 @@ bandscope() {
|
||||
ENABLE_FMRADIO=0 \
|
||||
ENABLE_VOX=0 \
|
||||
ENABLE_AIRCOPY=1 \
|
||||
ENABLE_FEAT_F4HWN_SCREENSHOT=1 \
|
||||
ENABLE_FEAT_F4HWN_GAME=0 \
|
||||
ENABLE_FEAT_F4HWN_PMR=1 \
|
||||
ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \
|
||||
@@ -68,6 +82,7 @@ broadcast() {
|
||||
ENABLE_FMRADIO=1 \
|
||||
ENABLE_VOX=1 \
|
||||
ENABLE_AIRCOPY=1 \
|
||||
ENABLE_FEAT_F4HWN_SCREENSHOT=1 \
|
||||
ENABLE_FEAT_F4HWN_GAME=0 \
|
||||
ENABLE_FEAT_F4HWN_PMR=1 \
|
||||
ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \
|
||||
@@ -96,7 +111,7 @@ basic() {
|
||||
ENABLE_FEAT_F4HWN_CHARGING_C=0 \
|
||||
ENABLE_FEAT_F4HWN_INV=1 \
|
||||
ENABLE_FEAT_F4HWN_CTR=0 \
|
||||
ENABLE_FEAT_F4HWN_NARROWER=0 \
|
||||
ENABLE_FEAT_F4HWN_NARROWER=1 \
|
||||
ENABLE_FEAT_F4HWN_RESCUE_OPS=0 \
|
||||
EDITION_STRING=Basic \
|
||||
TARGET=f4hwn.basic \
|
||||
@@ -111,6 +126,7 @@ rescueops() {
|
||||
ENABLE_FMRADIO=0 \
|
||||
ENABLE_VOX=1 \
|
||||
ENABLE_AIRCOPY=1 \
|
||||
ENABLE_FEAT_F4HWN_SCREENSHOT=1 \
|
||||
ENABLE_FEAT_F4HWN_GAME=0 \
|
||||
ENABLE_FEAT_F4HWN_PMR=1 \
|
||||
ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \
|
||||
@@ -142,6 +158,7 @@ game() {
|
||||
# ------------------ MENU ------------------
|
||||
|
||||
case "$1" in
|
||||
clean) clean ;;
|
||||
custom) custom ;;
|
||||
standard) standard ;;
|
||||
bandscope) bandscope ;;
|
||||
@@ -157,7 +174,7 @@ case "$1" in
|
||||
game
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {custom|standard|bandscope|broadcast|basic|rescueops|game|all} [--rebuild]"
|
||||
echo "Usage: $0 {clean|custom|standard|bandscope|broadcast|basic|rescueops|game|all}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "bsp/dp32g030/dma.h"
|
||||
#include "bsp/dp32g030/syscon.h"
|
||||
#include "bsp/dp32g030/uart.h"
|
||||
@@ -102,3 +103,15 @@ void UART_LogSend(const void *pBuffer, uint32_t Size)
|
||||
UART_Send(pBuffer, Size);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool UART_IsCableConnected(void) {
|
||||
for (size_t i = 0; i < sizeof(UART_DMA_Buffer); i++) {
|
||||
if (UART_DMA_Buffer[i] == 0x55) {
|
||||
UART_DMA_Buffer[i] = 0x00; // Clear only the matched byte
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -26,5 +26,9 @@ void UART_Init(void);
|
||||
void UART_Send(const void *pBuffer, uint32_t Size);
|
||||
void UART_LogSend(const void *pBuffer, uint32_t Size);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool UART_IsCableConnected(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# Quansheng K5Viewer
|
||||
|
||||
K5Viewer is a lightweight viewer that displays the live screen output of the Quansheng K5 transceiver (running F4HWN firmware), transmitted via UART to your computer through a Baofeng/Kenwood-style USB-to-Serial cable.
|
||||
|
||||
https://github.com/user-attachments/assets/578c8605-9820-4a2c-90e4-5a93b2be7d2b
|
||||
|
||||
> [!WARNING]
|
||||
> K5Viewer IS NOT A REMOTE CONTROL TOOL.
|
||||
|
||||
It simply displays the live screen of the Quansheng K5 on your computer and allows you to take screenshots.
|
||||
It can be useful for illustrating documentation, creating tutorials, or demonstrating how K5Viewer works — whether by screen sharing or using a video projector.
|
||||
It's also a handy tool for development and debugging, especially for display-related features.
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
- Realtime display of 128×64 monochrome screen via serial connection (UART)
|
||||
- Delta frame updates to minimize bandwidth usage
|
||||
- Capture screen snapshots in PNG format
|
||||
- Switch background color (gray, blue, or orange)
|
||||
- Toggle inverted video mode
|
||||
- Toggle LCD pixel rendering mode
|
||||
- Resize the window (zoom in/out)
|
||||
- Display current FPS in the window title
|
||||
|
||||
## 🛠️ Requirements
|
||||
|
||||
- Python **3.6+**
|
||||
- pip (Python package installer)
|
||||
|
||||
### 📦 Install dependencies:
|
||||
|
||||
If necessary, use this command to install dependencies:
|
||||
|
||||
```bash
|
||||
pip install pyserial pygame
|
||||
```
|
||||
|
||||
## ▶️ How to Run
|
||||
|
||||
1. Connect your **Quansheng K5** (running **F4HWN firmware** v4.2) to your computer using a Baofeng/Kenwood-style USB-to-Serial cable.
|
||||
|
||||
2. Run the `k5viewer` with the appropriate serial port:
|
||||
|
||||
```bash
|
||||
./k5viewer.py -port /dev/ttyUSB0 # Linux
|
||||
./k5viewer.py -port /dev/cu.usbserial-xxxx # macOS
|
||||
./k5viewer.py -port COM3 # Windows
|
||||
```
|
||||
> [!NOTE]
|
||||
> If no -port is provided, the script defaults to /dev/ttyUSB0.
|
||||
|
||||
Alternatively, you can manually edit the script and change the `DEFAULT_PORT` variable near the top of the file:
|
||||
|
||||
```python
|
||||
DEFAULT_PORT = '/dev/ttyUSB0' # Linux
|
||||
# or
|
||||
DEFAULT_PORT = '/dev/cu.usbserial-xxxx' # macOS
|
||||
# or
|
||||
DEFAULT_PORT = 'COM3' # Windows
|
||||
```
|
||||
|
||||
You can also list available serial ports to help you choose:
|
||||
|
||||
```bash
|
||||
./k5viewer.py --list-ports
|
||||
```
|
||||
|
||||
## 🎮 Controls
|
||||
|
||||
| Key | Action |
|
||||
|-----------|---------------------------------|
|
||||
| `Q` | Quit the viewer |
|
||||
| `G` | Set background to **gray** |
|
||||
| `B` | Set background to **blue** |
|
||||
| `O` | Set background to **orange** |
|
||||
| `W` | Set background to **white** |
|
||||
| `I` | Toggle **video inversion** |
|
||||
| `P` | Toggle **LCD pixel mode** |
|
||||
| `SPACE` | Save a screenshot as PNG |
|
||||
| `UP` | Increase window size |
|
||||
| `DOWN` | Decrease window size |
|
||||
|
||||
|
||||
Screenshots are saved as `screenshot_YYYYMMDD_HHMMSS.png` in the same directory.
|
||||
|
||||
## 📬 Contact
|
||||
|
||||
If you encounter issues or have suggestions, feel free to open an issue or submit a pull request. Enjoy building with your Quansheng K5! 📡
|
||||
Executable
+228
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
import argparse
|
||||
|
||||
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
||||
|
||||
import pygame
|
||||
import serial
|
||||
from serial.tools import list_ports
|
||||
|
||||
# Version
|
||||
VERSION = '1.0'
|
||||
|
||||
# Serial configuration
|
||||
DEFAULT_PORT = '/dev/ttyUSB0' # Change if needed (/dev/cu.usbserial-11130)
|
||||
BAUDRATE = 38400
|
||||
TIMEOUT = 0.5
|
||||
|
||||
# Screen configuration
|
||||
WIDTH, HEIGHT = 128, 64
|
||||
FRAME_SIZE = 1024
|
||||
|
||||
# Protocol
|
||||
HEADER = b'\xAA\x55'
|
||||
TYPE_SCREENSHOT = b'\x01'
|
||||
TYPE_DIFF = b'\x02'
|
||||
|
||||
# Framebuffer
|
||||
framebuffer = bytearray([0] * FRAME_SIZE)
|
||||
|
||||
|
||||
COLOR_SETS = { # {key: (name, foreground, background)}
|
||||
"g": ("Grey", pygame.Color(0, 0, 0), pygame.Color(202, 202, 202)),
|
||||
"o": ("Orange", pygame.Color(0, 0, 0), pygame.Color(255, 193, 37)),
|
||||
"b": ("Blue", pygame.Color(0, 0, 0), pygame.Color(28, 134, 228)),
|
||||
"w": ("White", pygame.Color(0, 0, 0), pygame.Color(255, 255, 255)),
|
||||
}
|
||||
|
||||
DEFAULT_COLOR = "g" # Must be a key of "COLOR_SETS"
|
||||
|
||||
def send_keepalive(ser: serial.Serial):
|
||||
# Send keepalive frame
|
||||
try:
|
||||
ser.write(b'\x55\xAA\x00\x00') # Keepalive frame
|
||||
except serial.SerialException:
|
||||
pass
|
||||
|
||||
def read_frame(ser: serial.Serial) -> bytearray:
|
||||
global framebuffer
|
||||
while True:
|
||||
try:
|
||||
b = ser.read(1)
|
||||
except serial.SerialException as e:
|
||||
#print(f"[ERROR] Serial read failed: {e}")
|
||||
print("[!] Your USB serial cable is probably being used by another application such as Chirp or Chrome.")
|
||||
sys.exit(1)
|
||||
if not b:
|
||||
return None
|
||||
if b == HEADER[0:1]:
|
||||
b2 = ser.read(1)
|
||||
if b2 == HEADER[1:2]:
|
||||
t = ser.read(1)
|
||||
size_bytes = ser.read(2)
|
||||
size = int.from_bytes(size_bytes, 'big')
|
||||
if t == TYPE_SCREENSHOT and size == FRAME_SIZE:
|
||||
payload = ser.read(FRAME_SIZE)
|
||||
framebuffer = bytearray(payload)
|
||||
return framebuffer
|
||||
elif t == TYPE_DIFF and size % 9 == 0:
|
||||
payload = ser.read(size)
|
||||
framebuffer = apply_diff(framebuffer, payload)
|
||||
return framebuffer
|
||||
|
||||
|
||||
def apply_diff(framebuffer: bytearray, diff_payload: bytes) -> bytearray:
|
||||
i = 0
|
||||
while i + 9 <= len(diff_payload):
|
||||
block_index = diff_payload[i]
|
||||
i += 1
|
||||
if block_index >= 128:
|
||||
break
|
||||
framebuffer[block_index * 8 : block_index * 8 + 8] = diff_payload[i : i + 8]
|
||||
i += 8
|
||||
return framebuffer
|
||||
|
||||
|
||||
def draw_frame(screen: pygame.Surface, framebuffer: bytearray, bg_color: pygame.Color, fg_color: pygame.Color, pixel_size: int = 4, pixel_lcd: int = 0) -> pygame.Surface:
|
||||
def get_bit(bit_idx):
|
||||
byte_idx = bit_idx // 8
|
||||
bit_pos = bit_idx % 8
|
||||
if byte_idx < len(framebuffer):
|
||||
return (framebuffer[byte_idx] >> bit_pos) & 0x01
|
||||
return 0
|
||||
|
||||
screen.fill(bg_color)
|
||||
bit_index = 0
|
||||
for y in range(64):
|
||||
for x in range(128):
|
||||
if get_bit(bit_index):
|
||||
px = x * (pixel_size - 1)
|
||||
py = y * pixel_size
|
||||
pygame.draw.rect(screen, fg_color, (px, py, pixel_size - 1 - pixel_lcd, pixel_size - pixel_lcd))
|
||||
bit_index += 1
|
||||
|
||||
pygame.display.flip()
|
||||
return pygame.display.get_surface().copy()
|
||||
|
||||
|
||||
def run_viewer(args: argparse.Namespace, ser: serial.Serial):
|
||||
pixel_size = 5
|
||||
pixel_lcd = 0
|
||||
pygame.init()
|
||||
screen = pygame.display.set_mode((WIDTH * (pixel_size - 1), HEIGHT * pixel_size))
|
||||
base_title = f"Quansheng K5Viewer v{VERSION} by F4HWN"
|
||||
pygame.display.set_caption(f"{base_title} – No data")
|
||||
|
||||
fg_color, bg_color = COLOR_SETS[DEFAULT_COLOR][1:]
|
||||
last_surface = None
|
||||
frame_count = 0
|
||||
frame_lost = 0
|
||||
last_time = time.monotonic()
|
||||
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
raise KeyboardInterrupt
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_q:
|
||||
raise KeyboardInterrupt
|
||||
if event.key == pygame.K_SPACE and last_surface:
|
||||
filename = datetime.datetime.now().strftime("screenshot_%Y%m%d_%H%M%S.png")
|
||||
pygame.image.save(last_surface, filename)
|
||||
print(f"[✔] Screenshot saved: {filename}")
|
||||
elif event.key == pygame.K_p:
|
||||
pixel_lcd = 1 - pixel_lcd
|
||||
draw_frame(screen, framebuffer, bg_color, fg_color, pixel_size, pixel_lcd)
|
||||
elif event.key == pygame.K_i:
|
||||
if bg_color == pygame.Color(0, 0, 0):
|
||||
bg_color, fg_color = fg_color, pygame.Color(0, 0, 0)
|
||||
else:
|
||||
bg_color, fg_color = pygame.Color(0, 0, 0), bg_color
|
||||
draw_frame(screen, framebuffer, bg_color, fg_color, pixel_size, pixel_lcd)
|
||||
elif event.key == pygame.K_UP:
|
||||
if pixel_size < 12:
|
||||
pixel_size += 1
|
||||
#print(f"[✔] Resize: {pixel_size, (WIDTH * (pixel_size - 1)), HEIGHT * pixel_size}")
|
||||
screen = pygame.display.set_mode((WIDTH * (pixel_size - 1), HEIGHT * pixel_size))
|
||||
draw_frame(screen, framebuffer, bg_color, fg_color, pixel_size, pixel_lcd)
|
||||
elif event.key == pygame.K_DOWN:
|
||||
if pixel_size > 3:
|
||||
pixel_size -= 1
|
||||
#print(f"[✔] Resize: {pixel_size, (WIDTH * (pixel_size - 1)), HEIGHT * pixel_size}")
|
||||
screen = pygame.display.set_mode((WIDTH * (pixel_size - 1), HEIGHT * pixel_size))
|
||||
draw_frame(screen, framebuffer, bg_color, fg_color, pixel_size, pixel_lcd)
|
||||
pressed_key = event.unicode
|
||||
if pressed_key in COLOR_SETS.keys():
|
||||
fg_color, bg_color = COLOR_SETS[pressed_key][1:]
|
||||
frame = read_frame(ser)
|
||||
if frame:
|
||||
last_surface = draw_frame(screen, framebuffer, bg_color, fg_color, pixel_size, pixel_lcd)
|
||||
frame_count += 1
|
||||
now = time.monotonic()
|
||||
if now - last_time >= 1.0:
|
||||
fps = frame_count / (now - last_time)
|
||||
pygame.display.set_caption(f"{base_title} – FPS: {fps:>04.1f}")
|
||||
frame_count = 0
|
||||
last_time = now
|
||||
frame_lost = 0
|
||||
else:
|
||||
frame_lost = min(frame_lost + 1, 5)
|
||||
if frame_lost == 5:
|
||||
pygame.display.set_caption(f"{base_title} – No data")
|
||||
|
||||
send_keepalive(ser)
|
||||
|
||||
|
||||
def cmd_list_ports(args: argparse.Namespace):
|
||||
ports = list_ports.comports()
|
||||
print("Available ports:")
|
||||
for port in ports:
|
||||
if port.vid is None: # Skipping virtual or non-USB ports
|
||||
continue
|
||||
description = " - ".join(filter(None, (port.product, port.manufacturer)))
|
||||
if description:
|
||||
print(f"- {description} : {port.device}")
|
||||
else:
|
||||
print(f"- {port.device}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="K5Viewer",
|
||||
description="A live viewer for UV-K5 radios with F4HWN firmware",
|
||||
epilog="F4HWN repo: https://github.com/armel/uv-k5-firmware-custom"
|
||||
)
|
||||
parser.add_argument("--list-ports", action="store_true", help="list available ports and exit")
|
||||
parser.add_argument("--port", type=str, help="serial port to use (in place of 'DEFAULT_PORT')")
|
||||
parser.add_argument("--version", action="version", version=f"%(prog)s {VERSION}", help="show program's version number and exit")
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.list_ports:
|
||||
cmd_list_ports(args)
|
||||
exit(0)
|
||||
# Running viewer
|
||||
if not args.port and not DEFAULT_PORT:
|
||||
print("Please specify the serial port to use or set 'DEFAULT_PORT', do 'k5viewer.py --help' for help")
|
||||
exit(1)
|
||||
serial_port = args.port or DEFAULT_PORT
|
||||
try:
|
||||
ser = serial.Serial(serial_port, BAUDRATE, timeout=TIMEOUT)
|
||||
except serial.SerialException as e:
|
||||
print(f"[!] Serial error: {e}")
|
||||
sys.exit(1)
|
||||
try:
|
||||
run_viewer(args, ser)
|
||||
except KeyboardInterrupt:
|
||||
print("[✔] Exiting")
|
||||
ser.close()
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -190,6 +190,9 @@ volatile bool gTxTimeoutReached;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
volatile uint16_t gRxTimerCountdown_500ms;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
volatile uint8_t gUART_LockScreenshot = 0; // lock screenshot if Chirp is used
|
||||
#endif
|
||||
#endif
|
||||
|
||||
volatile uint16_t gTailNoteEliminationCountdown_10ms;
|
||||
|
||||
@@ -256,6 +256,9 @@ extern volatile bool gTxTimeoutReached;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
extern volatile uint16_t gRxTimerCountdown_500ms;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
extern volatile uint8_t gUART_LockScreenshot; // lock screenshot if Chirp is used
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern volatile uint16_t gTailNoteEliminationCountdown_10ms;
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/* Copyright 2024 Armel F4HWN
|
||||
* https://github.com/armel
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "debugging.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "screenshot.h"
|
||||
#include "misc.h"
|
||||
|
||||
void getScreenShot(bool force)
|
||||
{
|
||||
static uint8_t previousFrame[1024] = {0}; // Last transmitted frame
|
||||
static uint8_t forcedBlock = 0; // Block forced for refresh on each frame
|
||||
static uint8_t keepAlive = 10; // Keepalive counter
|
||||
|
||||
// Use a single buffer to reduce stack usage
|
||||
static uint8_t currentFrame[1024]; // Current frame
|
||||
static uint8_t deltaFrame[128 * 9]; // Delta frame buffer
|
||||
uint16_t index = 0;
|
||||
uint8_t acc = 0;
|
||||
uint8_t bitCount = 0;
|
||||
|
||||
if (gUART_LockScreenshot > 0) // Lock screenshot if Chirp is in used
|
||||
{
|
||||
gUART_LockScreenshot--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 10;
|
||||
}
|
||||
|
||||
if (keepAlive > 0) {
|
||||
if (--keepAlive == 0) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Build current frame from status line (first 8 lines)
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gStatusLine[i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
currentFrame[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build remaining part of the frame (7 * 8 lines)
|
||||
for (uint8_t l = 0; l < 7; l++) {
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gFrameBuffer[l][i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
currentFrame[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitCount > 0)
|
||||
currentFrame[index++] = acc;
|
||||
|
||||
if (index != 1024)
|
||||
return; // Frame size mismatch, abort
|
||||
|
||||
// Generate delta frame
|
||||
uint16_t deltaLen = 0;
|
||||
|
||||
for (uint8_t block = 0; block < 128; block++) {
|
||||
uint8_t *cur = ¤tFrame[block * 8];
|
||||
uint8_t *prev = &previousFrame[block * 8];
|
||||
|
||||
bool changed = memcmp(cur, prev, 8) != 0;
|
||||
bool isForced = (block == forcedBlock);
|
||||
bool fullUpdate = force;
|
||||
|
||||
if (changed || isForced || fullUpdate) {
|
||||
deltaFrame[deltaLen++] = block;
|
||||
memcpy(&deltaFrame[deltaLen], cur, 8);
|
||||
deltaLen += 8;
|
||||
|
||||
memcpy(prev, cur, 8); // Update stored frame
|
||||
}
|
||||
}
|
||||
|
||||
forcedBlock = (forcedBlock + 1) % 128;
|
||||
|
||||
if (deltaLen == 0)
|
||||
return; // No update needed
|
||||
|
||||
// Send the delta frame over UART
|
||||
uint8_t header[5] = {
|
||||
0xAA, 0x55, 0x02,
|
||||
(uint8_t)(deltaLen >> 8),
|
||||
(uint8_t)(deltaLen & 0xFF)
|
||||
};
|
||||
|
||||
UART_Send(header, 5);
|
||||
UART_Send(deltaFrame, deltaLen);
|
||||
uint8_t end = 0x0A;
|
||||
UART_Send(&end, 1);
|
||||
}
|
||||
+4
-33
@@ -14,38 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "debugging.h"
|
||||
#include "driver/st7565.h"
|
||||
#ifndef SCREENSHOT_H
|
||||
#define SCREENSHOT_H
|
||||
|
||||
static inline void getScreenShot(void)
|
||||
{
|
||||
char str[2] = "";
|
||||
void getScreenShot(bool force);
|
||||
|
||||
LogUart("P1\n");
|
||||
LogUart("128 64\n");
|
||||
|
||||
for(uint8_t b = 0; b < 8; b++)
|
||||
{
|
||||
for(uint8_t i = 0; i < 128; i++)
|
||||
{
|
||||
sprintf(str, "%d ", ((gStatusLine[i] >> b) & 0x01));
|
||||
LogUart(str);
|
||||
}
|
||||
LogUart("\n");
|
||||
}
|
||||
|
||||
for(uint8_t l = 0; l < 7; l++)
|
||||
{
|
||||
for(uint8_t b = 0; b < 8; b++)
|
||||
{
|
||||
for(uint8_t i = 0; i < 128; i++)
|
||||
{
|
||||
sprintf(str, "%d ", ((gFrameBuffer[l][i] >> b) & 0x01));
|
||||
LogUart(str);
|
||||
}
|
||||
}
|
||||
LogUart("\n");
|
||||
}
|
||||
|
||||
LogUart("\n----------------\n");
|
||||
}
|
||||
#endif
|
||||
+2
-2
@@ -217,10 +217,10 @@ typedef struct {
|
||||
bool SCAN_LIST_ENABLED[3];
|
||||
uint8_t SCANLIST_PRIORITY_CH1[3];
|
||||
uint8_t SCANLIST_PRIORITY_CH2[3];
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
//#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE // Fix me !!! What the hell is this?
|
||||
uint8_t CURRENT_STATE;
|
||||
uint8_t CURRENT_LIST;
|
||||
#endif
|
||||
//#endif // Fix me !!! What the hell is this?
|
||||
|
||||
uint8_t field29_0x26;
|
||||
uint8_t field30_0x27;
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
#include "version.h"
|
||||
#include "bitmaps.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#endif
|
||||
|
||||
void UI_DisplayReleaseKeys(void)
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
@@ -198,5 +202,9 @@ void UI_DisplayWelcome(void)
|
||||
|
||||
//ST7565_BlitStatusLine(); // blank status line : I think it's useless
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user