---
title: Digitaltwin
---
# Digital Twin Sensor Project
**By Josef Dyrelöv (jd223eu)**
## Introduction
This project uses two sensors:
- One that measures temperature and humidity
- One that measures light intensity (photon sensor)
The data from these sensors is sent to a digital twin platform, where it is visualized in real time within a 3D model. This demonstrates how simple IoT setups can bring real-world sensor data into virtual environments for monitoring and analysis.
## Estimated Time
Depending on your experience and how advanced you want the setup to be, this project can take:
- **1 day** – for a basic proof of concept
- **Up to several weeks** – for a polished and integrated solution
# Objective
Digital twin (DT) is something that has been around for some time and is used in several fields. It is now becoming more relevant in the construction industry – in all stages, but especially in facility management.
The reason I made this project is:
- to learn more about digital twins
- to learn how to use the Tandem (Autodesk) platform
- to see how well it can scale
I have briefly worked with Azure's DT platform, but in my opinion, it was not the most user-friendly or optimal platform. It could not handle large models, and the data from 3D models did not transfer properly into the DT. Because of that, I wanted to try Tandem.
By connecting a sensor and testing how it works, I hope to get a better idea of how digital twins function in practice. Hopefully, this will help me understand both the limitations and possibilities of using sensors together with DTs.
# Materials
I bought the starter kit from Electrokit:
<https://www.electrokit.com/lnu-starter> — **349 SEK**
## Components used
- **Raspberry Pi Pico WH** — microcontroller used in the project — **99 SEK**
- **Breadboard** (840 tie-points) — for connecting components — **69 SEK**
- **DHT11** Digital temperature and humidity sensor — **49 SEK**
- **CdS Photoresistor** (4–7 kΩ) — **9.50 SEK**
- **LEDs** — **10pcs 45kr**:
- Green 5mm, diffuse, 80 mcd
- Yellow 5mm, diffuse, 1500 mcd
- Red 5mm, diffuse, 1500 mcd
- **13× Jumper wires** (male to male) **40pcs 55 SEK***
- **4× Carbon film resistors** (3x 330Ω 1x 4,7Ω) — **10pcs 10kr**
- **USB cable** (USB-A to micro USB-B, 1.8 m) — **39 SEK**
(All prices are from Electrokit)
## Short descriptions
| Component | Purpose |
|------------------------|---------------------------------------------------|
| Raspberry Pi Pico WH | Main microcontroller used to run the program |
| DHT11 sensor | Measures temperature and humidity |
| CdS Photoresistor | Measures light intensity (brightness) |
| LEDs (green/yellow/red)| Provide visual feedback based on sensor readings |
| Resistors & wires | For correct current/voltage and connections |
| Breadboard | Easy prototyping without soldering |
| USB cable | Powers the Pico and enables data upload |
The Raspberry Pi Pico WH was mounted to a breadboard. The DHT11 sensor was used to measure temperature and humidity levels, while the photoresistor was used to measure light intensity. The three LEDs were used as warning indicators depending on whether the temperature was too low or too high.
## Power and Current Calculations
To make sure the Raspberry Pi Pico WH can handle the connected components, I estimated the current and power usage:
### Components:
| Component | Estimated Current (mA) | Voltage (V) | Power (mW) |
|--------------------|------------------------|-------------|------------|
| DHT11 sensor | 0.5 mA | 3.3 V | 1.65 mW |
| Photoresistor (LDR)| ~0.1 mA (via divider) | 3.3 V | 0.33 mW |
| Green LED | 5–10 mA (with 220Ω) | 3.3 V | ~26 mW |
| Yellow LED | 5–10 mA (with 220Ω) | 3.3 V | ~26 mW |
| Red LED | 5–10 mA (with 220Ω) | 3.3 V | ~26 mW |
> Note: Only one LED is active at a time based on temperature.
### Total estimated current:
- Max draw ≈ **11–15 mA** (DHT11 + LDR + 1 active LED)
### Pico WH capabilities:
- The 3.3V output pin can supply up to **300 mA**
- Our total usage is **well below that**, so no external power source is needed
This confirms that the current setup is safe and stable for the Pico.
## Computer Setup
I used **Visual Studio Code (VS Code)** to program the Raspberry Pi Pico WH.
You can download VS Code here:
[https://code.visualstudio.com/](https://code.visualstudio.com/)
I chose VS Code because I already had it installed and was familiar with the environment,
which made it easier and faster to get started.
## Install Node.js
**Node.js** is required for the PyMakr extension, which simplifies communication between your computer and the Raspberry Pi Pico.
You can download Node.js from the official website:
[https://nodejs.org/](https://nodejs.org/)
## Extensions used:
- **PyMakr**
- **Python**
I installed these from the **Extensions** tab inside VS Code.
Then I connected the Pico to my computer using a USB cable.
---
## Flashing MicroPython
To install MicroPython on the Pico:
1. **Hold down the BOOTSEL button** on the Pico.
2. While holding it, **plug in the USB cable** to your computer.
3. A new device will appear in your file explorer (like a USB drive, often next to C:).
4. Go to [https://micropython.org/download/rp2-pico-w/](https://micropython.org/download/rp2-pico-w/)
5. Download the `.uf2` file.
6. Drag and drop the `.uf2` file to the Pico drive.
7. After that, the Pico will reboot and the drive will disappear.
8. Now it's ready to be used in VS Code with PyMakr.
## Project folder structure:
boot.py
main.py
Lib/
└── Library.py
pymakr.conf
keys.py
After flashing, i connected the devise via PyMakr by pressing the lightning symbol next to the device
I created a project folder with the structure above.
I used PyMakr to upload the files to the Pico and run the code.
No extra drivers or installations (like Node.js) were needed in my case.
# Raspberry Pi Pico Setup


(a beautifull picture of my Pico)
## DHT11 (Temperature and Humidity Sensor)
- Leftmost pin → **GP27** (signal)
- Middle pin → **3V3(OUT)** (power)
- Right pin → **GND** (ground)
## CdS Photoresistor (Light Sensor)
- One leg → **3V3(OUT)**
- Other leg → **GND** through a **4.7 kΩ resistor**
- A wire is connected between the resistor and the CdS → **GP26** (signal pin)
→ This forms a voltage divider, allowing analog reading based on light level.
## LED lights (Red, Yellow, Green)
- **Short leg** (cathode) → **GND**
- **Long leg** (anode) → connected to a **GPIO pin (GP22, GP21, GP20)** via a **220 Ω resistor**
## Power Considerations
- All components run at 3.3V, which is safe for the Raspberry Pi Pico.
- DHT11 draws around 0.5 mA during measurement.
- Each LED draws about 5–10 mA with 220 Ω resistors.
- **Total current** is well below the Pico’s maximum output current (typ. 300 mA on 3V3).
## Production Readiness
This setup is mostly for development or prototyping. It works well for learning, testing, and building a proof-of-concept.
For production use, you would typically:
- Replace the breadboard with a soldered PCB
- Add a casing for protection
- Use more robust connectors
- Possibly choose higher-precision sensors depending on your needs
# Platform
As platform I used **Tandem**, developed by Autodesk. There were several reasons for this choice:
- I built the 3D model in Revit (also Autodesk)
- I had previously tried Azure Digital Twins, but it does not provide the same type of integration between 3D model and data
- I wanted to try something new and more tailored for the AEC (architecture, engineering, construction) industry
Tandem is meant to provide everything you need to create a digital twin – from data linking, to visualization, to managing systems like HVAC, lighting, or sensors.

The screenshot above is from a demo building you can explore in Tandem. In this demo, 46 different types of connections are already prepared, including:
- Heat recovery units
- Ventilation systems
- Water systems
- Regular temperature and humidity sensors
Unlike Azure DT, Tandem keeps the object properties from the Revit model (e.g., a wall is still a wall with thickness, materials, etc., not just a line). This is important for facility management and future automation.
Tandem is a cloud-based platform, and at the time of writing it is available for free in preview mode. If the project were to be scaled up:
- It could handle many sensors per room
- Data from one system (e.g., temperature) could be used to adjust another (e.g., ventilation or heating)
- Could in theory improve energy use and comfort for people living or working in the building
# The Code
The coding part was not that hard — I followed the guides for the specific sensors I was using.
```python
from machine import ADC, Pin, WDT
import dht
tempSensor = dht.DHT11(Pin(27)) # DHT11 connected to GP27
ldrSensor = ADC(Pin(26)) # LDR (photoresistor) connected to GP26
led_green = Pin(13, Pin.OUT) # Green LED
led_yellow = Pin(14, Pin.OUT) # Yellow LED
led_red = Pin(15, Pin.OUT) # Red LED
```
The LEDs are controlled using a simple `if` statement.
For example: if the temperature is lower or higher than 20°C, a specific LED turns on.
## Wi-Fi Setup
To connect the Raspberry Pi Pico WH to Wi-Fi, I used a simple script in `boot.py`.
The connection is handled at startup so the device is always connected before sending data.
### `boot.py` (simplified)
```python
import network
import keys
def connect():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.config(pm=0xa11140) # Keep Wi-Fi active during sleep
wlan.connect(keys.WIFI_SSID, keys.WIFI_PASS)
...
```
### `keys.py`
To keep passwords out of the main code, I stored them in a separate file:
```python
WIFI_SSID = "YourNetworkName"
WIFI_PASS = "YourPassword"
```
Using a `keys.py` file makes the code cleaner and more secure if shared or uploaded to GitHub.
## Watchdog Timer and Memory Handling
I had some problems with the device stopping randomly.
To solve that, I used a **Watchdog Timer (WDT)** and freed up memory using `gc.collect()`, because I kept getting the error:
```[Errno 12] ENOMEM```
so i added the following:
```python
from machine import WDT
import gc
import time
# Restart if not fed within 8.388 seconds
wdt = WDT(timeout=8388)
# Inside main loop
wdt.feed() # feed the watchdog
gc.collect() # free up memory
time.sleep(5) # wait 5 seconds before next reading
```
At the start of the code, I initialize the WDT.
At the end of each loop, I call `wdt.feed()` to show that the program is still running.
If that doesn't happen within 8 seconds, the device restarts.
This made the program much more stable.
`gc.collect()` also helped to prevent the memory error by cleaning up unused memory.
# Transmitting Data
To send the sensor data, I used an **HTTPS POST request** to the Tandem API.

The image above shows a simplified flow:
- Sensor → Gateway → Tandem
- The data is sent using HTTPS (with authentication token in header)
---
## Problem: URL too long
When I copied the full URL from the **Connections** tab in Tandem...

...the string was too long and caused errors on the device.
To solve this, I **split the token and the URL** into parts and passed the token separately in the headers.
---
## Example: Sending humidity data
```python
import urequests
import ubinascii
# Token provided by Tandem
HUMIDITY_TOKEN = "0-tpd_ITRgmmbXyh61yp8g"
# Stream URL for this sensor
HUMIDITY_URL = "https://eu.tandem.autodesk.com/api/v1/timeseries/models/urn:adsk.dtm:s8OFJkPpRvKYsvpUlTEqSg/streams/AQAAAJVSxjXd6EufooUif3xlckwAAAAA"
# Create header with encoded token
def make_headers(token):
encoded = ubinascii.b2a_base64(b":" + token.encode()).strip().decode()
return {
"Authorization": "Basic " + encoded,
"Content-Type": "application/json"
}
HUMIDITY_HEADERS = make_headers(HUMIDITY_TOKEN)
# Send humidity value as JSON
response = urequests.post(HUMIDITY_URL, json={"humidity": humidity}, headers=HUMIDITY_HEADERS)
response.close()
```
I used a similar setup for temperature and light sensors, but with different URLs and tokens.
I solved hot to do it with the help on this site https://aps.autodesk.com/blog/adding-iot-sensors-tandem-api
### Code Summary
**Token**
Your personal key from Tandem that authenticates your device.
Used to prove that you are allowed to send data.
**Stream URL**
The full address to the specific stream in Tandem where your data will be sent.
Each sensor has its own unique URL.
**Header**
This is where the token is encoded (Base64) and added to the HTTP headers.
Tandem expects this for security and authorization.
**JSON**
The actual sensor data is sent as a JSON object.
In the example, it sends the humidity reading.
## Communication and Protocol Choices
**How often is the data sent?**
Every 5 seconds, controlled by a delay in the main loop.
**Wireless protocol used:**
Wi-Fi (built-in support in the Raspberry Pi Pico WH)
**Transport protocol:**
HTTPS over Wi-Fi — no MQTT or WebSocket used in this project.
**Why HTTPS?**
Because Tandem's API uses REST over HTTPS, and security is important when sending sensor data to the cloud.
## Scaling Considerations and Design Choices
If this project were to be scaled up, smaller and more accurate sensors would probably be needed.
The current setup with Wi-Fi and relatively simple sensors works well for testing or if you're only using one device.
However, Wi-Fi has some downsides when it comes to larger installations:
- **Range**: Wi-Fi typically covers only 10–50 meters indoors, which can be a problem on large construction sites or in buildings with thick walls.
- **Power consumption**: Wi-Fi uses more energy than other wireless protocols, making it less ideal for battery-powered sensors that need to run for a long time.
Because of this, a protocol like **LoRaWAN** could be a better choice when scaling up.
LoRa offers:
- Much longer range (up to several kilometers)
- Very low power usage, ideal for battery-powered sensors
- But it has lower data rates and requires more setup (e.g., gateways)
In a real-world construction project, you might not have reliable Wi-Fi coverage everywhere.
That's why a low-power, long-range solution like LoRa makes more sense in those environments.
Also, sensors in a construction environment would need to be more rugged, resistant to dust, moisture, and rough handling.
So if the system was to be used during the construction phase, you’d need robust, weather-resistant sensors and a communication system that can handle that kind of environment.
In summary:
- Wi-Fi is easy to use for development
- But for scaling, both the hardware and communication protocol would likely need to be upgraded
In the construction phase, it can be difficult to have stable Wi-Fi coverage everywhere.
In that case, a wireless protocol with longer range (like LoRa) and sensors that can survive rough environments would be a better choice.
# Presenting the Data
To present the data, I used Autodesk Revit to create a digital model of a building, and then visualized the sensor values in Autodesk Tandem.
---
## 1. Building the model in Revit
I first created a simple building using the tools under the **Architecture** tab in Revit:

You can make it very simple (just a box for testing), or create a more complete building layout.
Example layout:

---
## 2. Prepare for data mapping
There are two important steps to make data mapping possible later in Tandem:
### a) Assign rooms
Use the Room (can be seen in the image above) tool in Revit to define enclosed areas. This is important for heatmaps and room-based data later.
### b) Create a sensor object
Use **Model In-Place** under *Component*:

Choose the category **Generic Models**(i did this).
---
## 3. Add shared parameters
To connect your real sensor data to the model, you need to create **shared parameters** in Revit:
1. Go to **Manage > Shared Parameters**
2. Create a group (e.g. "SensorData")
3. Add parameters like:
- `Temperature`
- `Humidity`
- `LightLevel`
Each should have the data type **Number**.

Next, go to **Project Parameters** and assign the parameters to your model:

Make sure:
- You choose **Shared Parameter**
- You select the category `Generic Models`
Now you can place the sensor model where it belongs in your building and export the file as `.rvt`.
---
## 4. Import to Tandem
- Create a free account at [Tandem](https://intandem.autodesk.com)
- Create a new project
- Upload your `.rvt` file
OR use a demo model provided by Tandem
---
## 5. Classification and sensor connections
To get the data flowing, you need to configure classification codes (if they dont already exist).
In Tandem:
1. Go to **Manage > Classification Systems**
2. Download the XLSX template
3. Add custom codes like:
- `C.Temp` — Temperatursensor
- `C.Humidity` — Luftfuktighetssensor
- `C.Light` — Ljussensor
4. Re-upload the edited classification system

---
## 6. Create and configure connections
Once sensors and classification are set:
- Go to the **Connections** tab
- Press **+ Create** to add a new connection
- Name it and assign the correct classification
Example:

Then map your sensor’s payload to the Tandem parameter:

---
## Database & Storage
Tandem also functions as the time series database. All sensor data is sent using the REST API and stored automatically.
- In the free version, data is stored for up to 14 days.
(Longer storage requires a subscription.)
- Data is sent and saved every 5 seconds as long as the device is online.
- When viewing data in the dashboard, values are shown as averages over 15-minute intervals (or other selected time spans).
---
## Automation and Triggers
Tandem currently supports basic visualization and threshold alerts, but not full automation (like triggering systems based on data).
However, Tandem allows you to:
- Set warning or alert thresholds
→ Useful for identifying critical values in the time series
- Use webhooks or external scripts to build further automation
For example, here is a temperature chart showing a warning when the temperature reaches 26 °C:

This makes it possible to use Tandem not only for passive monitoring, but also as part of a smarter, more responsive building system, especially when combined with external platforms.
## Visualisation
Here are some ways to visualize the data in Tandem.
### Graphs
You can view time series data for each sensor using built-in graph widgets.

(the long line is when the Wi-Fi went down and i forgot to turn the sensor back on)
In this example, the temperature data shows:
- Average over a 15-minute window
- Min/Max for the same period
- Threshold lines that trigger warnings or alerts
---
### Heatmap View
Tandem also supports heatmap overlays on the 3D model.
This is useful for:
- Visualizing for example light level, temperature, or humidity
- Understanding spatial distribution across rooms
#### Light Level Heatmap (example)
Heatmap shows different color zones based on light intensity:
- **92% light**

- **77%**

- **7%**

Changing color gradients manually is difficult, but built-in presets can still give a good overview. This feature can also be used for temperature and humidity, although it is harder to interpret visually in those cases.
---
### Dashboard View
You can also track sensor streams directly in the dashboard table.

It includes:
- Latest reading per sensor
- Threshold values
- Room and model placement
# Conclusion
## What Could Have Been Improved
- Spend more time choosing sensors, and maybe invest in more of them
- Add better error handling for network failures and sensor issues
- Consider designing a custom PCB instead of using a breadboard, to make the project more stable and portable
## Final Thoughts
This was a really fun project and I learned a lot.
I’ll probably buy some additional sensors and connect more rooms, perhaps add a carbon monoxide sensor or a more accurate temperature sensor.
Tandem was really nice to work with. From what I’ve seen so far, it was fairly simple to use, but I’d like to dive deeper into what more the platform can do, especially in terms of automation, data analysis, and system integration.
Printing a case for the device is probably also a good idea, it would look better and offer protection against dust and damage.