diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 4abc148e95b04a5ef0aab0ee5332601f23703194..378c2d50bba8c75bfc95df776e82a15de6e5631f 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -127,6 +127,7 @@ set(SRCS
   qa/CanvasConfig.cxx
   qa/PadConfig.cxx
   qa/QaData.cxx
+  qa/unpack/StsDigiQa.cxx
   ca/TrackingChain.cxx
   ca/qa/CaInputQa.cxx
   ca/qa/CaOutputQa.cxx
@@ -163,6 +164,7 @@ target_include_directories(Algo
          ${CMAKE_CURRENT_SOURCE_DIR}/detectors
          ${CMAKE_CURRENT_SOURCE_DIR}/qa
          ${CMAKE_CURRENT_SOURCE_DIR}/ca
+         ${CMAKE_CURRENT_SOURCE_DIR}/qa/unpack
          ${CMAKE_CURRENT_SOURCE_DIR}/ca/qa
          ${CMAKE_CURRENT_SOURCE_DIR}
          ${CMAKE_SOURCE_DIR}/core/data/global
@@ -226,6 +228,8 @@ install(DIRECTORY detectors/tof TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/trd TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/trd2d TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY ca/qa TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
+install(DIRECTORY qa TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
+install(DIRECTORY qa/unpack TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY ca TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 
 
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 9ad4d2d9a054e46d6aa09251866e822df5da87de..22f753ef4624c5e595241f4d71a9dcf6edb38600 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -8,6 +8,7 @@
 #include "EventbuildChain.h"
 #include "Exceptions.h"
 #include "HistogramSender.h"
+#include "StsDigiQa.h"
 #include "bmon/Unpack.h"
 #include "ca/TrackingChain.h"
 #include "compat/OpenMP.h"
@@ -115,6 +116,11 @@ void Reco::Init(const Options& opts)
     auto walkMap = yaml::ReadFromFile<sts::WalkMap>(Opts().ParamsDir() / "StsWalkMap.yaml");
     sts::Unpack::Config cfg{.readout = readout, .walkMap = walkMap};
     fStsUnpack = std::make_unique<sts::Unpack>(cfg);
+    if (fSender != nullptr) {
+      fStsDigiQa = std::make_unique<sts::DigiQa>(fSender);
+      fStsDigiQa->RegisterReadoutSetup(readoutSetup);
+      fStsDigiQa->Init();
+    }
   }
 
   if (Opts().Has(Subsystem::TOF) && Opts().Has(Step::Unpack)) {
@@ -214,6 +220,12 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       QueueEvbuildMetrics(evbuildMonitor);
     }
 
+    // --- Raw digi QAs
+    if (fSender != nullptr && Opts().Has(Subsystem::STS) && Opts().Has(Step::Unpack)) {
+      fStsDigiQa->RegisterDigiData(&digis.fSts);
+      fStsDigiQa->Exec();
+    }
+
     sts::HitfinderMonitor stsHitfinderMonitor;
     PartitionedSpan<sts::Hit> stsHits;
     PartitionedVector<sts::Cluster> stsClusters;
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index 6846544a888e080c1812cecf6e55ffe70474fa7b..9a193027e82724331cce58bcbd22bf066c2f912d 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -45,6 +45,7 @@ namespace cbm::algo
   namespace sts
   {
     class Unpack;
+    class DigiQa;
   }
 
   namespace tof
@@ -121,6 +122,7 @@ namespace cbm::algo
 
     // STS
     std::unique_ptr<sts::Unpack> fStsUnpack;
+    std::unique_ptr<sts::DigiQa> fStsDigiQa;  ///< Raw STS-digis QA
     sts::HitfinderChain fStsHitFinder;
 
     // TOF
diff --git a/algo/qa/CanvasConfig.cxx b/algo/qa/CanvasConfig.cxx
index e0c3e3cb5973643afb63c5e18d989f4be5eec026..653e33c5471da955d5d468caf3d7abc086d347bd 100644
--- a/algo/qa/CanvasConfig.cxx
+++ b/algo/qa/CanvasConfig.cxx
@@ -60,5 +60,5 @@ std::string CanvasConfig::ToString() const
       cfg << ';' << padCfg;
     }
   }
-  return cfg.str();
+  return cfg.str() + ";";
 }
diff --git a/algo/qa/QaData.cxx b/algo/qa/QaData.cxx
index da0ed39365552dfb17946660ee03581cc6615d83..7ab94afa0fe4d54277b194b087a8237d2b864fa5 100644
--- a/algo/qa/QaData.cxx
+++ b/algo/qa/QaData.cxx
@@ -68,6 +68,6 @@ void Data::Send(std::shared_ptr<HistogramSender> histoSender)
   auto nP1 = std::distance(fHistograms.fvP1.begin(), fHistograms.fvP1.end());
   auto nP2 = std::distance(fHistograms.fvP2.begin(), fHistograms.fvP2.end());
   L_(info) << fsName << ": Published " << nH1 << " 1D- and " << nH2 << " 2D-histograms, " << nP1 << " 1D- and " << nP2
-           << "2D-profiles";
+           << " 2D-profiles";
   this->Reset();
 }
