<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.recessim.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Gamerpaddy</id>
	<title>RECESSIM - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.recessim.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Gamerpaddy"/>
	<link rel="alternate" type="text/html" href="https://wiki.recessim.com/view/Special:Contributions/Gamerpaddy"/>
	<updated>2026-06-16T00:45:55Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.34.1</generator>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3619</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3619"/>
		<updated>2026-06-12T15:09:45Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Teardown / Internal Pictures / Hardware analysis */ typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All files used and mentioned are found on the official Optima XR220amx Application Software and Linux OS DVD 5406106-10 Rev3, which i cannot provide due to being copyrighted content.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. There is a Windows software but it is not obtainable. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.  for example studying the fluid movement in plants. https://www.youtube.com/watch?v=j-FHbHoiwNk (Xray timelapse by Ben Krasnow / Applied Science using a GE Flashpad)&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''' (Or FeiTian, Mammo, Gryphon etc.) to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
==Script Download==&lt;br /&gt;
&lt;br /&gt;
The detector does not have fixed acquisition commands. Instead the host&lt;br /&gt;
downloads small programs, called scripts, that the detector stores by ID and&lt;br /&gt;
then runs on command. A script is a sequence of primitive operations: program&lt;br /&gt;
the ROE (readout electronics) registers, wait, acquire an image, signal an&lt;br /&gt;
event. The acquisition behavior of the panel is defined entirely by these&lt;br /&gt;
downloaded scripts, which are taken from the application mode XML files (for&lt;br /&gt;
example IDC_URP_SE_2200.xml for single energy, 2048 by 2048).&lt;br /&gt;
&lt;br /&gt;
===The GENERIC_SCRIPT command===&lt;br /&gt;
&lt;br /&gt;
A script is sent to the detector with the GENERIC_SCRIPT command, cmd_type 5.&lt;br /&gt;
The wire format is:&lt;br /&gt;
&lt;br /&gt;
 [cmd_type   : 4 LE]   = 5&lt;br /&gt;
 [length     : 4 LE]   = 8 + sum(command sizes) + 2&lt;br /&gt;
 [script header : 8 bytes]&lt;br /&gt;
 [packed command 1]&lt;br /&gt;
 [packed command 2]&lt;br /&gt;
 ...&lt;br /&gt;
 [terminator : 2 bytes] = 00 00&lt;br /&gt;
