ZeeTweak is a combination of firmware modifications and a companion zeetweak.py tool for the Zeeweii DSO3D12 oscilloscope, including:
- Saved waveform download: Capture screenshot data via serial and render new high quality screenshots, text reports, and CSV data for use with applications like sigrok PulseView.
- Arbitrary signal generator waveforms: Add new waveforms to the firmware
- Graphics: New fonts, images, and home screen with multiple background options
- UI tweaks: Screenshot counter repositioned, CH2 50% auto-set level centered
- Sample buffer download: Capture and extract the secret buffer dump mode (up to 60k samples)
Waveform capture and screenshot render:
The provided example firmware has all mods applied, see Flashing below to apply the firmware.
- To customize the firmware, place the desired mod files in a directory and use FLSTweak to generate a new firmware file.
- Most UI mods in this repo cover resources that are directly stored in the firmware (not part of the compiled code), so images are altered but not functionally changed.
- With release 4.x, some mods are based on reverse engineering of the firmware code (screenshot transfer, arbitrary waveforms, UI tweaks), which opens up the possibility of more functional changes. See the
Ghidrafiles section below. - Since UI preferences are highly subjective, post an Issue or Pull Request for what you'd like to see - there is plenty of room for improvement. Discussion and (especially) contributions are welcome!
- 4.1
- New: Cursors measurements for rendered screenshots
- New: CH2 50% auto-set level to 0 (originally offset by a few pixels)
- New: Generate waveform CSV files for other applications
- New: Support for DSO2512G screenshot data files (requires modified firmware)
- Updated: Ghidra project 0.3 - all functions are now labeled!
- Fixed: Windows serial port handling
- Fixed: Voltage calculations for probes x1/x100 modes
- 4.0
- New:
zeetweak.py- Screenshot data capture and rendering to new images with matplotlib
- Arbitrary signal generator waveforms
- Buffer dump capture (via secret debug mode)
- New: Screenshot counter UI repositioned, no longer obscures waveforms or cursor measurements
- Updated: Home screen icons
- New:
- 3.2
- New: Zeeweii firmware 3.0.7
- New: image for the new DMM side menu (press Menu in full-screen DMM mode)
- New: images for the new channel math menu (long-press Menu in scope mode)
- Updated: images for the DMM mode selection menu
- Updated: flashing with wm-tools
- 3.1
- New:
zrle2bmp.pytool to convert Zeeweii-custom RLE compressed images - New: images from @Dmitur: sig. gen. numbers, trigger edge icon
- New: images for channel level icon, trigger icon
- Updated: measurement label background
- Updated: menu display setting for persistence relabeled from 1s to 3s based on measured fade time
- New:
- 3.0
- New bitmaps for most UI elements
- Added image previews
- Forked from FLSTweak repo
- 2.0
- Updated example
mod.flsfile with measurement label changes, fixedNormallabel
- Updated example
- 1.1
- Rendered new small font with all characters shifted down 1 pixel
- 1.0
- Initial release
-
zeetweak.py- Companion tool for screenshot capture, image/text report generation, arbitrary waveform generation, and buffer dump capture. See below for usage. -
Zeeweii_DSO3D12directory:dso3d12_v3.0.7_III_mod_v4.1.fls- an example firmware with all mods applied.images- original and modified UI images.mods- binary patches for use with FLSTweak, including customizable mods (different signal generator waveforms, home screen backgrounds, etc).
-
Ghidradirectory:dso3d12_v3.0.7_ghidra_v0.3.gar- reverse engineering project for Ghidra.- This is an early stage, with all functions labeled along with embedded data, peripherals and SDK library functions with the SDK data types. Labels and purposes are always up for debate, but it's far enough along to be able to understand how the entire firmware works and spot opportunities for modifications.
- Usage:
- Install the ghidra_csky_WinnerMicro extension - I've forked the original extension to add all C-SKY instructions seen in the Zeeweii firmware for complete disassembly and decompilation.
- Open the firmware project in Ghidra using File > Restore Project.
- Double-click the firmware image to launch CodeBrowser and view the disassembly and decompiler output.
- Feedback appreciated! Feel free to post an Issue or in the EEVBlog forum thread.
wm-tools (Linux/macOS/Windows):
- A flashing utility by @rssdev10 based on Winner Micro's tool that does not require installing Python or other extras. Note that the Linux and Windows versions are currently untested.
- Download and extract the WM IoT SDK.
- Install required packages - from the
wm_iot_sdkdirectory:python -m pip install --user -r tools/wm/requirements.txt - With the DSO3D12 turned off, press and hold the power button - the scope will enter a boot loop and enable the scope's serial port to allow for flashing. Keep the power button pressed until flashing is complete.
- Check the name of the serial port - on macOS, use the
tty.wchusbserialdevice if two ports are detected:% ls /dev/tty* /dev/tty.Bluetooth-Incoming-Port /dev/tty.usbserial-1410 /dev/tty.wchusbserial1410 - From the
wm_iot_sdk/tools/wm/directory, runflash.pywith the serial port and firmware:% python3 flash.py --port /dev/tty.wchusbserial1410 --image dso3d12_v3.0.7_III_mod.fls connecting serial... serial connected trying reset device... wait serial sync... serial sync success trying baudrate 2000000... start download image... download dso3d12_v3.0.7_III_mod.fls... 0% [##############################] 100% flash device complete - Done! Release the power button.
- Download Upgrade Tools.
- Change the language to English from the top left menu (third option).
- Set "Chip" to
W80Xand set the image to the firmware .fls file. - Press and hold the scope power button to enable the serial port until flashing is complete.
- Select the new COM port, click "Open Serial", and then "Download" to flash.
- Done! Release the power button.

- Install Python.
- Install the required packages:
- macOS/Linux:
pip install pyserial numpy matplotlib - Windows:
py -m pip install pyserial numpy matplotlib
- macOS/Linux:
- Run the script with the serial port to capture screenshots/buffer dumps, or a file/directory to process existing data:
- macOS/Linux:
python zeetweak.py /dev/tty.usbserial-1410 - Windows:
python zeetweak.py COM1
- macOS/Linux:
screenshot_viewer_mod is a firmware patch combining two features:
-
Screenshot count repositioning - moves the image count to the upper right of the display, replacing the battery level display.
- Caveat: This replaces the total screenshot count (displays
1instead of1/63). The extra code space has been repurposed for...
- Caveat: This replaces the total screenshot count (displays
-
Screenshot data download - the scope will now automatically transfer the waveform data and scope state/settings via serial when a screenshot is viewed.
- After capture,
zeetweak.pywill process and generate:- Waveform image: Uses scope data with matplotlib to build new, high quality screenshots.
- Text report: Includes settings/status data and measurements, with extended data when using the
--debugoption. - CSV file: Structured waveform data for use with other applications.
- sigrok PulseView - select "Import comma-separated values", select the .csv file, then set "Column format specs" to "t,3a" for 1 channel or "t,6a" for 2 channels.
- Note: The scope doesn't save the original screenshot as a bitmap image, etc. Instead, it saves the waveform data and settings and regenerates the display as the screenshot.
- Some data (like channel math) is freshly calculated by the scope when the screenshot is viewed instead of being stored. The script currently doesn't handle this - it's likely more useful to render the data in something like PyQtGraph for fully interactive cursors/math/inversion etc.
- If you prefer not to have the scope send this data, use the
screenshot_viewer_mod_count_reposition_only.binmod file instead.
- After capture,
- Launch
zeetweak.pywith the scope's serial port:% python zeetweak.py /dev/tty.usbserial-1410 # Windows: COM1, etc Waiting for /dev/tty.usbserial-1410... (Ctrl+C to cancel) - Power on the scope (it may reset off when the serial port is initialized).
- Once the scope is connected, open the screenshot browser and open a saved waveform - the data will be transferred and processed automatically:
Connected, open saved waveforms to trigger capture: Saved data: Zeeweii_Screenshot_20251225_164225.bin Saved CSV: Zeeweii_Screenshot_20251225_164225.csv Saved text: Zeeweii_Screenshot_20251225_164225.txt Saved image: Zeeweii_Screenshot_20251225_164225.png
zeetweak.py [-h] [-o OUTPUT] [--color1 COLOR1] [--color2 COLOR2] [--meas MEAS] [--trigline] [--title TITLE] [--datetime] [-w WIDTH] [--debug] source
source- Specify the serial port of the scope, a filename (to re-process a previously saved .bin file), or a directory containing multiple saved .bin files to batch process.-o OUTPUT- Specify a filename prefix for the output. If the source is a single file, this will be the entire filename.--color1, --color2- Specify new channel 1 and channel 2 colors:--color1="yellow" --color2="#ff00ff"--meas- Specify the measurement labels to display in the image (the text output will always show all measurements). By default, the tool will use the measurements displayed in the original screenshot.- You can retroactively display all measurements, or select individually:
--meas="all"--meas="Freq,PkPk,Avg,RMS,Amp,+Duty,+T,-T,T,Max,Min,Top,Base,-Duty"
- You can retroactively display all measurements, or select individually:
--trigline- Display a dashed line at the trigger line--nocurs- Disable drawing cursors and measurements--title TITLE- Add a custom title to the image and text report.--datetime- Add the current date and time to custom titles and filenames when processing single files or directories. Timestamp is always added to serial captured files.-w WIDTH- Specify a width in pixels (default:1650for 150dpi)--dso2512g_v1and--dso2512g_v2- Generate screenshots, CSV, and text reports from DSO2512G scope data files. Requires modified 1.x and 2.x firmware.--debug- Adds extra debugging information during serial capture, including all scope state/settings data in the text report and a diff between sequential images at the console output to help identify additional scope data.
waveform_pattern_mod - most signal generator waveforms are calculated, but the Sinc function (pattern 7) is actually stored as hardcoded data. This makes it possible to replace it with other waveforms that fit in 256 bytes.
- Quirk: Possibly due to a compiler optimization, the starting sample (1 byte) is hardcoded into the firmware and the remaining 255 bytes are normal data. I've patched the starting sample to be
0x80(the centerline). zeetweak.pycan automatically convert images to waveforms, optionally scale to full dynamic range (0-255), adjust the waveform to align with the hardcoded starting sample, and produce the correctly sized mod data to flash.
To generate a new waveform:
- Create a 256x256 image (bmp, png, etc) with a black background and white waveform. The script will resize larger images and pick the bright pixels to use as waveform data.
- Run:
zeetweak.py --waveform my_waveform.png(add--scaleif needed) - Use the output
waveform_pattern_mod.binfile with FLSTweak to generate a new flash file. - Flash the firmware and select the Sinc waveform in the signal generator to see your waveform!
A few included examples:
mod_staircase / mod_am:
mod_ecg / mod_heart:
Reverse engineering the firmware revealed a function that reads out the FPGA sample buffer (up to 60k samples). This allows capturing higher resolution data that may be usable with other tools to analyze the data. To capture the data:
- Launch
zeetweak.pywith the scope's serial port:% python3 zeetweak.py /dev/tty.usbserial-1410 Waiting for /dev/tty.usbserial-1410... (Ctrl+C to cancel) - Power on the scope (it may reset off when the serial port is initialized).
- Acquire the signal(s), then press
Stopto freeze the sample buffer. - Press
Menu, thenStopso that the channel 1 measurements menu is open. - Long-press
Save- this kicks off the debug dump:2 channels:Connected, open saved waveforms to trigger capture: Receiving data... Saved debug dump: Zeeweii_Debug_20251216_180138.bin (270024 bytes) Extracted CH1: Zeeweii_Debug_20251216_180138-ch1.bin (60000 samples)Receiving data... Saved debug dump: Zeeweii_Debug_20251216_145708.bin (285042 bytes) Extracted CH1: Zeeweii_Debug_20251216_145708-ch1.bin (30000 samples) Extracted CH2: Zeeweii_Debug_20251216_145708-ch2.bin (30000 samples) - I recommend also taking a screenshot of the stopped waveform and transferring it - the buffer dump does not include any scope state/settings data (v/div, timebase, etc).
Notes:
- At full 60k sample depth, it can take up to 20s to transfer the data. At the fastest ranges (5ns, etc), the debug function switches to a fast mode with interpolation and sends 300 samples.
- The original debug dump converts the data to ASCII and prints it with spaces, line endings, etc.
zeetweak.pyextracts the data for each channel and saves as normal binary. - It should be possible to modify the firmware to directly send hex data and also include the scope state/settings data, on the todo list.
The home screen (accessed by pressing the power button once) is simply a full-color image (320x240 16bpp), which opens up the possibility to use any photo or image. I've included a few different options as an example of customizing this UI.
Original -> Mod:
mod_mono / mod_texture:
mod_smoke / mod_webb:
-
font_large, font_small- fonts covering ASCII characters 32-126 as 1-bit data.- ImageMagick can convert the raw data to a series of images:
$ magick -depth 1 -size 8x13 gray:font_small_ref.bin font_small_ref.png$ magick -depth 1 -size 16x16 gray:font_large_ref.bin font_large_ref.png
- Thanks to @timschuerewegen for developing the fonts (originally for the DSO2512G) and permitting their addition to this repo! Check out the Discord channel for a detailed discussion of Zeeweii firmware and reverse engineering work for the DSO2512G.
- ImageMagick can convert the raw data to a series of images:
-
image_XXXXXX- images are either monochrome 1-bit per pixel or color 16-bits per pixel (RGB565 format); filenames are the offset of the data in the firmware file.- 1bpp images:
- Viewable as PNG files in the
imagesdirectory - Convert raw data to image (must specify the size):
$ magick -depth 1 -size 67x198 gray:image_368172_ref.bin image_368172_ref.png - Convert image to raw data:
$ magick image.png -depth 1 gray:image.bin
- Viewable as PNG files in the
- 16bpp images:
- Viewable as BMP files in
images
- Viewable as BMP files in
- Thanks to @Dmitur on the EEVBlog forums for locating and documenting these resources, and for permitting their addition to this repo! See the post for additional info.
- 1bpp images:
-
label_calib- fixes typos for DMM calibration text -
label_measurements- alters the measurements labels and matches the labels inimage_364864_mod.bin -
label_normal- fixes typo for the "Normal" trigger message
Original -> Mod:
- Firmware: Available through Zeeweii's official support page
- Discussion thread: EEVBlog forums Another DSO+DMM - Zeeweii DSO3D12, claimed 120MHz/250MSps (june 2023)














