diff --git a/include/MonitorDisplay.h b/include/MonitorDisplay.h
index c327d26bfd223a19e2065d3ed45848d8b2748c03..7ea54c75cb23f04da21bfb280d1a5b8aa0b8286f 100644
--- a/include/MonitorDisplay.h
+++ b/include/MonitorDisplay.h
@@ -1,9 +1,6 @@
 #pragma once
 
 R__LOAD_LIBRARY(libScandalizer.so)
-#include "scandalizer/PostProcessors.h"
-#include "scandalizer/ScriptHelpers.h"
-#include "scandalizer/SpectrometerMonitor.h"
 #include "monitor/DetectorDisplay.h"
 #include "monitor/EventDisplays.h"
 #include "monitor/ExperimentMonitor.h"
@@ -48,7 +45,6 @@ static std::vector<CanvasMeta> update_canvases = {
 };
 
 
-
 hallc::MonitoringDisplay *CreateMonitorDisplay(const std::string &root_dir, int port, TTree *tree)
 {
     gStyle->SetHistFillStyle(3002);
@@ -90,6 +86,7 @@ hallc::MonitoringDisplay *CreateMonitorDisplay(const std::string &root_dir, int
             }
         );
     }
+
     return dply;
 }
 
diff --git a/include/WaveformDisplay.h b/include/WaveformDisplay.h
new file mode 100644
index 0000000000000000000000000000000000000000..122c22220641db67833307fafc268b3343643d6d
--- /dev/null
+++ b/include/WaveformDisplay.h
@@ -0,0 +1,103 @@
+#pragma once
+
+R__LOAD_LIBRARY(libScandalizer.so)
+#include "monitor/DetectorDisplay.h"
+#include "monitor/EventDisplays.h"
+#include "monitor/ExperimentMonitor.h"
+#include "TStyle.h"
+#include "utils.h"
+
+struct WFCanvasMeta {
+    std::string subdir, name, title;
+    int nsamples, ymin, ymax;
+    double xmg, ymg, subxmg, subymg;
+    std::vector<std::vector<std::string>> channels;
+    std::vector<std::pair<int, int>> pad;
+};
+
+static std::vector<WFCanvasMeta> wfdata = {
+    {"raw/", "ec_waveform", "EC Waveform", 64, 300, 1000, 0.0, 0.0, 0.0, 0.0, {
+        {"C4"}, {"C6_1", "C6_4", "C6_2", "C6_3"}, {"C7_1", "C7_4", "C7_2", "C7_3"},
+        {"C5_1", "C5_4", "C5_2", "C5_3"}, {"C9_1", "C9_2", "C9_4", "C9_3"}, {"C8_2", "C8_3", "C8_1", "C8_4"},
+        {"C1"}, {"C2"}, {"C3"},
+    }, {}},
+
+    {"raw/", "cher_waveform", "MaPMT Waveform", 64, 0, 500, 0.0, 0.0, 0.0, 0.0, {
+        {"Cer11_5"}, {"Cer12_5"}, {"Cer13_5"}, {"Cer14_5"},
+        {"Cer21_5"}, {"Cer22_5"}, {"Cer23_5"}, {"Cer24_5"},
+        {"Cer31_5"}, {"Cer32_5"}, {"Cer33_5"}, {"Cer34_5"},
+        {"Cer41_5"}, {"Cer42_5"}, {"Cer43_5"}, {"Cer44_5"},
+    }, {}},
+};
+
+
+inline void set_graph(TGraph *gr, int npt, double value = 0.)
+{
+    for (int i = 0; i < npt; ++i) {
+        gr->SetPoint(i, i, value);
+    }
+}
+
+hallc::MonitoringDisplay *CreateWaveformDisplay(const std::string &root_dir, int port, std::unordered_map<std::string, BranchData> &brdata, int &mode)
+{
+    auto dply = new hallc::MonitoringDisplay(port);
+    dply->SetRootFolder(root_dir);
+
+    for (auto &w : wfdata) {
+        dply->CreateDisplayPlot(w.subdir, w.name,
+            [&w] (hallc::DisplayPlot &plt) {
+                plt._plot_data._canvas = new TCanvas(w.name.c_str(), w.title.c_str(), 1200, 700);
+                plt._plot_data._canvas->DivideSquare(w.channels.size(), w.xmg, w.ymg);
+                w.pad.clear();
+                for (size_t i = 0; i < w.channels.size(); ++i) {
+                    auto gch = w.channels[i];
+                    plt._plot_data._canvas->cd(i + 1);
+                    if (gch.size() > 1) {
+                        int ndiv = int(std::sqrt(gch.size()) - 1e-4) + 1;
+                        gPad->Divide(ndiv, ndiv, w.subxmg, w.subymg);
+                    }
+                    for (size_t j = 0; j < gch.size(); ++j) {
+                        gPad->cd(j + 1);
+                        auto gr = new TGraph();
+                        set_graph(gr, w.nsamples);
+                        gr->SetLineColor(kRed);
+                        gr->SetLineWidth(2);
+                        gr->SetTitle(gch[j].c_str());
+                        gr->Draw("L");
+                        plt._plot_data._graphs1.push_back(gr);
+                        w.pad.push_back({i + 1, j + 1});
+                    }
+                }
+                return 0;
+            },
+            [&w, &brdata, &mode] (hallc::DisplayPlot &plt) {
+                if (mode != 1) {
+                    std::cout << "Skip raw waveform plot because data mode = " << mode << std::endl;
+                    return 0;
+                }
+
+                for (size_t i = 0; i < plt._plot_data._graphs1.size(); ++i) {
+                    auto gr = plt._plot_data._graphs1[i];
+                    auto pn = w.pad[i];
+                    plt._plot_data._canvas->cd(pn.first)->cd(pn.second);
+
+                    auto it = brdata.find(gr->GetTitle());
+                    if (it != brdata.end()) {
+                        auto data = it->second;
+                        for (int k = 0; k < 64; ++k) {
+                            gr->SetPoint(k, k, (k < data.nraw) ? data.raw[k] : 0.);
+                        }
+                    } else {
+                        std::cout << "Warning: cannot find raw data for " << gr->GetTitle() << std::endl;
+                    }
+                    gr->Draw("L");
+                    gr->GetYaxis()->SetRangeUser(w.ymin, w.ymax);
+                    gr->GetXaxis()->SetRangeUser(0, w.nsamples);
+                }
+                return 0;
+        });
+    }
+    return dply;
+}
+
+
diff --git a/include/utils.h b/include/utils.h
index 1d3f4d27e1d2520f4935dfd8bbeb3d3495c97ae3..db158a6b4ca81095306a0a1d137a8991f7ed8104 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -9,6 +9,16 @@
 #include "ConfigObject.h"
 
 