&lt;br /&gt;
The 8 byte script header is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset!!Size!!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|0||2 LE||scriptID||the slot the script is stored in&lt;br /&gt;
|-&lt;br /&gt;
|2||2 LE||repeatCount||0 = run once, 65535 = loop forever&lt;br /&gt;
|-&lt;br /&gt;
|4||4 LE||repeatEvent||event ID that breaks an infinite loop (0 if unused)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: the application mode XML lists a fourth header value, ROEdata. This is a&lt;br /&gt;
substitution source for the script's commands, not a field on the wire. The wire&lt;br /&gt;
header is exactly 8 bytes.&lt;br /&gt;
&lt;br /&gt;
===Packed command primitives===&lt;br /&gt;
&lt;br /&gt;
Each command inside the script starts with a one byte DETECTORCODE that selects&lt;br /&gt;
its type and length:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!DETECTORCODE!!Command!!Size!!Layout (after the code byte)&lt;br /&gt;
|-&lt;br /&gt;
|1||Acquisition||17||typeMode:1, imageId:1, noScrubs:1, scrubDuration:4 LE, maxExposeTime:4 LE, tailTime:4 LE, transferMode:1&lt;br /&gt;
|-&lt;br /&gt;
|2||ROE command||14||responseFlag:1, timerValue:4 LE, roeCmd:4 LE, roeData:4 LE&lt;br /&gt;
|-&lt;br /&gt;
|3||Send host event||5||eventId:4 LE&lt;br /&gt;
|-&lt;br /&gt;
|4||Wait for host event||9||eventId:4 LE, timeout:4 LE&lt;br /&gt;
|-&lt;br /&gt;
|5||Delay||5||delayMicroseconds:4 LE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example, a ROE command sets a readout register (roeCmd, roeData) and an&lt;br /&gt;
Acquisition command triggers the actual exposure window and image readout.&lt;br /&gt;
&lt;br /&gt;
===Download sequence===&lt;br /&gt;
&lt;br /&gt;
#The host sends the GENERIC_SCRIPT packet for a given scriptID.&lt;br /&gt;
#The detector replies with a bare acknowledgement carrying the same sequence ID. (It may also send a GENERIC_SCRIPT status reply with a status byte; status 0 means accepted.)&lt;br /&gt;
#The host repeats this for each script it wants resident on the detector.&lt;br /&gt;
#When ready, the host sends EXECUTE_SCRIPT, cmd_type 6, which runs the stored scripts. During and after execution the detector sends status notifications, and finally EXECUTION_COMPLETE, cmd_type 0x10000.&lt;br /&gt;
&lt;br /&gt;
Scripts are persistent for the session: once downloaded they can be executed&lt;br /&gt;
without resending.&lt;br /&gt;
&lt;br /&gt;
===The standard script set===&lt;br /&gt;
&lt;br /&gt;
The single energy mode downloads four scripts in order. The scriptID in the&lt;br /&gt;
header is the slot; the XML refers to them by a separate DETECTOR_SCRIPT_ID.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!scriptID!!Purpose!!Notes&lt;br /&gt;
|-&lt;br /&gt;
|7||ROE initialization||Scan setup plus a sequence of zero pulses, ends by sending host event 17&lt;br /&gt;
|-&lt;br /&gt;
|8||Standby loop||repeatCount 65535, broken by event 41; keeps the panel idle between acquisitions&lt;br /&gt;
|-&lt;br /&gt;
|0||Standard acquisition||Requires a real X-ray exposure to complete&lt;br /&gt;
|-&lt;br /&gt;
|1||Dark / offset acquisition||Completes with no X-ray; used to test the full flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For a no X-ray test, download script 7 then script 1 and execute. The detector&lt;br /&gt;
runs the ROE init, performs the dark acquisition, and reports&lt;br /&gt;
EXECUTION_COMPLETE in a few seconds.&lt;br /&gt;
&lt;br /&gt;
===Inspecting the encoded scripts===&lt;br /&gt;
See [[#Main test py script: Usage and Parameters|Main test py script]]&lt;br /&gt;
The exact wire bytes of every built script can be printed without touching the&lt;br /&gt;
detector:&lt;br /&gt;
&lt;br /&gt;
 python flashpad_acquire.py --dump-scripts&lt;br /&gt;
&lt;br /&gt;
This shows the full GENERIC_SCRIPT encoding (header, packed commands, and&lt;br /&gt;
terminator) as hex, which is useful for verifying the format against the XML.&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU7105&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=User:Gamerpaddy&amp;diff=3618</id>
		<title>User:Gamerpaddy</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=User:Gamerpaddy&amp;diff=3618"/>
		<updated>2026-06-11T12:40:36Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* GE Medical Flashpad Digital X-ray Detector  changes*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==About==&lt;br /&gt;
&lt;br /&gt;
'''YouTube''': [https://www.youtube.com/gamerpaddy gamerpaddy YouTube Channel]&amp;lt;br&amp;gt;&lt;br /&gt;
'''Email''': gamerpaddyschannel at gmail dot com&amp;lt;br&amp;gt;&lt;br /&gt;
'''OSHW Repository''': [https://oshwlab.com/gamerpaddy gamerpaddy open source hardware lab]&lt;br /&gt;
&lt;br /&gt;
===Biography===&lt;br /&gt;
&lt;br /&gt;
I grew up in germany where every once in a while people would put their broken and unused household electronics on the streets for disposal. I picked up whatever I could, took it apart and tried to understand how it worked.&lt;br /&gt;
&lt;br /&gt;
Over the years that turned from just taking things apart into actually fixing and reverse engineering them. I built myself an electronics workshop and gathered knowledge from whatever I had on hand or access to, learning by doing and from the resources available on the internet. That eventually led to designing and manufacturing things myself, a few of which ended up helping other people too.&lt;br /&gt;
&lt;br /&gt;
I am a full supporter of the right to repair movement and contribute through work, knowledge and helping others where I can.&lt;br /&gt;
&lt;br /&gt;
===Projects===&lt;br /&gt;
&lt;br /&gt;
====Yamaha EBike Battery Dongle====&lt;br /&gt;
&lt;br /&gt;
Starting in 2016 I worked on an emulator module to use third-party lithium-ion batteries on Yamaha powered e-bikes. The module emulates the datastream of a healthy battery and sends it to the Yamaha PW series engine, allowing any power source in the 10-cell lithium-ion voltage range (27-42V) to be used in place of the original battery.&lt;br /&gt;
&lt;br /&gt;
Through this work most of the Yamaha battery communication protocol was figured out, which let people extend their range with DIY batteries and repair their failed originals, which made up 30-50% of the cost of a new e-bike. Discussion and findings were shared in the german pedelec forums [https://www.pedelecforum.de/forum/index.php?threads/yamaha-reverse-engineering.39480/].&lt;br /&gt;
&lt;br /&gt;
More information on the [[Yamaha EBike Battery Dongle]] wiki page.&lt;br /&gt;
&lt;br /&gt;
====Topping PA5 Amplifier D01 Replacement Module====&lt;br /&gt;
&lt;br /&gt;
In 2023 I worked on a replacement module to help people repair their failed Topping PA5 Class-D Amplifiers, as documented in the audiosciencereview forums [https://www.audiosciencereview.com/forum/index.php?threads/topping-pa5-fix-d01-module-replacement-for-everyone.44219/#post-1572910].&lt;br /&gt;
&lt;br /&gt;
The original units had a manufacturing flaw where potting compound used to protect the pre-amplifier module from being copied would expand and contract during thermal cycling, eventually cracking solder joints and taking the whole amplifier with it. I depotted the original module, reverse engineered it, and rebuilt it with a more efficient layout for cheaper manufacturing. It has since been built and used by people all over the world with none of the replacements failing so far. In some countries warranty claims were not a viable option due to the product being shipped from overseas, making a DIY fix the only practical solution.&lt;br /&gt;
&lt;br /&gt;
More information on the [[Topping PA5]] wiki page.&lt;br /&gt;
&lt;br /&gt;
====GE Medical Flashpad Digital X-ray Detector====&lt;br /&gt;
&lt;br /&gt;
I got hold of a GE Flashpad, a 2048x2048 digital radiography image sensor originally used in GE Optima 220AMX mobile X-ray units. These show up on the used market mostly in unknown or broken condition.&lt;br /&gt;
&lt;br /&gt;
I tore it down, documented the hardware and spent time reverse engineering the URP/PDAP communication protocol it uses over Ethernet. I got to the point where the detector can be fully identified over the network, scripts can be downloaded and executed, and a acquisition run can be driven from a custom host implementation without any GE software involved.&lt;br /&gt;
&lt;br /&gt;
More information on the [[GE Medical Flashpad Digital Xray Detector]] wiki page.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=User:Gamerpaddy&amp;diff=3617</id>
		<title>User:Gamerpaddy</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=User:Gamerpaddy&amp;diff=3617"/>
		<updated>2026-06-11T12:38:33Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: added ge flashpad project&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
&lt;br /&gt;
'''YouTube''': [https://www.youtube.com/gamerpaddy gamerpaddy YouTube Channel]&amp;lt;br&amp;gt;&lt;br /&gt;
'''Email''': gamerpaddyschannel at gmail dot com&amp;lt;br&amp;gt;&lt;br /&gt;
'''OSHW Repository''': [https://oshwlab.com/gamerpaddy gamerpaddy open source hardware lab]&lt;br /&gt;
&lt;br /&gt;
=== Biography ===&lt;br /&gt;
&lt;br /&gt;
I grew up in germany where every once in a while people would put their broken and unused household electronics on the streets for disposal. I picked up whatever I could, took it apart and tried to understand how it worked.&lt;br /&gt;
&lt;br /&gt;
Over the years that turned from just taking things apart into actually fixing and reverse engineering them. I built myself an electronics workshop and gathered knowledge from whatever I had on hand or access to, learning by doing and from the resources available on the internet. That eventually led to designing and manufacturing things myself, a few of which ended up helping other people too.&lt;br /&gt;
&lt;br /&gt;
I am a full supporter of the right to repair movement and contribute through work, knowledge and helping others where I can.&lt;br /&gt;
&lt;br /&gt;
=== Projects ===&lt;br /&gt;
&lt;br /&gt;
==== Yamaha EBike Battery Dongle ====&lt;br /&gt;
&lt;br /&gt;
Starting in 2016 I worked on an emulator module to use third-party lithium-ion batteries on Yamaha powered e-bikes. The module emulates the datastream of a healthy battery and sends it to the Yamaha PW series engine, allowing any power source in the 10-cell lithium-ion voltage range (27-42V) to be used in place of the original battery.&lt;br /&gt;
&lt;br /&gt;
Through this work most of the Yamaha battery communication protocol was figured out, which let people extend their range with DIY batteries and repair their failed originals, which made up 30-50% of the cost of a new e-bike. Discussion and findings were shared in the german pedelec forums [https://www.pedelecforum.de/forum/index.php?threads/yamaha-reverse-engineering.39480/].&lt;br /&gt;
&lt;br /&gt;
More information on the [[Yamaha EBike Battery Dongle]] wiki page.&lt;br /&gt;
&lt;br /&gt;
==== Topping PA5 Amplifier D01 Replacement Module ====&lt;br /&gt;
&lt;br /&gt;
In 2023 I worked on a replacement module to help people repair their failed Topping PA5 Class-D Amplifiers, as documented in the audiosciencereview forums [https://www.audiosciencereview.com/forum/index.php?threads/topping-pa5-fix-d01-module-replacement-for-everyone.44219/#post-1572910].&lt;br /&gt;
&lt;br /&gt;
The original units had a manufacturing flaw where potting compound used to protect the pre-amplifier module from being copied would expand and contract during thermal cycling, eventually cracking solder joints and taking the whole amplifier with it. I depotted the original module, reverse engineered it, and rebuilt it with a more efficient layout for cheaper manufacturing. It has since been built and used by people all over the world with none of the replacements failing so far. In some countries warranty claims were not a viable option due to the product being shipped from overseas, making a DIY fix the only practical solution.&lt;br /&gt;
&lt;br /&gt;
More information on the [[Topping PA5]] wiki page.&lt;br /&gt;
&lt;br /&gt;
==== GE Medical Flashpad Digital X-ray Detector ====&lt;br /&gt;
&lt;br /&gt;
I got hold of a GE Flashpad, a 2048x2048 digital radiography image sensor originally used in GE Optima 220AMX mobile X-ray units. These show up on the used market mostly in unknown or broken condition.&lt;br /&gt;
&lt;br /&gt;
I tore it down, documented the hardware and spent time reverse engineering the URP/PDAP communication protocol it uses over Gigabit Ethernet. I got to the point where the detector can be fully identified over the network, scripts can be downloaded and executed, and a complete dark acquisition run can be driven from a custom host implementation without any GE software involved.&lt;br /&gt;
&lt;br /&gt;
More information on the [[GE Medical Flashpad Digital Xray Detector]] wiki page.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3616</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3616"/>
		<updated>2026-06-11T12:29:00Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All files used and mentioned are found on the official Optima XR220amx Application Software and Linux OS DVD 5406106-10 Rev3, which i cannot provide due to being copyrighted content.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. There is a Windows software but it is not obtainable. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.  for example studying the fluid movement in plants. https://www.youtube.com/watch?v=j-FHbHoiwNk (Xray timelapse by Ben Krasnow / Applied Science using a GE Flashpad)&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''' (Or FeiTian, Mammo, Gryphon etc.) to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
==Script Download==&lt;br /&gt;
&lt;br /&gt;
The detector does not have fixed acquisition commands. Instead the host&lt;br /&gt;
downloads small programs, called scripts, that the detector stores by ID and&lt;br /&gt;
then runs on command. A script is a sequence of primitive operations: program&lt;br /&gt;
the ROE (readout electronics) registers, wait, acquire an image, signal an&lt;br /&gt;
event. The acquisition behavior of the panel is defined entirely by these&lt;br /&gt;
downloaded scripts, which are taken from the application mode XML files (for&lt;br /&gt;
example IDC_URP_SE_2200.xml for single energy, 2048 by 2048).&lt;br /&gt;
&lt;br /&gt;
===The GENERIC_SCRIPT command===&lt;br /&gt;
&lt;br /&gt;
A script is sent to the detector with the GENERIC_SCRIPT command, cmd_type 5.&lt;br /&gt;
The wire format is:&lt;br /&gt;
&lt;br /&gt;
 [cmd_type   : 4 LE]   = 5&lt;br /&gt;
 [length     : 4 LE]   = 8 + sum(command sizes) + 2&lt;br /&gt;
 [script header : 8 bytes]&lt;br /&gt;
 [packed command 1]&lt;br /&gt;
 [packed command 2]&lt;br /&gt;
 ...&lt;br /&gt;
 [terminator : 2 bytes] = 00 00&lt;br /&gt;
&lt;br /&gt;
The 8 byte script header is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset!!Size!!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|0||2 LE||scriptID||the slot the script is stored in&lt;br /&gt;
|-&lt;br /&gt;
|2||2 LE||repeatCount||0 = run once, 65535 = loop forever&lt;br /&gt;
|-&lt;br /&gt;
|4||4 LE||repeatEvent||event ID that breaks an infinite loop (0 if unused)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: the application mode XML lists a fourth header value, ROEdata. This is a&lt;br /&gt;
substitution source for the script's commands, not a field on the wire. The wire&lt;br /&gt;
header is exactly 8 bytes.&lt;br /&gt;
&lt;br /&gt;
===Packed command primitives===&lt;br /&gt;
&lt;br /&gt;
Each command inside the script starts with a one byte DETECTORCODE that selects&lt;br /&gt;
its type and length:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!DETECTORCODE!!Command!!Size!!Layout (after the code byte)&lt;br /&gt;
|-&lt;br /&gt;
|1||Acquisition||17||typeMode:1, imageId:1, noScrubs:1, scrubDuration:4 LE, maxExposeTime:4 LE, tailTime:4 LE, transferMode:1&lt;br /&gt;
|-&lt;br /&gt;
|2||ROE command||14||responseFlag:1, timerValue:4 LE, roeCmd:4 LE, roeData:4 LE&lt;br /&gt;
|-&lt;br /&gt;
|3||Send host event||5||eventId:4 LE&lt;br /&gt;
|-&lt;br /&gt;
|4||Wait for host event||9||eventId:4 LE, timeout:4 LE&lt;br /&gt;
|-&lt;br /&gt;
|5||Delay||5||delayMicroseconds:4 LE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example, a ROE command sets a readout register (roeCmd, roeData) and an&lt;br /&gt;
Acquisition command triggers the actual exposure window and image readout.&lt;br /&gt;
&lt;br /&gt;
===Download sequence===&lt;br /&gt;
&lt;br /&gt;
#The host sends the GENERIC_SCRIPT packet for a given scriptID.&lt;br /&gt;
#The detector replies with a bare acknowledgement carrying the same sequence ID. (It may also send a GENERIC_SCRIPT status reply with a status byte; status 0 means accepted.)&lt;br /&gt;
#The host repeats this for each script it wants resident on the detector.&lt;br /&gt;
#When ready, the host sends EXECUTE_SCRIPT, cmd_type 6, which runs the stored scripts. During and after execution the detector sends status notifications, and finally EXECUTION_COMPLETE, cmd_type 0x10000.&lt;br /&gt;
&lt;br /&gt;
Scripts are persistent for the session: once downloaded they can be executed&lt;br /&gt;
without resending.&lt;br /&gt;
&lt;br /&gt;
===The standard script set===&lt;br /&gt;
&lt;br /&gt;
The single energy mode downloads four scripts in order. The scriptID in the&lt;br /&gt;
header is the slot; the XML refers to them by a separate DETECTOR_SCRIPT_ID.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!scriptID!!Purpose!!Notes&lt;br /&gt;
|-&lt;br /&gt;
|7||ROE initialization||Scan setup plus a sequence of zero pulses, ends by sending host event 17&lt;br /&gt;
|-&lt;br /&gt;
|8||Standby loop||repeatCount 65535, broken by event 41; keeps the panel idle between acquisitions&lt;br /&gt;
|-&lt;br /&gt;
|0||Standard acquisition||Requires a real X-ray exposure to complete&lt;br /&gt;
|-&lt;br /&gt;
|1||Dark / offset acquisition||Completes with no X-ray; used to test the full flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For a no X-ray test, download script 7 then script 1 and execute. The detector&lt;br /&gt;
runs the ROE init, performs the dark acquisition, and reports&lt;br /&gt;
EXECUTION_COMPLETE in a few seconds.&lt;br /&gt;
&lt;br /&gt;
===Inspecting the encoded scripts===&lt;br /&gt;
See [[#Main test py script: Usage and Parameters|Main test py script]]&lt;br /&gt;
The exact wire bytes of every built script can be printed without touching the&lt;br /&gt;
detector:&lt;br /&gt;
&lt;br /&gt;
 python flashpad_acquire.py --dump-scripts&lt;br /&gt;
&lt;br /&gt;
This shows the full GENERIC_SCRIPT encoding (header, packed commands, and&lt;br /&gt;
terminator) as hex, which is useful for verifying the format against the XML.&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3615</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3615"/>
		<updated>2026-06-11T12:27:12Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Sequence Counter Rules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. There is a Windows software but it is not obtainable. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.  for example studying the fluid movement in plants. https://www.youtube.com/watch?v=j-FHbHoiwNk (Xray timelapse by Ben Krasnow / Applied Science using a GE Flashpad)&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''' (Or FeiTian, Mammo, Gryphon etc.) to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
== Script Download ==&lt;br /&gt;
&lt;br /&gt;
The detector does not have fixed acquisition commands. Instead the host&lt;br /&gt;
downloads small programs, called scripts, that the detector stores by ID and&lt;br /&gt;
then runs on command. A script is a sequence of primitive operations: program&lt;br /&gt;
the ROE (readout electronics) registers, wait, acquire an image, signal an&lt;br /&gt;
event. The acquisition behavior of the panel is defined entirely by these&lt;br /&gt;
downloaded scripts, which are taken from the application mode XML files (for&lt;br /&gt;
example IDC_URP_SE_2200.xml for single energy, 2048 by 2048).&lt;br /&gt;
&lt;br /&gt;
=== The GENERIC_SCRIPT command ===&lt;br /&gt;
&lt;br /&gt;
A script is sent to the detector with the GENERIC_SCRIPT command, cmd_type 5.&lt;br /&gt;
The wire format is:&lt;br /&gt;
&lt;br /&gt;
 [cmd_type   : 4 LE]   = 5&lt;br /&gt;
 [length     : 4 LE]   = 8 + sum(command sizes) + 2&lt;br /&gt;
 [script header : 8 bytes]&lt;br /&gt;
 [packed command 1]&lt;br /&gt;
 [packed command 2]&lt;br /&gt;
 ...&lt;br /&gt;
 [terminator : 2 bytes] = 00 00&lt;br /&gt;
&lt;br /&gt;
The 8 byte script header is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Offset !! Size !! Field !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 2 LE || scriptID || the slot the script is stored in&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 2 LE || repeatCount || 0 = run once, 65535 = loop forever&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 4 LE || repeatEvent || event ID that breaks an infinite loop (0 if unused)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: the application mode XML lists a fourth header value, ROEdata. This is a&lt;br /&gt;
substitution source for the script's commands, not a field on the wire. The wire&lt;br /&gt;
header is exactly 8 bytes.&lt;br /&gt;
&lt;br /&gt;
=== Packed command primitives ===&lt;br /&gt;
&lt;br /&gt;
Each command inside the script starts with a one byte DETECTORCODE that selects&lt;br /&gt;
its type and length:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! DETECTORCODE !! Command !! Size !! Layout (after the code byte)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Acquisition || 17 || typeMode:1, imageId:1, noScrubs:1, scrubDuration:4 LE, maxExposeTime:4 LE, tailTime:4 LE, transferMode:1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || ROE command || 14 || responseFlag:1, timerValue:4 LE, roeCmd:4 LE, roeData:4 LE&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Send host event || 5 || eventId:4 LE&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Wait for host event || 9 || eventId:4 LE, timeout:4 LE&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Delay || 5 || delayMicroseconds:4 LE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example, a ROE command sets a readout register (roeCmd, roeData) and an&lt;br /&gt;
Acquisition command triggers the actual exposure window and image readout.&lt;br /&gt;
&lt;br /&gt;
=== Download sequence ===&lt;br /&gt;
&lt;br /&gt;
# The host sends the GENERIC_SCRIPT packet for a given scriptID.&lt;br /&gt;
# The detector replies with a bare acknowledgement carrying the same sequence ID. (It may also send a GENERIC_SCRIPT status reply with a status byte; status 0 means accepted.)&lt;br /&gt;
# The host repeats this for each script it wants resident on the detector.&lt;br /&gt;
# When ready, the host sends EXECUTE_SCRIPT, cmd_type 6, which runs the stored scripts. During and after execution the detector sends status notifications, and finally EXECUTION_COMPLETE, cmd_type 0x10000.&lt;br /&gt;
&lt;br /&gt;
Scripts are persistent for the session: once downloaded they can be executed&lt;br /&gt;
without resending.&lt;br /&gt;
&lt;br /&gt;
=== The standard script set ===&lt;br /&gt;
&lt;br /&gt;
The single energy mode downloads four scripts in order. The scriptID in the&lt;br /&gt;
header is the slot; the XML refers to them by a separate DETECTOR_SCRIPT_ID.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! scriptID !! Purpose !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 7 || ROE initialization || Scan setup plus a sequence of zero pulses, ends by sending host event 17&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Standby loop || repeatCount 65535, broken by event 41; keeps the panel idle between acquisitions&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Standard acquisition || Requires a real X-ray exposure to complete&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Dark / offset acquisition || Completes with no X-ray; used to test the full flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For a no X-ray test, download script 7 then script 1 and execute. The detector&lt;br /&gt;
runs the ROE init, performs the dark acquisition, and reports&lt;br /&gt;
EXECUTION_COMPLETE in a few seconds.&lt;br /&gt;
&lt;br /&gt;
=== Inspecting the encoded scripts ===&lt;br /&gt;
See [[#Main test py script: Usage and Parameters|Main test py script]]&lt;br /&gt;
The exact wire bytes of every built script can be printed without touching the&lt;br /&gt;
detector:&lt;br /&gt;
&lt;br /&gt;
 python flashpad_acquire.py --dump-scripts&lt;br /&gt;
&lt;br /&gt;
This shows the full GENERIC_SCRIPT encoding (header, packed commands, and&lt;br /&gt;
terminator) as hex, which is useful for verifying the format against the XML.&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3614</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3614"/>
		<updated>2026-06-11T12:23:50Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Communication Protocol */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. There is a Windows software but it is not obtainable. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.  for example studying the fluid movement in plants. https://www.youtube.com/watch?v=j-FHbHoiwNk (Xray timelapse by Ben Krasnow / Applied Science using a GE Flashpad)&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''' (Or FeiTian, Mammo, Gryphon etc.) to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3613</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3613"/>
		<updated>2026-06-11T12:22:07Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. There is a Windows software but it is not obtainable. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.  for example studying the fluid movement in plants. https://www.youtube.com/watch?v=j-FHbHoiwNk (Xray timelapse by Ben Krasnow / Applied Science using a GE Flashpad)&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3612</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3612"/>
		<updated>2026-06-11T12:21:28Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Purpose and Motivation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. There is a Windows software but it is not obtainable. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
*Study an research for example studying the fluid movement in plants. https://www.youtube.com/watch?v=j-FHbHoiwNk (Xray timelapse by Ben Krasnow / Applied Science using a GE Flashpad)&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3611</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3611"/>
		<updated>2026-06-11T12:15:58Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Purpose and Motivation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering using a different detector]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter using a different detector]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge flashpad main pcb closeup 3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Main test py script: Usage and Parameters==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
===Default network configuration===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Setting!!Default!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|Detector IP||192.168.1.30||the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
|Host IP||192.168.1.1||this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
|Detector port||8100||where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
|Host command port||5550||where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
|Host image port||6660||where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
|Discovery port||4500||where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Connection and general options===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!Default!!Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP||192.168.1.30||detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP||192.168.1.1||host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR||.||where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS||5.0||UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS||60||how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery||off||send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet||off||suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Read and diagnostic modes (non destructive)===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors||Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side.||A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init||With --sensors, skips the Script7 ROE init before reading.||Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data||Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14).||A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR]||Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/.||A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI||Limits the --backup sweep, for example 0x40-0xA0.||Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts||Prints the wire bytes of all built scripts as hex and exits.||No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen||Passively listens on the host command port for 30 seconds.||Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Acquisition modes===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does!!What to expect&lt;br /&gt;
|-&lt;br /&gt;
|(no flag)||Standard acquisition (Script 0).||Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark||Runs the dark/offset acquisition (Script 1) before the standard one.||Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only||Runs only the dark acquisition (Script 1).||Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby||Omits the Script 8 standby loop.||Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec||Executes Script 7 alone, waits, then Script 1. Only with --dark-only.||Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel||Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9.||Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq||Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push.||Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview||Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push.||One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
===Registration and write operations===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Option!!What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self||Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC||The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write||Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE||Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit||Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Typical workflow===&lt;br /&gt;
&lt;br /&gt;
#Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
#Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
#Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
#Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
#When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
#If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Known Dead Ends==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3610</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3610"/>
		<updated>2026-06-11T12:13:36Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: added the main script to read &amp;amp; set data&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
{{Note|UNFINISHED Project, it was not yet possible to retrieve a exposure or dark frame in my testing, further investigation needed. any help is appreciated on recessim discord.}}&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|AI assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
===Reading the Drop and Shock Event Log===&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
====How to read it====&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
====Example output====&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
====Interpreting the records====&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Reading the summary fields====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Teardown / Internal Pictures / Hardware analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; heights=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
File:GE Flashpad under the hood.jpg|the Carbon fiber sleeve is held by 9 screws and can taken off without force.&lt;br /&gt;
File:Ge flashpad powersupply pcb in handle.jpg|Power supply PCB&lt;br /&gt;
File:Ge flashpad uwb board pcb.jpg|UWB PCB for Wireless USB using a RTU2705&lt;br /&gt;
File:Ge flashpad uwb pcb backside.jpg|Backside of UWB PCB&lt;br /&gt;
File:Ge flashpad power supply pcb backside.jpg|Backside of Power supply PCB&lt;br /&gt;
File:Ge flashpad main pcb closeup1.jpg|Main area of the PCB with its Altera Cyclone 3 FPGA&lt;br /&gt;
File:Ge flashpad main pcb closeup2.jpg|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX&lt;br /&gt;
File:Ge flashpad bottom connector ethernet isolation pcb.jpg|Isolation PCB for bottom connector&lt;br /&gt;
File:Ge flashpad main pcb powersupply section.jpg|Powersupply section for FPGA and ROIC&lt;br /&gt;
File:Ge_flashpad_main_pcb_closeup_3.jpg|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder&lt;br /&gt;
File:Ge flashpad main pcb closeup 4.jpg|Accelerometer is a 834-0500 500G 3 Axis unit, and TJ500AE SPDT gigabit LAN switch&lt;br /&gt;
File:Ge flashpad bottom connector.jpg|bottom connector for docking stand&lt;br /&gt;
File:Ge flashpad main pcb closeup 5.jpg|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver&lt;br /&gt;
File:Ge flashpad main pcb closeup 6.jpg|Altera EPM570f100c5n CPLD&lt;br /&gt;
File:Ge flashpad main pcb closeup 7.jpg|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything&lt;br /&gt;
File:Ge flashpad main pcb closeup 8.jpg|24LC256I 32k EEPROM for storing the accelerometer events&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Main test py script: Usage and Parameters ==&lt;br /&gt;
&lt;br /&gt;
current iteration found here: https://pastebin.com/vEfAresk&lt;br /&gt;
&lt;br /&gt;
flashpad_acquire.py is a single file Python 3 tool that speaks the FlashPad&lt;br /&gt;
URP/PDAP protocol over UDP. It implements discovery, the control handshake,&lt;br /&gt;
script download and execution, sensor readout, full data backup, and the host&lt;br /&gt;
registration (pairing) write. It requires only the Python standard library.&lt;br /&gt;
&lt;br /&gt;
=== Default network configuration ===&lt;br /&gt;
&lt;br /&gt;
These defaults match the values in ConnectionPoint.cfg and can be overridden on&lt;br /&gt;
the command line.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Setting !! Default !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Detector IP || 192.168.1.30 || the panel; listens for all commands on UDP 8100&lt;br /&gt;
|-&lt;br /&gt;
| Host IP || 192.168.1.1 || this machine, announced in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| Detector port || 8100 || where commands are sent&lt;br /&gt;
|-&lt;br /&gt;
| Host command port || 5550 || where the detector returns protocol replies&lt;br /&gt;
|-&lt;br /&gt;
| Host image port || 6660 || where pixel data would stream&lt;br /&gt;
|-&lt;br /&gt;
| Discovery port || 4500 || where the detector sends its beacons&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Connection and general options ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Option !! Default !! Effect&lt;br /&gt;
|-&lt;br /&gt;
| --detector-ip IP || 192.168.1.30 || detector address&lt;br /&gt;
|-&lt;br /&gt;
| --host-ip IP || 192.168.1.1 || host address sent in SYSTEM_STARTUP&lt;br /&gt;
|-&lt;br /&gt;
| --output-dir DIR || . || where raw images are written&lt;br /&gt;
|-&lt;br /&gt;
| --timeout SECONDS || 5.0 || UDP receive timeout&lt;br /&gt;
|-&lt;br /&gt;
| --exec-timeout SECONDS || 60 || how long to wait for EXECUTION_COMPLETE&lt;br /&gt;
|-&lt;br /&gt;
| --skip-discovery || off || send SYSTEM_STARTUP once and proceed, skipping the discovery loop&lt;br /&gt;
|-&lt;br /&gt;
| --quiet || off || suppress the verbose per packet log&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Read and diagnostic modes (non destructive) ===&lt;br /&gt;
&lt;br /&gt;
These only read from the detector. None of them write anything.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Option !! What it does !! What to expect&lt;br /&gt;
|-&lt;br /&gt;
| --sensors || Reads the full 32 entry sensor table using cmd 0x7902 (converted) and 0x7900 (raw), side by side. || A decoded table of supply rails in volts. Temperatures show as railed. Unimplemented sensors (accelerometer DEM id, battery, grid) are flagged as stale.&lt;br /&gt;
|-&lt;br /&gt;
| --no-roe-init || With --sensors, skips the Script7 ROE init before reading. || Slightly faster sensor read, marginally less reliable.&lt;br /&gt;
|-&lt;br /&gt;
| --probe-data || Reads DetectorInfo, the HostList (registration state), and cal results via the upload protocol (cmd 0x13/0x14). || A hexdump and text decode of each. Shows whether any host is registered and which HostId the detector expects.&lt;br /&gt;
|-&lt;br /&gt;
| --backup [DIR] || Sweeps all upload IDs 0x00 to 0xFF and saves every readable blob to DIR/detector_backup_timestamp/. || A manifest plus one .bin per readable ID, including the full 64 MB flash image and the calibration maps. Do this before any write. Large blobs take a few minutes each.&lt;br /&gt;
|-&lt;br /&gt;
| --backup-range LO-HI || Limits the --backup sweep, for example 0x40-0xA0. || Faster, partial backup.&lt;br /&gt;
|-&lt;br /&gt;
| --dump-scripts || Prints the wire bytes of all built scripts as hex and exits. || No network activity; useful for inspecting the script encoding.&lt;br /&gt;
|-&lt;br /&gt;
| --listen || Passively listens on the host command port for 30 seconds. || Prints any packets the detector sends; useful for watching beacons.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Acquisition modes ===&lt;br /&gt;
&lt;br /&gt;
The default invocation (no mode flag) runs a standard acquisition, which&lt;br /&gt;
requires an actual X-ray exposure to complete.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Option !! What it does !! What to expect&lt;br /&gt;
|-&lt;br /&gt;
| (no flag) || Standard acquisition (Script 0). || Needs an X-ray exposure to reach EXECUTION_COMPLETE.&lt;br /&gt;
|-&lt;br /&gt;
| --dark || Runs the dark/offset acquisition (Script 1) before the standard one. || Two acquisitions; the dark one needs no X-ray.&lt;br /&gt;
|-&lt;br /&gt;
| --dark-only || Runs only the dark acquisition (Script 1). || Completes in a few seconds with no X-ray. The detector acquires and holds an 8 buffer image. Best way to test the full flow.&lt;br /&gt;
|-&lt;br /&gt;
| --no-standby || Omits the Script 8 standby loop. || Use if the standby loop blocks acquisition.&lt;br /&gt;
|-&lt;br /&gt;
| --two-exec || Executes Script 7 alone, waits, then Script 1. Only with --dark-only. || Tests the two stage execution hypothesis.&lt;br /&gt;
|-&lt;br /&gt;
| --parallel || Answers the detector status query (0x30000) with the cmd 0x99 ParallelImageTransfer reply instead of cmd 9. || Experiment to see if the parallel reply triggers a pixel push. (Result so far: it does not.)&lt;br /&gt;
|-&lt;br /&gt;
| --sweep-acq || Sweeps the dark acquisition across type_mode and transfer_mode values, watching for a 0x0F pixel push. || Non destructive experiment; reports which combination, if any, makes the detector stream.&lt;br /&gt;
|-&lt;br /&gt;
| --preview || Single dark acquisition on the preview path (transfer_mode 2, 4 buffer image). Watches port 6660 for a push. || One clean connection; run a packet capture on the detector address in parallel.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: at present the detector acquires and holds an image but does not push the&lt;br /&gt;
0x0F pixel frames. The acquisition modes complete the handshake and acquisition,&lt;br /&gt;
but image streaming is still being investigated.&lt;br /&gt;
&lt;br /&gt;
=== Registration and write operations ===&lt;br /&gt;
&lt;br /&gt;
These can write to the detector's flash. Every write is a dry run by default and&lt;br /&gt;
only takes effect when --commit is added. Always run --backup first.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Option !! What it does&lt;br /&gt;
|-&lt;br /&gt;
| --register-self || Adds this host to the detector HostList and sets it as the primary host, so the detector should stream images to it. Appends an entry, sets IndexToPrimaryHost, recomputes the CRC, and writes via id 0x71. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --host-mac MAC || The MAC of the interface talking to the detector, used to derive this host's HostId for --register-self. Default 00:6f:00:01:0a:3a.&lt;br /&gt;
|-&lt;br /&gt;
| --smoke-test-write || Reads the HostList and writes back the identical bytes to validate the write and commit path without changing anything. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --restore-hostlist FILE || Writes a saved HostList blob (for example detector_backup_*/upload_0x71_504.bin) back to the detector. This is the undo button. Dry run unless --commit.&lt;br /&gt;
|-&lt;br /&gt;
| --commit || Arms the actual flash write for the operations above. Without it they only print what they would do. This writes the detector's flash and is irreversible at the chip level, although the HostList region can be restored from a backup.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Typical workflow ===&lt;br /&gt;
&lt;br /&gt;
# Read the current state: &amp;lt;code&amp;gt;python flashpad_acquire.py --probe-data&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make a full backup: &amp;lt;code&amp;gt;python flashpad_acquire.py --backup&amp;lt;/code&amp;gt;&lt;br /&gt;
# Check sensors and power rails: &amp;lt;code&amp;gt;python flashpad_acquire.py --sensors&amp;lt;/code&amp;gt;&lt;br /&gt;
# Dry run the registration: &amp;lt;code&amp;gt;python flashpad_acquire.py --register-self --host-mac YOUR:MAC&amp;lt;/code&amp;gt;&lt;br /&gt;
# When satisfied, commit it: add &amp;lt;code&amp;gt;--commit&amp;lt;/code&amp;gt;&lt;br /&gt;
# If needed, undo: &amp;lt;code&amp;gt;python flashpad_acquire.py --restore-hostlist detector_backup_*/upload_0x71_504.bin --commit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Known Dead Ends ==&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3609</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3609"/>
		<updated>2026-06-11T12:00:53Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* FlashPad Detector: Internal Flash Dump and Data Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
==Reading the Drop and Shock Event Log==&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
===How to read it===&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
===Example output===&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
===Interpreting the records===&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Reading the summary fields===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Known Dead Ends===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 4.jpg|thumb|left|Accelerometer is a 834-0500 500G 3 Axis unit. abd TJ500AE SPDT GIGABIT LAN SWITCH]]&lt;br /&gt;
[[File:Ge flashpad bottom connector.jpg|thumb|left|bottom connector for docking stand]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 5.jpg|thumb|left|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 6.jpg|thumb|left|Altera EPM570f100c5n CPLD ]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 7.jpg|thumb|left|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything.]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 8.jpg|thumb|left|24LC256I 32k EEPROM for storing the accelerometer events.]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3608</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3608"/>
		<updated>2026-06-11T11:59:52Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Status of Reverse Engineering */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works but without image readout its speculation.&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
==Reading the Drop and Shock Event Log==&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
===How to read it===&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
===Example output===&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
===Interpreting the records===&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Reading the summary fields===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Known Dead Ends===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 4.jpg|thumb|left|Accelerometer is a 834-0500 500G 3 Axis unit. abd TJ500AE SPDT GIGABIT LAN SWITCH]]&lt;br /&gt;
[[File:Ge flashpad bottom connector.jpg|thumb|left|bottom connector for docking stand]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 5.jpg|thumb|left|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 6.jpg|thumb|left|Altera EPM570f100c5n CPLD ]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 7.jpg|thumb|left|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything.]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 8.jpg|thumb|left|24LC256I 32k EEPROM for storing the accelerometer events.]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3607</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3607"/>
		<updated>2026-06-11T11:58:19Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Technical Specifications */  linking to shock event log&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Purpose and Motivation==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
*Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
*Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
*Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage. See section [[#Reading the Drop and Shock Event Log|drop and shock event log]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
===Status of Reverse Engineering===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!What!!Status&lt;br /&gt;
|-&lt;br /&gt;
|Connect, beacon, ACK||Works&lt;br /&gt;
|-&lt;br /&gt;
|PORT_SETUP||Works&lt;br /&gt;
|-&lt;br /&gt;
|SIGNATURE_REQUEST / serial number readout||Works&lt;br /&gt;
|-&lt;br /&gt;
|Script download (Scripts 7, 8, 1)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTE_SCRIPT||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (dark acquisition)||Works&lt;br /&gt;
|-&lt;br /&gt;
|EXECUTION_COMPLETE (standard acquisition)||Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
|Image data on port 6660||Does not work yet, under investigation. Will not send Imagedata yet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Communication Protocol==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
===Network Setup===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Role!!IP!!Port!!Direction&lt;br /&gt;
|-&lt;br /&gt;
|All commands: host -&amp;gt; detector||192.168.1.30||'''8100''' (UDP)||Host sends here&lt;br /&gt;
|-&lt;br /&gt;
|Discovery beacons: detector -&amp;gt; host||-||'''4500''' (UDP)||Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
|Protocol replies: detector -&amp;gt; host||-||'''5550''' (UDP)||Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
|Image pixel data: detector -&amp;gt; host||-||'''6660''' (UDP)||Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
===Protocol Layers===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
;URP (Unified Registration Protocol)&lt;br /&gt;
:8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
;PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
:The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
*&amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Connecting to the Detector===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
#Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
#The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
#Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
====Step 1: SYSTEM_STARTUP====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 2: Receiving the BEACON and ACKing====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 3: PORT_SETUP====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Step 4: Reading the Serial Number (SIGNATURE_REQUEST)====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Running an Acquisition===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
====ROE Initialisation (Script 7)====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
====Dark / Offset Acquisition (Script 1)====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
====Standard Acquisition (Script 0)====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
====Sequence Overview====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sequence Counter Rules===&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
*After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
*Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
*Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
*The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sensor Readout and Host Registration==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
===Sensor Readout===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
====Commands====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!cmd_type!!Meaning!!Request payload!!Reply payload&lt;br /&gt;
|-&lt;br /&gt;
|0x7900||Raw sensor read||[sensorId:4 LE]||[value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
|0x7902||Converted sensor read||[sensorId:4 LE]||[value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
|0x7904||Detailed / radio||[selector:4 LE]||[value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
====Power rails (live readings)====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Sensor!!sensorId!!Converted!!Value&lt;br /&gt;
|-&lt;br /&gt;
|DCIN_RAW||10||12100 mV||+12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
|LCORE_UNREG||11||1724 mV||+1.72 V&lt;br /&gt;
|-&lt;br /&gt;
|LPANA_UNREG||12||5776 mV||+5.78 V&lt;br /&gt;
|-&lt;br /&gt;
|LNANA_UNREG||13||-5801 mV||-5.80 V&lt;br /&gt;
|-&lt;br /&gt;
|SCAN_VCC||14||5086 mV||+5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
|P5V_REF||16||5025 mV||+5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
|V_ON||17||11085 mV||+11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
|V_OFF||18||-12378 mV||-12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
|V_COMMON||19||-9385 mV||-9.39 V&lt;br /&gt;
|-&lt;br /&gt;
|3V3||29||3177 mV||+3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
|VCC_UNREG||37||3399 mV||+3.40 V&lt;br /&gt;
|-&lt;br /&gt;
|PARCPREG||38||3768 mV||+3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
|NARCPREG||39||-3732 mV||-3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
|PANA_UNREG||45||18352 mV||+18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
|NANA_UNREG||46||-19251 mV||-19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
====Known limitations====&lt;br /&gt;
&lt;br /&gt;
*'''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
*'''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
*'''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
===Host Registration (Pairing)===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
====Host identity (MAC and HostId)====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
#Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
#Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
#The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
====HostList structure====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
====HostList from the unit under test====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Index!!HostId!!Derived MAC!!Name&lt;br /&gt;
|-&lt;br /&gt;
|0||2C6400045FB42C64||00:04:5F:B4:2C:64||Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
|1||B044E8393512B044||E8:39:35:12:B0:44||Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
|2||5CF800045FB15CF8||00:04:5F:B1:5C:F8||Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
====Checksum (CRC)====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
*Polynomial: 0x04C11DB7&lt;br /&gt;
*Initial value: 0&lt;br /&gt;
*MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
*Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
====Read and write transport====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x0E||[downloadId:4 LE][totalSize:4 LE]||[status:1]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x0F||[bufId:4 LE][numBytes:4 LE][data]||[status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
|Commit||0x10||(empty)||[status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
====Registering a new host====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
==Reading the Drop and Shock Event Log==&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
===How to read it===&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
===Example output===&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
===Interpreting the records===&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Date and time!!VibrationX!!VibrationY!!VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
|2017-04-20 06:59:47||0||0||135&lt;br /&gt;
|-&lt;br /&gt;
|2020-01-03 11:04:06||0||0||183&lt;br /&gt;
|-&lt;br /&gt;
|2020-06-14 07:52:04||-172||0||-113&lt;br /&gt;
|-&lt;br /&gt;
|2020-11-13 05:40:30||0||0||170&lt;br /&gt;
|-&lt;br /&gt;
|2020-12-17 06:11:45||0||0||-101&lt;br /&gt;
|-&lt;br /&gt;
|2021-11-07 07:03:55||0||0||-123&lt;br /&gt;
|-&lt;br /&gt;
|2022-01-27 06:21:30||-132||0||-119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Reading the summary fields===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Field!!Meaning&lt;br /&gt;
|-&lt;br /&gt;
|LastKnownGoodTime||Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL3Events||Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
|NumberOfL5Events||Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
|ShockDataTimeList||Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
==FlashPad Detector: Internal Flash Dump and Data Files==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
===Extraction method===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Step!!cmd_type!!host to detector!!detector to host&lt;br /&gt;
|-&lt;br /&gt;
|Configure||0x13||[uploadId:4 LE]||[status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
|Buffer||0x14||[bufId:4 LE][numBytes:4 LE]||[bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
===Recovered files===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Upload ID!!Size (bytes)!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0xFD||67108864||Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
|0xFF||16777216||16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
|0xFE||524288||Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
|0x10||6815783||Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
|0x11||6815783||Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
|0x12||6815783||Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
|0x06||49254||Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
|0x50||19938||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x51||19582||Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
|0x07||12187||Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
|0x72||3001||Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x0E||624||Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
|0x71||504||Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
|0x0F||233||Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
|0x08||62||Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
|0x0A||48||Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
|0x52||30||Serial string&lt;br /&gt;
|-&lt;br /&gt;
|0x0B||24||Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Flash image layout (upload ID 0xFD)===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Range!!Contents&lt;br /&gt;
|-&lt;br /&gt;
|0x000000 to 0x800000||Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
|0x800000 to 0x940000||Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
|0x940000||Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
|0x1000000 to 0x2800000||Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
|0x2800000 to 0x4000000||Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
===Notes on individual files===&lt;br /&gt;
&lt;br /&gt;
*'''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
*'''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
*'''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
*'''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
*'''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
===Significance===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Known Dead Ends===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
;0xBEEF (48879) as reply port&lt;br /&gt;
:This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
;CmdFlag=1 as data packet flag&lt;br /&gt;
:Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
;Image data on alternate ports&lt;br /&gt;
:Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
;PORT_SETUP byte order (big-endian)&lt;br /&gt;
:Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
;Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
:Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Teardown / Internal Pictures / Hardware analysis==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 4.jpg|thumb|left|Accelerometer is a 834-0500 500G 3 Axis unit. abd TJ500AE SPDT GIGABIT LAN SWITCH]]&lt;br /&gt;
[[File:Ge flashpad bottom connector.jpg|thumb|left|bottom connector for docking stand]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 5.jpg|thumb|left|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 6.jpg|thumb|left|Altera EPM570f100c5n CPLD ]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 7.jpg|thumb|left|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything.]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 8.jpg|thumb|left|24LC256I 32k EEPROM for storing the accelerometer events.]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3606</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3606"/>
		<updated>2026-06-11T11:56:26Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Sensor Readout and Host Registration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
== Purpose and Motivation ==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
* Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
* Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
* Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
=== Status of Reverse Engineering ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! What !! Status&lt;br /&gt;
|-&lt;br /&gt;
| Connect, beacon, ACK || Works&lt;br /&gt;
|-&lt;br /&gt;
| PORT_SETUP || Works&lt;br /&gt;
|-&lt;br /&gt;
| SIGNATURE_REQUEST / serial number readout || Works&lt;br /&gt;
|-&lt;br /&gt;
| Script download (Scripts 7, 8, 1) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTE_SCRIPT || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (dark acquisition) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (standard acquisition) || Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
| Image data on port 6660 || Does not work yet, under investigation. Will not send Imagedata yet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Communication Protocol ==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
=== Network Setup ===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Role !! IP !! Port !! Direction&lt;br /&gt;
|-&lt;br /&gt;
| All commands: host -&amp;gt; detector || 192.168.1.30 || '''8100''' (UDP) || Host sends here&lt;br /&gt;
|-&lt;br /&gt;
| Discovery beacons: detector -&amp;gt; host || - || '''4500''' (UDP) || Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
| Protocol replies: detector -&amp;gt; host || - || '''5550''' (UDP) || Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
| Image pixel data: detector -&amp;gt; host || - || '''6660''' (UDP) || Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Layers ===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
; URP (Unified Registration Protocol)&lt;br /&gt;
: 8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
; PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
: The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connecting to the Detector ===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
# Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
# The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
==== Step 1: SYSTEM_STARTUP ====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Receiving the BEACON and ACKing ====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 3: PORT_SETUP ====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Reading the Serial Number (SIGNATURE_REQUEST) ====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running an Acquisition ===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
==== ROE Initialisation (Script 7) ====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
==== Dark / Offset Acquisition (Script 1) ====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
==== Standard Acquisition (Script 0) ====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
==== Sequence Overview ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sequence Counter Rules ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
* After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
* Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
* Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
* The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sensor Readout and Host Registration ==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
=== Sensor Readout ===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
==== Commands ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! cmd_type !! Meaning !! Request payload !! Reply payload&lt;br /&gt;
|-&lt;br /&gt;
| 0x7900 || Raw sensor read || [sensorId:4 LE] || [value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7902 || Converted sensor read || [sensorId:4 LE] || [value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7904 || Detailed / radio || [selector:4 LE] || [value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
==== Power rails (live readings) ====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Sensor !! sensorId !! Converted !! Value&lt;br /&gt;
|-&lt;br /&gt;
| DCIN_RAW || 10 || 12100 mV || +12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
| LCORE_UNREG || 11 || 1724 mV || +1.72 V&lt;br /&gt;
|-&lt;br /&gt;
| LPANA_UNREG || 12 || 5776 mV || +5.78 V&lt;br /&gt;
|-&lt;br /&gt;
| LNANA_UNREG || 13 || -5801 mV || -5.80 V&lt;br /&gt;
|-&lt;br /&gt;
| SCAN_VCC || 14 || 5086 mV || +5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
| P5V_REF || 16 || 5025 mV || +5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
| V_ON || 17 || 11085 mV || +11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
| V_OFF || 18 || -12378 mV || -12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
| V_COMMON || 19 || -9385 mV || -9.39 V&lt;br /&gt;
|-&lt;br /&gt;
| 3V3 || 29 || 3177 mV || +3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
| VCC_UNREG || 37 || 3399 mV || +3.40 V&lt;br /&gt;
|-&lt;br /&gt;
| PARCPREG || 38 || 3768 mV || +3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
| NARCPREG || 39 || -3732 mV || -3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
| PANA_UNREG || 45 || 18352 mV || +18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
| NANA_UNREG || 46 || -19251 mV || -19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
==== Known limitations ====&lt;br /&gt;
&lt;br /&gt;
* '''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
* '''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
* '''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
=== Host Registration (Pairing) ===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
==== Host identity (MAC and HostId) ====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
# Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
# Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
# The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
==== HostList structure ====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
==== HostList from the unit under test ====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Index !! HostId !! Derived MAC !! Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 2C6400045FB42C64 || 00:04:5F:B4:2C:64 || Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
| 1 || B044E8393512B044 || E8:39:35:12:B0:44 || Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 5CF800045FB15CF8 || 00:04:5F:B1:5C:F8 || Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
==== Checksum (CRC) ====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
* Polynomial: 0x04C11DB7&lt;br /&gt;
* Initial value: 0&lt;br /&gt;
* MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
* Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
==== Read and write transport ====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x0E || [downloadId:4 LE][totalSize:4 LE] || [status:1]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x0F || [bufId:4 LE][numBytes:4 LE][data] || [status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
| Commit || 0x10 || (empty) || [status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
==== Registering a new host ====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
== Reading the Drop and Shock Event Log ==&lt;br /&gt;
&lt;br /&gt;
The detector contains an accelerometer based shock watchdog that records drop&lt;br /&gt;
and impact events into non volatile memory. This log is useful when evaluating a&lt;br /&gt;
used unit, since it reveals how many serious shocks the panel has survived and&lt;br /&gt;
when. The same data is held both in the main flash (read over the network) and&lt;br /&gt;
in the small onboard EEPROM on the detector PCB.&lt;br /&gt;
&lt;br /&gt;
=== How to read it ===&lt;br /&gt;
&lt;br /&gt;
The shock log is exposed as data category 0x72 on the upload (read) interface.&lt;br /&gt;
It is retrieved with the standard non destructive configure and buffer sequence&lt;br /&gt;
(cmd 0x13 to configure, cmd 0x14 to pull the chunks). On the unit under test the&lt;br /&gt;
blob is 3001 bytes of plain text in the GE .dyn configuration format, so no&lt;br /&gt;
decoding is required beyond reading it as ASCII.&lt;br /&gt;
&lt;br /&gt;
The same records are mirrored in binary form in the onboard 24LC256 EEPROM&lt;br /&gt;
(32 KB I2C part) for anyone reading the hardware directly.&lt;br /&gt;
&lt;br /&gt;
=== Example output ===&lt;br /&gt;
&lt;br /&gt;
The following is the actual log read from detector serial UA45829-7. The file&lt;br /&gt;
has two parts: a list of individual events (DetectorShockEvents) and a summary&lt;br /&gt;
block (DetectorShockData). Each event carries a record key, a severity, a&lt;br /&gt;
timestamp, and the peak acceleration on each axis. Two representative records:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;DetectorShockEvents&amp;gt;&lt;br /&gt;
 [149266438700135]&lt;br /&gt;
      Severity.Type = String&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Type = String&lt;br /&gt;
      TimeStamp.Val = 2017-4-20,6:59:47&lt;br /&gt;
      ValidTime.Type = Number&lt;br /&gt;
      ValidTime.Val = 0&lt;br /&gt;
      VibrationX.Type = Number&lt;br /&gt;
      VibrationX.Val = 0&lt;br /&gt;
      VibrationY.Type = Number&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Type = Number&lt;br /&gt;
      VibrationZ.Val = 135&lt;br /&gt;
&lt;br /&gt;
 [1592113924-1720-113]&lt;br /&gt;
      Severity.Val = Serious&lt;br /&gt;
      TimeStamp.Val = 2020-6-14,7:52:4&lt;br /&gt;
      VibrationX.Val = -172&lt;br /&gt;
      VibrationY.Val = 0&lt;br /&gt;
      VibrationZ.Val = -113&lt;br /&gt;
&lt;br /&gt;
The summary block at the end lists the totals and an index of all event keys:&lt;br /&gt;
&lt;br /&gt;
 [DetectorShockData]&lt;br /&gt;
      LastKnownGoodTime.Val = 2024-4-4,6:53:59&lt;br /&gt;
      NumberOfL3Events.Val = 0&lt;br /&gt;
      NumberOfL5Events.Val = 7&lt;br /&gt;
      ShockDataTimeList.Val = { 1643260890-1320-119,  163626503500-123,&lt;br /&gt;
        160818190500-101,  160524243000170,  1592113924-1720-113,&lt;br /&gt;
        157804584600183,  149266438700135 }&lt;br /&gt;
&lt;br /&gt;
=== Interpreting the records ===&lt;br /&gt;
&lt;br /&gt;
The record key encodes the event time and the peak axis values (an epoch style&lt;br /&gt;
timestamp followed by the concatenated vibration readings), so it is unique per&lt;br /&gt;
event. The VibrationX, VibrationY, and VibrationZ values are signed peak&lt;br /&gt;
acceleration counts on each axis. Severity is reported as Serious for all&lt;br /&gt;
logged events on this unit, which corresponds to the L5 (highest) severity class&lt;br /&gt;
counted in the summary.&lt;br /&gt;
&lt;br /&gt;
The seven recorded events for this detector:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Date and time !! VibrationX !! VibrationY !! VibrationZ&lt;br /&gt;
|-&lt;br /&gt;
| 2017-04-20 06:59:47 || 0 || 0 || 135&lt;br /&gt;
|-&lt;br /&gt;
| 2020-01-03 11:04:06 || 0 || 0 || 183&lt;br /&gt;
|-&lt;br /&gt;
| 2020-06-14 07:52:04 || -172 || 0 || -113&lt;br /&gt;
|-&lt;br /&gt;
| 2020-11-13 05:40:30 || 0 || 0 || 170&lt;br /&gt;
|-&lt;br /&gt;
| 2020-12-17 06:11:45 || 0 || 0 || -101&lt;br /&gt;
|-&lt;br /&gt;
| 2021-11-07 07:03:55 || 0 || 0 || -123&lt;br /&gt;
|-&lt;br /&gt;
| 2022-01-27 06:21:30 || -132 || 0 || -119&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Reading the summary fields ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Field !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| LastKnownGoodTime || Timestamp of the last successful self check (here 2024-04-04), the most recent point at which the detector was known to be operating normally&lt;br /&gt;
|-&lt;br /&gt;
| NumberOfL3Events || Count of lower severity (L3) shock events; zero on this unit&lt;br /&gt;
|-&lt;br /&gt;
| NumberOfL5Events || Count of high severity (L5) shock events; seven on this unit, matching the seven records above&lt;br /&gt;
|-&lt;br /&gt;
| ShockDataTimeList || Index of all stored event keys, newest first&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On this example the panel logged seven serious impacts over roughly five years&lt;br /&gt;
of field use and last reported a good self check in April 2024.&lt;br /&gt;
&lt;br /&gt;
== FlashPad Detector: Internal Flash Dump and Data Files ==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
=== Extraction method ===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
=== Recovered files ===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Upload ID !! Size (bytes) !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD || 67108864 || Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF || 16777216 || 16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE || 524288 || Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 6815783 || Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
| 0x11 || 6815783 || Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x12 || 6815783 || Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || 49254 || Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
| 0x50 || 19938 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x51 || 19582 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x07 || 12187 || Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
| 0x72 || 3001 || Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E || 624 || Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
| 0x71 || 504 || Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F || 233 || Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || 62 || Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A || 48 || Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
| 0x52 || 30 || Serial string&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B || 24 || Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Flash image layout (upload ID 0xFD) ===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Range !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0x000000 to 0x800000 || Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
| 0x800000 to 0x940000 || Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
| 0x940000 || Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1000000 to 0x2800000 || Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2800000 to 0x4000000 || Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
=== Notes on individual files ===&lt;br /&gt;
&lt;br /&gt;
* '''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
* '''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
* '''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
* '''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
* '''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
=== Significance ===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Known Dead Ends ===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
; 0xBEEF (48879) as reply port&lt;br /&gt;
: This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
; CmdFlag=1 as data packet flag&lt;br /&gt;
: Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
; Image data on alternate ports&lt;br /&gt;
: Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
; PORT_SETUP byte order (big-endian)&lt;br /&gt;
: Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
; Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
: Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Teardown / Internal Pictures / Hardware analysis ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 4.jpg|thumb|left|Accelerometer is a 834-0500 500G 3 Axis unit. abd TJ500AE SPDT GIGABIT LAN SWITCH]]&lt;br /&gt;
[[File:Ge flashpad bottom connector.jpg|thumb|left|bottom connector for docking stand]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 5.jpg|thumb|left|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 6.jpg|thumb|left|Altera EPM570f100c5n CPLD ]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 7.jpg|thumb|left|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything.]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 8.jpg|thumb|left|24LC256I 32k EEPROM for storing the accelerometer events.]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3605</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3605"/>
		<updated>2026-06-11T11:55:30Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Teardown / Internal Pictures / Hardware analysis */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
== Purpose and Motivation ==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
* Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
* Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
* Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
=== Status of Reverse Engineering ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! What !! Status&lt;br /&gt;
|-&lt;br /&gt;
| Connect, beacon, ACK || Works&lt;br /&gt;
|-&lt;br /&gt;
| PORT_SETUP || Works&lt;br /&gt;
|-&lt;br /&gt;
| SIGNATURE_REQUEST / serial number readout || Works&lt;br /&gt;
|-&lt;br /&gt;
| Script download (Scripts 7, 8, 1) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTE_SCRIPT || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (dark acquisition) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (standard acquisition) || Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
| Image data on port 6660 || Does not work yet, under investigation. Will not send Imagedata yet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Communication Protocol ==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
=== Network Setup ===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Role !! IP !! Port !! Direction&lt;br /&gt;
|-&lt;br /&gt;
| All commands: host -&amp;gt; detector || 192.168.1.30 || '''8100''' (UDP) || Host sends here&lt;br /&gt;
|-&lt;br /&gt;
| Discovery beacons: detector -&amp;gt; host || - || '''4500''' (UDP) || Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
| Protocol replies: detector -&amp;gt; host || - || '''5550''' (UDP) || Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
| Image pixel data: detector -&amp;gt; host || - || '''6660''' (UDP) || Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Layers ===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
; URP (Unified Registration Protocol)&lt;br /&gt;
: 8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
; PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
: The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connecting to the Detector ===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
# Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
# The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
==== Step 1: SYSTEM_STARTUP ====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Receiving the BEACON and ACKing ====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 3: PORT_SETUP ====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Reading the Serial Number (SIGNATURE_REQUEST) ====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running an Acquisition ===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
==== ROE Initialisation (Script 7) ====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
==== Dark / Offset Acquisition (Script 1) ====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
==== Standard Acquisition (Script 0) ====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
==== Sequence Overview ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sequence Counter Rules ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
* After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
* Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
* Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
* The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sensor Readout and Host Registration ==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
=== Sensor Readout ===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
==== Commands ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! cmd_type !! Meaning !! Request payload !! Reply payload&lt;br /&gt;
|-&lt;br /&gt;
| 0x7900 || Raw sensor read || [sensorId:4 LE] || [value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7902 || Converted sensor read || [sensorId:4 LE] || [value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7904 || Detailed / radio || [selector:4 LE] || [value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
==== Power rails (live readings) ====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Sensor !! sensorId !! Converted !! Value&lt;br /&gt;
|-&lt;br /&gt;
| DCIN_RAW || 10 || 12100 mV || +12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
| LCORE_UNREG || 11 || 1724 mV || +1.72 V&lt;br /&gt;
|-&lt;br /&gt;
| LPANA_UNREG || 12 || 5776 mV || +5.78 V&lt;br /&gt;
|-&lt;br /&gt;
| LNANA_UNREG || 13 || -5801 mV || -5.80 V&lt;br /&gt;
|-&lt;br /&gt;
| SCAN_VCC || 14 || 5086 mV || +5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
| P5V_REF || 16 || 5025 mV || +5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
| V_ON || 17 || 11085 mV || +11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
| V_OFF || 18 || -12378 mV || -12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
| V_COMMON || 19 || -9385 mV || -9.39 V&lt;br /&gt;
|-&lt;br /&gt;
| 3V3 || 29 || 3177 mV || +3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
| VCC_UNREG || 37 || 3399 mV || +3.40 V&lt;br /&gt;
|-&lt;br /&gt;
| PARCPREG || 38 || 3768 mV || +3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
| NARCPREG || 39 || -3732 mV || -3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
| PANA_UNREG || 45 || 18352 mV || +18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
| NANA_UNREG || 46 || -19251 mV || -19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
==== Known limitations ====&lt;br /&gt;
&lt;br /&gt;
* '''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
* '''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
* '''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
=== Host Registration (Pairing) ===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
==== Host identity (MAC and HostId) ====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
# Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
# Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
# The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
==== HostList structure ====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
==== HostList from the unit under test ====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Index !! HostId !! Derived MAC !! Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 2C6400045FB42C64 || 00:04:5F:B4:2C:64 || Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
| 1 || B044E8393512B044 || E8:39:35:12:B0:44 || Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 5CF800045FB15CF8 || 00:04:5F:B1:5C:F8 || Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
==== Checksum (CRC) ====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
* Polynomial: 0x04C11DB7&lt;br /&gt;
* Initial value: 0&lt;br /&gt;
* MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
* Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
==== Read and write transport ====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x0E || [downloadId:4 LE][totalSize:4 LE] || [status:1]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x0F || [bufId:4 LE][numBytes:4 LE][data] || [status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
| Commit || 0x10 || (empty) || [status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
==== Registering a new host ====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FlashPad Detector: Internal Flash Dump and Data Files ==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
=== Extraction method ===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
=== Recovered files ===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Upload ID !! Size (bytes) !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD || 67108864 || Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF || 16777216 || 16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE || 524288 || Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 6815783 || Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
| 0x11 || 6815783 || Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x12 || 6815783 || Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || 49254 || Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
| 0x50 || 19938 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x51 || 19582 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x07 || 12187 || Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
| 0x72 || 3001 || Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E || 624 || Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
| 0x71 || 504 || Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F || 233 || Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || 62 || Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A || 48 || Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
| 0x52 || 30 || Serial string&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B || 24 || Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Flash image layout (upload ID 0xFD) ===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Range !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0x000000 to 0x800000 || Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
| 0x800000 to 0x940000 || Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
| 0x940000 || Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1000000 to 0x2800000 || Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2800000 to 0x4000000 || Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
=== Notes on individual files ===&lt;br /&gt;
&lt;br /&gt;
* '''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
* '''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
* '''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
* '''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
* '''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
=== Significance ===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Known Dead Ends ===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
; 0xBEEF (48879) as reply port&lt;br /&gt;
: This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
; CmdFlag=1 as data packet flag&lt;br /&gt;
: Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
; Image data on alternate ports&lt;br /&gt;
: Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
; PORT_SETUP byte order (big-endian)&lt;br /&gt;
: Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
; Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
: Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Teardown / Internal Pictures / Hardware analysis ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 4.jpg|thumb|left|Accelerometer is a 834-0500 500G 3 Axis unit. abd TJ500AE SPDT GIGABIT LAN SWITCH]]&lt;br /&gt;
[[File:Ge flashpad bottom connector.jpg|thumb|left|bottom connector for docking stand]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:left; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 5.jpg|thumb|left|Ethernet transformers and ADM34 RS-485/RS-422 Transceiver]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 6.jpg|thumb|left|Altera EPM570f100c5n CPLD ]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 7.jpg|thumb|left|Spansion GL512P10FF1R1 512 Mbit NOR flash IC holding everything.]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup 8.jpg|thumb|left|24LC256I 32k EEPROM for storing the accelerometer events.]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_8.jpg&amp;diff=3604</id>
		<title>File:Ge flashpad main pcb closeup 8.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_8.jpg&amp;diff=3604"/>
		<updated>2026-06-11T11:54:40Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;showing the 24LC256 eeprom&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_7.jpg&amp;diff=3603</id>
		<title>File:Ge flashpad main pcb closeup 7.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_7.jpg&amp;diff=3603"/>
		<updated>2026-06-11T11:53:31Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;showing the main flash ic&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_6.jpg&amp;diff=3602</id>
		<title>File:Ge flashpad main pcb closeup 6.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_6.jpg&amp;diff=3602"/>
		<updated>2026-06-11T11:52:38Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;showing the sub fpga altera epm570f100c5n&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_5.jpg&amp;diff=3601</id>
		<title>File:Ge flashpad main pcb closeup 5.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_5.jpg&amp;diff=3601"/>
		<updated>2026-06-11T11:51:14Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;eth  transformers&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_bottom_connector.jpg&amp;diff=3600</id>
		<title>File:Ge flashpad bottom connector.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_bottom_connector.jpg&amp;diff=3600"/>
		<updated>2026-06-11T11:50:02Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;with original humand crud in the crevices&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_4.jpg&amp;diff=3599</id>
		<title>File:Ge flashpad main pcb closeup 4.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_4.jpg&amp;diff=3599"/>
		<updated>2026-06-11T11:48:21Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;showing the main accelerometer module&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3598</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3598"/>
		<updated>2026-06-11T11:47:31Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