diff --git a/algo/qa/unpack/QaBase.h b/algo/qa/unpack/QaBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8b2e1a05b4984c0a4164414f565d38c65f4740d
--- /dev/null
+++ b/algo/qa/unpack/QaBase.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   QaBase.h
+/// \date   03.03.2024
+/// \brief  Base class for digi QA (header)
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "Definitions.h"
+#include "PODVector.h"
+#include "QaData.h"
+
+#include <string>
+
+namespace cbm::algo::sts
+{
+  /// \class  cbm::algo::sts::QaBase
+  /// \brief  QA module for STS raw digis
+  /// \tparam Digi          A digi class for a given detector subsystem
+  /// \tparam ReadoutSetup  A read-out config for a given detector subsystem
+  template<class Digi, class ReadoutSetup>
+  class QaBase {
+   public:
+    /// \brief Constructor
+    /// \param pSender  Pointer to histogram sender
+    /// \param dirname
+    QaBase(std::shared_ptr<HistogramSender> pSender, const std::string& dirname) : fQaData(dirname), fpSender(pSender)
+    {
+    }
+
+    /// \brief Default constructor
+    QaBase() = delete;
+
+    /// \brief Copy constructor
+    QaBase(const QaBase&) = delete;
+
+    /// \brief Move constructor
+    QaBase(QaBase&&) = delete;
+
+    /// \brief Copy assignment operator
+    QaBase& operator=(const QaBase&) = delete;
+
+    /// \brief Move assignment operator
+    QaBase& operator=(QaBase&&) = delete;
+
+    /// \brief Checks, if the histogram sender is defined
+    bool IsSenderDefined() const { return static_cast<bool>(fpSender.get()); }
+
+    /// \brief Register digi-qa data
+    void RegisterDigiData(const PODVector<Digi>* pvDigis) { fpvDigis = pvDigis; }
+
+    /// \brief Register read-out setup config
+    void RegisterReadoutSetup(const ReadoutSetup& setup) { fpReadoutSetup = std::make_shared<ReadoutSetup>(setup); }
+
+   protected:
+    qa::Data fQaData;                                     ///< QA data
+    std::shared_ptr<HistogramSender> fpSender = nullptr;  ///< Histogram sender
+
+    std::shared_ptr<ReadoutSetup> fpReadoutSetup = nullptr;  ///< Readout config instance
+    const PODVector<Digi>* fpvDigis              = nullptr;  ///< Digis input
+  };
+}  // namespace cbm::algo::sts
diff --git a/algo/qa/unpack/StsDigiQa.cxx b/algo/qa/unpack/StsDigiQa.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..db9a8b0472a515519d7967311d90c909930cb7dc
--- /dev/null
+++ b/algo/qa/unpack/StsDigiQa.cxx
@@ -0,0 +1,107 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   StsDigiQa.h
+/// \date   03.03.2024
+/// \brief  QA module for STS raw digis (source)
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#include "StsDigiQa.h"
+
+#include "CbmStsAddress.h"
+
+#include <fmt/format.h>
+
+using cbm::algo::sts::DigiQa;
+using fmt::format;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void DigiQa::Init()
+{
+  using cbm::algo::qa::CanvasConfig;
+  using cbm::algo::qa::Data;
+  using cbm::algo::qa::H1D;
+  using cbm::algo::qa::H2D;
+  using cbm::algo::qa::PadConfig;
+
+  if (!fpSender.get()) {
+    return;
+  }
+
+  int nModules = fpReadoutSetup->modules.size();
+
+  // Histograms per address
+  {
+    fvphAddressChannel.resize(nModules);
+    fvphAddressCharge.resize(nModules);
+    fvphAddressChannelCharge.resize(nModules);
+    for (int iM = 0; iM < nModules; ++iM) {
+      int32_t address       = fpReadoutSetup->modules.at(iM).address;
+      fmAddressMap[address] = iM;
+
+      auto cName = format("sts_digi/sts_digi_vs_channel_charge_addr{:#10x}", address);
+      auto cTitl = format("STS digis per channel and charge for module {:#10x}", address);
+      auto canv  = CanvasConfig(cName, cTitl, 3, 1);
+      {
+        auto pad               = PadConfig();
+        auto name              = format("sts_digi_addr{}_channel", iM);
+        auto titl              = format("Number of digis per channel for address {:#10x};channel;N_{{digis}}", address);
+        fvphAddressChannel[iM] = fQaData.MakeObj<H1D>(name, titl, 2048, -0.5, 2047.5);
+        pad.RegisterHistogram(fvphAddressChannel[iM], "hist");
+        canv.AddPadConfig(pad);
+      }
+      {
+        auto pad              = PadConfig();
+        auto name             = format("sts_digi_addr{}_charge", iM);
+        auto titl             = format("STS digi charge for address {:#10x};charge [ADS units];N_{{digis}}", address);
+        fvphAddressCharge[iM] = fQaData.MakeObj<H1D>(name, titl, 32, -0.5, 31.5);
+        pad.RegisterHistogram(fvphAddressCharge[iM], "hist");
+        canv.AddPadConfig(pad);
+      }
+      {
+        auto pad  = PadConfig();
+        auto name = format("sts_digi_addr{}_channel_charge", iM);
+        auto titl = format("STS digi charge for address {:#10x};charge [ADS units];channel", address);
+        fvphAddressChannelCharge[iM] = fQaData.MakeObj<H2D>(name, titl, 32, -0.5, 31.5, 2048, -0.5, 2047.5);
+        pad.RegisterHistogram(fvphAddressChannelCharge[iM], "colz");
+        canv.AddPadConfig(pad);
+      }
+      fQaData.AddCanvasConfig(canv);
+    }
+  }
+
+  fQaData.Init(fpSender);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void DigiQa::Exec()
+{
+  if (!fpSender.get()) {
+    return;
+  }
+
+  // Loop over STS digis and fill histograms
+  for (const auto& digi : (*fpvDigis)) {
+    int32_t address = digi.GetAddress();
+    double channel  = digi.GetChannel();
+    double charge   = digi.GetCharge();
+
+    //  Ensure, that the address is defined
+    auto itHistID = fmAddressMap.find(address);
+    if (itHistID == fmAddressMap.end()) {
+      L_(error) << format("std::DigiQa: found address {:x}, which is not defined in the ReadoutSetup config", address);
+      continue;
+    }
+
+    int iM = itHistID->second;
+    fvphAddressChannel[iM]->Fill(channel);
+    fvphAddressCharge[iM]->Fill(charge);
+    fvphAddressChannelCharge[iM]->Fill(charge, channel);
+  }
+
+
+  fQaData.Send(fpSender);
+}
diff --git a/algo/qa/unpack/StsDigiQa.h b/algo/qa/unpack/StsDigiQa.h
new file mode 100644
index 0000000000000000000000000000000000000000..49780f8d81b984c0e4272660c815360cdf9dbdd8
--- /dev/null
+++ b/algo/qa/unpack/StsDigiQa.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   StsDigiQa.h
+/// \date   03.03.2024
+/// \brief  QA module for STS raw digis (header)
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "CbmStsDigi.h"
+#include "Definitions.h"
+#include "QaBase.h"
+#include "sts/ReadoutConfig.h"
+
+#include <unordered_map>
+#include <vector>
+
+namespace cbm::algo::sts
+{
+  using DigiQaBase = QaBase<CbmStsDigi, ReadoutSetup>;
+
+  /// \class cbm::algo::sts::DigiQa
+  /// \brief QA module for STS raw digis
+  class DigiQa : public DigiQaBase {
+   public:
+    /// \brief Constructor
+    DigiQa(std::shared_ptr<HistogramSender> pSender) : DigiQaBase(pSender, "RawDigi/STS") {}
+
+    /// \brief Default constructor
+    DigiQa() = delete;
+
+    /// \brief Copy constructor
+    DigiQa(const DigiQa&) = delete;
+
+    /// \brief Move constructor
+    DigiQa(DigiQa&&) = delete;
+
+    /// \brief Copy assignment operator
+    DigiQa& operator=(const DigiQa&) = delete;
+
+    /// \brief Move assignment operator
+    DigiQa& operator=(DigiQa&&) = delete;
+
+    /// \brief Executes QA (filling histograms)
+    void Exec();
+
+    /// \brief Initializes QA (initialization of histograms and canvases)
+    void Init();
+
+   private:
+    std::unordered_map<int32_t, int> fmAddressMap;  ///< Map of address to histogram index
+
+    // ---- Histograms
+    std::vector<qa::H1D*> fvphAddressChannel;        ///< hist: digi channel in different sensors
+    std::vector<qa::H1D*> fvphAddressCharge;         ///< hist: digi charge in different sensors
+    std::vector<qa::H2D*> fvphAddressChannelCharge;  ///< hist: digi channel vs. charge in different sensors
+
+    qa::H2D* fvphFebAsic = nullptr;  ///< hist: digi FEB vs ASIC
+  };
+}  // namespace cbm::algo::sts
diff --git a/core/base/utils/flestools/CbmFlesCanvasTools.cxx b/core/base/utils/flestools/CbmFlesCanvasTools.cxx
index fbf6d17f364d838631d1dfd13e6c237f31111828..fb466c9fd77b0cb54153b55640a09972676bc4e5 100644
--- a/core/base/utils/flestools/CbmFlesCanvasTools.cxx
+++ b/core/base/utils/flestools/CbmFlesCanvasTools.cxx
@@ -407,6 +407,7 @@ CanvasConfig ExtractCanvasConfigFromString(std::string sFullConfig)
       std::cerr << "ExtractCanvasConfigFromString => Empty configuration string while " << uPadIdx << " over "
                 << uNbPads << " pads remain! "
                 << "last ones will have default config!" << std::endl;
+      continue;
     }  // if( 0 == sNext.size() )
 
     /// Extract Pad config