+#define MAX_NPEAKS 20
+#define MAX_RAW 300
+struct BranchData
+{
+    int npul, nraw;
+    float integral[MAX_NPEAKS], peak[MAX_NPEAKS], time[MAX_NPEAKS];
+    int raw[MAX_RAW];
+    float ped_mean, ped_err;
+};
+
 enum ModuleType
 {
     kFADC250 = 0,
diff --git a/online/monitor.cxx b/online/monitor.cxx
index 1da910dd6fd0307df7b2cb396454b78cf4ccf62a..a2e3dbb29fd6a705d40b6ded81f40d96d9bf467d 100644
--- a/online/monitor.cxx
+++ b/online/monitor.cxx
@@ -1,4 +1,5 @@
 #include "MonitorDisplay.h"
+#include "WaveformDisplay.h"
 #include <iostream>
 #include <iomanip>
 #include <fstream>
@@ -93,16 +94,6 @@ bool parseEvent(const uint32_t *buf, bool verbose = false)
     */
 }
 
-#define MAX_NPEAKS 20
-#define MAX_RAW 300
-struct BranchData
-{
-    int npul, nraw;
-    float integral[MAX_NPEAKS], peak[MAX_NPEAKS], time[MAX_NPEAKS];
-    int raw[MAX_RAW];
-    float ped_mean, ped_err;
-};
-
 #define BUF_SIZE 1000
 static double buffer[BUF_SIZE], wfbuf[BUF_SIZE], bkbuf[BUF_SIZE];
 void refine_pedestal(const std::vector<uint32_t> &raw, float &ped_mean, float &ped_err)
@@ -309,76 +300,6 @@ void processEvent(const uint32_t *buf, int &count, TTree *tree, bool &init_tree,
 }
 
 