== Purpose and Motivation ==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
* Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
* Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
* Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
=== Status of Reverse Engineering ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! What !! Status&lt;br /&gt;
|-&lt;br /&gt;
| Connect, beacon, ACK || Works&lt;br /&gt;
|-&lt;br /&gt;
| PORT_SETUP || Works&lt;br /&gt;
|-&lt;br /&gt;
| SIGNATURE_REQUEST / serial number readout || Works&lt;br /&gt;
|-&lt;br /&gt;
| Script download (Scripts 7, 8, 1) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTE_SCRIPT || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (dark acquisition) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (standard acquisition) || Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
| Image data on port 6660 || Does not work yet, under investigation. Will not send Imagedata yet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Communication Protocol ==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
=== Network Setup ===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Role !! IP !! Port !! Direction&lt;br /&gt;
|-&lt;br /&gt;
| All commands: host -&amp;gt; detector || 192.168.1.30 || '''8100''' (UDP) || Host sends here&lt;br /&gt;
|-&lt;br /&gt;
| Discovery beacons: detector -&amp;gt; host || - || '''4500''' (UDP) || Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
| Protocol replies: detector -&amp;gt; host || - || '''5550''' (UDP) || Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
| Image pixel data: detector -&amp;gt; host || - || '''6660''' (UDP) || Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Layers ===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
; URP (Unified Registration Protocol)&lt;br /&gt;
: 8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
; PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
: The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connecting to the Detector ===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
# Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
# The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
==== Step 1: SYSTEM_STARTUP ====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Receiving the BEACON and ACKing ====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 3: PORT_SETUP ====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Reading the Serial Number (SIGNATURE_REQUEST) ====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running an Acquisition ===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
==== ROE Initialisation (Script 7) ====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
==== Dark / Offset Acquisition (Script 1) ====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
==== Standard Acquisition (Script 0) ====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
==== Sequence Overview ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sequence Counter Rules ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
* After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
* Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
* Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
* The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sensor Readout and Host Registration ==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
=== Sensor Readout ===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
==== Commands ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! cmd_type !! Meaning !! Request payload !! Reply payload&lt;br /&gt;
|-&lt;br /&gt;
| 0x7900 || Raw sensor read || [sensorId:4 LE] || [value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7902 || Converted sensor read || [sensorId:4 LE] || [value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7904 || Detailed / radio || [selector:4 LE] || [value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
==== Power rails (live readings) ====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Sensor !! sensorId !! Converted !! Value&lt;br /&gt;
|-&lt;br /&gt;
| DCIN_RAW || 10 || 12100 mV || +12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
| LCORE_UNREG || 11 || 1724 mV || +1.72 V&lt;br /&gt;
|-&lt;br /&gt;
| LPANA_UNREG || 12 || 5776 mV || +5.78 V&lt;br /&gt;
|-&lt;br /&gt;
| LNANA_UNREG || 13 || -5801 mV || -5.80 V&lt;br /&gt;
|-&lt;br /&gt;
| SCAN_VCC || 14 || 5086 mV || +5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
| P5V_REF || 16 || 5025 mV || +5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
| V_ON || 17 || 11085 mV || +11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
| V_OFF || 18 || -12378 mV || -12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
| V_COMMON || 19 || -9385 mV || -9.39 V&lt;br /&gt;
|-&lt;br /&gt;
| 3V3 || 29 || 3177 mV || +3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
| VCC_UNREG || 37 || 3399 mV || +3.40 V&lt;br /&gt;
|-&lt;br /&gt;
| PARCPREG || 38 || 3768 mV || +3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
| NARCPREG || 39 || -3732 mV || -3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
| PANA_UNREG || 45 || 18352 mV || +18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
| NANA_UNREG || 46 || -19251 mV || -19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
==== Known limitations ====&lt;br /&gt;
&lt;br /&gt;
* '''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
* '''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
* '''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
=== Host Registration (Pairing) ===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
==== Host identity (MAC and HostId) ====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
# Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
# Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
# The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
==== HostList structure ====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
==== HostList from the unit under test ====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Index !! HostId !! Derived MAC !! Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 2C6400045FB42C64 || 00:04:5F:B4:2C:64 || Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
| 1 || B044E8393512B044 || E8:39:35:12:B0:44 || Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 5CF800045FB15CF8 || 00:04:5F:B1:5C:F8 || Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
==== Checksum (CRC) ====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
* Polynomial: 0x04C11DB7&lt;br /&gt;
* Initial value: 0&lt;br /&gt;
* MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
* Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
==== Read and write transport ====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x0E || [downloadId:4 LE][totalSize:4 LE] || [status:1]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x0F || [bufId:4 LE][numBytes:4 LE][data] || [status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
| Commit || 0x10 || (empty) || [status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
==== Registering a new host ====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FlashPad Detector: Internal Flash Dump and Data Files ==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
=== Extraction method ===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
=== Recovered files ===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Upload ID !! Size (bytes) !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD || 67108864 || Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF || 16777216 || 16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE || 524288 || Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 6815783 || Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
| 0x11 || 6815783 || Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x12 || 6815783 || Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || 49254 || Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
| 0x50 || 19938 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x51 || 19582 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x07 || 12187 || Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
| 0x72 || 3001 || Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E || 624 || Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
| 0x71 || 504 || Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F || 233 || Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || 62 || Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A || 48 || Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
| 0x52 || 30 || Serial string&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B || 24 || Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Flash image layout (upload ID 0xFD) ===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Range !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0x000000 to 0x800000 || Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
| 0x800000 to 0x940000 || Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
| 0x940000 || Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1000000 to 0x2800000 || Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2800000 to 0x4000000 || Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
=== Notes on individual files ===&lt;br /&gt;
&lt;br /&gt;
* '''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
* '''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
* '''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
* '''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
* '''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
=== Significance ===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Known Dead Ends ===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
; 0xBEEF (48879) as reply port&lt;br /&gt;
: This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
; CmdFlag=1 as data packet flag&lt;br /&gt;
: Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
; Image data on alternate ports&lt;br /&gt;
: Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
; PORT_SETUP byte order (big-endian)&lt;br /&gt;
: Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
; Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
: Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Teardown / Internal Pictures / Hardware analysis ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3597</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3597"/>
		<updated>2026-06-11T11:46:32Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: added details about everything&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
[[File:GE Flashpad.jpg|thumb|Flashpad with Anti-Scatter grid (left), manual and recovery dvd for 220amx and tether cord (right)]]&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
== Purpose and Motivation ==&lt;br /&gt;
&lt;br /&gt;
This project documents the reverse engineering of the GE FlashPad wireless&lt;br /&gt;
digital radiography detector used with the Optima XR200/220 AMX mobile X-ray&lt;br /&gt;
system, with the goal of freeing these detectors for independent use.&lt;br /&gt;
[[File:Xray capture pcb.png|thumb|Xray capture of a PCB for Reverse Engineering]]&lt;br /&gt;
Large numbers of these detectors reach the used market, but each one is&lt;br /&gt;
cryptographically and administratively bound to its original Optima 220 AMX&lt;br /&gt;
console. Once separated from that console, or once the console is&lt;br /&gt;
decommissioned, the detector is effectively useless even though the hardware is&lt;br /&gt;
fully functional. The aim of this work is to remove that artificial barrier so&lt;br /&gt;
that a standalone FlashPad can be paired, configured, and read out by any host,&lt;br /&gt;
without the half million dollar console it was sold with.&lt;br /&gt;
&lt;br /&gt;
The intended beneficiaries are:&lt;br /&gt;
&lt;br /&gt;
* Hobbyists, researchers, and engineers who want a high quality flat panel detector for their own imaging projects.&lt;br /&gt;
* Veterinary practices, which can put surplus human grade detectors to good use at a fraction of the cost of new equipment.&lt;br /&gt;
* Clinics and hospitals in countries and regions where a complete commercial system is unaffordable, allowing serviceable detectors to keep providing diagnostic imaging instead of being scrapped.&lt;br /&gt;
[[File:Xray current clamp multimeter.png|thumb|xray of a current clamp multimeter]]&lt;br /&gt;
This is done for human good. Every detector returned to service is one less&lt;br /&gt;
piece of working medical hardware sent to landfill, and potentially one more&lt;br /&gt;
place that can offer X-ray imaging where it otherwise could not.&lt;br /&gt;
&lt;br /&gt;
All files, findings, protocol documentation, and tools produced by this project&lt;br /&gt;
are public and open source, so that anyone can study, reproduce, and build on&lt;br /&gt;
the work.&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
[[File:Ge flashpad tether cable pinout diagram.png|thumb|Pinout Diagram of the Tether cable]]&lt;br /&gt;
[[File:Ge flashpad tether wiring for ethernet cable.jpg|thumb|Professional Tether wiring to attach a Ethernet Cable and 12V socket to it.]]&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
=== Status of Reverse Engineering ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! What !! Status&lt;br /&gt;
|-&lt;br /&gt;
| Connect, beacon, ACK || Works&lt;br /&gt;
|-&lt;br /&gt;
| PORT_SETUP || Works&lt;br /&gt;
|-&lt;br /&gt;
| SIGNATURE_REQUEST / serial number readout || Works&lt;br /&gt;
|-&lt;br /&gt;
| Script download (Scripts 7, 8, 1) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTE_SCRIPT || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (dark acquisition) || Works&lt;br /&gt;
|-&lt;br /&gt;
| EXECUTION_COMPLETE (standard acquisition) || Needs real X-ray trigger to test&lt;br /&gt;
|-&lt;br /&gt;
| Image data on port 6660 || Does not work yet, under investigation. Will not send Imagedata yet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
There is a 100MBit Ethernet interface trough the Tether Cable which can be tapped and used for Communication.&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Communication Protocol ==&lt;br /&gt;
&lt;br /&gt;
This section documents the URP/PDAP protocol used by the GE Flashpad (codename '''Apollo''') to communicate with a host over Ethernet. All findings are based on live capture tests, configuration files, and reverse-engineering of the GE SuperBee software stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings are based on a single unit and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
=== Network Setup ===&lt;br /&gt;
&lt;br /&gt;
All findings were over Ethernet only, no UWB or WIFI has been used. &lt;br /&gt;
Default detector IP is &amp;lt;code&amp;gt;192.168.1.30&amp;lt;/code&amp;gt;. Set your host to a static IP in the same subnet, &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; works fine.&lt;br /&gt;
&lt;br /&gt;
At first by sending random bytes over Python it answered to port 48879 (0xBEEF), which seems to be either the default port or set by Python. &lt;br /&gt;
After SYSTEM_SETUP it got set to 5550.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Role !! IP !! Port !! Direction&lt;br /&gt;
|-&lt;br /&gt;
| All commands: host -&amp;gt; detector || 192.168.1.30 || '''8100''' (UDP) || Host sends here&lt;br /&gt;
|-&lt;br /&gt;
| Discovery beacons: detector -&amp;gt; host || - || '''4500''' (UDP) || Detector sends here initially&lt;br /&gt;
|-&lt;br /&gt;
| Protocol replies: detector -&amp;gt; host || - || '''5550''' (UDP) || Detector sends here after setup&lt;br /&gt;
|-&lt;br /&gt;
| Image pixel data: detector -&amp;gt; host || - || '''6660''' (UDP) || Detector streams frames here&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In practice you can listen on port '''5550''' for everything (beacons and replies) by advertising that port in both &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt;. The detector sends to whichever port was most recently configured.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Layers ===&lt;br /&gt;
&lt;br /&gt;
Two layers, carried over UDP:&lt;br /&gt;
&lt;br /&gt;
; URP (Unified Registration Protocol)&lt;br /&gt;
: 8-byte wrapper on every packet. Handles sequencing and acknowledgement.&lt;br /&gt;
&lt;br /&gt;
; PDAP (Proprietary Detector Access Protocol)&lt;br /&gt;
: The actual command layer, present inside URP packets when &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Every UDP packet starts with a URP header:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[SeqId : 4 bytes LE] [CmdFlag : 4 bytes LE]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 0&amp;lt;/code&amp;gt; -- data packet, PDAP command follows.&lt;br /&gt;
* &amp;lt;code&amp;gt;CmdFlag = 1&amp;lt;/code&amp;gt; -- bare ACK, no PDAP body. SeqId echoes the packet being acknowledged.&lt;br /&gt;
&lt;br /&gt;
Both sides must ACK every data packet immediately. The detector silently drops packets with a SeqId it has already seen, so always increment SeqId for each new command.&lt;br /&gt;
&lt;br /&gt;
When CmdFlag is 0, the PDAP header follows immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[cmd_type : 4 bytes LE] [payload_len : 4 bytes LE] [payload ...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connecting to the Detector ===&lt;br /&gt;
&lt;br /&gt;
The connection sequence is:&lt;br /&gt;
&lt;br /&gt;
# Broadcast &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; to tell the detector where to reply.&lt;br /&gt;
# The detector sends a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; back -- ACK it and sync your sequence counter.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;PORT_SETUP&amp;lt;/code&amp;gt; to configure the reply and image ports.&lt;br /&gt;
# Send &amp;lt;code&amp;gt;SIGNATURE_REQUEST&amp;lt;/code&amp;gt; to read the detector identity (serial number, model, firmware, MAC).&lt;br /&gt;
&lt;br /&gt;
==== Step 1: SYSTEM_STARTUP ====&lt;br /&gt;
&lt;br /&gt;
Broadcast UDP to &amp;lt;code&amp;gt;192.168.1.255:8100&amp;lt;/code&amp;gt;. Always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
URP : [00 00 00 00]  SeqId = 0&lt;br /&gt;
      [00 00 00 00]  CmdFlag = 0&lt;br /&gt;
PDAP: [01 00 00 00]  cmd_type = 1&lt;br /&gt;
      [06 00 00 00]  payload_len = 6&lt;br /&gt;
      [15 AE]        host reply port = 5550 (big-endian)&lt;br /&gt;
      [C0 A8 01 01]  host IP = 192.168.1.1 (network byte order)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import socket, struct&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)&lt;br /&gt;
sock.bind((&amp;quot;0.0.0.0&amp;quot;, 5550))&lt;br /&gt;
&lt;br /&gt;
HOST_IP   = &amp;quot;192.168.1.1&amp;quot;&lt;br /&gt;
DET_IP    = &amp;quot;192.168.1.30&amp;quot;&lt;br /&gt;
HOST_PORT = 5550&lt;br /&gt;
IMG_PORT  = 6660&lt;br /&gt;
&lt;br /&gt;
def make_urp_packet(seq_id, pdap_bytes):&lt;br /&gt;
    return struct.pack(&amp;quot;&amp;lt;II&amp;quot;, seq_id, 0) + pdap_bytes&lt;br /&gt;
&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 1, 6) + struct.pack(&amp;quot;&amp;gt;H&amp;quot;, HOST_PORT) + socket.inet_aton(HOST_IP)&lt;br /&gt;
sock.sendto(make_urp_packet(0, pdap), (&amp;quot;192.168.1.255&amp;quot;, 8100))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Receiving the BEACON and ACKing ====&lt;br /&gt;
&lt;br /&gt;
The detector broadcasts a &amp;lt;code&amp;gt;BEACON&amp;lt;/code&amp;gt; (cmd_type=1) roughly every 2 seconds. After sending SYSTEM_STARTUP you should get one quickly. Parse the detector SeqId from the URP header and ACK it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
data, addr = sock.recvfrom(4096)&lt;br /&gt;
det_seq = struct.unpack_from(&amp;quot;&amp;lt;I&amp;quot;, data, 0)[0]&lt;br /&gt;
&lt;br /&gt;
# send bare ACK&lt;br /&gt;
ack = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, det_seq, 1)&lt;br /&gt;
sock.sendto(ack, (DET_IP, 8100))&lt;br /&gt;
&lt;br /&gt;
# all subsequent commands start from here&lt;br /&gt;
host_seq = det_seq + 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 3: PORT_SETUP ====&lt;br /&gt;
&lt;br /&gt;
Tells the detector which host ports to use for replies and image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PDAP: [02 00 00 00]  cmd_type = 2&lt;br /&gt;
      [04 00 00 00]  payload_len = 4&lt;br /&gt;
      [15 AE]        host cmd port = 5550 (big-endian)&lt;br /&gt;
      [1A 04]        host image port = 6660 (big-endian)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 2, 4) + struct.pack(&amp;quot;&amp;gt;HH&amp;quot;, HOST_PORT, IMG_PORT)&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
