Hi all,

Back in 2021 I started off with a very simple Heltec wifi kit 32 and since then I’ve been happily logging data using DIDAP (old method EXP1) protocol with a very basic interface and switched over to an Espressif ESP32 wrover-e with a little more advanced version thanks to it’s hardware specs and possibilities (e.g ADC counters) for actual moving average calculations.

Since the beginning of this year (2026) I’ve completely reworked my code with the help of AI, at first I was weary like many but I must admit it fixed a lot of issues I knew I had (and even those I didn’t). As for many it’s a juggle between family, work, home(work), renovations and mostly time. So I embraced it to help me better achieve my goals.

It improved hugely my dash-boarding, logging, math formulas, API integrations and even GM tube configurations. I was able to finally and correctly implement per tube data specifics such as the voltage ranges, uSv/h, (easy) and the most difficult of all the tube dead times.

My main station:

https://www.uradmonitor.com/tools/dashboard-09/?open=13000212

Radu asked me to create a little write up on how you can use the DIDAP (EXP) protocol yourselves

1. First things first:

You will need to get your account and credentials to be able to upload, without this you can’t do squat.

  1. So first you’ll need to create an account -> https://www.uradmonitor.com/dashboard/
  2. Then go to the API tab and copy your
    1. User ID
    2. User Key (API hash)
  3. Then back when I started I had help from a friend that showed me how to use Fiddler to get the API call set up and retrieve the X-* headers. This is still an option but tedious if you don’t know how to use Fiddler or curl. So I created a little Windows PowerShell script with a GUI that allows you some options to view, create, consult your account and device(s).

Link -> A small PowerShell helper for the uRADMonitor REST API

Once you’ve created your station. Contact uRADMonitoring support to add it your dashboard if it’s not showing, make sure to write down your station ID!

2. How the DIDAP (EXP) protocol is set up and used

Format example

The referenced ESP32 firmware builds a POST request like this:

https://data.uradmonitor.com/api/v1/upload/exp
/01/<unix-time>
/02/<temperature>
/03/<pressure>
/04/<humidity>


The values are then encoded in the URL path as alternating pairs:

/<field-id1>/<value1>/<field-id>2/<value3>/...

The request uses these X-headers:

X-User-id: <uRADMonitor user ID>
X-User-hash: <uRADMonitor user key>
X-Device-id: <assigned device ID>

The request body is empty. The firmware performs the upload approximately every 61 seconds after collecting a snapshot of its sensor values.

Example of a full post:

POST https://data.uradmonitor.com/api/v1/upload/exp/01/1756800000/02/22.5/03/101325/04/48.2/05/350/06/125000/07/620/08/0.012/09/8/0B/145/0C/410/0D/48/0E/107/0F/124/10/0x3/12/5/13/12
X-User-id: 12345
X-User-hash: <user-key>
X-Device-id: 13001234

Some EXP fields used by the firmware

The following table reflects the field IDs implemented in my code -> Environmental_Stationary_Logger_V1.4.ino.

Field IDMeasurementValue expectedUnit / formatStatus in firmware
01Local timeUnix timestampSeconds since Unix epochMandatory
02TemperatureDecimal number°CUploaded
03Barometric pressureDecimal numberPaUploaded
04Relative humidityDecimal number% RHUploaded
05LuminosityInteger/decimalFirmware displays lux; source comment describes relative luminosityUploaded
06VOC / gas resistanceDecimal numberOhmsUploaded
07Carbon dioxideDecimal numberppmUploaded
08FormaldehydeDecimal numberppmUploaded
09PM2.5Integer/decimalµg/m³Uploaded
0ABattery voltageDecimal numberVCommented out in firmware
0BRadiationInteger/decimalCPMUploaded
0CGeiger tube high voltageDecimal numberVUploaded
0DHV drive indexDecimal number%Uploaded; estimated from tube voltage
0EHardware versionIntegerFirmware-defined version numberUploaded as 107
0FSoftware versionIntegerFirmware-defined version numberUploaded as 124
10Tube IDIdentifierFirmware-defined value, e.g. 0x3Uploaded
11NoiseDecimal numberdBCommented out in firmware
12PM1.0Integer/decimalµg/m³Uploaded
13PM10Integer/decimalµg/m³Uploaded
14OzoneDecimal numberppbCommented out in firmware

For ESP32 the below ino sketch example could be used as a starting point

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>

const char *WIFI_SSID = "YOUR_WIFI_SSID";
const char *WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char *USER_ID = "YOUR_USER_ID";
const char *USER_KEY = "YOUR_USER_KEY";
const char *DEVICE_ID = "YOUR_DEVICE_ID";

unsigned long wifiConnectedAt = 0;
bool uploadSent = false;

void setup()
{
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print('.');
  }

  wifiConnectedAt = millis();
  Serial.println("\nWi-Fi connected; upload will run after 61 seconds.");
}

void loop()
{
  if (!uploadSent && (millis() - wifiConnectedAt >= 61000UL))
  {
    WiFiClientSecure tlsClient;
    tlsClient.setInsecure(); // Test only; use the server CA certificate in production.

    const char *uploadUrl =
      "https://data.uradmonitor.com/api/v1/upload/exp/"
      "01/1788355200/02/22.5/03/101325/04/48.2/05/350/06/125000/"
      "07/620/08/0.012/09/8/0B/145/0C/410/0D/48/0E/107/0F/124/"
      "10/0x3/12/5/13/12";

    HTTPClient http;
    http.begin(tlsClient, uploadUrl);
    http.addHeader("X-User-id", USER_ID);
    http.addHeader("X-User-hash", USER_KEY);
    http.addHeader("X-Device-id", DEVICE_ID);

    int statusCode = http.POST("");
    Serial.printf("uRADMonitor upload status: %d\n", statusCode);
    Serial.println(http.getString());
    http.end();
    uploadSent = true;
  }
}

Sources:

Happy logging!