diff --git a/services/histserv/app/Application.cxx b/services/histserv/app/Application.cxx
index 2882c42ed7970580f09833fa237dc96d676353c2..a391c615df5650611825e3dfbbbd44be95075a0e 100644
--- a/services/histserv/app/Application.cxx
+++ b/services/histserv/app/Application.cxx
@@ -31,6 +31,8 @@
 #include <mutex>
 #include <zmq_addon.hpp>
 
+#include <fmt/format.h>
+
 std::mutex mtx;
 
 namespace b_io = boost::iostreams;
@@ -519,8 +521,15 @@ namespace cbm::services::histserv
 
     LOG(info) << " All histos found for canvas " << conf.GetName().data() << ", now preparing it";
 
+    // Temporary solution to save canvases into directories
+    std::string sNameFull = conf.GetName();
+    size_t lastSlashPos   = sNameFull.find_last_of('/');
+    std::string sNamePart = lastSlashPos > sNameFull.size() ? sNameFull : sNameFull.substr(lastSlashPos + 1);
+    std::string sDir      = lastSlashPos > sNameFull.size() ? "" : sNameFull.substr(0, lastSlashPos);
+    std::string canvDir   = sDir.empty() ? "canvases" : fmt::format("canvases/{}", sDir);
+
     /// Create new canvas and pads
-    TCanvas* pNewCanv = new TCanvas(conf.GetName().data(), conf.GetTitle().data());
+    TCanvas* pNewCanv = new TCanvas(sNamePart.c_str(), conf.GetTitle().data());
     pNewCanv->Divide(conf.GetNbPadsX(), conf.GetNbPadsY());
 
     /// Loop on pads
@@ -561,7 +570,7 @@ namespace cbm::services::histserv
       }    // for( uint32_t uObjIdx = 0; uObjIdx < uNbObj; ++uObjIdx )
     }      // for( uint32_t uPadIdx = 0; uPadIdx < uNbPads; ++uPadIdx )
 
-    fvCanvas[uCanvIdx] = std::pair<TCanvas*, std::string>(pNewCanv, "canvases");
+    fvCanvas[uCanvIdx] = std::pair<TCanvas*, std::string>(pNewCanv, canvDir);
     fServer->Register(Form("/%s", fvCanvas[uCanvIdx].second.data()), fvCanvas[uCanvIdx].first);
     fvbCanvasRegistered[uCanvIdx] = true;