# expect bare ACK from detector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Reading the Serial Number (SIGNATURE_REQUEST) ====&lt;br /&gt;
&lt;br /&gt;
Empty command, the detector replies with a 54-byte payload containing its identity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
pdap = struct.pack(&amp;quot;&amp;lt;II&amp;quot;, 3, 0)   # cmd_type=3, payload_len=0&lt;br /&gt;
sock.sendto(make_urp_packet(host_seq, pdap), (DET_IP, 8100))&lt;br /&gt;
host_seq += 1&lt;br /&gt;
&lt;br /&gt;
# reply is 70 bytes total: 8 URP + 8 PDAP header + 54 payload&lt;br /&gt;
data, _ = sock.recvfrom(4096)&lt;br /&gt;
payload = data[16:]   # skip URP (8) + PDAP header (8)&lt;br /&gt;
&lt;br /&gt;
mac      = payload[0:6]&lt;br /&gt;
serial   = payload[22:34].rstrip(b'\x00 ').decode()&lt;br /&gt;
model    = payload[34:46].rstrip(b'\x00 ').decode()&lt;br /&gt;
fw_bytes = payload[46:54]&lt;br /&gt;
firmware = &amp;quot;.&amp;quot;.join(str(b) for b in fw_bytes)&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;MAC:      {':'.join(f'{b:02X}' for b in mac)}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Serial:   {serial}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Model:    {model}&amp;quot;)&lt;br /&gt;
print(f&amp;quot;Firmware: {firmware}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MAC:      40:F4:A0:00:78:4D&lt;br /&gt;
Serial:   UA45829-7&lt;br /&gt;
Model:    5340000-7&lt;br /&gt;
Firmware: 1.6.0.4.2.0.1.3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running an Acquisition ===&lt;br /&gt;
&lt;br /&gt;
After the connection sequence above, download scripts to the detector and then execute them.&lt;br /&gt;
&lt;br /&gt;
==== ROE Initialisation (Script 7) ====&lt;br /&gt;
&lt;br /&gt;
Always run this first. It initialises the readout electronics and takes about 4-5 seconds. The detector sends host event ID 17 when done.&lt;br /&gt;
&lt;br /&gt;
==== Dark / Offset Acquisition (Script 1) ====&lt;br /&gt;
&lt;br /&gt;
No X-ray source required. Runs a dark-field exposure for offset calibration. Use this to verify the acquisition pipeline without a generator.&lt;br /&gt;
&lt;br /&gt;
==== Standard Acquisition (Script 0) ====&lt;br /&gt;
&lt;br /&gt;
Requires an actual X-ray exposure. &amp;lt;code&amp;gt;EXECUTION_COMPLETE&amp;lt;/code&amp;gt; will '''not''' arrive without a real X-ray trigger.&lt;br /&gt;
&lt;br /&gt;
==== Sequence Overview ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HOST                                     DETECTOR&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SYSTEM_STARTUP (broadcast) -----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- BEACON (cmd_type=1) --------------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- PORT_SETUP (cmd_type=2) --------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- SIGNATURE_REQUEST (cmd_type=3) -------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- SIGNATURE_REPLY (54 bytes) -------------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script7 (ROE init) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK or SCRIPT_DOWNLOAD_REPLY ------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- GENERIC_SCRIPT Script1 (dark acq) ----&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |-- EXECUTE_SCRIPT (cmd_type=6) ----------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
 |&amp;lt;- EXECUTE_SCRIPT_REPLY (~230ms) ----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |&amp;lt;- DETECTOR_STATE_NOTIFY (~890ms) ---------|  state_id=17&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- EXECUTION_COMPLETE (0x10000) -----------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |                                           |&lt;br /&gt;
 |&amp;lt;- IMAGE_XFER_STATUS_QUERY (0x30000) ------|&lt;br /&gt;
 |-- bare ACK ------------------------------&amp;gt;|&lt;br /&gt;
 |-- IMAGE_XFER_STATUS_REPLY (cmd_type=9) -&amp;gt;|  numMissed=0&lt;br /&gt;
 |&amp;lt;- bare ACK -------------------------------|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sequence Counter Rules ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;SYSTEM_STARTUP&amp;lt;/code&amp;gt; always uses &amp;lt;code&amp;gt;SeqId = 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
* After the first beacon, set host SeqId to beacon_SeqId + 1.&lt;br /&gt;
* Increment SeqId by 1 for every new data packet (CmdFlag=0).&lt;br /&gt;
* Bare ACKs echo the SeqId of the packet being acknowledged and do not consume a SeqId.&lt;br /&gt;
* The detector silently drops any packet with a SeqId it has already processed, so never reuse one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sensor Readout and Host Registration ==&lt;br /&gt;
&lt;br /&gt;
This section documents the reverse engineered sensor telemetry interface and the&lt;br /&gt;
host registration (pairing) mechanism of the GE Optima XR200/220 AMX FlashPad&lt;br /&gt;
(URP detector, SuperBee, FW 1.6.0.4.2.0.1.3). All commands are PDAP over UDP to&lt;br /&gt;
the detector at port 8100; replies return to the host command port.&lt;br /&gt;
&lt;br /&gt;
=== Sensor Readout ===&lt;br /&gt;
&lt;br /&gt;
The detector exposes an analog sensor interface (the DEM, Detector Environment&lt;br /&gt;
Monitor) that is independent of the image transfer path and works regardless of&lt;br /&gt;
acquisition state.&lt;br /&gt;
&lt;br /&gt;
==== Commands ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! cmd_type !! Meaning !! Request payload !! Reply payload&lt;br /&gt;
|-&lt;br /&gt;
| 0x7900 || Raw sensor read || [sensorId:4 LE] || [value:4 LE] (12 bit ADC count)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7902 || Converted sensor read || [sensorId:4 LE] || [value:4 LE] (engineering units, signed)&lt;br /&gt;
|-&lt;br /&gt;
| 0x7904 || Detailed / radio || [selector:4 LE] || [value:4 LE]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: cmd 0x7902 IS supported on this firmware and returns calibrated&lt;br /&gt;
engineering units (millivolts for the supply rails, signed; 0.1 degree C for&lt;br /&gt;
temperatures). Earlier documentation that marked 0x7902 as unsupported is&lt;br /&gt;
incorrect. The host side conversion coefficients are not required; the detector&lt;br /&gt;
performs the conversion internally.&lt;br /&gt;
&lt;br /&gt;
The full sensor map (name, sensorId) was recovered from the detector's own&lt;br /&gt;
[Sensor] configuration table, read back over the upload interface (see the&lt;br /&gt;
Registration section for the read protocol).&lt;br /&gt;
&lt;br /&gt;
==== Power rails (live readings) ====&lt;br /&gt;
&lt;br /&gt;
All supply rails read correctly via 0x7902 and are within normal range for a&lt;br /&gt;
flat panel detector. Representative readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Sensor !! sensorId !! Converted !! Value&lt;br /&gt;
|-&lt;br /&gt;
| DCIN_RAW || 10 || 12100 mV || +12.10 V (main DC input)&lt;br /&gt;
|-&lt;br /&gt;
| LCORE_UNREG || 11 || 1724 mV || +1.72 V&lt;br /&gt;
|-&lt;br /&gt;
| LPANA_UNREG || 12 || 5776 mV || +5.78 V&lt;br /&gt;
|-&lt;br /&gt;
| LNANA_UNREG || 13 || -5801 mV || -5.80 V&lt;br /&gt;
|-&lt;br /&gt;
| SCAN_VCC || 14 || 5086 mV || +5.09 V (gate driver)&lt;br /&gt;
|-&lt;br /&gt;
| P5V_REF || 16 || 5025 mV || +5.03 V (5 V reference)&lt;br /&gt;
|-&lt;br /&gt;
| V_ON || 17 || 11085 mV || +11.09 V (TFT gate on)&lt;br /&gt;
|-&lt;br /&gt;
| V_OFF || 18 || -12378 mV || -12.38 V (TFT gate off)&lt;br /&gt;
|-&lt;br /&gt;
| V_COMMON || 19 || -9385 mV || -9.39 V&lt;br /&gt;
|-&lt;br /&gt;
| 3V3 || 29 || 3177 mV || +3.18 V (3.3 V logic)&lt;br /&gt;
|-&lt;br /&gt;
| VCC_UNREG || 37 || 3399 mV || +3.40 V&lt;br /&gt;
|-&lt;br /&gt;
| PARCPREG || 38 || 3768 mV || +3.77 V (ARC preamp +)&lt;br /&gt;
|-&lt;br /&gt;
| NARCPREG || 39 || -3732 mV || -3.73 V (ARC preamp -)&lt;br /&gt;
|-&lt;br /&gt;
| PANA_UNREG || 45 || 18352 mV || +18.35 V (photodiode bias +)&lt;br /&gt;
|-&lt;br /&gt;
| NANA_UNREG || 46 || -19251 mV || -19.25 V (photodiode bias -)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Switched rails that are inactive while the panel is idle (PARCVA_U,&lt;br /&gt;
P5VA_SW, N5VA_SW, FGATE_NVC_L, FGATE_PVC_L, etc.) read at or near zero.&lt;br /&gt;
&lt;br /&gt;
==== Known limitations ====&lt;br /&gt;
&lt;br /&gt;
* '''Temperatures''' (Temp_Surface, sensorId 336; Temp_Panel, sensorId 352): the raw 0x7900 read rails at 0x3FF (1023, the ADC maximum), indicating an open thermistor path while the panel is idle. This is a hardware/state condition, not a command problem, and the temperature is not readable from the host in this state.&lt;br /&gt;
* '''Unimplemented sensors''': sensorIds 70 (Accelerator), 78 (Gravity), and 256 to 259 (Battery status, name, life, capacity), and 272 (Grid status) are not implemented on this DEM. The detector returns the leftover conversion register contents (a duplicate of a previously read sensor) rather than a real value, so these rows must be discarded.&lt;br /&gt;
* '''Accelerometer''': functional through a separate addressing scheme using raw cmd 0x7900 with selectors 0x42 (X), 0x43 (Y), 0x44 (Z), returning 12 bit per axis counts.&lt;br /&gt;
&lt;br /&gt;
=== Host Registration (Pairing) ===&lt;br /&gt;
&lt;br /&gt;
URP stands for Unified Registration Protocol. The detector maintains a host&lt;br /&gt;
list in its internal NOR flash. Image data is delivered only to a registered&lt;br /&gt;
primary host, so registration is a prerequisite for image transfer.&lt;br /&gt;
&lt;br /&gt;
==== Host identity (MAC and HostId) ====&lt;br /&gt;
&lt;br /&gt;
Each host is identified by a 16 character HostId derived deterministically from&lt;br /&gt;
the host's eth0 MAC address. The derivation (from the vendor generateHostId&lt;br /&gt;
script) is:&lt;br /&gt;
&lt;br /&gt;
# Take the eth0 MAC, remove the colon separators, convert to upper case (12 hex characters).&lt;br /&gt;
# Prepend the last 4 characters to the full 12 characters.&lt;br /&gt;
# The result is 16 hex characters.&lt;br /&gt;
&lt;br /&gt;
Example: MAC 00:6f:00:01:0a:3a becomes 006F00010A3A, then the last four (0A3A)&lt;br /&gt;
are prepended, giving HostId 0A3A006F00010A3A.&lt;br /&gt;
&lt;br /&gt;
==== HostList structure ====&lt;br /&gt;
&lt;br /&gt;
The host list is stored in flash at offset 0x940000 and can be read back over&lt;br /&gt;
the upload interface as data category 0x71. Layout:&lt;br /&gt;
&lt;br /&gt;
 Offset  Size  Field&lt;br /&gt;
 0x00    16    DetectorDeviceId (ASCII)&lt;br /&gt;
 0x10    16    ConnectionSecretKey (ASCII; &amp;quot;XXXXXXXXXXXXXXXX&amp;quot; when unset)&lt;br /&gt;
 0x20    16    DetectorName (ASCII)&lt;br /&gt;
 0x30    16    DetectorCode (ASCII)&lt;br /&gt;
 0x40     2    CurrentNumberOfHosts (uint16 LE)&lt;br /&gt;
 0x42     2    IndexToPrimaryHost (uint16 LE; 0xFFFF = none)&lt;br /&gt;
 0x44   144*N  Host entries, 144 bytes each:&lt;br /&gt;
                 +0x00  16  HostId (ASCII)&lt;br /&gt;
                 +0x10  ..  host name / department string&lt;br /&gt;
                 +0x50  ..  location string&lt;br /&gt;
 ...      4    CRC (big endian; see below)&lt;br /&gt;
