DejaOS Application Development Prompts
1. What is DejaOS
DejaOS is a JavaScript runtime that runs on embedded devices. It is built on the QuickJS engine and LVGL graphics library and provides dozens of components that bridge C libraries to JavaScript. Developers can build device applications using ES6+ and these JS components.
2. Application Structure
app/
├── app.dxproj # App config: selected components and their versions
├── dxmodules/ # All component .js and .so files; auto-downloaded per config; use only, do not modify
├── resource/ # App resources: images, fonts, WAV audio, etc.
│ ├── font
│ ├── font.ttf # Usually required if the UI is not English-only
├── src/
│ ├── main.js # Entry point; required
The src directory is synced to the device at absolute path /app/code/src. Data files are conventionally placed under /app/data.
2.1 Supported Device Types
DW200_V20, VF105_V12, VF203_V12, VF202_V12, VF114_V12
Developers must decide the device type first.
2.2 app.dxproj Format
Each device type has its own reference file with all optional components and latest version numbers (e.g. DW200_V20 → DW200_V20-app.dxproj).
2.3 Module Overview
- Base (required):
dxLogger(logging, use instead of console.log),dxStd(system standard),dxOs(system),dxDriver(drivers),dxMap(cross-worker shared memory),dxEventBus(cross-worker messaging),dxCommonUtils(utilities and algorithms) - UI:
dxUi(required if the app has a UI) - Storage:
dxSqliteDB(SQLite),dxKeyValueDB(key-value) - Network:
dxNetwork(required for network management),dxHttpClient,dxHttpServer,dxMqttClient - Audio:
dxAudio(WAV playback, TTS) - GPIO:
dxGpio(output),dxGpioKey(input) - Others:
dxBarcode,dxNfc,dxUart,dxPwm,dxWatchdog,dxNtp,dxOta,dxConfiguration
When generating
app.dxprojfor a new app: if the user has no special requirements, select only the base modules. If the user needs a UI, also select dxUi on top of the base modules.
2.4 Application Notes
1. General
- The VSCode DejaOS extension lets you choose components and versions in
app.dxproj(usually latest). Clickinginstalldownloads all component JS sources intodxmodules. Use those sources as API reference. If a JS file is missing underdxmodules, tell the user to enable the component inapp.dxprojand click the plugin’sinstallbutton. - Unlike Node.js’s single-threaded event loop, DejaOS apps use a multi-worker model; each worker is still single-threaded.
dxStd.setTimeoutanddxStd.setIntervalare supported but are “pseudo-async” within the same worker (still executed in order). main.jsshould only initialize components and workers, not implement business logic.- Do not use QuickJS’s native worker init; use
dxEventBus.newWorkerinstead. - Most apps use only a subset of components.
- Put all resources (images, fonts, etc.) in the
resourcedirectory next tosrc. - Reference resource files (images, fonts) with absolute paths, e.g.
dxui.Image.build('img1', parent).source('/app/code/resource/logo.png'). - DejaOS runs on embedded devices with limited memory and CPU. Avoid large objects and long-running loops; release references when no longer needed.
- Uncaught exceptions can stop the current worker. Wrap potentially failing code in try-catch, especially hardware, network, and file I/O.
- All component modules live under
dxmodules; use correct relative import paths (e.g.import log from "../../dxmodules/dxLogger.js"). Watch path depth when moving files. This is not Node’snode_modulesmechanism. - Compress images and fonts when possible. Prefer PNG (with transparency) or JPEG for photos; avoid oversized bitmaps. Use TTF fonts that only include needed character sets; watch size for CJK fonts.
dxDriverprovides device-specific info (e.g.dxDriver.DISPLAY.WIDTH/HEIGHT,dxDriver.CHANNEL.UART_PATH). These vary by device; avoid hardcoding.- Creating font objects repeatedly hurts performance. Use
UIManager.font(size, style)to get fonts; it caches them. Avoid creating new fonts on every render.
2. UI
-
Run UI in a dedicated worker, not the main thread.
-
Relevant JS:
dxmodules/dxUi.jsand alldxmodules/ui*.js, including:uiBase.js: Base class for all controls (size, position, events, style)uiButton.js: ButtonuiButtons.js: Button matrix/groupuiCheckbox.js: CheckboxuiDropdown.js: DropdownuiFont.js: Font wrapperuiImage.js: ImageuiKeyboard.js: On-screen keyboard (e.g. pinyin)uiLabel.js: LabeluiLine.js: LineuiList.js: ListuiSlider.js: SlideruiStyle.js: StyleuiSwitch.js: SwitchuiTextarea.js: Multi-line text inputuiUtils.js: UI utilities, constants, enums, init helpersuiView.js: Container view (like a div)
-
For page management use UIManager.js, which implements single-screen, stack-based multi-page management.
-
Prefer small font files. If UI text includes Chinese or other non-ASCII, you must provide a TTF for that language, typically at
/app/code/resource/font/font.ttf, and get the font in code viaUIManager.font(size, style). If the app language is not English, remind the user to provide the TTF. -
uiButtonhas notextproperty; to show text, add aLabelinside the button and settext/textFonton the Label. -
uiImagedoes not support click events; for clickable images, wrap in a transparentViewand attach the click handler to that View. -
Using
dxui.Utils.LAYER.TOPas parent keeps the UI on top (e.g. popups, status bar). -
uiImagedoes not auto-scale the image; make the image size match the Image control size. -
uiViewhas default padding; setpadAll(0)before computing inner layout. Default scroll is on; usually callscroll(false).
3. UART / Serial
- Use a dedicated worker for UART; do not send/receive on the main thread.
- Main API:
dxmodules/dxUart.js. Referencedxmodules/vgUartWorker.jsif needed. - Device-related params:
dxDriver.CHANNELindxmodules/dxDriver.js. - UART send/receive is asynchronous; use events or RPC from
dxmodules/dxEventBus.js. - For encoding/decoding use codec utilities in
dxmodules/dxCommonUtils.js. - Example for baud rate etc.:
dxUart.ioctl(6, '921600-8-N-1'). receiveblocks untilsizebytes arrive; with a short timeout it may return with no data before completion.
4. Database
- Prefer
dxmodules/dxSqliteDB.jsfor CRUD. For simple key-value data usedxKeyValueDB.jsorkvdbWorker.js. Key-value is faster but no SQL and not for complex queries. - Database access does not require a dedicated worker for simple, fast queries or small writes; use a separate DB worker only for heavy or frequent writes.
- When using a DB worker, use
dxEventBusevents or RPC to pass data between UI/business and the DB worker. - Convention: place DB files under
/app/data/.
5. Logging
- Use
dxmodules/dxLogger.jsonly; do not useconsole.log. Example:import log from "../../dxmodules/dxLogger.js";thenlog.debug/log.info/log.error. - Logger accepts multiple arguments, e.g.
log.info("HomePage onShow", data). - For
Errorobjects uselog.error(e); the logger will expandmessageandstack.
6. Time
- Use
dxmodules/dxNtp.jsfor NTP sync and system time (and simple timezone/offset). This is enough for most apps. - For full timezone logic (e.g. world clock, multiple cities) use
dxmodules/dxTimeZones.js.
7. Network
- Network is managed by the app via
dxmodules/dxNetwork.js. - After starting the network it will try to connect and will auto-reconnect after disconnects; no need to call connect again explicitly.
8. Face recognition
- Reference:
test/src/worker/faceworker.js+test/src/worker/uiworker.js. - Component:
dxmodules/dxFacial.js. - Threading: Run
face.loop()in a dedicatedfaceWorker; UI thread only renders and handles input (e.g. readface.getDetectionData()for drawing boxes). - Required API:
face.init(),face.loop(),face.setCallbacks({ onRecognition }). - Result: In
onRecognition(event)useevent.userId,event.picPath,event.compareScore,event.rect,event.is_rec,event.isCompare, etc., and forward viadxEventBusto UI/business (e.g. door open, weighing, logging). - Enrollment (optional):
- Camera:
face.getFeaByCap(timeoutMs)→face.addFea(userId, feature)(optionallyface.deleteFea(userId)orface.updateFeafirst). - File:
face.getFeaByFile(filePath)→face.addFea(userId, feature)(local file path).
- Camera:
9. Watchdog
- Component:
dxmodules/dxWatchdog.js. - Purpose: hardware/software watchdog to reboot the system on hang or deadlock.
- Multiple channels: assign different channels to different workers; all enabled channels must be kicked in time or the system reboots.
- Usage: init with
watchdog.init()inmain.js;watchdog.enable(channel, true);watchdog.start(timeout_ms); callwatchdog.restart(channel)periodically (e.g. every 5 seconds).
10. HTTP
- Use
dxmodules/dxHttpClient.js. It is stateless; each request uses a separate native instance (thread-safe). - Response shape:
{ code, status, message, data }:code: native result; 0 = success, others indicate timeout, DNS failure, etc.status: HTTP status; 200 = success.data: response body string; usuallyJSON.parse(result.data).message: error description.
3. Core Code Templates
3.1 Standard UI Page (with UIManager)
import dxui from "../../dxmodules/dxUi.js";
import log from "../../dxmodules/dxLogger.js";
import dxDriver from "../../dxmodules/dxDriver.js";
import utils from "../../dxmodules/dxCommonUtils.js";
import UIManager from "../UIManager.js"; // assume UIManager in src
const MyPage = {
id: "myhomePage",
init: function () {
const parent = UIManager.getRoot();
this.root = dxui.View.build(this.id, parent);
this.root.setSize(dxDriver.DISPLAY.WIDTH, dxDriver.DISPLAY.HEIGHT);
this.root.radius(0);
this.root.borderWidth(0);
this.root.padAll(0);
this.root.bgColor(0x000000);
this.initView();
return this.root;
},
initView: function () {
this.btn = dxui.Button.build(this.id + "_btn", this.root);
this.btn.setSize(100, 50);
this.btn.align(dxui.Utils.ALIGN.CENTER, 0, 0);
this.btnLabel = dxui.Label.build(this.id + "_btn_label", this.btn);
this.btnLabel.textFont(UIManager.font(16, dxui.Utils.FONT_STYLE.BOLD));
this.btnLabel.text("Click me");
this.btnLabel.align(dxui.Utils.ALIGN.CENTER, 0, 0);
this.btn.on(dxui.Utils.EVENT.CLICK, () => {
log.info("Button clicked");
});
this.iconArea = dxui.View.build(this.id + "_icon_area", this.root);
this.iconArea.setSize(48, 48);
this.iconArea.bgOpa(0);
this.iconArea.radius(0);
this.iconArea.borderWidth(0);
this.iconArea.padAll(0);
this.iconArea.setPos(dxDriver.DISPLAY.WIDTH - 48 - 16, 16);
this.iconImage = dxui.Image.build(this.id + "_icon", this.iconArea);
this.iconImage.source("/app/code/resource/image/icon_admin.png");
this.iconImage.align(dxui.Utils.ALIGN.CENTER, 0, 0);
this.iconLabel = dxui.Label.build(this.id + "_icon_label", this.iconArea);
this.iconLabel.text("Admin");
this.iconLabel.textFont(UIManager.font(14, dxui.Utils.FONT_STYLE.NORMAL));
this.iconLabel.alignTo(this.iconImage, dxui.Utils.ALIGN.BOTTOM_MID, 0, 4);
this.iconArea.on(dxui.Utils.EVENT.CLICK, () => {
log.info("Click admin icon");
});
},
onShow: function (data) {
if (data) log.info("Received data:", data);
},
onHide: function () {},
};
export default MyPage;
3.2 UI Worker Entry (uiWorker.js)
import dxui from "../dxmodules/dxUi.js";
import log from "../dxmodules/dxLogger.js";
import std from "../dxmodules/dxStd.js";
import UIManager from "./UIManager.js";
import HomePage from "./pages/HomePage.js";
try {
dxui.init({ orientation: 1 });
UIManager.init();
UIManager.register("home", HomePage);
UIManager.open("home");
std.setInterval(() => {
dxui.handler();
}, 20);
} catch (error) {
log.error(error);
}
3.3 Main Thread (main.js)
import bus from "../dxmodules/dxEventBus.js";
import log from "../dxmodules/dxLogger.js";
function init() {}
try {
init();
} catch (e) {
log.error("init error", e);
}
const uiWorker = bus.newWorker("uiWorker", "/app/code/src/uiWorker.js");