diff --git a/docs/src/api.md b/docs/src/api.md
index b7165da8a80e3949ff12c466cbf454bd38e59d0d..ff63f9b30520e01b7a4b0eb22be82a26c9443eac 100644
--- a/docs/src/api.md
+++ b/docs/src/api.md
@@ -49,8 +49,9 @@ flush
 PMT
 DetectorModule
 Detector
-getmodule
 modules
+getmodule
+getpmt
 write(::AbstractString, ::Detector)
 write(::IO, ::Detector)
 Hydrophone
diff --git a/docs/src/manual/detector.md b/docs/src/manual/detector.md
index d9d1e4f6ac89d3db4a40e0a407792e846b891611..3a1f20971c849d4ac904501b5a0bc8953e2d3e93 100644
--- a/docs/src/manual/detector.md
+++ b/docs/src/manual/detector.md
@@ -70,7 +70,7 @@ is that base modules do not contain PMTs and are always sitting on floor 0.
     "n-th module".
     Accessing modules by their module ID however is the standard use case, see below.
 
-### Modules via "module ID"
+### Detector Modules
 
 Modules have a unique identification number called module ID (sometimes also
 called "DOM ID", where DOM stands for "Digital Optical Module") and we can use
@@ -82,9 +82,39 @@ The `.modules` field is a dictionary which maps the modules to their module IDs:
 det.modules
 ```
 
-To access a module with a given module ID, one can either use this dictionary or
+A flat vector of modules can be obtained with:
+
+```@example 1
+modules(det)
+```
+
+To access a module with a given module ID, one can either use the dictionary or
 index the [`Detector`](@ref) directly
 
 ```@example 1
 detector_module = det[808976933]
 ```
+
+Another, more verbose way is using the `getmodule(d::Detector, detector_id::Integer)` function
+
+```@example 1
+detector_module = getmodule(det, 808976933)
+```
+
+
+### PMTs
+
+Each optical module consists of PMTs, which can be access using the `getpmts(m::DetectorModule)` function:
+
+```@example 1
+getpmts(det)
+```
+
+To access a specific PMT with a given channel ID (TDC ID), use the
+`getpmt(m::DetectorModule, channel_id::Integer)` function. Here, we access the
+PMT at DAQ channel 0 of our previously obtained detector module:
+
+
+```@example 1
+getpmt(detector_module, 0)
+```
diff --git a/src/KM3io.jl b/src/KM3io.jl
index 7c4cfe72b2b9ad54dd02d07264bb3c8b3bbb7e68..9cbf6fd2ff4b6428d21dbcd7b57f3389ba269dce 100644
--- a/src/KM3io.jl
+++ b/src/KM3io.jl
@@ -21,7 +21,7 @@ export ROOTFile
 export H5File, H5CompoundDataset, create_dataset, addmeta
 
 export Direction, Position, UTMPosition, Location, Quaternion, Track, AbstractCalibratedHit
-export Detector, DetectorModule, PMT, Tripod, Hydrophone, center, isbasemodule, getmodule, modules
+export Detector, DetectorModule, PMT, Tripod, Hydrophone, center, isbasemodule, getmodule, modules, getpmt, getpmts
 
 # Acoustics
 export Waveform, AcousticSignal, AcousticsTriggerParameter, piezoenabled, hydrophoneenabled
diff --git a/src/hardware.jl b/src/hardware.jl
index a5d52e1e24affe10adc7f5db74a782d6ac445bc6..ed469bf5f2f1c2071a2c0676a444883b042c7dc9 100644
--- a/src/hardware.jl
+++ b/src/hardware.jl
@@ -62,6 +62,11 @@ The index in this context is the DAQ channel ID of the PMT, which is counting fr
 """
 Base.getindex(d::DetectorModule, i) = d.pmts[i+1]
 isbasemodule(d::DetectorModule) = d.location.floor == 0
+getpmts(d::DetectorModule) = d.pmts
+"""
+Get the PMT for a given DAQ channel ID (TDC)
+"""
+getpmt(d::DetectorModule, channel_id::Integer) = d[channel_id]
 
 """
 
diff --git a/test/hardware.jl b/test/hardware.jl
index df7ceeab7960385ae9e57aceb3e36c145a2371d6..90d839b415de028ae54406e0c61d1fe32479d8fc 100644
--- a/test/hardware.jl
+++ b/test/hardware.jl
@@ -88,6 +88,10 @@ const SAMPLES_DIR = joinpath(@__DIR__, "samples")
             @test 19 == length(d.strings)
 
             @test isapprox([116.60000547853453, 106.95689770873874, 60.463039635848226], d.modules[808992603].pos; atol=0.008)
+
+            @test 78.3430067946102 ≈ getpmt(d[15, 13], 0).pos.x
+            m = d[15, 13]
+            @test m.n_pmts == length(getpmts(m))
         end
 
         comments = Detector(joinpath(SAMPLES_DIR, "v3.detx")).comments