&lt;br /&gt;
==== HostList from the unit under test ====&lt;br /&gt;
&lt;br /&gt;
Detector: MAC 40:F4:A0:00:78:4D, serial UA45829-7, model 5340000-7,&lt;br /&gt;
firmware 1.6.0.4.2.0.1.3. DetectorName starShape_green. ConnectionSecretKey&lt;br /&gt;
unset. CurrentNumberOfHosts = 3, IndexToPrimaryHost = 0xFFFF (no primary host).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Index !! HostId !! Derived MAC !! Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 2C6400045FB42C64 || 00:04:5F:B4:2C:64 || Haus 207 / ITS&lt;br /&gt;
|-&lt;br /&gt;
| 1 || B044E8393512B044 || E8:39:35:12:B0:44 || Not Initialized&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 5CF800045FB15CF8 || 00:04:5F:B1:5C:F8 || Not_Initialized&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The unit is therefore not factory fresh; it carries registrations from a prior&lt;br /&gt;
deployment, but no primary host is currently designated.&lt;br /&gt;
&lt;br /&gt;
==== Checksum (CRC) ====&lt;br /&gt;
&lt;br /&gt;
The HostList (and other flash data blobs) are protected by a 4 byte trailing&lt;br /&gt;
CRC. Parameters:&lt;br /&gt;
&lt;br /&gt;
* Polynomial: 0x04C11DB7&lt;br /&gt;
* Initial value: 0&lt;br /&gt;
* MSB first, augmented message style (the data bit is shifted into the LSB; no input or output reflection; no final XOR)&lt;br /&gt;
* Stored big endian&lt;br /&gt;
&lt;br /&gt;
To generate: compute the CRC over the data followed by four zero bytes, then&lt;br /&gt;
append the result big endian. Verification: the CRC over (data plus stored CRC)&lt;br /&gt;
equals zero.&lt;br /&gt;
&lt;br /&gt;
 def crc(data, poly=0x04C11DB7, init=0):&lt;br /&gt;
     c = init&lt;br /&gt;
     for byte in data:&lt;br /&gt;
         for bit in (0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01):&lt;br /&gt;
             msb = c &amp;amp; 0x80000000&lt;br /&gt;
             c = (c &amp;lt;&amp;lt; 1) &amp;amp; 0xFFFFFFFF&lt;br /&gt;
             if byte &amp;amp; bit: c |= 1&lt;br /&gt;
             if msb: c ^= poly&lt;br /&gt;
     return c&lt;br /&gt;
 # trailer = crc(data + b&amp;quot;\x00\x00\x00\x00&amp;quot;), stored big endian&lt;br /&gt;
