Low-cost embedded software development method, program product and running method based on quickjs-ng and lvgl
By using the low-cost embedded software development methods of QuickJS-NG and LVGL, we have achieved complete decoupling of the interface and business logic in embedded systems and efficient UI rendering. This solves the problem of tight coupling between UI logic and business logic in existing technologies, and improves development efficiency and system stability.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2025-10-29
- Publication Date
- 2026-03-24
AI Technical Summary
In existing embedded systems, UI logic and business logic are tightly coupled. The HTML parsing layer introduces performance overhead, making it difficult to develop the interface and business logic independently, and also making it difficult to keep up with framework updates to maintain system stability and security.
We adopt a low-cost embedded software development approach using QuickJS-NG and LVGL. By building a bidirectional communication mechanism between the C language layer and the JavaScript layer, we achieve complete decoupling between the interface and business logic. We employ an efficient UI rendering mechanism that does not require HTML/CSS parsing, and use the EEZ designer to design forms, establish a UI information binding mechanism and a multi-device UI adaptation strategy to ensure system stability and security.
It enables independent development of the interface and business logic, reduces development difficulty, improves code maintainability and user experience, simplifies multi-device adaptation, ensures system security and stability, and allows for continuous updates to the framework.
Abstract
Description
Technical Field
[0001] This invention relates to the field of embedded system software development technology, and in particular to a low-cost embedded software development method, program product, and running method based on QuickJS-NG and LVGL. Background Technology
[0002] Embedded graphical user interface (GUI) development is a crucial component of embedded system applications, and its implementation directly impacts product development efficiency and maintenance costs. Two typical technical approaches exist in this field: one is native development using C / C++ to directly call lightweight graphics libraries such as LVGL; the other is application-layer development using HTML5 / JavaScript on advanced operating systems like Android. While the former can adapt to resource-constrained hardware environments, the latter has significant advantages in development efficiency and talent pool.
[0003] With the development of internet and software development technologies, developers have begun to explore the possibility of introducing web front-end technologies into the embedded field. Considering the high barrier to entry for C / C++ development, and the already mature use of JavaScript for writing business logic in the internet industry, the industry has begun to try integrating JavaScript engines and lightweight graphics libraries into embedded systems. Especially for scenarios where most embedded systems have low hardware configurations and cannot complete full HTML rendering, engineers have proposed a hybrid solution using LVGL as the GUI layer and a JavaScript engine as the logic layer.
[0004] In the prior art, Chinese invention patent application CN117348876A discloses an application development method, system, and medium based on a FreeRTOS embedded system. This solution achieves application development by combining an application layer, an engine processing layer, and a GUI framework layer. The engine processing layer includes a simplified HTML parser capable of parsing a limited number of elements and attributes, and passes the parsing results to the LVGL of the GUI framework layer for interface rendering, achieving static HTML display. Simultaneously, an embedded JavaScript engine monitors data changes, and the GUI framework layer monitors event triggers. Upon detecting an event, a response is executed through a bridging interface and the JavaScript engine, enabling dynamic changes to the HTML. This architecture lowers the development threshold to a certain extent, allowing developers to leverage familiar Web technologies for embedded application development.
[0005] However, existing solutions still have several technical limitations in practical applications:
[0006] First, the event handling mechanism in this solution relies on specific HTML parsing results, which leads to a tight coupling between UI logic and the parsing process, making it difficult to achieve true separation between the interface and business logic.
[0007] Secondly, although the solution uses a combination of HTML / CSS and LVGL to achieve basic interface rendering, the additional HTML parsing layer and the conversion process to native LVGL components introduce performance overhead. Furthermore, the development and implementation of this conversion process itself is highly complex. At the same time, due to the limitations of the HTML parser, the UI functions and interactive effects that can be achieved are also relatively limited.
[0008] Furthermore, this architecture is difficult to implement directly based on the original framework's interface. It requires modifications to the JavaScript engine and LVGL source code, which involves a large workload and cannot guarantee security and stability during subsequent updates to the original framework. Summary of the Invention
[0009] This invention proposes a low-cost embedded software development method, program product, and operation method based on QuickJS-NG and LVGL. Its objectives are: first, to establish a completely decoupled interface and business logic architecture, enabling independent development and maintenance of UI design and application logic; second, to construct an efficient UI rendering mechanism that does not rely on HTML / CSS parsing and conversion, avoiding the performance overhead and functional limitations associated with it; and finally, to form an integration solution that requires no modification to the JavaScript engine and LVGL source code, ensuring that the framework can continuously keep up with official version updates while maintaining system security and stability.
[0010] The technical solution of this invention is as follows:
[0011] A low-cost embedded software development method based on QuickJS-NG and LVGL includes:
[0012] Step 1: Establish a bidirectional communication mechanism between the C language layer and the JavaScript layer; the C language layer is used to run LVGL to implement user interface functions, and the JavaScript layer is used to run the QuickJS-NG engine;
[0013] The bidirectional communication mechanism includes three sub-mechanisms: a first communication sub-mechanism for LVGL to send events to QuickJS-NG, a second communication sub-mechanism for QuickJS-NG to send instructions to LVGL, and a third communication sub-mechanism for JavaScript to asynchronously call C language functions;
[0014] Step 2: Design the form based on the EEZ designer and establish a UI information binding mechanism to achieve component information sharing between the C language layer and the JavaScript layer;
[0015] Step 3: Build a multi-device UI adaptation strategy to enable automatic loading of corresponding components for each device model;
[0016] Step 4: Implement class-based component lifecycle management in the JavaScript layer to establish a standardized lifecycle model for components, enabling developers to organize business logic in an object-oriented manner and execute corresponding code in different component states.
[0017] Step 5: Implement the binding mechanism between UI events and JavaScript callback functions to enable the JavaScript layer to respond to UI events;
[0018] Step 6: Establish a routing mechanism to enable navigation and state management between pages or forms.
[0019] As a further improvement to the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL:
[0020] The first communication sub-mechanism is implemented as follows: a global event message queue is created in the C language layer to receive UI events and external events from LVGL; the JavaScript layer runs the main thread based on QuickJS-NG and creates Worker child threads. The Worker child threads call C functions in a loop to wait for messages in the global event message queue in a blocking manner; after the Worker child threads obtain the messages, they send them to the main thread for processing.
[0021] The second communication sub-mechanism is implemented as follows: when the JavaScript layer needs to control LVGL to perform UI rendering or update operations, it sends instructions to LVGL using a synchronous blocking call mechanism.
[0022] The specific process is as follows:
[0023] The JavaScript layer first creates a message object que_obj based on the instruction to be sent. This message object contains an inter-process shared semaphore sem and a string ret_str for storing the return value. The message object que_obj is added to the UI thread message queue of LVGL. Then, the sem_wait function is called in the JavaScript main thread, which puts the message object que_obj into a blocking wait state on the semaphore sem.
[0024] The LVGL graphics library uses its inherent timer mechanism to call the lv_timer_handler() function at preset time intervals to check and process the UI thread message queue. After retrieving the message object que_obj, LVGL executes the specific operation requested by the message in the UI thread. After the operation is completed, the result data is filled into the string ret_str of the message object que_obj, and the sem_post function is called to release the semaphore sem associated with the message object.
[0025] After the JavaScript main thread detects that the semaphore sem has been released, it is out of the blocked state. Then it reads the operation result returned by LVGL from the string ret_str of the message object que_obj and continues to execute the subsequent logic.
[0026] The third communication mechanism is implemented as follows: The JavaScript layer maintains a global mapping dictionary `waitAckList` and an incrementing unique ID generator `waitAckCurId`. When the JavaScript layer needs to handle time-consuming tasks by asynchronously calling a C language function, it creates a new Promise object. In the Promise object's constructor, it associates the `resolve` callback function (for successful results) and the `reject` callback function (for failed results) of the current Promise object with the unique ID of the current Promise object and stores them in the `waitAckList` dictionary. Then, it calls the C language function, passing this unique ID and operation parameters to the C language function. This C language function immediately starts a new worker thread to execute the actual time-consuming operation, and then the C language function itself immediately returns, thus allowing JavaScript to... The thread will not be blocked; when the constructor of the Promise object finishes execution, the created Promise object is immediately returned to the caller. After that, the main thread of the JavaScript layer continues to execute other tasks, while the C language worker thread processes time-consuming operations in parallel in the background. When the C language worker thread completes its task, it encapsulates the result data and the previously passed unique ID into an execution result message and sends it to the JavaScript layer through the global event message queue of the first communication sub-mechanism. The main thread of the JavaScript layer obtains the execution result message through the first communication sub-mechanism, and then finds the corresponding resolve callback function and reject callback function from the waitAckList dictionary according to the unique ID in the execution result message. Then, based on the execution result, it selects to execute the resolve callback function or the reject callback function, thereby changing the state of the Promise object and completing the entire asynchronous call process.
[0027] As a further improvement to the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL, step two specifically includes: performing visual UI design through the EEZ designer to generate C language code corresponding to the form; each form and its custom components correspond to a creation function;
[0028] The UI information binding mechanism is as follows:
[0029] First, the metadata binding function is called in the creation functions of custom components and forms. The name and type of the current component are put into the component object created by the creation function. The component tree of the form is constructed by using the names of all component objects and the tree structure generated during form design. Both the C language layer and the JavaScript layer locate the component index based on this component tree. Then, the generated C language code is modified to bind the form creation function to the route address, so that the JavaScript layer can find the corresponding creation function in the C language layer based on the route address.
[0030] As a further improvement to the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL, the multi-device UI adaptation strategy is based on a component aliasing mechanism:
[0031] First, in the designer, create corresponding entity components based on the basic components for different models. The name of the entity component includes the corresponding basic component name and model identifier.
[0032] Then, the component loading function in the C code generated by the EEZ designer is rewritten. It searches for the corresponding entity component and loads it based on the passed base component name and device identifier. If the corresponding entity component is not found, the corresponding base component is loaded directly.
[0033] As a further improvement to the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL, the implementation of form lifecycle management in the lifecycle model is as follows:
[0034] A1. Define a base lifecycle class in the JavaScript layer, which declares four core lifecycle functions:
[0035] createPage(): Called when the component is created and first loaded into memory;
[0036] destroyPage(): Called when the component is permanently destroyed and removed from memory;
[0037] enterPage(): This function is called every time the component becomes active.
[0038] exitPage(): This function is called each time the component becomes inactive.
[0039] A2. Create a derived class of the base lifecycle class for each component;
[0040] A3. Override the lifecycle functions in the derived class, and implement the specific business logic through the second communication sub-mechanism created in step one and writing JavaScript code.
[0041] As a further improvement to the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL, step five specifically includes:
[0042] B1. When designing a form, set triggerable events for components according to user requirements;
[0043] B2. Modify the form creation function in the C language layer. When the form is created, automatically traverse all components under the form, determine whether each component can trigger an event and the event type, and bind the component path and event type that trigger the event to the C language UI callback function.
[0044] B3. Implement a C language UI callback function. When the function is called, encapsulate the component path and event type into a UI event, and then send the UI event to the JavaScript layer based on the first communication sub-mechanism created in step one.
[0045] B4. Implement a JavaScript UI event binding function in the JavaScript layer. This function is called in the component's lifecycle function, binding the component path, event type, and corresponding JavaScript callback function and storing them in the global binding dictionary uiEnvList;
[0046] When the JavaScript layer receives a message corresponding to a UI event, it extracts the component path treePath and the event type envName from the message. Then, it looks up the previously bound JavaScript callback function cbFun from the global binding dictionary uiEnvList and executes it, thereby enabling the JavaScript layer to respond to UI events.
[0047] As a further improvement to the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL, step six is implemented as follows:
[0048] C1. Define a route list (routeList) in the JavaScript layer to describe all routable pages in the application, with each page corresponding to a form; each route item in the route list (routeList) contains the following properties:
[0049] name: Page name, used for identification during route navigation;
[0050] loadMode: Page loading mode, used to control the page loading and destruction strategy;
[0051] component: route address, which is associated with the creation function of the corresponding window through the UI information binding mechanism created in step two, and also corresponds to the lifecycle derived class file;
[0052] C2. Establish a routing stack and implement routing functions. The routing functions determine the form to be operated on through the routing stack, and then call the lifecycle functions of the corresponding form to implement the routing function.
[0053] The routing functions specifically include:
[0054] push(name, params): Used to push a new page onto the route stack, keeping the current page in the route stack, and supports going back using go(-1);
[0055] replace(name, params): Replaces the current page, does not retain the current page, and cannot be unrolled;
[0056] go(count): Moves the specified number of steps forward or backward in the routing history;
[0057] When the routing function is called, it will automatically trigger the lifecycle function of the corresponding page according to the loading mode defined in routeList, ensuring that the page state is managed correctly.
[0058] As a further improvement to the low-cost embedded software development method based on QuickJS-NG and LVGL, step seven: establish a focus control mechanism for physical button events to realize focus management and event triggering, and ensure accurate interaction;
[0059] Step 7: Implementation details:
[0060] D1. Set up a separate focus group for each page to manage all focusable UI components within that page; the focus group is initialized when the page is created and released when the page is destroyed;
[0061] D2. During the routing transition, the system performs the following focus control operations:
[0062] (1) Save the focus state of the current page and record the component that currently has focus;
[0063] (2) Clear the current focus group and remove all focus bindings;
[0064] (3) Bring the new page to the foreground and load its focus group;
[0065] (4) Determine the focus restoration strategy based on the jump direction:
[0066] (4-1) When moving forward, automatically focus on the first focusable object on the new page;
[0067] (4-2) When going back, restore the previously saved focus position in the history page;
[0068] D3. When a user presses a physical button, the system's underlying driver obtains the button event, determines the component that will receive the event based on the current focus group, and then sends the button event to the JavaScript layer through the first communication sub-mechanism, where it is handled by the corresponding JavaScript callback function.
[0069] The present invention also discloses a program product comprising a computer program stored in a non-volatile storage medium, which is obtained by the aforementioned low-cost embedded software development method based on QuickJS-NG and LVGL.
[0070] The present invention also discloses a method for operating the above-mentioned program product, comprising the following processes:
[0071] System startup and operation process: After the embedded device starts up, the QuickJS-NG engine and LVGL graphics library are initialized, and a bidirectional communication mechanism between the C language layer and the JavaScript layer is established; the lifecycle functions of the corresponding page window are called according to the initial route configuration to load and display; during loading, components that match the current model are automatically loaded based on the multi-model UI adaptation strategy;
[0072] User-triggered UI event execution process: When the user interacts with a component in the window, LVGL captures the UI event. The UI event is sent to the JavaScript layer through the first communication sub-mechanism. The JavaScript layer determines and calls the corresponding JavaScript callback function for processing based on the binding mechanism between the UI event and the JavaScript callback function.
[0073] During processing:
[0074] If the JavaScript layer needs to update LVGL, it sends a command to LVGL through the second communication sub-mechanism and obtains the operation result returned by LVGL.
[0075] If the JavaScript layer needs to handle time-consuming tasks, it asynchronously calls a C language function through the third communication sub-mechanism, obtains the execution result message through the first communication sub-mechanism, finds the corresponding resolve callback function and reject callback function from the waitAckList dictionary based on the unique ID in the execution result message, selects to execute the resolve callback function or the reject callback function based on the execution result, and selects whether to send instructions to LVGL to update the UI interface through the second communication sub-mechanism as needed.
[0076] The routing process is as follows: When a user triggers a routing event through a physical button or a window, the JavaScript layer receives the routing event through the first communication sub-mechanism and calls the corresponding routing function. The routing function calls the lifecycle function of the corresponding window and operates on the routing stack to complete the loading of the target page and realize the routing function.
[0077] Compared with the prior art, the present invention has the following beneficial effects:
[0078] 1. This invention achieves efficient collaboration between LVGL and QuickJS-NG without modifying their respective source code by constructing a bidirectional communication mechanism between the C language layer and the JavaScript layer. This mechanism comprises three core sub-mechanisms: using Worker child threads and a global event message queue to achieve zero-delay event delivery from LVGL to QuickJS-NG; using semaphore synchronization and a UI thread message queue to achieve synchronous blocking calls from JavaScript to LVGL, ensuring thread safety for UI operations; and using Promises and unique ID mapping to achieve asynchronous calls from JavaScript to C language functions, avoiding time-consuming tasks blocking the main thread. This architecture completely decouples interface rendering from business logic, allowing UI design to be performed independently through the EEZ designer, while application logic is entirely handled by JavaScript, reducing the difficulty of embedded GUI development.
[0079] 2. The UI information binding mechanism and class-based component lifecycle management proposed in this invention provide developers with a standardized development model. By embedding meta-information binding in the form code generated by EEZ, a component tree shared by the C language layer and the JavaScript layer is constructed, enabling both layers to locate components based on a unified path. Combined with object-oriented lifecycle classes, developers can clearly organize business logic at key nodes such as createPage, enterPage, exitPage, and destroyPage, achieving fine-grained state management and resource control of UI modules, and significantly improving code maintainability and reusability.
[0080] 3. The multi-device UI adaptation strategy designed in this invention achieves automatic loading of differentiated UIs through a component alias mechanism. During the design phase, multiple device-specific entity components are created for the same logical component. At runtime, the most suitable component is automatically selected for rendering based on the current device identifier. This solution eliminates the need to maintain multiple sets of independent UI code for different devices, simplifies the complexity of multi-device adaptation, shortens the product iteration cycle, and ensures consistent user experience across different hardware platforms.
[0081] 4. The routing and focus control mechanisms integrated in this invention provide a complete page navigation and interaction management solution for embedded applications. The routing system, through route lists, route stacks, and standard jump functions, enables smooth switching and state management of multi-page applications and automatically triggers the lifecycle functions of relevant pages. The focus control system maintains an independent focus group for each page, automatically saving, clearing, or restoring the focus state during route transitions, ensuring the accuracy and continuity of focus switching in physical button interaction scenarios and improving the user experience on non-touch devices.
[0082] 5. The overall architecture of this invention avoids the HTML / CSS parsing and conversion steps common in existing technologies, eliminating the performance overhead and functional limitations associated with them. All integration work is completed through official interfaces and plugin mechanisms, requiring no modification to the original QuickJS-NG and LVGL code. This allows the solution to safely and stably keep up with version updates from upstream projects, effectively ensuring the long-term maintainability of the system. Detailed Implementation
[0083] The technical solution of the present invention will be described in detail below. Obviously, the described embodiments are only some embodiments of the present invention, and not all embodiments.
[0084] Example 1
[0085] This embodiment provides a low-cost embedded software development method based on QuickJS-NG and LVGL.
[0086] QuickJS-NG is a community-driven, modern, and actively maintained fork of the JavaScript engine that evolved from the original QuickJS project. NG stands for "Next Generation".
[0087] LVGL stands for Light and Versatile Embedded Graphics Library, a lightweight, general-purpose graphics library.
[0088] The method includes the following steps:
[0089] Step one: Establish a bidirectional communication mechanism between the C language layer and the JavaScript layer. The C language layer is used to run LVGL to implement user interface functions, and the JavaScript layer is used to run the QuickJS-NG engine.
[0090] The bidirectional communication mechanism includes three sub-mechanisms: a first communication sub-mechanism for LVGL to send events to QuickJS-NG, a second communication sub-mechanism for QuickJS-NG to send instructions to LVGL, and a third communication sub-mechanism for JavaScript to asynchronously call C language functions.
[0091] The first communication sub-mechanism is implemented as follows: A global event message queue is created in the C language layer to receive UI events (UI refers to User Interface) from LVGL, as well as external events such as hardware button events, network events, and serial port events. The JavaScript layer runs the main thread based on QuickJS-NG and creates worker child threads. The worker child threads call C functions in a loop, waiting for messages in the global event message queue in a blocking manner; after obtaining a message, the worker child thread sends it to the main thread for processing.
[0092] Specifically, the JavaScript code is as follows:
[0093] let workParent = os.Worker && os.Worker.parent;
[0094] if (workParent) {
[0095] while (true) { / / The child thread is blocked at the C language layer, and sends the message to the main thread after receiving it.
[0096] let retStr = baseLib.getMessage();
[0097] workParent.postMessage(retStr);
[0098] }
[0099] }
[0100] The main thread initialization code is as follows:
[0101] / **
[0102] * Initialization (called by the main thread)
[0103] * @param {Function} cbFun A callback function used to handle messages from child threads.
[0104] /
[0105] export function initBaseWorker(cbFun) {
[0106] worker = new os.Worker("this JS file");
[0107] / / The parent process receives data from the child thread via a message mechanism
[0108] worker.onmessage = cbFun;
[0109] }
[0110] The QuickJS-NG engine typically uses epoll for event polling and response listening, which results in it getting stuck in epoll waiting when there are no events. Even when using os.setInterval or optimizing infinite recursion of os.setTimeout to call C language code using TCO, there are still obvious drawbacks: long intervals can lead to untimely responses, while short intervals can cause excessive CPU usage.
[0111] To insert instructions into the QuickJS-NG main thread in response to LVGL requests, this solution uses `os.Worker` to create an asynchronous child thread. Within this child thread, the `so` function is called repeatedly, and a blocking mechanism is used to prevent excessive CPU usage. A global event message queue is created in the `so` code, and `pthread_cond_timedwait` is used to implement a thread-safe waiting mechanism. When the asynchronous child thread (such as a network event or serial port event handling thread) adds an event to the queue, the mutex is released, allowing `pthread_cond_timedwait` to continue execution. The message is then returned and pushed to the JavaScript parent thread via `postMessage`, triggering `epoll` to return. The core of this mechanism lies in using a global event message queue and a dedicated child thread to forward various external events to the QuickJS-NG main thread for processing via messages. This overcomes the limitation that `epoll` cannot directly insert events, achieving "indirect insertion" and delay-free event handling.
[0112] The second communication sub-mechanism is implemented as follows: when the JavaScript layer needs to control LVGL to perform UI rendering or update operations, it sends instructions to LVGL using a synchronous blocking call mechanism.
[0113] The specific process is as follows:
[0114] The JavaScript layer first creates a message object `que_obj` based on the instruction to be sent. This message object contains an inter-process shared semaphore `sem` and a string `ret_str` to store the return value. The message object `que_obj` is then added to the LVGL UI thread message queue. Finally, the JavaScript main thread calls the `sem_wait` function, which enters a blocking wait state on the semaphore `sem` of the message object `que_obj`.
[0115] The LVGL graphics library, through its inherent timer mechanism, calls the `lv_timer_handler()` function at preset time intervals (e.g., 5 milliseconds) to check and process the UI thread message queue. After retrieving the message object `que_obj`, LVGL executes the specific operation requested by the message (such as creating controls, updating properties, etc.) in the UI thread. After the operation is completed, the result data is filled into the string `ret_str` of the message object `que_obj`, and the `sem_post` function is called to release the semaphore `sem` associated with the message object.
[0116] After the JavaScript main thread detects that the semaphore sem has been released and is no longer blocked, it reads the operation result returned by LVGL from the string ret_str of the message object que_obj and continues to execute subsequent logic.
[0117] The core C language implementation of this mechanism is as follows:
[0118] / / Initialize the semaphore within the message object, with an initial value of 0, indicating that the resource is unavailable.
[0119] sem_init(que_obj->sem, 0, 0);
[0120] / / Add the message object to the LVGL UI thread message queue
[0121] addQue(que_obj);
[0122] / / JavaScript thread is blocked, waiting for LVGL thread to complete its operation and release the semaphore.
[0123] sem_wait(que_obj->sem);
[0124] / / Get the result data returned after the LVGL thread finishes execution
[0125] que_obj->ret_str;
[0126] Among them, `sem_init`, `sem_wait`, and `sem_post` are POSIX standard system calls used for semaphore operations. `addQue` is a function used to safely post messages to the LVGL UI thread's message queue. The core of this mechanism lies in achieving synchronous blocking calls from JavaScript to LVGL through semaphore synchronization and message queues, ensuring thread safety for UI operations.
[0127] The third communication mechanism is implemented as follows: The JavaScript layer maintains a global mapping dictionary `waitAckList` and an incrementing unique ID generator `waitAckCurId`. When the JavaScript layer needs to handle time-consuming tasks by asynchronously calling a C language function, it creates a new Promise object. In the Promise object's constructor, it associates the `resolve` callback function (for successful results) and the `reject` callback function (for failed results) of the current Promise object with the Promise object's unique ID and stores them in the `waitAckList` dictionary. Then, it calls the C language function, passing this unique ID and operation parameters to the C language function. This C language function immediately starts a new worker thread to execute the actual time-consuming operation (such as file reading / writing, network requests), and then the C language function itself returns immediately, thus preventing the JavaScript thread from being blocked. When the Promise object's constructor finishes executing, the created Promise object is immediately returned to the caller. Afterward, the JavaScript layer's main thread continues to execute other tasks, while the C language worker thread processes the time-consuming operation in parallel in the background. After the C language worker thread completes its task, it encapsulates the result data and the previously passed unique ID into an execution result message, which is then sent to the JavaScript layer via the global event message queue of the first communication sub-mechanism. The JavaScript layer's main thread obtains this execution result message through the first communication sub-mechanism, then retrieves the corresponding resolve and reject callback functions from the waitAckList dictionary based on the unique ID in the execution result message. It then selects to execute either the resolve or reject callback function based on the execution result, thereby changing the state of the Promise object, triggering subsequent processing logic, and completing the entire asynchronous call process.
[0128] The JavaScript layer calling code for this mechanism is as follows:
[0129] let waitAckList = {}
[0130] let waitAckCurId = 0;
[0131] / / Calling an asynchronous function immediately returns a Promise object.
[0132] function asyncCallToC(params) {
[0133] return new Promise((resolve, reject) => {
[0134] / / The following code executes synchronously within the Promise constructor.
[0135] / / 1. Associate the callback function with the ID and store it in a dictionary
[0136] waitAckList["w" + waitAckCurId] = { resolve, reject};
[0137] / / 2. Call a C language function to start a background thread; the C language function returns immediately.
[0138] xxxLib.runFun(waitAckCurId, params);
[0139] / / 3. After the Promise constructor finishes executing, it returns a Promise object.
[0140] });
[0141] }
[0142] The C language thread execution code is as follows:
[0143] void threadRun(waitAckCurId,params){
[0144] sleep(...); / / Handle time-consuming tasks
[0145] msgQueAdd(waitAckCurId,retStr)
[0146] }
[0147] JavaScript asynchronous response handling:
[0148] worker.onmessage=(msgStr){
[0149] / / Parse out waitAckCurId and the resulting string retStr
[0150] waitAckCurId=xxxx;
[0151] retStr=xxxx;
[0152] / / Processing results
[0153] waitAckList["w"+waitAckCurId].resolve(retStr)
[0154] / / Recycle ID
[0155] delete waitAckList["w"+waitAckCurId];
[0156] }
[0157] This establishes a bidirectional communication mechanism between the C language layer and the JavaScript layer.
[0158] Step 2: Design the form based on the EEZ designer and establish a UI information binding mechanism to achieve component information sharing between the C language layer and the JavaScript layer.
[0159] First, let's clarify the following concepts:
[0160] 1. Component: The building block of a UI. It can be a simple button or label (often called a basic component) or a complex container.
[0161] 2. Custom Components: Reusable composite components made up of multiple basic components. For example, a specific button containing an icon and text, or a complete data display card.
[0162] 3. Form: A special top-level container component that typically occupies the entire screen or a window, serving as an independent page for UI interaction. It can be considered a special custom component.
[0163] Therefore, it can be seen that the form is composed of basic components and custom components nested in a tree structure, and the custom components are composed of basic components.
[0164] The EEZ Designer allows for visual UI design, generating C language code for each form. Each form and its custom components correspond to a creation function. For example, the creation function for the form (i.e., the page) pos_pos_main is create_user_widget__pos_pos_main.
[0165] The UI information binding mechanism is as follows:
[0166] First, the metadata binding function is called in the creation functions of custom components and forms. The name and type of the current component are placed into the component object created by this function. Then, the form's component tree is constructed using the names of all component objects and the tree structure generated during form design. Both the C and JavaScript layers use this component tree for component indexing and location. Next, the generated C code is modified to bind the form's creation function to a route address, allowing the JavaScript layer to find the corresponding creation function in the C layer based on the route address.
[0167] Specifically, the C language code example for the form generated by EEZ is as follows:
[0168] void create_user_widget__pos_pos_main(lv_obj_t *parent_obj, intstartWidgetIndex) {
[0169] lv_obj_t *obj = parent_obj;
[0170] {
[0171] lv_obj_t *parent_obj = obj;
[0172] {
[0173] lv_obj_t *obj = lv_obj_create(parent_obj);
[0174] lv_obj_set_pos(obj, 0, 0);
[0175] lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
[0176] / / Modify the code here to bind the name and type using the meta-information binding function.
[0177] lvjs_bind_obj(obj,"MyWidgetA",
[0178] "LVGLContainerWidget","",NULL);
[0179] }
[0180] }
[0181] }
[0182] / / This section binds the current form's creation function to the route address.
[0183] uiMapPut("#pos / pos_main",create_user_widget__pos_pos_main);
[0184] Both JavaScript and C code can locate components based on the component tree. For example, "abcd" means that form a is the root node, component b is a child node of a, component c is a child node of b, and the component d to be operated on is a child node of c.
[0185] Step 3: Build a multi-device UI adaptation strategy to enable automatic loading of corresponding components for each device model.
[0186] The multi-device UI adaptation strategy achieves automatic adaptation to multiple device models through a component aliasing mechanism. Its core lies in providing multiple entity components optimized for different device models for components with the same logical function, and automatically selecting the most suitable entity component based on the current target device model during loading.
[0187] The specific implementation method is as follows:
[0188] First, in the designer, create corresponding entity components based on the basic component and for different device models. The name of the entity component includes the corresponding basic component name and the device model identifier. For example, create a dedicated entity component for the s_k device model based on the basic component menu_bar and name it menu_bar@s_k.
[0189] Then, the component loading function in the C code generated by the EEZ designer is rewritten. It searches for the corresponding entity component and loads it based on the passed base component name and device identifier. If the corresponding entity component is not found, the corresponding base component is loaded directly.
[0190] Example of calling the rewritten component loading function `createLvWidget` in the form creation function:
[0191] / / ... code for creating the parent container object...
[0192] lv_obj_t *obj = lv_obj_create(parent_obj);
[0193] / / ... Set position, size, etc...
[0194] / / 1. Binding metadata, "view_sys / menu_bar" declared here is the base component name at this location.
[0195] lvjs_bind_obj(obj, "w_banner", "LVGLUserWidgetWidget", "view_sys / menu_bar", NULL);
[0196] / / 2. Call the custom loading function, which will automatically select and load entity components such as "menu_bar" or "menu_bar@s_k" based on the current device model.
[0197] createLvWidget(obj, "view_sys / menu_bar");
[0198] This achieves automatic loading of corresponding entity components for different device models, completes multi-device adaptation, and greatly simplifies the design and development work for multiple devices.
[0199] Step four: Implement class-based component lifecycle management in the JavaScript layer, establish a standardized lifecycle model for components, enable developers to organize business logic in an object-oriented manner, and execute corresponding code in different component states (creation, activation, dormancy, destruction).
[0200] The implementation method for form lifecycle management is as follows:
[0201] A1. Define a base lifecycle class in the JavaScript layer, which declares four core lifecycle functions:
[0202] createPage(): Called when the component is created and first loaded into memory;
[0203] destroyPage(): Called when the component is permanently destroyed and removed from memory;
[0204] enterPage(): This function is called every time the component becomes active (e.g., when the page is switched to a new page).
[0205] exitPage(): This function is called every time the component becomes inactive (e.g., when the page is switched out).
[0206] A2. Create a derived class of the base lifecycle class for each component.
[0207] A3. Override the lifecycle functions in the derived class, and implement the specific business logic through the second communication sub-mechanism created in step one and writing JavaScript code.
[0208] For example:
[0209] In createPage(), initialize the component's data, call the form creation function bound in step two, and complete event binding, etc.
[0210] In enterPage(), retrieve the latest data and update the UI display.
[0211] In exitPage(), pause the timer or animation to save resources.
[0212] In destroyPage(), resource cleanup is performed.
[0213] JavaScript code example:
[0214] / / 1. Define the base class
[0215] export class BaseLVGLUi {
[0216] createPage() { / * Default empty implementation * / }
[0217] destroyPage() { / * Default empty implementation * / }
[0218] enterPage() { / * Default empty implementation * / }
[0219] exitPage() { / * Default empty implementation * / }
[0220] }
[0221] / / 2. & 3. Create derived classes for specific pages and implement the logic.
[0222] export class myView extends BaseLVGLUi {
[0223] createPage() {
[0224] / / Write the page initialization logic here
[0225] …
[0226] }
[0227] enterPage() {
[0228] / / Write the relevant logic here
[0229] }
[0230] exitPage() {
[0231] / / Write the relevant logic here}
[0232] }
[0233] Thus, through the above development operations, a class-based component lifecycle management mechanism has been established.
[0234] Step 5: Implement the binding mechanism between UI events and JavaScript callback functions to enable the JavaScript layer to respond to UI events.
[0235] The specific implementation method is as follows:
[0236] B1. When designing a form, set triggerable events for components according to user needs.
[0237] B2. Modify the form creation function in the C language layer. When creating the form, automatically traverse all components under the form, determine whether each component can trigger an event and the event type, and bind the component path and event type that trigger the event to the C language UI callback function.
[0238] B3. Implement a C language UI callback function. When this function is called, encapsulate the component path and event type into a UI event, and then send the UI event to the JavaScript layer based on the first communication sub-mechanism created in step one.
[0239] B4. Implement a JavaScript UI event binding function in the JavaScript layer. This function is called in the component's lifecycle function and binds the component path, event type, and corresponding JavaScript callback function, storing them in the global binding dictionary uiEnvList.
[0240] In this embodiment, the JavaScript UI event binding function is bindEnv(wPath, envName, cbFun), which can be called in lifecycle functions (usually in createPage). Parameters include:
[0241] wPath: Component path, supports fuzzy matching (e.g., "abc", "c", "bd").
[0242] envName: Event type (e.g., "click").
[0243] cbFun: The JavaScript callback function triggered when the event is executed.
[0244] Internally, this function resolves the short path wPath into the complete long path ePath in the component tree, and then uses the combination of envName and ePath as the key to store the callback function cbFun and the component path information into a global binding dictionary uiEnvList.
[0245] The code example is as follows:
[0246] / / C language layer: General event callback function (provided by the framework and called in the EEZ generated code)
[0247] static void formEventCb(lv_event_t *e) {
[0248] lv_event_code_t code = lv_event_get_code(e);
[0249] if(code == LV_EVENT_CLICKED) {
[0250] lv_obj_t *target = lv_event_get_target(e);
[0251] char treePath[LVJS_OBJPATH_MAX_LEN] = {0};
[0252] if(getObjPath(NULL, target, treePath, sizeof(treePath)) > 0){
[0253] / / Construct a message and send it to the JavaScript layer
[0254] JaJson msg;
[0255] msg.JaAddMember("funName", "uiEvent");
[0256] msg.JaAddMember("data / envName", "click"); / / Event type
[0257] msg.JaAddMember("data / treePath", treePath); / / Component path
[0258] sendStdMsg(msg); / / Send the message to the JavaScript layer via the message queue
[0259] }
[0260] }
[0261] }
[0262] / / JavaScript layer: Developers bind events in the page class
[0263] export class MyPage extends BaseLVGLUi {
[0264] createPage() {
[0265] / / The developer calls bindEnv to associate and bind the button's click event with the JavaScript callback function handleButtonClick.
[0266] bindEnv("myButton", "click", this.handleButtonClick.bind(this));
[0267] }
[0268] handleButtonClick() {
[0269] / / Developers write event handling logic here
[0270] console.log("Button clicked!");
[0271] }
[0272] }
[0273] / / JavaScript layer: Implementation of JavaScript language UI event binding functions
[0274] function bindEnv(wPath, envName, cbFun) {
[0275] let ePath = wPath2ePath(wPath); / / Convert short path to long path
[0276] uiEnvList[`${envName},${ePath}`] = { cbFun, ePath};
[0277] }
[0278] When the JavaScript layer receives a message corresponding to a UI event, it extracts the component path treePath and the event type envName from the message. Then, it looks up the previously bound JavaScript callback function cbFun from the global binding dictionary uiEnvList and executes it, thereby enabling the JavaScript layer to respond to UI events.
[0279] Step 6: Establish a routing mechanism to enable navigation and state management between pages or forms.
[0280] Specific implementation method:
[0281] C1. Define a route list (routeList) in the JavaScript layer to describe all routable pages in the application, with each page corresponding to a form. Each route item in the route list (routeList) contains the following properties:
[0282] name: Page name, used for identification during route navigation;
[0283] loadMode: Page loading mode, used to control the page loading and destruction strategy;
[0284] component: The route address is associated with the creation function of the corresponding window through the UI information binding mechanism created in step two, and also corresponds to the lifecycle derived class file.
[0285] Here is an example of a route list:
[0286] / / Javascript code
[0287] routeList = [
[0288] {
[0289] name: 'pos_main',
[0290] loadMode: 1,
[0291] component: '#pos / pos_main',
[0292] },
[0293] {
[0294] name: 'config_portrait',
[0295] loadMode: 2,
[0296] component: '#public / config_portrait',
[0297] }, ]
[0299] C2. Establish a routing stack and implement routing functions. The routing functions determine the form to be operated on through the routing stack, and then call the lifecycle functions of the corresponding form to implement the routing function.
[0300] The routing functions specifically include:
[0301] push(name, params): Used to push a new page onto the route stack, keeping the current page in the route stack, and supports going back using go(-1);
[0302] replace(name, params): Replaces the current page, does not retain the current page, and cannot be unrolled;
[0303] go(count): Moves the specified number of steps forward or backward in the routing history.
[0304] When a route function is invoked, it automatically triggers the corresponding page's lifecycle functions (such as createPage, destroyPage, enterPage, and exitPage) based on the loading mode defined in routeList, ensuring proper page state management. For example:
[0305] (1) Call the exitPage function of the current page to pause resources such as timers and animations;
[0306] (2) Determine whether to destroy the current page based on the target page's loadMode;
[0307] (3) Call the createPage or enterPage function of the target page to complete page initialization or transfer;
[0308] (4) Update the routing stack and interface display to complete the page switching.
[0309] Step 7: Establish a focus control mechanism for physical button events to manage focus and trigger events, ensuring accurate interaction.
[0310] Specific implementation method:
[0311] D1. Create a separate key group for each page to manage all focusable UI components within that page. The key group is initialized when the page is created and released when the page is destroyed.
[0312] D2. During the routing transition, the system performs the following focus control operations:
[0313] (1) Save the focus state of the current page and record the component that currently has focus;
[0314] (2) Clear the current focus group and remove all focus bindings;
[0315] (3) Bring the new page to the foreground and load its focus group;
[0316] (4) Determine the focus restoration strategy based on the jump direction (forward or backward):
[0317] (4-1) When moving forward, automatically focus on the first focusable object on the new page;
[0318] (4-2) When going back, restore the previously saved focus position in the history page.
[0319] The C language code for focus control involves the following functions:
[0320] / / Save current focus state
[0321] saveFocusInfo();
[0322] / / Show new page
[0323] lv_obj_clear_flag(pWidget, LV_OBJ_FLAG_HIDDEN);
[0324] / / Clear the current focus group
[0325] clearKeyGroup();
[0326] / / Move the new page to the foreground
[0327] lv_obj_move_foreground(pWidget);
[0328] / / Load the focus group of the new page
[0329] loadKeyGroup(pWidget);
[0330] / / Restore focus based on the jump direction
[0331] if (isBack) {
[0332] focusHistoryObj(pWidget);
[0333] } else {
[0334] focusFirstObj(pWidget);
[0335] }
[0336] The specific implementation of the above function can be achieved according to the steps described above, and will not be repeated here.
[0337] D3. When a user presses a physical button, the system's underlying driver obtains the button event, determines the component that will receive the event based on the current focus group, and then sends the button event to the JavaScript layer through the first communication sub-mechanism, where it is handled by the corresponding JavaScript callback function.
[0338] Example 2
[0339] This embodiment provides a program product developed based on the development method described in Embodiment 1. The program product includes a computer program stored in a non-volatile storage medium.
[0340] When the program runs on an embedded device, it can perform the following functions: execute JavaScript code through the QuickJS-NG engine to process business logic and UI control, implement user interface rendering through the LVGL graphics library, realize event transmission and instruction sending between the C language layer and the JavaScript layer through a two-way communication mechanism, realize multi-page navigation and lifecycle management through a routing mechanism, realize user interaction based on physical buttons through a focus control mechanism, and realize differentiated UI presentation through a multi-device adaptation mechanism.
[0341] Example 3
[0342] This embodiment provides a method for running the program product described in Embodiment 2, including the following running process:
[0343] System startup and operation process: After the embedded device starts, the QuickJS-NG engine and LVGL graphics library are initialized, and a bidirectional communication mechanism is established between the C language layer and the JavaScript layer. Based on the initial routing configuration, the lifecycle functions of the corresponding page window are called to load and display them. During loading, components matching the current device model are automatically loaded based on a multi-device UI adaptation strategy.
[0344] User-triggered UI event execution process: When the user interacts with a component in the window, LVGL captures the UI event. The UI event is sent to the JavaScript layer through the first communication sub-mechanism. The JavaScript layer determines and calls the corresponding JavaScript callback function for processing based on the binding mechanism between the UI event and the JavaScript callback function.
[0345] During processing:
[0346] If the JavaScript layer needs to update LVGL, it sends a command to LVGL through the second communication sub-mechanism and obtains the operation result returned by LVGL.
[0347] If the JavaScript layer needs to handle time-consuming tasks, it asynchronously calls a C language function through the third communication sub-mechanism, then obtains the execution result message through the first communication sub-mechanism, and then finds the corresponding resolve callback function and reject callback function from the waitAckList dictionary based on the unique ID in the execution result message. Based on the execution result, it selects to execute the resolve callback function or the reject callback function, and selects whether to send instructions to LVGL to update the UI interface through the second communication sub-mechanism as needed.
[0348] The routing process is as follows: When a user triggers a routing event through a physical button or a window, the JavaScript layer receives the routing event through the first communication sub-mechanism and calls the corresponding routing function. The routing function calls the lifecycle function of the corresponding window and operates on the routing stack to complete the loading of the target page and realize the routing function.
[0349] Simultaneously, a focus control mechanism is activated during route navigation: the system saves or clears the focus state of the current page and loads its own independent focus group for the new page. After navigation is complete, the focus is automatically positioned on the first focusable component of the new page or the original focus of the historical page is restored, depending on the forward or backward direction, thus ensuring the accuracy and continuity of physical button interactions.
[0350] Through the above process, the program product of this invention can achieve efficient embedded application operation, ensure timely UI response, accurate business logic processing, reasonable resource management, and provide a consistent user experience on different hardware models.
[0351] It should be noted that, as will be apparent to those skilled in the art, the present invention is not limited to the details of the exemplary embodiments described above, and that the present invention can be implemented in other specific forms without departing from the spirit or essential characteristics thereof. The scope of the present invention is defined by the claims rather than the foregoing description.
Claims
1. A low-cost embedded software development method based on QuickJS-NG and LVGL, characterized in that, include: Step 1: Establish a bidirectional communication mechanism between the C language layer and the JavaScript layer; the C language layer is used to run LVGL to implement user interface functions, and the JavaScript layer is used to run the QuickJS-NG engine; The bidirectional communication mechanism includes three sub-mechanisms: a first communication sub-mechanism for LVGL to send events to QuickJS-NG, a second communication sub-mechanism for QuickJS-NG to send instructions to LVGL, and a third communication sub-mechanism for JavaScript to asynchronously call C language functions; The first communication sub-mechanism is implemented by creating a global event message queue in the C language layer to receive UI events from LVGL and external events. The JavaScript layer runs the main thread based on QuickJS-NG and creates worker child threads. The worker child threads call C functions in a loop, waiting for messages in the global event message queue in a blocking manner. After the worker child threads obtain the messages, they send them to the main thread for processing. The second communication sub-mechanism is implemented as follows: when the JavaScript layer needs to control LVGL to perform UI rendering or update operations, it sends instructions to LVGL using a synchronous blocking call mechanism. The specific process is as follows: The JavaScript layer first creates a message object que_obj based on the instruction to be sent. This message object contains an inter-process shared semaphore sem and a string ret_str for storing the return value. The message object que_obj is then added to the UI thread message queue of LVGL. Then, the sem_wait function is called on the main JavaScript thread, which enters a blocked waiting state on the semaphore sem of the message object que_obj; The LVGL graphics library uses its inherent timer mechanism to call the lv_timer_handler() function at preset time intervals to check and process the UI thread message queue. After retrieving the message object que_obj, LVGL executes the specific operation requested by the message in the UI thread. After the operation is completed, the result data is filled into the string ret_str of the message object que_obj, and the sem_post function is called to release the semaphore sem associated with the message object. After the JavaScript main thread detects that the semaphore sem has been released, it is out of the blocked state. Then it reads the operation result returned by LVGL from the string ret_str of the message object que_obj and continues to execute the subsequent logic. The third communication mechanism is implemented as follows: The JavaScript layer maintains a global mapping dictionary `waitAckList` and an incrementing unique ID generator `waitAckCurId`. When the JavaScript layer needs to handle time-consuming tasks by asynchronously calling a C language function, it creates a new Promise object. In the Promise object's constructor, it associates the `resolve` callback function (for successful results) and the `reject` callback function (for failed results) of the current Promise object with the unique ID of the current Promise object and stores them in the `waitAckList` dictionary. Then, it calls the C language function, passing this unique ID and operation parameters to the C language function. This C language function immediately starts a new worker thread to execute the actual time-consuming operation, and then the C language function itself returns immediately, thus preventing the JavaScript thread from being blocked. Once the Promise object's constructor finishes executing, the created Promise object is immediately returned to the caller. The JavaScript main thread then continues executing other tasks, while the C worker thread processes time-consuming operations in the background. When the C worker thread completes its task, it encapsulates the result data and the previously passed unique ID into an execution result message, which is then sent to the JavaScript layer via the global event message queue of the first communication sub-mechanism. The JavaScript main thread obtains this execution result message through the first communication sub-mechanism, then retrieves the corresponding resolve and reject callback functions from the waitAckList dictionary based on the unique ID in the message. It then selects to execute either the resolve or reject callback function based on the execution result, thereby changing the state of the Promise object and completing the entire asynchronous call process. Step 2: Design the form based on the EEZ designer and establish a UI information binding mechanism to achieve component information sharing between the C language layer and the JavaScript layer; Step 3: Build a multi-device UI adaptation strategy to enable automatic loading of corresponding components for each device model; Step 4: Implement class-based component lifecycle management in the JavaScript layer to establish a standardized lifecycle model for components, enabling developers to organize business logic in an object-oriented manner and execute corresponding code in different component states. Step 5: Implement the binding mechanism between UI events and JavaScript callback functions to enable the JavaScript layer to respond to UI events; Step 6: Establish a routing mechanism to enable navigation and state management between pages or forms.
2. The low-cost embedded software development method based on QuickJS-NG and LVGL as described in claim 1, characterized in that, Step two specifically includes: performing visual UI design using the EEZ designer to generate C language code corresponding to the form; each form and its custom components correspond to a creation function; The UI information binding mechanism is as follows: First, the metadata binding function is called in the creation functions of custom components and forms. The name and type of the current component are put into the component object created by the creation function. The component tree of the form is constructed by using the names of all component objects and the tree structure generated during form design. Both the C language layer and the JavaScript layer locate the component index based on this component tree. Then, the generated C language code is modified to bind the form creation function to the route address, so that the JavaScript layer can find the corresponding creation function in the C language layer based on the route address.
3. The low-cost embedded software development method based on QuickJS-NG and LVGL as described in claim 1, characterized in that, Multi-device UI adaptation strategy is based on component alias mechanism: First, in the designer, create corresponding entity components based on the basic components for different models. The name of the entity component includes the corresponding basic component name and model identifier. Then, the component loading function in the C code generated by the EEZ designer is rewritten. It searches for the corresponding entity component and loads it based on the passed base component name and device identifier. If the corresponding entity component is not found, the corresponding base component is loaded directly.
4. The low-cost embedded software development method based on QuickJS-NG and LVGL as described in claim 1, characterized in that, The implementation method of form lifecycle management in the lifecycle model is as follows: A1. Define a base lifecycle class in the JavaScript layer, which declares four core lifecycle functions: createPage(): Called when the component is created and first loaded into memory; destroyPage(): Called when the component is permanently destroyed and removed from memory; enterPage(): This function is called every time the component becomes active. exitPage(): This is called each time the component becomes inactive; A2. Create a derived class of the base lifecycle class for each component; A3. Override the lifecycle functions in the derived class, and implement the specific business logic through the second communication sub-mechanism created in step one and writing JavaScript code.
5. The low-cost embedded software development method based on QuickJS-NG and LVGL as described in claim 1, characterized in that, Step five specifically includes: B1. When designing a form, set triggerable events for components according to user requirements; B2. Modify the form creation function in the C language layer. When creating the form, automatically traverse all components under the form, determine whether each component can trigger an event and the event type, and bind the component path and event type that trigger the event to the C language UI callback function. B3. Implement a C language UI callback function. When the function is called, encapsulate the component path and event type into a UI event, and then send the UI event to the JavaScript layer based on the first communication sub-mechanism created in step one. B4. Implement a JavaScript UI event binding function in the JavaScript layer. This function is called in the component's lifecycle function, binding the component path, event type, and corresponding JavaScript callback function and storing them in the global binding dictionary uiEnvList; When the JavaScript layer receives a message corresponding to a UI event, it extracts the component path treePath and the event type envName from the message. Then, it looks up the previously bound JavaScript callback function cbFun from the global binding dictionary uiEnvList and executes it, thereby enabling the JavaScript layer to respond to UI events.
6. The low-cost embedded software development method based on QuickJS-NG and LVGL as described in claim 1, characterized in that, The specific implementation method for step six is as follows: C1. Define a route list (routeList) in the JavaScript layer to describe all routable pages in the application, with each page corresponding to a form; each route item in the route list (routeList) contains the following properties: name: Page name, used for identification during route navigation; loadMode: Page loading mode, used to control the page loading and destruction strategy; component: route address, which is associated with the creation function of the corresponding window through the UI information binding mechanism created in step two, and also corresponds to the lifecycle derived class file; C2. Establish a routing stack and implement routing functions. The routing functions determine the form to be operated on through the routing stack, and then call the lifecycle functions of the corresponding form to implement the routing function. The routing functions specifically include: push(name, params): Used to push a new page onto the route stack, keeping the current page in the route stack, and supports going back using go(-1); replace(name, params): Replaces the current page, does not retain the current page, and cannot be unrolled; go(count): Moves the specified number of steps forward or backward in the routing history; When the routing function is called, it will automatically trigger the lifecycle function of the corresponding page according to the loading mode defined in routeList, ensuring that the page state is managed correctly.
7. The low-cost embedded software development method based on QuickJS-NG and LVGL as described in claim 1, characterized in that, It also includes step seven: establishing a focus control mechanism for physical button events to manage focus and trigger events, ensuring accurate interaction; Step 7: Implementation details: D1. Set up a separate focus group for each page to manage all focusable UI components within that page; the focus group is initialized when the page is created and released when the page is destroyed; D2. During the routing transition, the system performs the following focus control operations: (1) Save the focus state of the current page and record the component that currently has focus; (2) Clear the current focus group and remove all focus bindings; (3) Bring the new page to the foreground and load its focus group; (4) Determine the focus restoration strategy based on the jump direction: (4-1) When moving forward, automatically focus on the first focusable object on the new page; (4-2) When going back, restore the previously saved focus position in the history page; D3. When a user presses a physical button, the system's underlying driver obtains the button event, determines the component that will receive the event based on the current focus group, and then sends the button event to the JavaScript layer through the first communication sub-mechanism, where it is handled by the corresponding JavaScript callback function.
8. A program product comprising a computer program stored in a non-volatile storage medium, characterized in that: The computer program is obtained through the low-cost embedded software development method based on QuickJS-NG and LVGL as described in any one of claims 1 to 7.
9. A method for operating the program product as described in claim 8, characterized in that, Includes the following processes: System startup and operation process: After the embedded device starts up, the QuickJS-NG engine and LVGL graphics library are initialized, and a bidirectional communication mechanism between the C language layer and the JavaScript layer is established; The corresponding page window's lifecycle function is called based on the initial route configuration to load and display the content; during loading, components matching the current device model are automatically loaded based on a multi-device UI adaptation strategy. User-triggered UI event execution process: When the user interacts with a component in the window, LVGL captures the UI event. The UI event is sent to the JavaScript layer through the first communication sub-mechanism. The JavaScript layer determines and calls the corresponding JavaScript callback function for processing based on the binding mechanism between the UI event and the JavaScript callback function. During processing: If the JavaScript layer needs to update LVGL, it sends a command to LVGL through the second communication sub-mechanism and obtains the operation result returned by LVGL. If the JavaScript layer needs to handle time-consuming tasks, it asynchronously calls a C language function through the third communication sub-mechanism, obtains the execution result message through the first communication sub-mechanism, finds the corresponding resolve callback function and reject callback function from the waitAckList dictionary based on the unique ID in the execution result message, selects to execute the resolve callback function or the reject callback function based on the execution result, and selects whether to send instructions to LVGL to update the UI interface through the second communication sub-mechanism as needed. The routing process is as follows: When a user triggers a routing event through a physical button or a window, the JavaScript layer receives the routing event through the first communication sub-mechanism and calls the corresponding routing function. The routing function calls the lifecycle function of the corresponding window and operates on the routing stack to complete the loading of the target page and realize the routing function.
Citation Information
Patent Citations
Embedded application development and operation method with full-function Node.js
CN116820431A
Application development method and system based on freeRTOS embedded system and medium
CN117348876A