-static std::vector<std::vector<std::string>> ec_channels = {
-    {"C4"}, {"C6_1", "C6_4", "C6_2", "C6_3"}, {"C7_1", "C7_4", "C7_2", "C7_3"},
-    {"C5_1", "C5_4", "C5_2", "C5_3"}, {"C9_1", "C9_2", "C9_4", "C9_3"}, {"C8_2", "C8_3", "C8_1", "C8_4"},
-    {"C1"}, {"C2"}, {"C3"},
-};
-static std::vector<std::pair<int, int>> padnum;
-
-inline void set_graph(TGraph *gr, int npt, double value = 0.)
-{
-    for (int i = 0; i < npt; ++i) {
-        gr->SetPoint(i, i, value);
-    }
-}
-
-void AddWaveformDisplay(hallc::MonitoringDisplay *dply, std::unordered_map<std::string, BranchData> &brdata, int &mode)
-{
-
-    dply->CreateDisplayPlot("raw/", "waveform",
-        [] (hallc::DisplayPlot &plt) {
-            plt._plot_data._canvas = new TCanvas("ec_waveform_test2", "EC Waveform", 1200, 700);
-            plt._plot_data._canvas->DivideSquare(ec_channels.size());
-            padnum.clear();
-            for (size_t i = 0; i < ec_channels.size(); ++i) {
-                auto gch = ec_channels[i];
-                plt._plot_data._canvas->cd(i + 1);
-                int ndiv = int(std::sqrt(gch.size()) - 1e-4) + 1;
-                gPad->Divide(ndiv, ndiv, 0., 0.);
-                for (size_t j = 0; j < gch.size(); ++j) {
-                    gPad->cd(j + 1);
-                    auto gr = new TGraph();
-                    set_graph(gr, 64);
-                    gr->SetLineColor(kRed);
-                    gr->SetLineWidth(2);
-                    gr->SetTitle(gch[j].c_str());
-                    gr->Draw("L");
-                    plt._plot_data._graphs1.push_back(gr);
-                    padnum.push_back({i + 1, j + 1});
-                }
-            }
-            return 0;
-        },
-        [&brdata, &mode] (hallc::DisplayPlot &plt) {
-            if (mode != 1) {
-                std::cout << "Skip raw waveform plot because data mode = " << mode << std::endl;
-                return 0;
-            }
-
-            for (size_t i = 0; i < plt._plot_data._graphs1.size(); ++i) {
-                auto gr = plt._plot_data._graphs1[i];
-                auto pn = padnum[i];
-                plt._plot_data._canvas->cd(pn.first);
-                gPad->cd(pn.second);
-
-                auto it = brdata.find(gr->GetTitle());
-                if (it != brdata.end()) {
-                    auto data = it->second;
-                    for (int k = 0; k < 64; ++k) {
-                        gr->SetPoint(k, k, (k < data.nraw) ? data.raw[k] : 0.);
-                    }
-                } else {
-                    std::cout << "Warning: cannot find raw data for " << gr->GetTitle() << std::endl;
-                }
-                gr->Draw("L");
-                gr->GetYaxis()->SetRangeUser(300, 1000);
-                gr->GetXaxis()->SetRangeUser(0, 64);
-            }
-            return 0;
-        });
-}
-
 void monitor(const std::string &cpath = "config/online_monitor.conf")
 {
     // read configuration
@@ -424,10 +345,11 @@ void monitor(const std::string &cpath = "config/online_monitor.conf")
 
     // display plots
     auto dply = CreateMonitorDisplay(server_dir, server_port, tree);
-    // monitor display for raw waveform
-    AddWaveformDisplay(dply, brdata, data_mode);
     dply->InitAll();
 
+    auto dply2 = CreateWaveformDisplay(server_dir, server_port, brdata, data_mode);
+    dply2->InitAll();
+
 
     // timer
     auto timer = std::chrono::steady_clock::now();
@@ -443,6 +365,8 @@ void monitor(const std::string &cpath = "config/online_monitor.conf")
             std::cout << "Monitor update, processed events - " << count << std::endl;
             dply->Process();
             dply->UpdateAll();
+            dply2->Process();
+            dply2->UpdateAll();
         }
 
         int status = et_chan.ReadEvent();
@@ -464,6 +388,8 @@ void monitor(const std::string &cpath = "config/online_monitor.conf")
 
     dply->Process();
     dply->UpdateAll();
+    dply2->Process();
+    dply2->UpdateAll();
     std::cout << "Read and processed events - " << count << std::endl;
 
     hfile->Write();