&lt;br /&gt;
==== Read and write transport ====&lt;br /&gt;
&lt;br /&gt;
Data blobs (including the HostList) are moved with a configure / buffer /&lt;br /&gt;
finalize sequence.&lt;br /&gt;
&lt;br /&gt;
Read (upload, detector to host), non destructive:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Write (download, host to detector), persistent (writes flash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x0E || [downloadId:4 LE][totalSize:4 LE] || [status:1]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x0F || [bufId:4 LE][numBytes:4 LE][data] || [status:1][reserved:4]&lt;br /&gt;
|-&lt;br /&gt;
| Commit || 0x10 || (empty) || [status:1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
bufId is a zero based chunk index. The HostList uses id 0x71 for both read and&lt;br /&gt;
write. The commit command (0x10) reuses the firmware flash path, so a malformed&lt;br /&gt;
download can corrupt flash; the operation is irreversible on this hardware.&lt;br /&gt;
&lt;br /&gt;
==== Registering a new host ====&lt;br /&gt;
&lt;br /&gt;
To register a host and make it the image destination, read the current HostList&lt;br /&gt;
(0x71), append a 144 byte entry carrying the new HostId, increment&lt;br /&gt;
CurrentNumberOfHosts, set IndexToPrimaryHost to the new entry's index,&lt;br /&gt;
recompute the trailing CRC, and write the blob back via the download sequence&lt;br /&gt;
(0x0E / 0x0F / 0x10).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FlashPad Detector: Internal Flash Dump and Data Files ==&lt;br /&gt;
&lt;br /&gt;
This section documents how the full contents of the detector's internal flash&lt;br /&gt;
were extracted over the network and what each recovered file contains. The&lt;br /&gt;
detector under test is a GE Optima XR200/220 AMX FlashPad (URP detector,&lt;br /&gt;
serial UA45829-7, MAC 40:F4:A0:00:78:4D, firmware 1.6.0.4.2.0.1.3).&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
The detector's main storage is a Spansion/Cypress S29GL512P (marked&lt;br /&gt;
GL512P10FFCR2), a 512 Mbit (64 MB) parallel NOR flash. A direct chip read&lt;br /&gt;
requires bus access or desoldering and a programmer. However, the detector&lt;br /&gt;
firmware exposes its stored data blobs, including a full image of the flash,&lt;br /&gt;
through the read (upload) side of the data transport protocol. This makes a&lt;br /&gt;
complete, byte exact dump possible over UDP with no physical access.&lt;br /&gt;
&lt;br /&gt;
=== Extraction method ===&lt;br /&gt;
&lt;br /&gt;
The upload interface is the read counterpart of the firmware download path and&lt;br /&gt;
is non destructive (it only reports and returns data; nothing is written).&lt;br /&gt;
Each data category is identified by an 8 bit upload ID:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! cmd_type !! host to detector !! detector to host&lt;br /&gt;
|-&lt;br /&gt;
| Configure || 0x13 || [uploadId:4 LE] || [status:1][totalSize:4 LE]&lt;br /&gt;
|-&lt;br /&gt;
| Buffer || 0x14 || [bufId:4 LE][numBytes:4 LE] || [bufId:4 LE][numBytes:4 LE][data]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A configure request returns status 0 and a total size for a valid ID, or a non&lt;br /&gt;
zero status for an unsupported ID. The dump tool sweeps all IDs from 0x00 to&lt;br /&gt;
0xFF, and for every readable ID it pulls the full blob in fixed size chunks&lt;br /&gt;
(bufId is a zero based chunk index) and writes it to a file. The 64 MB flash&lt;br /&gt;
image transfers as 65536 chunks of 1024 bytes.&lt;br /&gt;
&lt;br /&gt;
=== Recovered files ===&lt;br /&gt;
&lt;br /&gt;
A sweep of the unit returned 18 readable blobs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Upload ID !! Size (bytes) !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0xFD || 67108864 || Full 64 MB NOR flash image&lt;br /&gt;
|-&lt;br /&gt;
| 0xFF || 16777216 || 16 MB region (firmware / FPGA mirror or image buffer)&lt;br /&gt;
|-&lt;br /&gt;
| 0xFE || 524288 || Bootloader (512 KB; SPI loader, Nios reset code)&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 6815783 || Calibration map, dose level 1&lt;br /&gt;
|-&lt;br /&gt;
| 0x11 || 6815783 || Calibration map, dose level 2&lt;br /&gt;
|-&lt;br /&gt;
| 0x12 || 6815783 || Calibration map, dose level 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || 49254 || Table referencing conditioner/generator serial UA2010-8U005&lt;br /&gt;
|-&lt;br /&gt;
| 0x50 || 19938 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x51 || 19582 || Per mode calibration coefficients&lt;br /&gt;
|-&lt;br /&gt;
| 0x07 || 12187 || Sensor conversion table (text; see Sensor Readout)&lt;br /&gt;
|-&lt;br /&gt;
| 0x72 || 3001 || Shock / drop event log (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0E || 624 || Panel geometry / configuration (binary)&lt;br /&gt;
|-&lt;br /&gt;
| 0x71 || 504 || Host list / registration record (see Host Registration)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0F || 233 || Host to detector compatibility table (text)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || 62 || Serial and model strings&lt;br /&gt;
|-&lt;br /&gt;
| 0x0A || 48 || Manufacturing codes&lt;br /&gt;
|-&lt;br /&gt;
| 0x52 || 30 || Serial string&lt;br /&gt;
|-&lt;br /&gt;
| 0x0B || 24 || Small marker / identifier&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Flash image layout (upload ID 0xFD) ===&lt;br /&gt;
&lt;br /&gt;
A block scan of the 64 MB image shows the following regions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Range !! Contents&lt;br /&gt;
|-&lt;br /&gt;
| 0x000000 to 0x800000 || Firmware and FPGA configuration&lt;br /&gt;
|-&lt;br /&gt;
| 0x800000 to 0x940000 || Sparse configuration area&lt;br /&gt;
|-&lt;br /&gt;
| 0x940000 || Host list / registration record (matches upload ID 0x71)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1000000 to 0x2800000 || Calibration and image data (16 to 40 MB)&lt;br /&gt;
|-&lt;br /&gt;
| 0x2800000 to 0x4000000 || Erased / unused (40 to 64 MB)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The host list strings (the DetectorName starShape_green, the registered host&lt;br /&gt;
name Haus 207 / ITS, and the unset ConnectionSecretKey placeholder) appear at&lt;br /&gt;
0x940000, confirming that the 504 byte 0x71 read maps to this flash region.&lt;br /&gt;
&lt;br /&gt;
=== Notes on individual files ===&lt;br /&gt;
&lt;br /&gt;
* '''0x07 (sensor table)''': plain text, header DetectorSerialNumber = UA45829-7, revision 1.2. Lists every sensor by name, command (0x7902), and sensorId. Also carries panel geometry in its [Common] section: 16 bit depth, 2048 by 2048 pixels, active area from (12, 12) to (2035, 2035), pixel pitch 0.2 mm, corner radius 1416.8, panel saturation 150.&lt;br /&gt;
* '''0x10, 0x11, 0x12 (calibration maps)''': three distinct blobs of identical size, corresponding to the low, medium, and high dose calibration sets. Each begins with the firmware version and serial (header bytes 01 06 00 04 02 00 01 03 followed by the serial). Required to apply gain and offset correction to raw images.&lt;br /&gt;
* '''0xFE (bootloader)''': contains the strings spi_load.S and ../../boot and Nios II reset vector code.&lt;br /&gt;
* '''0x72 (shock log)''': text records of drop / vibration events with timestamps and per axis values; the same data is mirrored in the detector's small onboard EEPROM.&lt;br /&gt;
* '''0x06''': references a different serial, UA2010-8U005, likely the conditioner or generator board rather than the panel.&lt;br /&gt;
&lt;br /&gt;
=== Significance ===&lt;br /&gt;
&lt;br /&gt;
The complete flash image and all calibration data are recoverable over the&lt;br /&gt;
network without opening the detector, providing a full byte exact backup of the&lt;br /&gt;
unit before any write operation. The calibration maps are needed for image&lt;br /&gt;
correction, and the firmware image (the live, deployed build) is more complete&lt;br /&gt;
than the partial firmware files shipped on the recovery media.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Known Dead Ends ===&lt;br /&gt;
&lt;br /&gt;
Things that were tried and did not work or led nowhere:&lt;br /&gt;
&lt;br /&gt;
; 0xBEEF (48879) as reply port&lt;br /&gt;
: This was a bug in early test scripts. The port has no meaning in the protocol. Use 5550.&lt;br /&gt;
&lt;br /&gt;
; CmdFlag=1 as data packet flag&lt;br /&gt;
: Early scripts had the URP fields swapped, setting CmdFlag=1 on data packets. The detector ignores all PDAP content in these packets. Power cycle the detector if this happened, it may retain stale port state.&lt;br /&gt;
&lt;br /&gt;
; Image data on alternate ports&lt;br /&gt;
: Tried listening on 48879, 6660, 5550, 8100, 1050, 6661, 9999, 4444, 7000, 7001, 9001. Nothing arrived on any of them.&lt;br /&gt;
&lt;br /&gt;
; PORT_SETUP byte order (big-endian)&lt;br /&gt;
: Current implementation sends port fields as big-endian. If the detector reads them as little-endian it computes port 44565 and 1050 instead of 5550 and 6660, which would explain why no image data arrives. Not yet confirmed.&lt;br /&gt;
&lt;br /&gt;
; Image data streaming automatically after EXECUTE_SCRIPT&lt;br /&gt;
: Frames may not push during execution at all. The detector may require an explicit IMAGE_RETRIVAL_REQUEST (cmd_type=0x41) and IMAGE_RETRIVAL (cmd_type=0x98) after the 0x30000 notification before it streams anything. Not yet tested.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Teardown / Internal Pictures / Hardware analysis ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; display:flex; gap:8px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:GE Flashpad under the hood.jpg|thumb|none|220px|the Carbon fiber sleeve is held by 9 screws and can taken off without force.]]&lt;br /&gt;
[[File:Ge flashpad powersupply pcb in handle.jpg|thumb|none|220px|Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad uwb board pcb.jpg|thumb|none|220px|UWB PCB for Wireless USB using a RTU2705]]&lt;br /&gt;
[[File:Ge flashpad uwb pcb backside.jpg|thumb|Backside of UWB PCB]]&lt;br /&gt;
[[File:Ge flashpad power supply pcb backside.jpg|thumb|Backside of Power supply PCB]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup1.jpg|thumb|Main area of the PCB with its Altera Cyclone 3 FPGA]]&lt;br /&gt;
[[File:Ge flashpad main pcb closeup2.jpg|thumb|AD7892 is a 600ksps 12bit ADC, SN74LVC8T245 a 8bit bus transceiver and DG9408EDN a 8ch MUX]]&lt;br /&gt;
[[File:Ge flashpad bottom connector ethernet isolation pcb.jpg|thumb|Isolation PCB for bottom connector]]&lt;br /&gt;
[[File:Ge flashpad main pcb powersupply section.jpg|thumb|Powersupply section for FPGA and ROIC]]&lt;br /&gt;
[[File:Ge_flashpad_main_pcb_closeup_3.jpg|thumb|AD9764AR 14-Bit, 125 MSPS DAC and DS1682 integrated elapsed-time recorder]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_3.jpg&amp;diff=3596</id>
		<title>File:Ge flashpad main pcb closeup 3.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup_3.jpg&amp;diff=3596"/>
		<updated>2026-06-11T11:42:13Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;closeup of the ad9764 and max4490&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_powersupply_section.jpg&amp;diff=3595</id>
		<title>File:Ge flashpad main pcb powersupply section.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_powersupply_section.jpg&amp;diff=3595"/>
		<updated>2026-06-11T11:40:43Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;powersupply section of main pcb&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_bottom_connector_ethernet_isolation_pcb.jpg&amp;diff=3594</id>
		<title>File:Ge flashpad bottom connector ethernet isolation pcb.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_bottom_connector_ethernet_isolation_pcb.jpg&amp;diff=3594"/>
		<updated>2026-06-11T11:40:07Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;isolation pcb for bottom interface&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup2.jpg&amp;diff=3593</id>
		<title>File:Ge flashpad main pcb closeup2.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup2.jpg&amp;diff=3593"/>
		<updated>2026-06-11T11:38:09Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;closeup of area with ad7892 600ks/s ADC&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup1.jpg&amp;diff=3592</id>
		<title>File:Ge flashpad main pcb closeup1.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_main_pcb_closeup1.jpg&amp;diff=3592"/>
		<updated>2026-06-11T11:36:41Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Closeup of main pcb with fpga&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_power_supply_pcb_backside.jpg&amp;diff=3591</id>
		<title>File:Ge flashpad power supply pcb backside.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_power_supply_pcb_backside.jpg&amp;diff=3591"/>
		<updated>2026-06-11T11:35:50Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ge flashpad power supply pcb backside&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_uwb_pcb_backside.jpg&amp;diff=3590</id>
		<title>File:Ge flashpad uwb pcb backside.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_uwb_pcb_backside.jpg&amp;diff=3590"/>
		<updated>2026-06-11T11:35:09Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ge flashpad uwb pcb backside&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_uwb_board_pcb.jpg&amp;diff=3589</id>
		<title>File:Ge flashpad uwb board pcb.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_uwb_board_pcb.jpg&amp;diff=3589"/>
		<updated>2026-06-11T11:33:51Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ge flashpad uwb board pcb&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_powersupply_pcb_in_handle.jpg&amp;diff=3588</id>
		<title>File:Ge flashpad powersupply pcb in handle.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_powersupply_pcb_in_handle.jpg&amp;diff=3588"/>
		<updated>2026-06-11T11:31:44Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ge flashpad powersupply pcb in handle&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_tether_wiring_for_ethernet_cable.jpg&amp;diff=3587</id>
		<title>File:Ge flashpad tether wiring for ethernet cable.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_tether_wiring_for_ethernet_cable.jpg&amp;diff=3587"/>
		<updated>2026-06-11T11:29:57Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ge flashpad tether wiring for ethernet cable&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_tether_cable_pinout_diagram.png&amp;diff=3586</id>
		<title>File:Ge flashpad tether cable pinout diagram.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Ge_flashpad_tether_cable_pinout_diagram.png&amp;diff=3586"/>
		<updated>2026-06-11T11:28:37Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ge flashpad tether cable pinout diagram&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Xray_current_clamp_multimeter.png&amp;diff=3585</id>
		<title>File:Xray current clamp multimeter.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Xray_current_clamp_multimeter.png&amp;diff=3585"/>
		<updated>2026-06-11T11:27:59Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;xray of a current clamp multimeter&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Xray_capture_pcb.png&amp;diff=3584</id>
		<title>File:Xray capture pcb.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Xray_capture_pcb.png&amp;diff=3584"/>
		<updated>2026-06-11T11:26:59Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;xray capture of a ta caruso main pcb&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:GE_Flashpad.jpg&amp;diff=3583</id>
		<title>File:GE Flashpad.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:GE_Flashpad.jpg&amp;diff=3583"/>
		<updated>2026-06-11T11:24:47Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Device with Anti-Scatter grid (left), Software and Manual ontop and Tether cable Right.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:GE_Flashpad_under_the_hood.jpg&amp;diff=3582</id>
		<title>File:GE Flashpad under the hood.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:GE_Flashpad_under_the_hood.jpg&amp;diff=3582"/>
		<updated>2026-06-11T11:22:27Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Teardown picture with the carbon fiber sleeve taken off.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3581</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3581"/>
		<updated>2026-06-11T10:54:28Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: removed links of some words&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a Digital Radiography image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
==Technical Specifications==&lt;br /&gt;
&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm CsI scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Parameter!!Value&lt;br /&gt;
|-&lt;br /&gt;
|Resolution||2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
|Bit depth||16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
|Spatial resolution||Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
|Scintillator material||Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
|Detector type||TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
|Panel size||~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an anti-scatter grid can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
==Hardware Features==&lt;br /&gt;
&lt;br /&gt;
===Shock logging===&lt;br /&gt;
The unit contains an internal accelerometer that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
===Wireless connectivity===&lt;br /&gt;
Some units include a UWB transmitter for Wireless USB; others may be equipped with a Wi-Fi module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
===Ethernet interface===&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling Gigabit Ethernet connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3580</id>
		<title>GE Medical Flashpad Digital Xray Detector</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=GE_Medical_Flashpad_Digital_Xray_Detector&amp;diff=3580"/>
		<updated>2026-06-11T10:51:52Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: Page creation, initial commit.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
The '''GE Flashpad''' is a [[Digital Radiography]] image sensor from approximately 2010, originally used in the '''GE Optima 220AMX''' mobile X-ray unit. It was designed to replace analog film in radiology, dramatically reducing image acquisition time from hours to seconds.&lt;br /&gt;
&lt;br /&gt;
Due to its high original cost and specialized application, used units occasionally appear on professional B2B marketplaces in the $15,000–$50,000 range. On eBay, prices typically fall between $1,500 and $5,000, though units at this price point are often in poor condition and may fail the built-in self-test or not function at all.&lt;br /&gt;
&lt;br /&gt;
{{Note|All findings on this page are based on a single unit and the time spent working on it. Information is subject to speculation and may not be fully accurate.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|[[Artificial intelligence|AI]] assistance was used in writing this page for improved formatting and readability, as well as in the process of finding information, testing, and analyzing firmware dumps and backups.}}&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
The Flashpad uses a '''~40 × 40 cm [[Caesium iodide|CsI]] scintillator''' bonded to a [[Thin-film transistor|TFT]] photodetector array mounted on glass. The assembly is highly sensitive to shock and impact damage.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Value&lt;br /&gt;
|-&lt;br /&gt;
| Resolution || 2048 × 2048 px&lt;br /&gt;
|-&lt;br /&gt;
| Bit depth || 16-bit per pixel&lt;br /&gt;
|-&lt;br /&gt;
| Spatial resolution || Up to 5 lp/mm (theoretical)&lt;br /&gt;
|-&lt;br /&gt;
| Scintillator material || Caesium iodide (CsI)&lt;br /&gt;
|-&lt;br /&gt;
| Detector type || TFT photodetector array (glass substrate)&lt;br /&gt;
|-&lt;br /&gt;
| Panel size || ~40 × 40 cm&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The theoretical 5 lp/mm spatial resolution is primarily limited in practice by the focal spot size of the X-ray source. Use of an [[anti-scatter grid]] can improve effective resolution.&lt;br /&gt;
&lt;br /&gt;
== Hardware Features ==&lt;br /&gt;
&lt;br /&gt;
=== Shock logging ===&lt;br /&gt;
The unit contains an internal [[accelerometer]] that logs significant shock events — but only when a battery is inserted. As there is no backup battery, shock events occurring while unpowered are not recorded.&lt;br /&gt;
&lt;br /&gt;
=== Wireless connectivity ===&lt;br /&gt;
Some units include a [[Ultra-wideband|UWB]] transmitter for Wireless USB; others may be equipped with a [[Wi-Fi]] module instead. The detector can also be operated over the tethered connection alone.&lt;br /&gt;
&lt;br /&gt;
=== Ethernet interface ===&lt;br /&gt;
Exposed metal contacts on the bottom connector are isolated via relays by default. Enabling [[Gigabit Ethernet]] connectivity requires shorting or driving two specific pins. This has not been investigated further at this time.&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3567</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3567"/>
		<updated>2026-06-03T22:00:39Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Raw Viewer update to rev5*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 first web only edition running in javascript on the device.                                                                                &lt;br /&gt;
&lt;br /&gt;
rev4..5: added CLAHE contrast enhancement, noise reduction, peformance improvement, see blow.                                                                                                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html''' https://pastebin.com/LM3Gmeez&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.js''' https://pastebin.com/n9wAVW63&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
''See &amp;quot;Automatic Dead Pixels and Flatmap Correction&amp;quot; below how to create new dead pixel and flatmaps using the system menu.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Breakoutboard d8x3c dali v1.1.png|alt=|thumb|Breakout board for D8X3C]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to have a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
A open source project can be found here, it includes pin access to:&lt;br /&gt;
&lt;br /&gt;
*FPGA JTAG&lt;br /&gt;
*UART 3.3V with pin compatible FT232 socket&lt;br /&gt;
*Power 5V over USB-C&lt;br /&gt;
*5 Button inputs&lt;br /&gt;
*Buffered video output and socket for USB Videograbber&lt;br /&gt;
*Digital 14 bit video out header with hsync vsync and clk.&lt;br /&gt;
*&lt;br /&gt;
&lt;br /&gt;
the pcb layout has been tested and seems to work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Very basic fanout of pins to 2.54mm headers with a socket for a Video grabber and FT232 adapter and buttons. by gamerpaddy&lt;br /&gt;
https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb&amp;lt;/blockquote&amp;gt;&amp;lt;blockquote&amp;gt;Professional adapter with integrated Video grabber and USB/UART bridge built in including a FFC connector for pin fanout and buttons. by TOA-HI / Killy.THI&lt;br /&gt;
&lt;br /&gt;
https://github.com/TOA-HI/Dali-Tech-D8X3C-Thermal-Camera-Backpack&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sending Serial Commands directly over UART==&lt;br /&gt;
&lt;br /&gt;
Sending Serial commands directly over UART to the core requires some additional bytes.&lt;br /&gt;
&lt;br /&gt;
The packet structure is as follows (Strip spaces, commands are allways uppercase and 3 chars)&lt;br /&gt;
 &amp;lt;STX&amp;gt; &amp;lt;LENGTH1&amp;gt; &amp;lt;LENGTH2&amp;gt; Command, Value, Checksum &amp;lt;ETX&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pseudocode to craft a command packet may look like this&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//cmd is allways 3 byte uppercase chars&lt;br /&gt;
//values can be multiple comma seperated like 320,240&lt;br /&gt;
function make_packet(cmd, values):&lt;br /&gt;
    payload = cmd + &amp;quot;,&amp;quot; + join(values, &amp;quot;,&amp;quot;) + &amp;quot;,&amp;quot;&lt;br /&gt;
    bytes = ascii(payload)&lt;br /&gt;
    length = len(bytes)&lt;br /&gt;
    checksum = sum(bytes) mod 256&lt;br /&gt;
    return [0x02, length&amp;amp;0xFF, length&amp;gt;&amp;gt;8] + bytes + [checksum, 0x03]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzed much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3215</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3215"/>
		<updated>2025-09-14T18:17:16Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: added professional pcb adapter&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
''See &amp;quot;Automatic Dead Pixels and Flatmap Correction&amp;quot; below how to create new dead pixel and flatmaps using the system menu.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Breakoutboard d8x3c dali v1.1.png|alt=|thumb|Breakout board for D8X3C]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to have a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
A open source project can be found here, it includes pin access to:&lt;br /&gt;
&lt;br /&gt;
*FPGA JTAG&lt;br /&gt;
*UART 3.3V with pin compatible FT232 socket&lt;br /&gt;
*Power 5V over USB-C&lt;br /&gt;
*5 Button inputs&lt;br /&gt;
*Buffered video output and socket for USB Videograbber&lt;br /&gt;
*Digital 14 bit video out header with hsync vsync and clk.&lt;br /&gt;
*&lt;br /&gt;
&lt;br /&gt;
the pcb layout has been tested and seems to work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Very basic fanout of pins to 2.54mm headers with a socket for a Video grabber and FT232 adapter and buttons. by gamerpaddy&lt;br /&gt;
https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb&amp;lt;/blockquote&amp;gt;&amp;lt;blockquote&amp;gt;Professional adapter with integrated Video grabber and USB/UART bridge built in including a FFC connector for pin fanout and buttons. by TOA-HI / Killy.THI&lt;br /&gt;
&lt;br /&gt;
https://github.com/TOA-HI/Dali-Tech-D8X3C-Thermal-Camera-Backpack&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sending Serial Commands directly over UART==&lt;br /&gt;
&lt;br /&gt;
Sending Serial commands directly over UART to the core requires some additional bytes.&lt;br /&gt;
&lt;br /&gt;
The packet structure is as follows (Strip spaces, commands are allways uppercase and 3 chars)&lt;br /&gt;
 &amp;lt;STX&amp;gt; &amp;lt;LENGTH1&amp;gt; &amp;lt;LENGTH2&amp;gt; Command, Value, Checksum &amp;lt;ETX&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pseudocode to craft a command packet may look like this&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//cmd is allways 3 byte uppercase chars&lt;br /&gt;
//values can be multiple comma seperated like 320,240&lt;br /&gt;
function make_packet(cmd, values):&lt;br /&gt;
    payload = cmd + &amp;quot;,&amp;quot; + join(values, &amp;quot;,&amp;quot;) + &amp;quot;,&amp;quot;&lt;br /&gt;
    bytes = ascii(payload)&lt;br /&gt;
    length = len(bytes)&lt;br /&gt;
    checksum = sum(bytes) mod 256&lt;br /&gt;
    return [0x02, length&amp;amp;0xFF, length&amp;gt;&amp;gt;8] + bytes + [checksum, 0x03]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3206</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3206"/>
		<updated>2025-08-29T13:08:46Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Using the Thermal module without Webserver */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
''See &amp;quot;Automatic Dead Pixels and Flatmap Correction&amp;quot; below how to create new dead pixel and flatmaps using the system menu.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Breakoutboard d8x3c dali v1.1.png|alt=|thumb|Breakout board for D8X3C]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to have a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
A open source project can be found here, it includes pin access to:&lt;br /&gt;
&lt;br /&gt;
* FPGA JTAG&lt;br /&gt;
* UART 3.3V with pin compatible FT232 socket&lt;br /&gt;
* Power 5V over USB-C&lt;br /&gt;
* 5 Button inputs&lt;br /&gt;
* Buffered video output and socket for USB Videograbber&lt;br /&gt;
* Digital 14 bit video out header with hsync vsync and clk.&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
the pcb layout has been tested and seems to work.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sending Serial Commands directly over UART ==&lt;br /&gt;
&lt;br /&gt;
Sending Serial commands directly over UART to the core requires some additional bytes.&lt;br /&gt;
&lt;br /&gt;
The packet structure is as follows (Strip spaces, commands are allways uppercase and 3 chars)&lt;br /&gt;
 &amp;lt;STX&amp;gt; &amp;lt;LENGTH1&amp;gt; &amp;lt;LENGTH2&amp;gt; Command, Value, Checksum &amp;lt;ETX&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pseudocode to craft a command packet may look like this&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//cmd is allways 3 byte uppercase chars&lt;br /&gt;
//values can be multiple comma seperated like 320,240&lt;br /&gt;
function make_packet(cmd, values):&lt;br /&gt;
    payload = cmd + &amp;quot;,&amp;quot; + join(values, &amp;quot;,&amp;quot;) + &amp;quot;,&amp;quot;&lt;br /&gt;
    bytes = ascii(payload)&lt;br /&gt;
    length = len(bytes)&lt;br /&gt;
    checksum = sum(bytes) mod 256&lt;br /&gt;
    return [0x02, length&amp;amp;0xFF, length&amp;gt;&amp;gt;8] + bytes + [checksum, 0x03]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3205</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3205"/>
		<updated>2025-08-29T13:03:50Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Using the Thermal module without Webserver */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
''See &amp;quot;Automatic Dead Pixels and Flatmap Correction&amp;quot; below how to create new dead pixel and flatmaps using the system menu.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Breakoutboard d8x3c dali v1.1.png|alt=|thumb|Breakout board for D8X3C]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to have a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
A open source project can be found here, it includes pin access to:&lt;br /&gt;
&lt;br /&gt;
FPGA JTAG&lt;br /&gt;
&lt;br /&gt;
UART 3.3V with pin compatible FT232 socket&lt;br /&gt;
&lt;br /&gt;
Power 5V over USB-C&lt;br /&gt;
&lt;br /&gt;
5 Button inputs&lt;br /&gt;
&lt;br /&gt;
Buffered video output and socket for USB Videograbber&lt;br /&gt;
&lt;br /&gt;
Digital 14 bit video out header with hsync vsync and clk.&lt;br /&gt;
&lt;br /&gt;
the pcb has been tested and seems to work.https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=File:Breakoutboard_d8x3c_dali_v1.1.png&amp;diff=3204</id>
		<title>File:Breakoutboard d8x3c dali v1.1.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=File:Breakoutboard_d8x3c_dali_v1.1.png&amp;diff=3204"/>
		<updated>2025-08-29T13:01:29Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;fixed version&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3198</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3198"/>
		<updated>2025-08-15T17:14:50Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
''See &amp;quot;Automatic Dead Pixels and Flatmap Correction&amp;quot; below how to create new dead pixel and flatmaps using the system menu.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Dali D8X3C Breakout board.png|thumb|Breakout board for D8X3C ]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to make a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A breakout board is still a work in progress&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3196</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3196"/>
		<updated>2025-08-06T00:26:32Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: breakout board setback&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There still needs to be work done to create own maps and flatfields but the cameras are still very capable and useable with fully cleared maps.&lt;br /&gt;
&lt;br /&gt;
To clear the maps, just go to these offsets and fill with values around 0x3F or 0x0 for total length of   X Resolution x Y Resolution x 2 bytes (0x96000 for 640x480)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Dali D8X3C Breakout board.png|thumb|Breakout board for D8X3C ]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to make a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A breakout board is still a work in progress&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3193</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3193"/>
		<updated>2025-08-04T18:00:06Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Raw Viewer for the internal Webserver (recommended) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                                                                                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': ''&amp;lt;u&amp;gt;If it does not work&amp;lt;/u&amp;gt;'', check the contents of both files. FTP sometimes messes them up.&lt;br /&gt;
&lt;br /&gt;
also run &lt;br /&gt;
 dos2unix /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
because linux &amp;lt;&amp;gt; windows difficulties that couldnt be resolved in over 30 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There still needs to be work done to create own maps and flatfields but the cameras are still very capable and useable with fully cleared maps.&lt;br /&gt;
&lt;br /&gt;
To clear the maps, just go to these offsets and fill with values around 0x3F or 0x0 for total length of   X Resolution x Y Resolution x 2 bytes (0x96000 for 640x480)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Dali D8X3C Breakout board.png|thumb|Breakout board for D8X3C ]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to make a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The breakout board files can be found [https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb here] but may be still work in progress.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3192</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3192"/>
		<updated>2025-08-04T17:49:54Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Automatic Dead Pixels and Flatmap Correction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There still needs to be work done to create own maps and flatfields but the cameras are still very capable and useable with fully cleared maps.&lt;br /&gt;
&lt;br /&gt;
To clear the maps, just go to these offsets and fill with values around 0x3F or 0x0 for total length of   X Resolution x Y Resolution x 2 bytes (0x96000 for 640x480)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Area - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Dali D8X3C Breakout board.png|thumb|Breakout board for D8X3C ]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to make a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The breakout board files can be found [https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb here] but may be still work in progress.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3191</id>
		<title>Dali D8X3N Thermal Camera</title>
		<link rel="alternate" type="text/html" href="https://wiki.recessim.com/w/index.php?title=Dali_D8X3N_Thermal_Camera&amp;diff=3191"/>
		<updated>2025-08-04T16:27:04Z</updated>

		<summary type="html">&lt;p&gt;Gamerpaddy: /* Raw Viewer for the internal Webserver (recommended) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Dali D8X3N Thermal Camera Reverse Engineering=&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Introduction===&lt;br /&gt;
The [https://www.dali-irtech.com/productinfo/741567.html Dali D8X3N] Thermal Camera module comes in two versions, a 384x288 and 640x480 resolution, utilizing the [http://www.dali-tech.us/products/dld64017m-uncooled-infrared-fpa-detector-86.html DLD Series] of amorphous-Silicon Microbolometers.&lt;br /&gt;
&lt;br /&gt;
They were mainly used in [http://www.dali-tech.us/products/dm60-w--online-body-temperature-thermal-imaging-camera-55.html Dali DM60-W] or WS-1 Body Temperature screening cameras in conjunction with a Black Body radiator set to 37°C to detect infected individuals at the start of the 2020 Covid Pandemic.&lt;br /&gt;
[[File:ICI FM320P Innerts.jpg|thumb|Internals. A TP-Link Switch, HikVision Visible Camera and the D8x3N Network thermal camera module]]&lt;br /&gt;
[https://infraredcameras.com/ ICI] rebranded these models as FM 320 P and FM 640 P, and distributed them on the local US market, but as the need for such Cameras exploded, those models were built with what was in stock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So happens, that some of the FM 320 P Models had a DLD640 640x480 instead of a DLD384 384x288 Microbolometer in them, and just got their Firmware modified.&lt;br /&gt;
&lt;br /&gt;
It also could be, because they have excessive amounts of bad pixels outside the 384x288 area, that they were binned to a lower grade. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Discussions about these Cameras started around 2022 in the [https://www.eevblog.com/forum/thermal-imaging/ici-and-dali-thermal-camera-software/ EEVBlog Forums.] when they started to appear secondhand and affordable. &lt;br /&gt;
&lt;br /&gt;
Originally they were priced around US$ 20... 30.000 by ICI, and a unknown amount by Dali Tech.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Modules specs according to the manufacturer are:&lt;br /&gt;
&lt;br /&gt;
≤50..60mK@F1，300K，50Hz&lt;br /&gt;
&lt;br /&gt;
17 or 25µm pixel pitch&lt;br /&gt;
&lt;br /&gt;
Hisilicon SoC running Linux to provide Onvif and RTSP streams, analysis and a Web UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Module.jpg|thumb|A real 384x288 model with a DLD384 sensor. notice the Productnumber ]]&lt;br /&gt;
ICI FM Cameras are found all over Online secondhand marketplaces for scrap Prices, as they are no longer needed.&lt;br /&gt;
&lt;br /&gt;
But you never know, what are you getting. It could be a real DLD384 or the better DLD640 inside. Its a gamble.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
======640x480 Imagers======&lt;br /&gt;
are labeled 22D843 Nxxxx and have a DLD640 sensor in it, they can be upgraded if they run the 384x288 firmware.&lt;br /&gt;
&lt;br /&gt;
======384x288 Imagers======&lt;br /&gt;
are labeled 22D883Nxxxx and have a DLD384 sensor in it, they cannot be upgraded&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==D8X3N Module closer look==&lt;br /&gt;
From now on, i will refer to the Thermal Imaging Module as TIM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On the front there is a 2 Element Germanium Lens of a unknown focal length and aperture.&lt;br /&gt;
&lt;br /&gt;
It can be focus-adjusted when unscrewing the clamp around the housing. It can be focussed from infinity down to 10..20cm with some quality loss.&lt;br /&gt;
&amp;lt;br /&amp;gt;The outter diameter of the Lens is 42mm with a 1 1/3 inch thread diameter of undetermined pitch. &lt;br /&gt;
&lt;br /&gt;
Dimensions of the germanium elements are 21mm to 3 and 6mm and 14grams in weight.&lt;br /&gt;
[[File:Disassembled Thermal Camera Lens.jpg|thumb|Two element Germanium Lens of unknown focal length and aperture]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The TIM is composed of 6 Stacked PCB modules, 3 of which are for the Web Server functionality.  taking them off will give you the basic configuration of a [https://www.dalithermal.com/productinfo/741568.html Dali D8X3C] TIM&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Dali DLD640 Thermal Image Sensor.jpeg|DLD640 Sensor&lt;br /&gt;
File:Dali DLD384 Thermal Imaging Sensor.jpeg|DLD384 Sensor&lt;br /&gt;
File:Dali D8X3N Board1 Back.jpeg|Network &amp;amp; GPIO Board Back&lt;br /&gt;
File:Dali D8X3N Board1 Front.jpeg|Network &amp;amp; GPIO Board Front&lt;br /&gt;
File:Dali D8X3N Glue Board Back.jpeg|Board between Logic and IO Back&lt;br /&gt;
File:Dali D8X3N Glue Board Front.jpeg|Board between Logic and IO Front&lt;br /&gt;
File:Dali D8X3N SoC Board Back.jpeg|SoC Board back&lt;br /&gt;
File:Dali D8X3N SoC Board front.jpeg|SoC board front&lt;br /&gt;
File:Dali D8X3N FPGA Board Back.jpeg|FPGA Board of the D8X3C Thermal Imager including a PMIC&lt;br /&gt;
File:Dali D8X3N FPGA Board Front.jpeg|FPGA Board Front with Video DAC and SPI Flash&lt;br /&gt;
File:Dali D8X3N Sensor Power and ADC Board.jpeg|Sensor control and ADC board&lt;br /&gt;
File:Dali D8X3N Thermal Core Debug wiring.jpeg|Debug wiring for sniffing SPI and UART&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===User Manual===&lt;br /&gt;
including pinout diagram and important data&lt;br /&gt;
&lt;br /&gt;
can be found here https://archive.org/details/dali-d-8-x-3-cuser-manualen-v-1.0-1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing the Video Stream==&lt;br /&gt;
[[File:Onvif Device Manager ODM.png|thumb|ODM streaming the Thermal view]]&lt;br /&gt;
when first connecting it to power and Ethernet, the camera will have 192.168.1.102 as a Static ip.&lt;br /&gt;
&lt;br /&gt;
If you are running a 192.168.0.x or .178.x net, you need to change it or add a subnet under windows ipv4 settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
to view it, you can use VLC and the stream URL &lt;br /&gt;
 rtsp://admin@admin:&amp;lt;camera ip&amp;gt;/ONVIFMedia&lt;br /&gt;
there is /ONVIFMedia /ONVIFMediaM and /ONVIFMediaS for different stream Profiles.&lt;br /&gt;
&lt;br /&gt;
VLC is quite slow and delayed, rather use Onvif Device Manager ODM,&lt;br /&gt;
&lt;br /&gt;
it has no delay and auto-detects cameras in the network. &lt;br /&gt;
&lt;br /&gt;
Be sure to log in as user: admin  pass: admin in ODM.&lt;br /&gt;
&lt;br /&gt;
in there, you can also set you camera to DHCP under Network settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Web Interface===&lt;br /&gt;
[[File:Dali D8X3N web interface.png|thumb|Since browsers cant display RTSP anymore, open in Edge and select IE Compatibility Mode.]]&lt;br /&gt;
the web interface can be accessed by opening the camera IP in a browser&lt;br /&gt;
&lt;br /&gt;
login is:&lt;br /&gt;
 user: admin pass: admin&lt;br /&gt;
You can set Temperature markers and areas, but without using the Black Body calibration source, your readings may be way off or not working.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====OSD====&lt;br /&gt;
there is a debug-login with &lt;br /&gt;
 user: super  pass: 871897 &lt;br /&gt;
that lets you send Key commands to the Imager to navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
Setting the OSD to english is accomplished with option #11&lt;br /&gt;
[[File:Dali D8X3C OSD.png|thumb|OSD in english, if yours is Chinese. go to option #11 and click + or -]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To acces some locked menus, press &lt;br /&gt;
 + - + - + -&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The password for the locked System and Main menu is&lt;br /&gt;
 +-MC+-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Be careful in the UART menu to not change the BAUD rate or Protocol, as your ability to control it via the Web API gets lost.&lt;br /&gt;
&lt;br /&gt;
You need to reflash or solder buttons on the thermal core then.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Viewing Raw Thermal Data from the Web API==&lt;br /&gt;
there is a hidden endpoint in the stream.so file that could be found while decompiling&lt;br /&gt;
&lt;br /&gt;
accessing&lt;br /&gt;
[[File:Arduino Nano blink sketch thermal gif.gif|thumb|Arduino Nano running blink sketch while thermally recording it, notice the led and resistor on the right]]&lt;br /&gt;
 &amp;lt;ip of camera&amp;gt;:5000/stream?Type=JPEG&amp;amp;Source=Jpeg&amp;amp;Mode=TCP&lt;br /&gt;
lets you download a JPEG. but there are more options available (not all have been tested)&lt;br /&gt;
&lt;br /&gt;
we are mainly interessted in Raw thermal data.&lt;br /&gt;
&lt;br /&gt;
====Source (Stream Type)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Major&amp;lt;/code&amp;gt; - Main video stream (channel 0)&lt;br /&gt;
*&amp;lt;code&amp;gt;Minor&amp;lt;/code&amp;gt; - Sub video stream (channel 1)&lt;br /&gt;
*&amp;lt;code&amp;gt;MinorAV&amp;lt;/code&amp;gt; - Sub video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;MajorAV&amp;lt;/code&amp;gt; - Main video + audio stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Jpeg&amp;lt;/code&amp;gt; - JPEG snapshot stream&lt;br /&gt;
*&amp;lt;code&amp;gt;Raw&amp;lt;/code&amp;gt; - Raw thermal data stream&lt;br /&gt;
*&amp;lt;code&amp;gt;YUV&amp;lt;/code&amp;gt; - YUV raw video stream&lt;br /&gt;
&lt;br /&gt;
====Type (Data Format)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;H264&amp;lt;/code&amp;gt; - H.264 video encoding&lt;br /&gt;
*&amp;lt;code&amp;gt;JPEG&amp;lt;/code&amp;gt; - JPEG image format&lt;br /&gt;
*&amp;lt;code&amp;gt;RAW&amp;lt;/code&amp;gt; - Raw sensor data&lt;br /&gt;
*&amp;lt;code&amp;gt;QBOX&amp;lt;/code&amp;gt; - Custom protocol format&lt;br /&gt;
*&amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; - Frame-based format&lt;br /&gt;
&lt;br /&gt;
====Mode (Transport Protocol)====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt; - TCP streaming (default)&lt;br /&gt;
*&amp;lt;code&amp;gt;UDP&amp;lt;/code&amp;gt; - UDP streaming&lt;br /&gt;
*&amp;lt;code&amp;gt;MUL&amp;lt;/code&amp;gt; - Multicast streaming&lt;br /&gt;
&lt;br /&gt;
====Quality Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Quality=[1-100]&amp;lt;/code&amp;gt; - Video quality level&lt;br /&gt;
*&amp;lt;code&amp;gt;Frames=[number]&amp;lt;/code&amp;gt; - Number of frames to send&lt;br /&gt;
*&amp;lt;code&amp;gt;Interval=[ms]&amp;lt;/code&amp;gt; - Frame interval in milliseconds&lt;br /&gt;
&lt;br /&gt;
====Network Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;IPAddr=[ip]&amp;lt;/code&amp;gt; - Client IP address for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Port=[port]&amp;lt;/code&amp;gt; - Client port for UDP/multicast&lt;br /&gt;
*&amp;lt;code&amp;gt;Heart-beat=[Yes/No]&amp;lt;/code&amp;gt; - Enable/disable heartbeat&lt;br /&gt;
&lt;br /&gt;
====Snapshot Parameters====&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;Snap=[Yes/No]&amp;lt;/code&amp;gt; - Enable snapshot mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
====Raw Viewer for PC (Python)====&lt;br /&gt;
[[File:Dali D8X3N Raw Thermal viewer.png|thumb|Raw Data Viewer in python]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the raw stream, i made a python program that converts the 14bit thermal data to a visible image.&lt;br /&gt;
&lt;br /&gt;
it is low fps tho, due  to bad optimizations. &lt;br /&gt;
&lt;br /&gt;
rev1 first working version  https://pastebin.com/dyNWDPT3&lt;br /&gt;
&lt;br /&gt;
rev2 with temperature readout  https://pastebin.com/u107Q85w&lt;br /&gt;
&lt;br /&gt;
rev3 and onward will only be available directly as a Webpage on the Device (see below).                &lt;br /&gt;
&lt;br /&gt;
====Raw Viewer for the internal Webserver (recommended)====&lt;br /&gt;
It is also possible to use the Raw viewer directly in the browser.&lt;br /&gt;
&lt;br /&gt;
To achieve this, the stream had to be proxied from port 5000 to 80 due to CORS.&lt;br /&gt;
&lt;br /&gt;
luckily the internal lighttpd had the CGI module enabled, so piping it troug (using wget ... i know) worked with very low overhead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To install, you need '''Terminal access''' to the Camera. see below.&lt;br /&gt;
&lt;br /&gt;
optional for simplicity: '''ftp''' access&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Webviewer.png|thumb|Web Raw Viewer with histogram (rev3)]]&lt;br /&gt;
&lt;br /&gt;
Create the files and the code inside.&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/raw.html'''      https://pastebin.com/kaiXwAgD&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
'''/app/web/webpages/cgi-bin/proxy.cgi'''      https://pastebin.com/vii1HXcU&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
then run &lt;br /&gt;
 chmod +x /app/web/webpages/cgi-bin/proxy.cgi&lt;br /&gt;
'''restart''' your camera.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It should be accessible now trough '''http://&amp;lt;camera ip&amp;gt;/raw.html''' in the browser and click Start Stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;performance may need improvement.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Getting Root Terminal Access==&lt;br /&gt;
To access the Terminal you need to solder or plug in a UART connection to Connector J7 on the &amp;quot;Glue&amp;quot; board. &lt;br /&gt;
&lt;br /&gt;
Pinout is (left to right) '''RX TX GND 3.3V Baud 115200 3.3V!''' &lt;br /&gt;
&lt;br /&gt;
But the system will spam with debug messages, making patching the files a hard job unless you use &amp;quot;tftp&amp;quot; or &amp;quot;sed&amp;quot; commands.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet access to make patching easier====&lt;br /&gt;
[[File:Upgrade ici dali firmware updater.png|thumb|ICI Firmware updater software with modified firmware loaded]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
there is a easier way using a modified Firmware updater to enable Telnet temporarily to patch the Files.&lt;br /&gt;
&lt;br /&gt;
Download the '''upgrade_ici.exe''' form [https://archive.org/details/fwupdate here] and run it. &lt;br /&gt;
&lt;br /&gt;
Then you should be able to connect to it via Telnet.&lt;br /&gt;
&lt;br /&gt;
The Login credentials are '''User: roo'''t  '''Password: DLroot'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Patching Device===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patching the Web Server===&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Enabling Telnet and ftpd permanently====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This only works if you also patch the Thermal Camera module flash! &lt;br /&gt;
&lt;br /&gt;
to permanently enable Telnet, go to '''/etc/init.d''' and edit '''S90app'''&lt;br /&gt;
 vi S90app&lt;br /&gt;
in vi, move your cursor to the '''#''' at '''#telnetd''' and press x&amp;lt;blockquote&amp;gt;optional: enable ftp by removing the '''#''' at '''ftpd''' and adjust the path to /  &amp;lt;/blockquote&amp;gt;now press ''':''' and type '''wq''' and hit '''enter'''. this should be it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Patching the decoder and streamer to 640x480:====&lt;br /&gt;
go to /app and edit the mach.cfg&lt;br /&gt;
&lt;br /&gt;
replace DL384 with D640 and edit the resolution to 640 480 &lt;br /&gt;
&lt;br /&gt;
(press i in vi to enter edit mode, press ESC to exit edit mode.   :wq  to write and quit.)&lt;br /&gt;
&lt;br /&gt;
repeat that procedure for the dali.cfg&lt;br /&gt;
&lt;br /&gt;
=====(optional) patching app.sh to prevent it from rebooting=====&lt;br /&gt;
edit the app.sh in /app and remove all &amp;quot;reboot&amp;quot; occasions to prevent the camera from rebooting if onvifserver, httpd or daliServer arent running for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Patching the Thermal Camera module====&lt;br /&gt;
To patch it, you need to disassemble everything down to the FPGA Board,&lt;br /&gt;
&lt;br /&gt;
on the bottom there is a SPI SOIC8 Flash.    &lt;br /&gt;
&lt;br /&gt;
Use a CH341 EEPROM programmer (be sure to have a fixed or modified version to not put out 5V!, set it to 3.3V) and SOIC8 clamp.&lt;br /&gt;
&lt;br /&gt;
Use NeoProgrammer (i used 2.2.0.10 but there are newer available, use the latest) and click Detect. It should find W25Q128 chips, select the FV one.&lt;br /&gt;
&lt;br /&gt;
Dont forget to download a backup of your flash and save it !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Download the Binary &amp;quot;Dali 640x480 '''actual''' cleared pixelmap.bin&amp;quot;  from #3 below and load it into NeoProgrammer,   &lt;br /&gt;
&lt;br /&gt;
Click Erase, Program and Verify. &lt;br /&gt;
&lt;br /&gt;
This can take a while.&lt;br /&gt;
&lt;br /&gt;
Your imager should start up right after assembly by hearing the shutter click.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardware Hacking==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flash Contents===&lt;br /&gt;
On archive, click on show all files to download them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;1 a Flash dump of the original downgraded D8X3N can be found [https://archive.org/details/dali-d-843n-flash-dump here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;2 a Flash dump of a original 640x480 model can be found [https://archive.org/details/d-843-n-640x-480 here]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;3 a modified Flash dump of a 640x480 with cleared dead pixel map, flat map etc. can be found [https://archive.org/details/d-843-n-640x-480-cleared-pixelmap here]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a Flash dump of the web server SPI flash W25Q256JV can be found [https://archive.org/details/dm-60-webserver-flash-dump here] in case a bad firmware update caused a bootloop.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flash Binary Analysis===&lt;br /&gt;
The Flash IC is a Winbond 25Q128JV SPI Flash that contains the FPGA Bitstream, Deadpixel and Flatmap, aswell as the Device Settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a copy of the SPI Logic Capture at startup can be found [https://archive.org/details/dali-eeprom-capture here], the free software [https://www.dreamsourcelab.com/download/ DSView from DreamSourceLab] is required to view it.&lt;br /&gt;
[[File:Dali Thermal Imager SPI Flash capture.png|thumb|SPI Flash access while booting the Thermal Module]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
a approximation of the offsets resulted in this list:&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
0x000000	-	altera cyclone iv stuff&lt;br /&gt;
0x200000	-	settings&lt;br /&gt;
0x280000	-	Flat field correction map&lt;br /&gt;
0x4C0000	-	Bad Pixel correctio nmap&lt;br /&gt;
0x580000	-	image aswell? 36010 bytes&lt;br /&gt;
0x740000	-	GUIX?&lt;br /&gt;
0x7C0000	-	Strings for Gui?&lt;br /&gt;
0x800000	-	another shorter GUIX&lt;br /&gt;
0x880000	-	Strings for second gui?&lt;br /&gt;
0x8C0000	-	GUIX&lt;br /&gt;
0x940000	-	Strings for gui&lt;br /&gt;
0xE00000	-	?&lt;br /&gt;
0xE80000	-	dzxg? short&lt;br /&gt;
0xEC0000	- 	crosshair.bmp&lt;br /&gt;
0xF40000	-	3DTM?&lt;br /&gt;
0xF80000	-	fake? Bad pixel map BPRP&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Settings offset 0x200000====&lt;br /&gt;
The settings are read and written every startup, aswell when you close the OSD, sporadically and when executing the command MSV&lt;br /&gt;
&lt;br /&gt;
these are some of the settings i could figure out by changing and watching the logic capture what changed.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
offset size Cmd &lt;br /&gt;
0x0		1	SAG  	(0-2) auto gain&lt;br /&gt;
0x2		2	SVB	 	(0-16383) saved value is 16383 - SVB &lt;br /&gt;
0x4		2	SVC		(0-1023) set exposure or gain&lt;br /&gt;
0x6		1	SSM		image filtering&lt;br /&gt;
0x8		1	SEH		image enhancement&lt;br /&gt;
0xA		1	SWP		invert b/w polarity&lt;br /&gt;
0x14	1	SMR		Flip&lt;br /&gt;
0x16	1	SMR		Mirror&lt;br /&gt;
0x20	1	MENU	Cal source 0 IN, 1 OUT&lt;br /&gt;
0x3A	1	MENU	Enhance value 0-100&lt;br /&gt;
0x40	1	MENU	Rectify (shutter 0 OFF, 1 L, 2 H)&lt;br /&gt;
0x50	1	MENU	Language 0 english, 1 chinese, 2 russian&lt;br /&gt;
0xA2	-	SCO		set contrast gain&lt;br /&gt;
0xA4	-	IBR 	set brightness&lt;br /&gt;
0xAE	2	MENU	Fire Threshold&lt;br /&gt;
0xAC	1	SPA		set color palette&lt;br /&gt;
0xE2	1	SMS		display &amp;quot;collected 0/80&amp;quot; enabled&lt;br /&gt;
0xF8	1	SEM		set emissivity&lt;br /&gt;
0x110	1	SRT		set revise temp&lt;br /&gt;
0x116	1	SHD		set humidity&lt;br /&gt;
0x118	1	SET 	set ambient temp&lt;br /&gt;
0x11A	1	STD		set temp range&lt;br /&gt;
0x120	2	SLG		-500 to 500?&lt;br /&gt;
0x124	2	SRD		set distance&lt;br /&gt;
0x12C	1	SCP		???&lt;br /&gt;
0x130	2	SCT		???&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[File:Dali D8X3N Bad Pixel Map Viewer.png|thumb|Bad pixel map of a 640x512 sensor, about 0.56% of dead pixels in this map]]&lt;br /&gt;
So i let the AI create a [https://2476424.playcode.io/ Configurator page] that lets you change the Settings in your Flashdump to your liking &lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Bad Pixel and Flatframe Maps 0x280000 and 0x4C0000====&lt;br /&gt;
Thermal cameras require a bad pixel and flat frame map to compensate for manufacturing inconsistencies.&lt;br /&gt;
&lt;br /&gt;
unfourtainly, the maps of the downgraded cameras only contain enough data for a 384 model, not the full 640. &lt;br /&gt;
&lt;br /&gt;
at offset 0xF80000 there is a BPRP which was mistaken at first as the Bad pixel map, it could just be a mask to where to apply the offset maps, as its either 0x0 or 0x1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://2477569.playcode.io/ Viewer Page] is written in JS and does not require any special dependencies, it can run as a plain html file offline.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There still needs to be work done to create own maps and flatfields but the cameras are still very capable and useable with fully cleared maps.&lt;br /&gt;
&lt;br /&gt;
To clear the maps, just go to these offsets and fill with values around 0x3F or 0x0 for total length of   X Resolution x Y Resolution x 2 bytes (0x96000 for 640x480)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Dali D8X3N Flat Field Map Viewer.png|thumb|Flat Field viewer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Dali Commands==&lt;br /&gt;
To control the TIM you can either use Pelco or Dali command set. By default the Dali commandset is used at 38400 BAUD.&lt;br /&gt;
&lt;br /&gt;
Those commands consist of a 0x02 start byte followed by two length Bytes, then the actual command which is allways 3 uppercase Letters in ASCII (eg. KBD for keyboard) followed by a comma and the values.&lt;br /&gt;
&lt;br /&gt;
Some commands take multiple values, some dont. But you allways have to send the comma after the Command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The camera will answer with the Command and 0, 1 or 2.  Sending no value counts as zero.  &lt;br /&gt;
 0 is invalid parameters, too few or out of range&lt;br /&gt;
&lt;br /&gt;
 1 is OK, valid command&lt;br /&gt;
&lt;br /&gt;
 2 is Invalid command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Commands can be sent trough the Web server using the URL Api endpoint or directly trough the UART port. &lt;br /&gt;
&lt;br /&gt;
(keyboard, C button, causes the shutter to do a calibration)   &lt;br /&gt;
 &amp;lt;camera ip&amp;gt;/cgi-bin/dmcmd?Command=KBD,C    &lt;br /&gt;
Listening to the answer can be done while being in the Linux shell and typing   &lt;br /&gt;
 cat /dev/ttyAMA2 | strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By bruteforcing the UART with AAA to ZZZ a list of Commands could be obtained, but some with unknown Parameters or functionality yet. including some from a decompiled DM60comm.so module found in /app/modules folder.&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
KBD		KEY			Keyboard (F S M C + -)&lt;br /&gt;
SSM		0-1			Image Filtering Off/On&lt;br /&gt;
GSM		0			Image filter status&lt;br /&gt;
SEH		0-1			Image Enhancement Off/On&lt;br /&gt;
GEH		0-1			Image enhancer status&lt;br /&gt;
SCO		0-255		Set Gain (locked when SAG auto gain is on)&lt;br /&gt;
GCO		0-255		Get gain&lt;br /&gt;
INT		15-320		set actual gain? not just contrast sometimes disabled?&lt;br /&gt;
IBR		0-255		Set brightness&lt;br /&gt;
GBR		0-255		Get brightness&lt;br /&gt;
SMR		0-3			Mirror mode bit0=horizontal, bit1=vertical&lt;br /&gt;
GMR		0			Get rotation/flip&lt;br /&gt;
SDT		0-1			displays temp=44.65°C osd, but webserver sends date/time to the cam there....&lt;br /&gt;
GDT		0-1			Get temp display active?&lt;br /&gt;
GPT					Display temp oft SDT&lt;br /&gt;
GNU		0-1			Get Shutter closed/open&lt;br /&gt;
SNU		0-1 		Shutter close / open&lt;br /&gt;
STD		0-1			Set Range 0: -20-180°C 1: 100-600°C&lt;br /&gt;
GTD		0-1			Get Range&lt;br /&gt;
SET		°C			Set Ambient temperature&lt;br /&gt;
GET		°C			Get Ambient temperature&lt;br /&gt;
SEM		0-100		0-1 set emissivity x100&lt;br /&gt;
GEM		0-100		Get Emissivity&lt;br /&gt;
SRT		°C			Set revise temperature -100-100 -10-10°&lt;br /&gt;
GRT		°C			Get revise temperature&lt;br /&gt;
SRD		100			Set Distance 4.4m x100&lt;br /&gt;
GRD		100			Get Distance&lt;br /&gt;
SHD		0-100		Set Humidity&lt;br /&gt;
GHD		55			Get Humidity&lt;br /&gt;
SWP		0-1			invert s/w polarity image&lt;br /&gt;
GWP		1			is inverted?&lt;br /&gt;
SPA		0-10?		Set color palette&lt;br /&gt;
GPA		3			white hot&lt;br /&gt;
SMV		0-1			Show temperature 3x3 grid?? in combination with GTV maybe&lt;br /&gt;
GMV		0&lt;br /&gt;
SMS		0-1			displays &amp;quot;collected 0/&amp;lt;number set by SCT&amp;gt;&amp;quot; by SCP delay &amp;quot;saves the daily number of highest temperatures?&amp;quot;&lt;br /&gt;
GMS		0&lt;br /&gt;
SCT		1-500		sets the amount of &amp;quot;collected&amp;quot; points for SMS&lt;br /&gt;
GCT		80	&lt;br /&gt;
SCP		5-60		&amp;quot;collected&amp;quot; delay in seconds for SMS&lt;br /&gt;
GCP		60			&lt;br /&gt;
SAG		0-2			Auto brightness / gain&lt;br /&gt;
GAG		0			&lt;br /&gt;
SLR		0-9			???&lt;br /&gt;
GLR		0&lt;br /&gt;
SRP		0,0 - 639,479		set crosshair position&lt;br /&gt;
GRP		320,240		get crosshair position	&lt;br /&gt;
DRC		0-1			Show crosshair&lt;br /&gt;
SLG		-500-500	??&lt;br /&gt;
GLG					&lt;br /&gt;
SCE 	text		??&lt;br /&gt;
GCE		0	&lt;br /&gt;
SCB 	text		??&lt;br /&gt;
GCB		0	&lt;br /&gt;
SVB		0-16383		adc offset, exposure? good at 9650&lt;br /&gt;
GVB		6733		value is 16383 minus SVB&lt;br /&gt;
SVC		1-1023		set contrast but better&lt;br /&gt;
GVC		260&lt;br /&gt;
SVF		100-?		?? video freaks out 3400 seems good&lt;br /&gt;
SVS		100-?		?? video freaks out 3300 seems good&lt;br /&gt;
SOT		0-1			?? sensor supply, looks weird when off.&lt;br /&gt;
SZM		1-4			?&lt;br /&gt;
GZM		1&lt;br /&gt;
MSV					save settings too eeprom&lt;br /&gt;
ASV					Saves something aswell?&lt;br /&gt;
GCS		0			Get Core status &amp;amp; faults&lt;br /&gt;
ALD		0,2?		video blanks lots of eeprom activity. reload pixel map??&lt;br /&gt;
SAV		0-9999		set Fire threshold&lt;br /&gt;
GAV		2100		get Fire Threshold&lt;br /&gt;
BAC					change uart protocol? careful&lt;br /&gt;
CBD					Displays Recovery successful?&lt;br /&gt;
CBS					Displays Saved successfully&lt;br /&gt;
LRD					Displays Recovery successful?&lt;br /&gt;
LRS					Displays Saved successfully?&lt;br /&gt;
LRE					Displays Temperature Repair successful?&lt;br /&gt;
CBT					Displays Calibration successful?&lt;br /&gt;
DBP					??? shutter clicks&lt;br /&gt;
DPI					Show weird pixels&lt;br /&gt;
API					Hide weird pixels&lt;br /&gt;
CCM					??&lt;br /&gt;
ASN		0-4			??&lt;br /&gt;
GAB		4 vals		?? 3,-10,50,150&lt;br /&gt;
GAN		1			?? &lt;br /&gt;
GAR		11 vals		??  10,10,5,20,35,50,70,90,120,150,180,,&lt;br /&gt;
GCM		6 values	??&lt;br /&gt;
GFV					prints screen gray value?&lt;br /&gt;
GIT		200			??&lt;br /&gt;
GTV					Get the collected temp values?&lt;br /&gt;
PTS					echoes but with extra stuff??&lt;br /&gt;
QYA		7170		Query Gray Info (information mode)&lt;br /&gt;
QYI		7124		Query Gray Average mode&lt;br /&gt;
QYM		7512		Query Gray Minimum mode&lt;br /&gt;
VTP		400-4000	?? &lt;br /&gt;
MTD		U/S,0,0		?? S answers with bytes, U upload bytes?&lt;br /&gt;
MTC		U/S?		?? S answers with U,0,0, S doesnt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return 0 invalid params:&lt;br /&gt;
STS					??&lt;br /&gt;
STT					??&lt;br /&gt;
SAC					??&lt;br /&gt;
SAH					??&lt;br /&gt;
SAS					??&lt;br /&gt;
SAT					??&lt;br /&gt;
SCX					?? displays crosshair but invalid params&lt;br /&gt;
SCY					?? displays crosshair but invalid params&lt;br /&gt;
GAC					??&lt;br /&gt;
GAH					??&lt;br /&gt;
GAS					??&lt;br /&gt;
GAT					??&lt;br /&gt;
GME					??&lt;br /&gt;
GPC					??&lt;br /&gt;
GPI					??&lt;br /&gt;
GPM					??&lt;br /&gt;
GTA					??&lt;br /&gt;
GTC					??&lt;br /&gt;
GTI					??&lt;br /&gt;
GTM					??&lt;br /&gt;
GTS					??&lt;br /&gt;
GTT					??&lt;br /&gt;
&lt;br /&gt;
answer only:&lt;br /&gt;
&lt;br /&gt;
BPM		0-1			stream ready? answers with 0 stop, 1 go, 2 when prompted&lt;br /&gt;
AVS		0,1&lt;br /&gt;
MEH&lt;br /&gt;
PWI		0-1			changing palette from wh to fire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
unconfirmed (from dm60comm.so):&lt;br /&gt;
SDT		2024,03,15,14,30,25		Set Date Time with year,month,day,hour,minute,second&lt;br /&gt;
STS		1		Set Temperature Show display (0=off, 1=on)&lt;br /&gt;
SVA		2		Set Video Auto mode (0=manual, 2=auto)&lt;br /&gt;
SMP		0,1,1,0,100,150,200,250,300,350,90,25		Set Measurement Point (type,index,enable,mode,startX,startY,endX,endY,anchorX,anchorY,threshold,color)&lt;br /&gt;
STV		Stop Temperature View measurements&lt;br /&gt;
SPF		5		Set Profile parameter&lt;br /&gt;
SVL		-10		Set Video Left pan movement (signed value)&lt;br /&gt;
SVU		-5		Set Video Up tilt movement (signed value)&lt;br /&gt;
SVD		5		Set Video Down tilt movement (signed value)&lt;br /&gt;
SSP		17,1,0	Set System Parameter (param_id,value,index) #46 	temperature format 0 °C, 1 °F, 2 K&lt;br /&gt;
SCP		Set Current Preset (save current position)&lt;br /&gt;
GCP		Get Current Preset&lt;br /&gt;
STT		300,350	Set Temperature Threshold (low,high values *10)&lt;br /&gt;
SDC		1		Set Display Configuration parameter&lt;br /&gt;
GCS		Get Current Status&lt;br /&gt;
GMM		Get Memory Map information&lt;br /&gt;
GFV		Get Frame Value (grayscale data)&lt;br /&gt;
GFZ		Get Freeze Status&lt;br /&gt;
GSI		1		Get System Information (parameter=1)&lt;br /&gt;
GSS		Get System Status&lt;br /&gt;
GSP		21,0	Get System Parameter (param_21=surround temp, value=0)&lt;br /&gt;
GTT		Get Temperature Threshold range&lt;br /&gt;
GTV		1,3,10	Get Temperature Values (enable,type,count)&lt;br /&gt;
GNU		Get Network Update status (response command)&lt;br /&gt;
FRZ		Freeze current display frame&lt;br /&gt;
LIV		Live mode (unfreeze display)&lt;br /&gt;
CAL		Calibrate system&lt;br /&gt;
CCP		3		Call Current Preset (preset number 0-9)&lt;br /&gt;
APF		100	Auto Profile setting&lt;br /&gt;
MEH		Measure Enhancement&lt;br /&gt;
BPT		Built-in Test&lt;br /&gt;
MTC		S,0		Measure Temperature Capture (S=start, 0=sequence)&lt;br /&gt;
MTG		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Gray data transfer&lt;br /&gt;
MTD		U,0,1024,&amp;lt;data&amp;gt;	Measure Temperature Data transfer&lt;br /&gt;
UFP		S,0,2048		Update Firmware Prepare (S=start, 0=offset, 2048=size)&lt;br /&gt;
UFD		S,0,1024		Update Firmware Data transfer&lt;br /&gt;
BAB		U		Buffer operation (U=update)&lt;br /&gt;
TTH		S,0		Temperature Threshold operation&lt;br /&gt;
IPP		192.168.001.102,255.255.255.000,192.168.001.001,192.168.000.001,9989,9998	IP Parameters (IP,netmask,gateway,DNS,port1,port2)&lt;br /&gt;
MAC		00:60:A9:10:00:01	MAC Address setting&lt;br /&gt;
SNP		Set Network Parameters (response command)&lt;br /&gt;
QYI		Query Gray Info (information mode)&lt;br /&gt;
QYA		Query Gray Average mode&lt;br /&gt;
QYM		Query Gray Minimum mode&lt;br /&gt;
GAI		Get Alarm Info (response command)&lt;br /&gt;
YTC		&amp;lt;data&amp;gt;	Unknown command with data parameter&lt;br /&gt;
HTH&lt;br /&gt;
LRS&lt;br /&gt;
LRD&lt;br /&gt;
GAR&lt;br /&gt;
DRC&lt;br /&gt;
PTS&lt;br /&gt;
SCX&lt;br /&gt;
SCY&lt;br /&gt;
CBT&lt;br /&gt;
LRE&lt;br /&gt;
CBD&lt;br /&gt;
CBS&lt;br /&gt;
XXX&lt;br /&gt;
GSP&lt;br /&gt;
GTT&lt;br /&gt;
QYI&lt;br /&gt;
QYA&lt;br /&gt;
QYM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Secret Menu Overview==&lt;br /&gt;
The secret menus Main and System can be openend with the password '''+-MC+-'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Its structured like this, not all variables have been figured out yet.&lt;br /&gt;
&lt;br /&gt;
The format is Menu: submenu: submenu:&lt;br /&gt;
&lt;br /&gt;
and for menu entries: Name, default value, command, description or messages that pop up.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;vb&amp;quot;&amp;gt;&lt;br /&gt;
MAIN &amp;amp; SYSTEM PASS: +-MC+-&lt;br /&gt;
&lt;br /&gt;
entries are NAME VALUE COMMAND DESC or if its a folder its just &amp;quot;:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
System:&lt;br /&gt;
	System:&lt;br /&gt;
        Gain    255 SCO&lt;br /&gt;
        Bright  102 IBR&lt;br /&gt;
        BenDi   100 ?&lt;br /&gt;
        BenDiContrase   80  ?&lt;br /&gt;
        DisMode 0   ?&lt;br /&gt;
        Frequency   48hz (24,25,48,50)&lt;br /&gt;
        DigitalOut  8-14bit  14bit, BT656(_P), ?, BT601&lt;br /&gt;
        AutoFocus   N   ?&lt;br /&gt;
        Line    1   ?&lt;br /&gt;
        Ltemp0  11800   ?&lt;br /&gt;
        LTemp1  11000   ?&lt;br /&gt;
        HTemp0 9500     ?&lt;br /&gt;
        HTemp1 8700 ?&lt;br /&gt;
        DelayTime   0   ?&lt;br /&gt;
        Enhance   0-1 ?&lt;br /&gt;
        Enhanced E1  230 ?&lt;br /&gt;
        FPA_Gain 12pF   ? between 4 and 18 at random.&lt;br /&gt;
        NUCEn   N   ?&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
	Area:&lt;br /&gt;
        SE  2  ASN 0-4 &amp;quot;Partition&amp;quot;&lt;br /&gt;
        INT 200 INT &amp;quot;Points time&amp;quot;&lt;br /&gt;
        VS  3161    SVS&lt;br /&gt;
        VF  2800    SVF&lt;br /&gt;
        Vtemp 1401  VTP&lt;br /&gt;
        VtempArea   Low ?&lt;br /&gt;
        VtempBase   13500  ?&lt;br /&gt;
        VtempStep   -1  ?&lt;br /&gt;
        VsStep  8   ?&lt;br /&gt;
        DeadPixelModel  preset / calculate&lt;br /&gt;
        DP: Cursor to select dead pixels&lt;br /&gt;
            X 360 position&lt;br /&gt;
            Y 288 position&lt;br /&gt;
            AutoBP: 50  Press C to start automatic dotting&lt;br /&gt;
            Quit?   Number of bad pixels 961&amp;amp; &amp;quot;Blind element replacement in progress&amp;quot; &lt;br /&gt;
            Undo&lt;br /&gt;
        R2: Save data? blind pixel replacement in progress&lt;br /&gt;
        Replace Y   N = DPI, Y = API&lt;br /&gt;
        L:  Reading parameters&lt;br /&gt;
        S:  Saving parameters&lt;br /&gt;
&lt;br /&gt;
	Debug:&lt;br /&gt;
		Rectify N&lt;br /&gt;
        DisplayGary N&lt;br /&gt;
        TE  N   SDT&lt;br /&gt;
        AutoE  ?    ?&lt;br /&gt;
        AutoVS  Y   ?&lt;br /&gt;
        VSGrayH 8000    ?&lt;br /&gt;
        VSGrayL 7000    ?&lt;br /&gt;
        Trends  1   ?&lt;br /&gt;
        Shelter N   ?&lt;br /&gt;
        MotorCon    3   ?&lt;br /&gt;
        SaveFactoryPara:&lt;br /&gt;
        LanTyp  EN  ?   LANGUAGE&lt;br /&gt;
        DisplayMT   N   ?&lt;br /&gt;
        Version:&lt;br /&gt;
            PRJ D843 DLD640 ImLib, MTlib, Soft, Logic&lt;br /&gt;
&lt;br /&gt;
    BackDispose:&lt;br /&gt;
        GammaEN 1   ?&lt;br /&gt;
        Gain    1   ?&lt;br /&gt;
        EnhanceEN   0   ?&lt;br /&gt;
        Hpf_shift   5   ?&lt;br /&gt;
        Hpf_thrd    5   ?&lt;br /&gt;
        Enhance 150 ? &lt;br /&gt;
        Lthrd   50  ?&lt;br /&gt;
&lt;br /&gt;
    Monitor: &lt;br /&gt;
        Opens normal OSD&lt;br /&gt;
&lt;br /&gt;
    Alarm Set:&lt;br /&gt;
        Alarm Switch    Y   ?&lt;br /&gt;
        Alarm Mode  No  ?&lt;br /&gt;
        Alarm Temperature   -1°C  ?&lt;br /&gt;
        Reference Gray  Y   ?&lt;br /&gt;
        Alarm Level 1L  ?&lt;br /&gt;
        Alarm Gray  65535   ?&lt;br /&gt;
        Alarm PointNum  0   ?&lt;br /&gt;
        Correct Gray    0   ?&lt;br /&gt;
&lt;br /&gt;
    Thermometry:&lt;br /&gt;
        Data Collection 0   SMS&lt;br /&gt;
        Interval    60  SCP&lt;br /&gt;
        Ammount     90  SCT&lt;br /&gt;
        Environment normal  ?&lt;br /&gt;
        Export:&lt;br /&gt;
            Displays: Exportin normal temperature MTC data&lt;br /&gt;
        Correction  -1  ?&lt;br /&gt;
        Emissivity  100 SEM&lt;br /&gt;
        Calibration:&lt;br /&gt;
            BlackbodyTemp -10   grabs a val from GAB&lt;br /&gt;
            Save    CBS&lt;br /&gt;
            Cancel  CBD&lt;br /&gt;
        Correction:&lt;br /&gt;
            BlackbodyTemp   -10 Picks from the list GAR&lt;br /&gt;
            Grayscale   0   SLG&lt;br /&gt;
            Confirm LRE&lt;br /&gt;
            Save    LRS&lt;br /&gt;
            Cancel  LRD&lt;br /&gt;
        Ninepoint measurement   SMV&lt;br /&gt;
&lt;br /&gt;
    Cursor:&lt;br /&gt;
        Cur N   DRC enable cursor&lt;br /&gt;
        X   320 SRP set cursor position&lt;br /&gt;
        Y   240 SRP set cursor position&lt;br /&gt;
&lt;br /&gt;
Main:&lt;br /&gt;
    Auto    2   SAG Auto gain&lt;br /&gt;
    G   211 SVC &lt;br /&gt;
    B   10002   SVB &lt;br /&gt;
    P   1   SWP polarity&lt;br /&gt;
    Z   X   ?&lt;br /&gt;
    Settings:&lt;br /&gt;
        Image:&lt;br /&gt;
            FI  Y  ? &lt;br /&gt;
            FIValue 0 ? 0-31&lt;br /&gt;
            Freeze  N   Freeze image&lt;br /&gt;
            VE  N   SMR Vertical flip&lt;br /&gt;
            HO  N   SMR Horizontal flip&lt;br /&gt;
            AutoCali    L   same as rectify?&lt;br /&gt;
            O   IN  internal or external&lt;br /&gt;
        COM:&lt;br /&gt;
            Opens UART menu&lt;br /&gt;
        AvoidburnSwitch OFF enable anti burn in&lt;br /&gt;
        ProtectedTime   10S duration&lt;br /&gt;
        AvoidBurn   16383   threshold to trigger antiburn&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Automatic Dead Pixels and Flatmap Correction===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It it possible to generate a new flat and dead pixel map automatically within the camera after flashing the cleared-map binary.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Dead pixels:=====&lt;br /&gt;
In the secret menu &amp;quot;System&amp;quot;, navigate to Debug - DeadPixelModel and set the AutoBP (sensitivity for auto bad pixel finding) to 50 or so. Then press C Button &lt;br /&gt;
&lt;br /&gt;
This can take a while. &lt;br /&gt;
[[File:High emissivity peltier black body.jpg|thumb|A Peltier / TEC coated in Candle sood to give it a high emissivity surface. It can be cooled or heated by swapping the polarity. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afterwards, navigate to S: and press + to save it.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Flatfield:=====&lt;br /&gt;
In the Debug menu navigate to R2 and press +&lt;br /&gt;
&lt;br /&gt;
now place something '''Cold''' infront of the Camera with a high emissivity. (it needs to fill the whole image)&lt;br /&gt;
&lt;br /&gt;
Press '''+''', wait for it to says High temperature.&lt;br /&gt;
&lt;br /&gt;
Place something '''Hot''' infront and press '''+''' again.  &lt;br /&gt;
&lt;br /&gt;
Afterwards, save with '''S:''' again. &lt;br /&gt;
&lt;br /&gt;
Flatfield should now be calibrated&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;Note: Not all dead pixels are auto detected or get removed. you can manually select them in the Dead pixel editor but its not known yet how to select and correct them.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using the Thermal module without Webserver==&lt;br /&gt;
[[File:Dali D8X3C Breakout board.png|thumb|Breakout board for D8X3C ]]&lt;br /&gt;
to use it, you need to interface it directly.&lt;br /&gt;
&lt;br /&gt;
The module will put out a Analog video signal (High-Z, need to buffer when connecting it to 75Ohm Video input)&lt;br /&gt;
&lt;br /&gt;
Also a  8 or 14bit or  8-bit BT656 output.&lt;br /&gt;
&lt;br /&gt;
the header exposes the FPGA JTAG and UART aswell as 6 Hardware Keys to Navigate the OSD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To interface, you need to make a adapter board with a DF12-50DS-0.5V(86) Docking socket.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The breakout board files can be found [https://oshwlab.com/gamerpaddy/d8x3c-backpack-usb here] but may be still work in progress.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Sensor Pinout==&lt;br /&gt;
[[File:Dali DLD384 Sensor pinout basic.png|thumb|basic pinout of a DLD384]]&lt;br /&gt;
the pinout of the thermal sensor havent been analyzes much.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
besides some supply and Control voltages, 3 clocks and a analog output have been found.  &lt;br /&gt;
&lt;br /&gt;
17.5MHz&lt;br /&gt;
&lt;br /&gt;
14.705KHz&lt;br /&gt;
&lt;br /&gt;
50Hz&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gamerpaddy</name></author>
		
	</entry>
</feed>