diff --git a/src/api/class/src/class_core.cpp b/src/api/class/src/class_core.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c4b0aae5c251a72bddd19577638591b878868422
--- /dev/null
+++ b/src/api/class/src/class_core.cpp
@@ -0,0 +1,2434 @@
+/**
+ * @file   ClassCore.cpp
+ * @author Alfonso Domminguez <alfonso.dominguez@tecnalia.com> and   Leire Querejeta Lomas <leire.querejeta@tecnalia.com>
+ * @date   2020
+ *
+ * Copyright 2' Tecnalia Research & Innovation.
+ * Distributed under the GNU GPL v3.
+ * For full terms see https://www.gnu.org/licenses/gpl.txt
+ *
+ * @brief Interface to the funcionalities of Class.
+ */
+
+#include <class/class_core.hpp>
+#include <iostream>
+#include <numeric>
+#include <chrono>
+#include <thread>
+#include <regex>
+#include <math.h>
+#include <bitset>
+#include "../../commands.h"
+
+const double ACQ_SECONDS_DEFAULT = 2.0;
+const int FILTER_ORDER_DEFAULT = 2;
+const int MSG_MAX_LENGTH = 1024;
+
+ClassCore::ClassCore()
+{
+
+  udp_server_ = nullptr;
+  udp_client_ = nullptr;
+  butterworth_ = nullptr;
+
+  channels_number_ = 0;
+  gain_ = 0.0;
+  acq_seconds_ = ACQ_SECONDS_DEFAULT;
+  acq_buffer_capacity_ = 0;
+  is_acquiring_frames_ = false;
+  filter_order_ = FILTER_ORDER_DEFAULT;
+
+  waiting_ = false;
+
+  is_waiting_battery_ = false;
+  battery_received_ = false;
+  battery_ = 0.0;
+  tic_ = "";
+  firmware_ = "";
+  hardware_ = "";
+  device_ = "";
+  logevents_ = "";
+  hvstatus_ = "";
+  intervalstatus_ = "";
+  rtcstatus_ = "";
+  buzzerstatus_ = "";
+  frequencystatus_ = "";
+  sdfunctionstatus_ = "";
+  sdunamestatus_ = "";
+  patternstatus_ = "";
+  velecstatus_ = "";
+
+  lock_error_number_ = 0;
+  lock_error_number_max_ = 10;
+
+  language_ = "";
+  class_cb_ = nullptr;
+  waiting_thread_ = new std::thread(&ClassCore::waitingThreadFunction_, this);
+
+  lock_error_thread_ = new std::thread(&ClassCore::lockErrorThreadFunction_, this);
+  // myfile_.open ("msgs.txt");
+}
+
+ClassCore::~ClassCore()
+{
+  if (udp_server_ != nullptr)
+  {
+    delete udp_server_;
+  }
+
+  if (udp_client_ != nullptr)
+  {
+    delete udp_client_;
+  }
+
+  if (butterworth_ != nullptr)
+  {
+    for (int i = 0; i < channels_number_; ++i)
+    {
+      delete butterworth_[i];
+    }
+    delete[] butterworth_;
+  }
+
+  waiting_mutex_.lock();
+  waiting_ = false;
+  waiting_mutex_.unlock();
+
+  lock_error_mutex_.lock();
+  error_waiting_ = false;
+  lock_error_mutex_.unlock();
+  // myfile_.close();
+}
+
+ClassError::Error ClassCore::connect(const std::string &server_ip, const int &server_port, const std::string &local_ip, const int &local_port)
+{
+  if (udp_server_ != nullptr)
+  {
+    log_("[connect] There is already a local UDP server listening for messages coming from CLASS server");
+  }
+  else
+  {
+    log_stream_("[connect] Starting local server on port " << local_port);
+    udp_server_ = new UDPServer(local_port);
+    local_port_ = local_port;
+    udp_server_->startListening();
+    udp_server_->addListener((UdpServerListener *)this);
+    log_stream_("[connect] Waiting for CLASS server messages on local port " << local_port);
+  }
+
+  std::string server_port_str;
+  std::stringstream ss;
+  ss << server_port;
+  server_port_str = ss.str();
+  log_stream_("[connect] Creating a client which connects to CLASS server (IP " << server_ip << ":" << server_port_str << ")");
+  udp_client_ = new UDPClient(server_ip, server_port_str, -1);
+
+  ss.str(std::string());
+  ss << "connect " << local_port_;
+  std::string msg = ss.str();
+
+  return sendMsg(msg);
+}
+
+ClassError::Error ClassCore::disconnect()
+{
+  std::stringstream ss;
+  ss << "disconnect " << local_port_;
+  std::string msg = ss.str();
+
+  int response = sendMsg(msg);
+  if (response != 0)
+  {
+    return ClassError::ERROR_SENDING_MSG;
+  }
+
+  log_("[disconnect] Stopping local UPD server...");
+  if (udp_server_ != nullptr)
+  {
+    udp_server_->removeListener((UdpServerListener *)this);
+    udp_server_->stopListening();
+  }
+  log_("[disconnect] Local UDP server stopped");
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+ClassError::Error ClassCore::setAcqConfig(const double &frequency, const std::vector<int> &channel_numbers, const double &gain, const std::string &input)
+{
+  // log_stream_("[setAcqConfig]");
+  gain_ = gain;
+
+  if (butterworth_ != nullptr)
+  {
+    for (int i = 0; i < channels_number_; ++i)
+    {
+      if (butterworth_[i] != nullptr)
+      {
+        delete butterworth_[i];
+      }
+    }
+    delete[] butterworth_;
+  }
+
+  channels_number_ = channel_numbers.size();
+  log_stream_("[setAcqConfig] Number of channels: " << channels_number_);
+
+  int channels_int = 0;
+
+  for (int i = 0; i < channel_numbers.size(); i++)
+  {
+    if (channel_numbers[i] < 1 || channel_numbers[i] > 16)
+    {
+      log_error_stream_("[setAcqConfig] Channel " << i << "has the id " << channel_numbers[i] << " which is not in the interval >=1 and <=16");
+      return ClassError::ERROR_NOT_VALID_CHANNEL_ID;
+    }
+    channels_int += (int)(pow(2, channel_numbers[i] - 1));
+  }
+
+  std::stringstream stream;
+  stream << "0x"
+         << std::setfill('0') << std::setw(2) // 2 digits hex 0xAA (4 digits does not work)
+         << std::hex << channels_int;
+  std::string channels_hex_str(stream.str());
+  log_stream_("[setAcqConfig] channels hex: " << channels_hex_str);
+
+  acq_frequency_ = frequency;
+
+  log_("[setAcqConfig] setBufferCapacity");
+  setBufferCapacity(acq_seconds_);
+
+  log_("[setAcqConfig] configureFilter");
+  configureFilter(filter_order_, 30.2, 32.2, acq_frequency_);
+
+  return sendMsg(commands::ClassCommands::CMD_CONFIGACQ + std::to_string(frequency) + commands::ClassCommands::CMD_CONFIGACQ_PARAM1 +channels_hex_str + commands::ClassCommands::CMD_CONFIGACQ_PARAM2 + std::to_string(gain) + commands::ClassCommands::CMD_CONFIGACQ_PARAM3 + input + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::setBufferCapacity(const double &seconds)
+{
+  acq_seconds_ = seconds;
+  if (acq_buffer_mutex_.try_lock())
+  {
+    acq_buffer_.clear();
+    acq_buffer_capacity_ = (int)(acq_frequency_ * acq_seconds_);
+    acq_buffer_.set_capacity(acq_buffer_capacity_);
+    acq_buffer_mutex_.unlock();
+  }
+  else
+  {
+    log_warn_stream_("[setBufferCapacity] lock not acquired");
+    increaseLockError();
+  }
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+ClassError::Error ClassCore::configureFilter(int order, double low_freq, double up_freq, double acq_freq)
+{
+  filter_order_ = order;
+  double low = low_freq / (acq_freq / 2.0);
+  double up = up_freq / (acq_freq / 2.0);
+
+  if (butterworth_ != nullptr)
+  {
+    for (int i = 0; i < channels_number_; ++i)
+    {
+      delete butterworth_[i];
+    }
+    delete[] butterworth_;
+  }
+
+  butterworth_ = new Butterworth *[channels_number_];
+  for (int i = 0; i < channels_number_; ++i)
+  {
+    butterworth_[i] = new Butterworth();
+    butterworth_[i]->configure(order, low, up);
+  }
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+ClassError::Error ClassCore::acqOn()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_BUFFERFLUSH);
+  if (response != ClassError::CLASS_NO_ERROR)
+  {
+    return ClassError::ERROR_SENDING_MSG;
+  }
+
+  if (acq_buffer_mutex_.try_lock())
+  {
+    acq_buffer_.clear();
+    acq_buffer_mutex_.unlock();
+  }
+  else
+  {
+    log_warn_stream_("[acqOn] lock not acquired");
+    increaseLockError();
+  }
+
+  is_acquiring_frames_mutex_.lock();
+  is_acquiring_frames_ = true;
+  is_acquiring_frames_mutex_.unlock();
+
+  return sendMsg(commands::ClassCommands::CMD_ONACQ);
+}
+
+ClassError::Error ClassCore::acqOff()
+{
+  is_acquiring_frames_mutex_.lock();
+  is_acquiring_frames_ = false;
+  is_acquiring_frames_mutex_.unlock();
+
+  return sendMsg(commands::ClassCommands::CMD_OFFACQ);
+}
+
+ClassError::Error ClassCore::acqImpedanceOn()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_BUFFERFLUSH);
+  if (response != ClassError::CLASS_NO_ERROR)
+  {
+    return ClassError::ERROR_SENDING_MSG;
+  }
+
+  if (acq_buffer_mutex_.try_lock())
+  {
+    acq_buffer_.clear();
+    acq_buffer_mutex_.unlock();
+  }
+  else
+  {
+    log_warn_stream_("[acqImpedanceOn] lock not acquired");
+    increaseLockError();
+  }
+
+  is_acquiring_impedances_mutex_.lock();
+  is_acquiring_impedances_ = true;
+  is_acquiring_impedances_mutex_.unlock();
+
+  std::vector<std::string> msg_list;
+  msg_list.push_back(commands::ClassCommands::CMD_ONIMPEDANCEACQ);
+  msg_list.push_back(commands::ClassCommands::CMD_ONACQ);
+
+  return sendMsgs(msg_list);
+}
+
+ClassError::Error ClassCore::acqImpedanceOff()
+{
+  is_acquiring_impedances_mutex_.lock();
+  is_acquiring_impedances_ = false;
+  is_acquiring_impedances_mutex_.unlock();
+
+  std::vector<std::string> msg_list;
+  msg_list.push_back(commands::ClassCommands::CMD_OFFIMPEDANCEACQ);
+  msg_list.push_back(commands::ClassCommands::CMD_OFFACQ);
+
+  return sendMsgs(msg_list);
+}
+
+ClassError::Error ClassCore::acqStreamOn()
+{
+  return sendMsg(commands::ClassCommands::CMD_STREAMONACQ);
+}
+
+ClassError::Error ClassCore::acqStreamOff()
+{
+  return sendMsg(commands::ClassCommands::CMD_STREAMOFFACQ);
+}
+
+ClassError::Error ClassCore::acqInputNormal()
+{
+  return sendMsg(commands::ClassCommands::CMD_NORMALACQ);
+}
+
+ClassError::Error ClassCore::acqInputTest()
+{
+  return sendMsg(commands::ClassCommands::CMD_TESTACQ);
+}
+
+ClassError::Error ClassCore::stimOn()
+{
+  return sendMsg(commands::ClassCommands::CMD_ONSTIM);
+}
+
+ClassError::Error ClassCore::stimOff()
+{
+  return sendMsg(commands::ClassCommands::CMD_OFFSTIM);
+}
+
+ClassError::Error ClassCore::elecPads(const int &id, const int &pads_number)
+{
+  // chek whether the electrode exists. If so, update, if not insert
+  int electrode_index = -1;
+  for (unsigned int i = 0; i < electrodes_.size(); i++)
+  {
+    Elec elec = electrodes_[i];
+    if (elec.id == id)
+    {
+      electrode_index = i;
+      break;
+    }
+  }
+
+  if (electrode_index >= 0)
+  {
+    electrodes_[electrode_index].pads_number = pads_number;
+  }
+  else
+  {
+    Elec elec;
+    elec.id = id;
+    elec.pads_number = pads_number;
+
+    electrodes_.push_back(elec);
+  }
+
+  return sendMsg(commands::ClassCommands::CMD_CONFIGELEC + std::to_string(id) + commands::ClassCommands::CMD_CONFIGACQ_PARAM1 + std::to_string(pads_number) + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::stimFreq(const double &frequency)
+{
+  if(frequency < FREQ_MIN_VALUE || frequency > FREQ_MAX_VALUE)
+  {
+    return ClassError::ERROR_NOT_VALID_VALUE;
+  }
+  else
+  {
+    return sendMsg(commands::ClassCommands::CMD_SETFREQ +  std::to_string(frequency) + commands::ClassCommands::CMD_AUX);
+  }
+}
+
+ClassError::Error ClassCore::devName(const std::string& devicename)
+{
+  return sendMsg(commands::ClassCommands::CMD_SETDEVICENAME + devicename + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::hvValue(const std::string& hvvalue)
+{
+  if(std::stoi(hvvalue) < HV_MIN_VALUE || std::stoi(hvvalue) > HV_MAX_VALUE)
+  {
+    return ClassError::ERROR_NOT_VALID_VALUE;
+  }
+  else
+  {
+    return sendMsg(commands::ClassCommands::CMD_SETHV + hvvalue + commands::ClassCommands::CMD_AUX);
+  }
+}
+
+ClassError::Error ClassCore::hvOn()
+{
+  return sendMsg(commands::ClassCommands::CMD_ONHV);
+}
+
+ClassError::Error ClassCore::hvOff()
+{
+  return sendMsg(commands::ClassCommands::CMD_OFFHV);
+}
+
+ClassError::Error ClassCore::logeventsOn()
+{
+  return sendMsg(commands::ClassCommands::CMD_ONLOGEVENTS);
+}
+
+ClassError::Error ClassCore::logeventsOff()
+{
+  return sendMsg(commands::ClassCommands::CMD_OFFLOGEVENTS);
+}
+
+ClassError::Error ClassCore::intervalValue(const std::string& interval)
+{
+  if(std::stoi(interval) < INTERVAL_MIN_VALUE || std::stoi(interval) > INTERVAL_MAX_VALUE)
+  {
+    return ClassError::ERROR_NOT_VALID_VALUE;
+  }
+  else
+  {
+    return sendMsg(commands::ClassCommands::CMD_SETINTERVAL + interval + commands::ClassCommands::CMD_AUX);
+  }
+}
+
+ClassError::Error ClassCore::buzzerTempo(const std::string& tempo)
+{
+  if(std::stoi(tempo) < BUZZER_MIN_VALUE || std::stoi(tempo) > BUZZER_MAX_VALUE)
+  {
+    return ClassError::ERROR_NOT_VALID_VALUE;
+  }
+  else
+  {
+    return sendMsg(commands::ClassCommands::CMD_SETBUZZER + tempo + commands::ClassCommands::CMD_AUX);
+  }
+}
+
+ClassError::Error ClassCore::buzzerPlay()
+{
+  return sendMsg(commands::ClassCommands::CMD_PLAYBUZZER);
+}
+
+ClassError::Error ClassCore::rtcDate(const std::string& rtcDate)
+{
+  return sendMsg(commands::ClassCommands::CMD_SETRTCDATE + rtcDate + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::rtcTime(const std::string& rtcTime)
+{
+  return sendMsg(commands::ClassCommands::CMD_SETRTCTIME + rtcTime + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::shutdown()
+{
+  return sendMsg(commands::ClassCommands::CMD_SWITCHOFF);
+}
+
+ClassError::Error ClassCore::sdFunctionName(const std::string& sdfunctionname)
+{
+  return sendMsg(commands::ClassCommands::CMD_SETSDFUNCTION + sdfunctionname + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::sdUName(const std::string& sduname)
+{
+  return sendMsg(commands::ClassCommands::CMD_SETSDNAME + sduname + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::pattern(const int &id, const std::vector<Pattern> &pat)
+{
+  sendMsg(commands::ClassCommands::CMD_CONFIGPATTERN + std::to_string(id) + commands::ClassCommands::CMD_CONFIGACQ_PARAM1 + commands::ClassCommands::CMD_AUX);
+  
+  std::this_thread::sleep_for(std::chrono::milliseconds(300));
+  for(int i = 0; i < pat.size() ; i++)
+  {
+    
+    sendMsg(commands::ClassCommands::CMD_CONFIGPATTERN + std::to_string(id) + commands::ClassCommands::CMD_CONFIGPATTERN_PARAM2 + pat[i].amp + " " + pat[i].pw + " " + pat[i].r + " " + std::to_string(pat[i].ampval) + " " + std::to_string(pat[i].pwval) + " " + std::to_string(pat[i].time) + commands::ClassCommands::CMD_AUX);
+    std::this_thread::sleep_for(std::chrono::milliseconds(300));
+  }
+
+  return sendMsg(commands::ClassCommands::CMD_CONFIGPATTERN + std::to_string(id) + commands::ClassCommands::CMD_CONFIGACQ_PARAM3 + commands::ClassCommands::CMD_AUX);
+
+}
+
+ClassError::Error ClassCore::patternClear(const int& id)
+{
+  return sendMsg(commands::ClassCommands::CMD_CONFIGPATTERN + std::to_string(id) + commands::ClassCommands::CMD_CONFIGACQ_PARAM1 + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::velec(const int &id, const std::string &name, const int &electrode_id, const std::vector<int> &cathodes, const std::vector<int> &anodes, const std::vector<double> &amp, const std::vector<int> &width, const bool &selected, const bool &sync)
+{
+  // check whether there is an electrode in the list with id=electrode_id
+  int electrode_index = -1;
+  for (unsigned int i = 0; i < electrodes_.size(); i++)
+  {
+    Elec elec = electrodes_[i];
+    if (elec.id == electrode_id)
+    {
+      electrode_index = i;
+      break;
+    }
+  }
+
+  if (electrode_index < 0)
+  {
+    log_warn_stream_("[velec] No electrode with id: " << electrode_id << " configured from this client.");
+  }
+
+  // check whether there is an virtual_electrode in the list with id=id
+  int virtual_electrode_index = -1;
+  for (unsigned int i = 0; i < virtual_electrodes_.size(); i++)
+  {
+    Velec elec = virtual_electrodes_[i];
+    if (elec.id == id)
+    {
+      virtual_electrode_index = i;
+      break;
+    }
+  }
+
+  if (virtual_electrode_index < 0)
+  {
+    // new velec
+    Velec elec;
+    elec.id = id;
+    elec.selected = selected;
+    elec.name = name;
+
+    virtual_electrodes_.push_back(elec);
+  }
+  else
+  {
+    // update velec
+    virtual_electrodes_[electrode_index].selected = selected;
+    virtual_electrodes_[electrode_index].name = name;
+  }
+
+  if (cathodes.size() == 0)
+  {
+    log_err_("[virtualElectrode] Cathode vector is empty");
+    return ClassError::ERROR_EMPTY_CATHODE;
+  }
+
+  if (anodes.size() == 0)
+  {
+    log_err_("[virtualElectrode] Anode vector is empty");
+    return ClassError::ERROR_EMPTY_ANODE;
+  }
+
+  // check whether the length of cathodes, amp and width are the same
+  if (cathodes.size() != amp.size() || cathodes.size() != width.size())
+  {
+    log_err_("[virtualElectrode] Cathode, amp and width vector sizes are different");
+    return ClassError::ERROR_CATHODE_AMP_DIFFERENT_SIZE;
+  }
+
+  std::stringstream ss;
+  ss << "velec " << id << " *name " << name << " *elec " << electrode_id << " *pads ";
+  //commands::ClassCommands::CMD_CONFIGVELEC + id + commands::ClassCommands::CMD_CONFIGVELEC_PARAM1 + name + commands::ClassCommands::CMD_CONFIGVELEC_PARAM2 + electrode_id + commands::ClassCommands::CMD_CONFIGVELEC_PARAM3
+  for (unsigned int i = 0; i < cathodes.size(); i++)
+  {
+    ss << cathodes[i] << "=C"
+       << ",";
+  }
+  for (unsigned int i = 0; i < anodes.size(); i++)
+  {
+    ss << anodes[i] << "=A"
+       << ",";
+  }
+
+  ss << " *amp ";
+  for (unsigned int i = 0; i < amp.size(); i++)
+  {
+    ss << cathodes[i] << "=" << amp[i] << ",";
+  }
+  ss << " *width ";
+  for (unsigned int i = 0; i < width.size(); i++)
+  {
+    ss << cathodes[i] << "=" << width[i] << ",";
+  }
+  ss << " *selected ";
+  if (selected)
+  {
+    ss << "1";
+  }
+  else
+  {
+    ss << "0";
+  }
+  ss << " *sync ";
+  if (sync)
+  {
+    ss << "1";
+  }
+  else
+  {
+    ss << "0";
+  }
+  ss << "\r\n";
+  std::string msg = ss.str();
+
+  return sendMsg(msg);
+}
+
+ClassError::Error ClassCore::velecSelected(const int &id, const bool &selected)
+{
+  int virtual_electrode_index = -1;
+  for (unsigned int i = 0; i < virtual_electrodes_.size(); i++)
+  {
+    Velec elec = virtual_electrodes_[i];
+    if (elec.id == id)
+    {
+      virtual_electrode_index = i;
+      break;
+    }
+  }
+
+  if (virtual_electrode_index < 0)
+  {
+    log_warn_stream_("[velecSelected] No virtual electrode with id: " << id << " configured from this client.");
+  }
+
+  if(selected)
+  {
+    return sendMsg(commands::ClassCommands::CMD_CONFIGVELEC + std::to_string(id) + commands::ClassCommands::CMD_VELEC_SELECTED + commands::ClassCommands::CMD_AUX);
+  }
+  else
+  {
+    return sendMsg(commands::ClassCommands::CMD_CONFIGVELEC + std::to_string(id) + commands::ClassCommands::CMD_VELEC_NOTSELECTED + commands::ClassCommands::CMD_AUX);
+  }
+}
+
+ClassError::Error ClassCore::velecsSelected(const std::vector<int> &id, const std::vector<bool> &selected)
+{
+  if (id.size() != selected.size())
+  {
+    return ClassError::ERROR_DIF_VECTOR_SIZE;
+  }
+
+  std::vector<std::string> msg_list;
+
+  for (int velecIndex = 0; velecIndex < id.size(); velecIndex++)
+  {
+    int virtual_electrode_index = -1;
+    for (unsigned int i = 0; i < virtual_electrodes_.size(); i++)
+    {
+      Velec elec = virtual_electrodes_[i];
+      if (elec.id == id[velecIndex])
+      {
+        virtual_electrode_index = i;
+        break;
+      }
+    }
+
+    if (virtual_electrode_index < 0)
+    {
+      log_warn_stream_("[velecsSelected] No virtual electrode with id: " << id[velecIndex] << "configured from this client.");
+    }
+
+    std::stringstream ss;
+    ss << "velec " << id[velecIndex];
+    ss << " *selected ";
+    if (selected[velecIndex])
+    {
+      //commands::ClassCommands::CMD_CONFIGVELEC + id + commands::ClassCommands::CMD_VELEC_SELECTED + commands::ClassCommands::CMD_AUX
+      ss << "1";
+    }
+    else
+    {
+      //commands::ClassCommands::CMD_CONFIGVELEC + id + commands::ClassCommands::CMD_VELEC_NOTSELECTED + commands::ClassCommands::CMD_AUX
+      ss << "0";
+    }
+    ss << "\r\n";
+    std::string msg = ss.str();
+    msg_list.push_back(msg);
+  }
+  return sendMsgs(msg_list);
+}
+
+ClassError::Error ClassCore::stimVelec(const std::string &name)
+{
+  int virtual_electrode_index = -1;
+  for (unsigned int i = 0; i < virtual_electrodes_.size(); i++)
+  {
+    Velec elec = virtual_electrodes_[i];
+    if (elec.name == name)
+    {
+      virtual_electrode_index = i;
+      break;
+    }
+  }
+
+  if (virtual_electrode_index < 0)
+  {
+    log_warn_stream_("[velecSelected] No virtual electrode with name: " << name << " condifgured from this client.");
+  }
+
+  std::stringstream ss;
+  ss << "stim " << name << "\r\n";
+  std::string msg = ss.str();
+
+  return sendMsg(msg);
+}
+
+ClassError::Error ClassCore::acqImpedanceConfig(const bool &positive)
+{
+  std::stringstream ss;
+  ss << "acq impedance_config *channels ";
+  if (positive)
+  {
+    ss << "positives";
+  }
+  else
+  {
+    ss << "negatives";
+  }
+  ss << "\r\n";
+  std::string msg = ss.str();
+
+  return sendMsg(msg);
+}
+
+ClassError::Error ClassCore::acqImpedancePolarity(const bool &unipolar)
+{
+  std::stringstream ss;
+  ss << "acq config type ";
+  if (unipolar)
+  {
+    ss << "unipolar";
+  }
+  else
+  {
+    ss << "bipolar";
+  }
+  ss << "\r\n";
+  std::string msg = ss.str();
+
+  return sendMsg(msg);
+}
+
+ClassError::Error ClassCore::initCommunication(const std::string &init_mode)
+{
+  return sendMsg(commands::ClassCommands::CMD_INIT + init_mode + commands::ClassCommands::CMD_AUX);
+}
+
+ClassError::Error ClassCore::getAcqFrames(std::vector<AcqFrame> &acq_frames)
+{
+  bool is_acquiring_frames = false;
+  is_acquiring_frames_mutex_.lock();
+  is_acquiring_frames = is_acquiring_frames_;
+  is_acquiring_frames_mutex_.unlock();
+
+  bool is_acquiring_impedances = false;
+  is_acquiring_impedances_mutex_.lock();
+  is_acquiring_impedances = is_acquiring_impedances_;
+  is_acquiring_impedances_mutex_.unlock();
+
+  if (!is_acquiring_frames && !is_acquiring_impedances)
+  {
+    log_stream_("[getAcqFrames] The device is not acquiring frames");
+    return ClassError::ERROR_DEVICE_NOT_ACQUIRING;
+  }
+
+  acq_frames.clear();
+  if (acq_buffer_mutex_.try_lock())
+  {
+    for (int i = 0; i < acq_buffer_.size(); i++)
+    {
+      acq_frames.push_back(acq_buffer_[i]);
+    }
+    acq_buffer_mutex_.unlock();
+  }
+  else
+  {
+    log_warn_stream_("[getAcqFrames] lock not acquired");
+    increaseLockError();
+  }
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+ClassError::Error ClassCore::sendMsg(std::string msg)
+{
+  log_stream_("[sendMsg] Msg:" << msg);
+  if (udp_client_ == nullptr)
+  {
+    return ClassError::ERROR_UDP_CLIENT_NULL;
+  }
+  udp_client_->send(msg);
+
+  // log_stream_("[sendMsg] Msg sent");
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+ClassError::Error ClassCore::sendMsgs(std::vector<std::string> msgs)
+{
+  // wrap msgs in a string until MSG_MAX_LENGTH
+  std::string string_with_msgs_prev = "";
+  std::stringstream ss;
+  int msg_index = 0;
+  while (msg_index < msgs.size())
+  {
+    ss << msgs[msg_index];
+    std::string string_with_msgs = ss.str();
+
+    if (string_with_msgs.length() > MSG_MAX_LENGTH)
+    {
+      // clear stringstream
+      ss.str(std::string());
+      // send prev and do not increase index
+      ClassError::Error response = sendMsg(string_with_msgs_prev);
+      if (response != ClassError::CLASS_NO_ERROR)
+      {
+        return response;
+      }
+
+      string_with_msgs_prev = "";
+    }
+    else
+    {
+      // continue concatenating
+      msg_index += 1;
+      string_with_msgs_prev = string_with_msgs;
+    }
+  }
+
+  if (string_with_msgs_prev.length() != 0)
+  {
+    ClassError::Error response = sendMsg(string_with_msgs_prev);
+    if (response != ClassError::CLASS_NO_ERROR)
+    {
+      return response;
+    }
+  }
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+void ClassCore::udpMsgReceived(std::string msg, std::string ip)
+{
+  // processing depends on the status
+  //log_stream_("[udpMsgReceived] Msg received from CLASS server: " << msg << ". Msg length: " << msg.length());
+
+  tic_ = "";
+  firmware_ = "";
+  device_ = "";
+  logevents_ = "";
+  
+
+  std::string temphvstatus;
+  if (!hvstatus_.empty()){
+     temphvstatus = hvstatus_;
+   } else {
+     temphvstatus = "";
+   }
+  hvstatus_ =  "";
+
+  intervalstatus_ = "";
+  rtcstatus_ = "";
+  buzzerstatus_ = "";
+  frequencystatus_ = "";
+  sdfunctionstatus_ = "";
+  sdunamestatus_ = "";
+  patternstatus_ = "";
+  hardware_ = "";
+  velecstatus_ = "";
+
+  std::string battery_capacity_str = "battery *capacity=";
+  std::size_t found = msg.find(battery_capacity_str);
+
+  if (msg.find("tic ") != std::string::npos)
+  {
+    tic_ = msg;
+
+    double is_waiting_tic = false;
+    if (tic_mutex_.try_lock())
+    {
+      is_waiting_tic = is_waiting_tic_;
+      tic_mutex_.unlock();
+    }
+
+    if (is_waiting_tic)
+    {
+      if (tic_mutex_.try_lock())
+      {
+        tic_ = msg;
+        tic_received_ = true;
+        tic_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("firmware") != std::string::npos)
+  {
+    // firmware_ = msg;
+    double is_waiting_firmware = false;
+    if (firmware_mutex_.try_lock())
+    {
+      is_waiting_firmware = is_waiting_firmware_;
+      firmware_mutex_.unlock();
+    }
+
+    if (is_waiting_firmware)
+    {
+      if (firmware_mutex_.try_lock())
+      {
+        firmware_ = msg;
+        firmware_received_ = true;
+        firmware_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("velec ") != std::string::npos)
+  {
+    double is_waiting_velecstatus = false;
+    if (velecstatus_mutex_.try_lock())
+    {
+      is_waiting_velecstatus = is_waiting_velecstatus_;
+      velecstatus_mutex_.unlock();
+    }
+    if (is_waiting_velecstatus)
+    {
+      if (velecstatus_mutex_.try_lock())
+      {
+        velecstatus_ = msg;
+        velecstatus_received_ = true;
+        velecstatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("device") != std::string::npos)
+  {
+    double is_waiting_device = false;
+    if (device_mutex_.try_lock())
+    {
+      is_waiting_device = is_waiting_device_;
+      device_mutex_.unlock();
+    }
+
+    if (is_waiting_device)
+    {
+      if (device_mutex_.try_lock())
+      {
+        device_ = msg;
+        device_received_ = true;
+        device_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("hardware ") != std::string::npos)
+  {
+    //hardware_ = msg;
+    double is_waiting_hardware = false;
+    if (hardware_mutex_.try_lock())
+    {
+      is_waiting_hardware = is_waiting_hardware_;
+      hardware_mutex_.unlock();
+    }
+
+    if (is_waiting_hardware)
+    {
+      if (hardware_mutex_.try_lock())
+      {
+        hardware_ = msg;
+        hardware_received_ = true;
+        hardware_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("logevents ") != std::string::npos)
+  {
+    double is_waiting_logevents = false;
+    if (logevents_mutex_.try_lock())
+    {
+      is_waiting_logevents = is_waiting_logevents_;
+      logevents_mutex_.unlock();
+    }
+    if (is_waiting_logevents)
+    {
+      if (logevents_mutex_.try_lock())
+      {
+        logevents_ = msg;
+        logevents_received_ = true;
+        logevents_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("hv ") != std::string::npos)
+  {
+    double is_waiting_hvstatus = false;
+    if (hvstatus_mutex_.try_lock())
+    {
+      is_waiting_hvstatus = is_waiting_hvstatus_;
+      hvstatus_mutex_.unlock();
+    }
+    if (is_waiting_hvstatus)
+    {
+      if (hvstatus_mutex_.try_lock())
+      {
+        hvstatus_ = temphvstatus + msg;
+        hvstatus_received_ = true;
+        hvstatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("interval ") != std::string::npos)
+  {
+    double is_waiting_intervalstatus = false;
+    if (intervalstatus_mutex_.try_lock())
+    {
+      is_waiting_intervalstatus = is_waiting_intervalstatus_;
+      intervalstatus_mutex_.unlock();
+    }
+    if (is_waiting_intervalstatus)
+    {
+      if (intervalstatus_mutex_.try_lock())
+      {
+        intervalstatus_ = msg;
+        intervalstatus_received_ = true;
+        intervalstatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("rtc ") != std::string::npos)
+  {
+    double is_waiting_rtcstatus = false;
+    if (rtcstatus_mutex_.try_lock())
+    {
+      is_waiting_rtcstatus = is_waiting_rtcstatus_;
+      rtcstatus_mutex_.unlock();
+    }
+    if (is_waiting_rtcstatus)
+    {
+      if (rtcstatus_mutex_.try_lock())
+      {
+        rtcstatus_ = msg;
+        rtcstatus_received_ = true;
+        rtcstatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("buzzer ") != std::string::npos)
+  {
+    double is_waiting_buzzerstatus = false;
+    if (buzzerstatus_mutex_.try_lock())
+    {
+      is_waiting_buzzerstatus = is_waiting_buzzerstatus_;
+      buzzerstatus_mutex_.unlock();
+    }
+    if (is_waiting_buzzerstatus)
+    {
+      if (buzzerstatus_mutex_.try_lock())
+      {
+        buzzerstatus_ = msg;
+        buzzerstatus_received_ = true;
+        buzzerstatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("freq ") != std::string::npos)
+  {
+    double is_waiting_frequencystatus = false;
+    if (frequencystatus_mutex_.try_lock())
+    {
+      is_waiting_frequencystatus = is_waiting_frequencystatus_;
+      frequencystatus_mutex_.unlock();
+    }
+    if (is_waiting_frequencystatus)
+    {
+      if (frequencystatus_mutex_.try_lock())
+      {
+        frequencystatus_ = msg;
+        frequencystatus_received_ = true;
+        frequencystatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("function ") != std::string::npos)
+  {
+    double is_waiting_sdfunction = false;
+    if (sdfunction_mutex_.try_lock())
+    {
+      is_waiting_sdfunction = is_waiting_sdfunction_;
+      sdfunction_mutex_.unlock();
+    }
+    if (is_waiting_sdfunction)
+    {
+      if (sdfunction_mutex_.try_lock())
+      {
+        sdfunctionstatus_ = msg;
+        sdfunction_received_ = true;
+        sdfunction_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("uname ") != std::string::npos)
+  {
+    double is_waiting_sduname = false;
+    if (sduname_mutex_.try_lock())
+    {
+      is_waiting_sduname = is_waiting_sduname_;
+      sduname_mutex_.unlock();
+    }
+    if (is_waiting_sduname)
+    {
+      if (sduname_mutex_.try_lock())
+      {
+        sdunamestatus_ = msg;
+        sduname_received_ = true;
+        sduname_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("pattern ") != std::string::npos)
+  {
+    double is_waiting_patternstatus = false;
+    if (patternstatus_mutex_.try_lock())
+    {
+      is_waiting_patternstatus = is_waiting_patternstatus_;
+      patternstatus_mutex_.unlock();
+    }
+    if (is_waiting_patternstatus)
+    {
+      if (patternstatus_mutex_.try_lock())
+      {
+        patternstatus_ = msg;
+        patternstatus_received_ = true;
+        patternstatus_mutex_.unlock();
+      }
+    }
+  }
+  else if (msg.find("battery *capacity=") != std::string::npos)
+  {
+    // log_stream_("[udpMsgReceived] battery msg received:" << msg);
+    double is_waiting_battery = false;
+    if (battery_mutex_.try_lock())
+    {
+      is_waiting_battery = is_waiting_battery_;
+      battery_mutex_.unlock();
+    }
+
+    if (is_waiting_battery)
+    {
+      // log_stream_("[udpMsgReceived] extract battery level");
+      // extract battery level
+      double battery;
+
+      std::size_t found_space = msg.find(" ", found + battery_capacity_str.length());
+      if (found_space != std::string::npos)
+      {
+        std::string battery_str = msg.substr(found + battery_capacity_str.length(), found_space - (found + battery_capacity_str.length()));
+
+        // log_stream_("[udpMsgReceived] battery str: " << battery_str);
+
+        try
+        {
+          battery = std::stod(battery_str);
+          // log_stream_("[udpMsgReceived] battery: " << battery);
+          if (battery_mutex_.try_lock())
+          {
+            battery_ = battery;
+            battery_received_ = true;
+            battery_mutex_.unlock();
+          }
+        }
+        catch (std::exception ex)
+        {
+          log_error_stream_("[udpMsgReceived] battery. cannot convert to double: " << battery_str);
+          return;
+        }
+      }
+    }
+  }
+  else if (msg.find("turnning off") != std::string::npos)
+  {
+    log_stream_("[udpMsgReceived] turn off received:" << msg);
+
+    if (class_cb_ != nullptr)
+    {
+      if (language_ == "python")
+      {
+        class PyThreadStateLock PyThreadLock; // fix segmentation fault
+        class_cb_->turnOffHandle();
+      }
+      else
+      {
+        class_cb_->turnOffHandle();
+      }
+    }
+  }
+  else
+  {
+    bool is_acquiring_frames = false;
+    is_acquiring_frames_mutex_.lock();
+    is_acquiring_frames = is_acquiring_frames_;
+    is_acquiring_frames_mutex_.unlock();
+
+    bool is_acquiring_impedances = false;
+    is_acquiring_impedances_mutex_.lock();
+    is_acquiring_impedances = is_acquiring_impedances_;
+    is_acquiring_impedances_mutex_.unlock();
+
+    if (is_acquiring_frames || is_acquiring_impedances)
+    {
+      // myfile_ << msg;
+      int expected_bytes = 3 + 1 + 2 + channels_number_ * (1 + 3);
+      if (msg.length() != expected_bytes)
+      {
+        // log_warn_stream_("[udpMsgReceived] The message , which length is " << msg.length() << ", does not have the expected length " << expected_bytes << ". Msg:" <<  msg);
+        return;
+      }
+
+      std::string timestamp_bin = msg.substr(0, 6);
+
+      if (timestamp_bin.substr(3, 1) != ".")
+      {
+        // log_warn_stream_("[udpMsgReceived] Timestamp part does not have a point: " << msg);
+        return;
+      }
+
+      AcqFrame frame;
+
+      try
+      {
+        std::string timestamp_ms_bin = timestamp_bin.substr(0, 3);
+        int timestamp_ms = ((unsigned char)(timestamp_ms_bin.at(0)) << 16) + ((unsigned char)(timestamp_ms_bin.at(1)) << 8) + (unsigned char)(timestamp_ms_bin.at(2));
+
+        std::string timestamp_us_bin = timestamp_bin.substr(4, 2);
+        int timestamp_us = ((unsigned char)(timestamp_us_bin.at(0)) << 8) + (unsigned char)(timestamp_us_bin.at(1));
+
+        std::stringstream timestamp_stream;
+        timestamp_stream << timestamp_ms << "." << timestamp_us;
+        std::string timestamp_str(timestamp_stream.str());
+
+        frame.timestamp = std::stod(timestamp_str);
+
+        int starting_index = 7;
+        for (int i = 0; i < channels_number_; i++)
+        {
+          std::string data_bin_str = msg.substr(starting_index, 3);
+          int data_bin = ((unsigned char)(data_bin_str.at(0)) << 16) + ((unsigned char)(data_bin_str.at(1)) << 8) + (unsigned char)(data_bin_str.at(2));
+
+          // get value of bit 24
+          int bit_number = 23;
+          int mask = 1 << bit_number;
+          int masked_n = data_bin & mask;
+          int bit_value = masked_n >> bit_number;
+
+          int data;
+          if (bit_value == 1)
+          {
+            int data_bin_xor = data_bin ^ int(pow(2, bit_number + 1) - 1); // bit xor
+            data = (data_bin_xor + 1) * -1;
+          }
+          else
+          {
+            data = data_bin;
+          }
+
+          double chDataV = (data * 2 * 2.048) / (gain_ * pow(2.0, 24.0));
+          double chDataUV = round(chDataV * 1000000);
+
+          if (is_acquiring_frames)
+          {
+            // log_stream_("[udpMsgReceived] Value " << i << ": " << chDataUV);
+            // truncate
+            const auto result = trunc_n(chDataUV, 4);
+            frame.values.push_back(result.first);
+          }
+          else
+          {
+            // log_stream_("[udpMsgReceived] Value before filtering " << i << ": " << chDataUV);
+            if (butterworth_ == nullptr)
+            {
+              return;
+            }
+            double chDataUV_filtered;
+            butterworth_[i]->update(chDataUV, chDataUV_filtered);
+            double chDataNV_filtered = chDataUV_filtered * 1000.0;
+            // log_stream_("[udpMsgReceived] Value after filtering " << i << ": " << chDataNV_filtered);
+            double currentNA = 6;
+            // CHECK: it is a value per channel or unique value? do we have to take into account previous values?
+            double impedance = (chDataNV_filtered / currentNA) - 4700;
+            // log_stream_("[udpMsgReceived] Impedance " << i << ": " << impedance);
+
+            // truncate
+            const auto result = trunc_n(impedance, 4);
+            frame.values.push_back(result.first);
+          }
+          starting_index += (3 + 1); // 3bytes of channel info + 1byte of space
+        }
+      }
+      catch (std::exception ex)
+      {
+        // log_error_stream_("[udpMsgReceived] error converting to double some of the values: " << msg);
+        return;
+      }
+
+      if (is_acquiring_frames || is_acquiring_impedances)
+      {
+        if (acq_buffer_mutex_.try_lock())
+        {
+          acq_buffer_.push_back(frame);
+          acq_buffer_mutex_.unlock();
+        }
+        else
+        {
+          log_warn_stream_("[udpMsgReceived] lock not acquired");
+          increaseLockError();
+        }
+      }
+      else
+      {
+        acq_impedances_buffer_mutex_.lock();
+        acq_impedances_buffer_.push_back(frame);
+        acq_impedances_buffer_mutex_.unlock();
+      }
+    }
+  }
+}
+
+ClassError::Error ClassCore::impedanceFilterOrder(const int &order)
+{
+  if (order == filter_order_)
+  {
+    return ClassError::CLASS_NO_ERROR;
+  }
+
+  if (order <= 0)
+  {
+    return ClassError::ERROR_NOT_VALID_ORDER;
+  }
+
+  return configureFilter(order, 30.2, 32.2, acq_frequency_);
+}
+
+ClassError::Error ClassCore::bufferDuration(const double &ms)
+{
+  if (ms <= 0)
+  {
+    return ClassError::ERROR_NOT_VALID_MS;
+  }
+
+  return setBufferCapacity(ms / 1000.0);
+}
+
+ClassError::Error ClassCore::batteryLevel()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETBATTERY);
+
+  if (battery_mutex_.try_lock())
+  {
+    is_waiting_battery_ = true;
+    battery_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::firmwareVersion()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETFW);
+
+  if (firmware_mutex_.try_lock())
+  {
+    is_waiting_firmware_ = true;
+    firmware_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::deviceName()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETDEVICENAME);
+  if (device_mutex_.try_lock())
+  {
+    is_waiting_device_ = true;
+    device_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::logeventsStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETLOGEVENTS);
+
+  if (logevents_mutex_.try_lock())
+  {
+    is_waiting_logevents_ = true;
+    logevents_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::velecStatus(const int &number)
+{
+  std::stringstream ss;
+  ss << "velec " << number << " ?\r\n";
+  std::string msg = ss.str();
+
+  ClassError::Error response = sendMsg(msg);
+
+  if (velecstatus_mutex_.try_lock())
+  {
+    is_waiting_velecstatus_ = true;
+    velecstatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::patternStatus(const int &number)
+{
+  std::stringstream ss;
+  ss << "pattern " << number << " ?\r\n";
+  std::string msg = ss.str();
+
+  ClassError::Error response = sendMsg(msg);
+
+  if (patternstatus_mutex_.try_lock())
+  {
+    is_waiting_patternstatus_ = true;
+    patternstatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::hvStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETHV);
+
+  if (hvstatus_mutex_.try_lock())
+  {
+    is_waiting_hvstatus_ = true;
+    hvstatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::intervalStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETINTERVAL);
+
+  if (intervalstatus_mutex_.try_lock())
+  {
+    is_waiting_intervalstatus_ = true;
+    intervalstatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::rtcStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETRTC);
+
+  if (rtcstatus_mutex_.try_lock())
+  {
+    is_waiting_rtcstatus_ = true;
+    rtcstatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::buzzerStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETBUZZER);
+
+  if (buzzerstatus_mutex_.try_lock())
+  {
+    is_waiting_buzzerstatus_ = true;
+    buzzerstatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::frequencyStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETFRQUENCY);
+
+  if (frequencystatus_mutex_.try_lock())
+  {
+    is_waiting_frequencystatus_ = true;
+    frequencystatus_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::sdfunctionStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETSDFUNCTION);
+
+  if (sdfunction_mutex_.try_lock())
+  {
+    is_waiting_sdfunction_ = true;
+    sdfunction_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::sdunameStatus()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETSDNAME);
+
+  if (sduname_mutex_.try_lock())
+  {
+    is_waiting_sduname_ = true;
+    sduname_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::hardwareVersion()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETHW);
+
+  if (hardware_mutex_.try_lock())
+  {
+    is_waiting_hardware_ = true;
+    hardware_mutex_.unlock();
+  }
+
+  return response;
+}
+
+ClassError::Error ClassCore::tic()
+{
+  ClassError::Error response = sendMsg(commands::ClassCommands::CMD_GETTIC);
+
+  if (tic_mutex_.try_lock())
+  {
+    is_waiting_tic_ = true;
+    tic_mutex_.unlock();
+  }
+
+  return response;
+}
+
+void ClassCore::waitingThreadFunction_()
+{
+  waiting_ = true;
+
+  bool waiting = true;
+
+  log_stream_("[waitingThreadFunction_] Thread for waiting for callback messages started");
+
+  do
+  {
+    bool is_waiting_tic = false;
+    bool tic_received = false;
+
+    bool is_waiting_firmware = false;
+    bool firmware_received = false;
+
+    bool is_waiting_hardware = false;
+    bool hardware_received = false;
+
+    bool is_waiting_battery = false;
+    bool battery_received = false;
+
+    bool is_waiting_device = false;
+    bool device_received = false;
+
+    bool is_waiting_logevents = false;
+    bool logevents_received = false;
+
+    bool is_waiting_hvstatus = false;
+    bool hvstatus_received = false;
+    
+    bool is_waiting_intervalstatus = false;
+    bool intervalstatus_received = false;
+
+    bool is_waiting_rtcstatus = false;
+    bool rtcstatus_received = false;
+
+    bool is_waiting_buzzerstatus = false;
+    bool buzzerstatus_received = false;
+
+    bool is_waiting_frequencystatus = false;
+    bool frequencystatus_received = false;
+
+    bool is_waiting_sdfunctionstatus = false;
+    bool sdfunctionstatus_received = false;
+
+    bool is_waiting_sdunamestatus = false;
+    bool sdunamestatus_received = false;
+
+    bool is_waiting_patternstatus = false;
+    bool patternstatus_received = false;
+
+    bool is_waiting_velecstatus = false;
+    bool velecstatus_received = false;
+
+    double battery = 0;
+
+    std::string tic = "";
+    std::string firmware = "";
+    std::string hardware = "";
+    std::string device = "";
+    std::string logevents = "";
+    std::string hvstatus = "";
+    std::string intervalstatus = "";
+    std::string rtcstatus = "";
+    std::string buzzerstatus = "";
+    std::string frequencystatus = "";
+    std::string sdfunctionstatus = "";
+    std::string sdunamestatus = "";
+    std::string patternstatus = "";
+    std::string velecstatus = "";
+
+    // FIRMWARE
+    // if (firmware_ != "")
+    // {
+    //   firmware = firmware_;
+    //   class_cb_->firmwareHandle(firmware);
+    // }
+
+    if (firmware_mutex_.try_lock())
+    {
+      is_waiting_firmware = is_waiting_firmware_;
+      firmware_received = firmware_received_;
+      firmware = firmware_;
+      firmware_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] firmware lock not acquired");
+    }
+
+    if (is_waiting_firmware && firmware_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->firmwareHandle(firmware);
+        }
+        else
+        {
+          class_cb_->firmwareHandle(firmware);
+        }
+        if (firmware_mutex_.try_lock())
+        {
+          firmware_received_ = false;
+          is_waiting_firmware_ = false;
+          firmware_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    // END OF FIRMWARE
+if (velecstatus_mutex_.try_lock())
+    {
+      is_waiting_velecstatus = is_waiting_velecstatus_;
+      velecstatus_received = velecstatus_received_;
+      velecstatus = velecstatus_;
+      velecstatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] velecstatus lock not acquired");
+    }
+
+    if (is_waiting_velecstatus && velecstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->velecstatusHandle(velecstatus);
+        }
+        else
+        {
+          class_cb_->velecstatusHandle(velecstatus);
+        }
+        if (velecstatus_mutex_.try_lock())
+        {
+          velecstatus_received_ = false;
+          is_waiting_velecstatus_ = false;
+          velecstatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    // END OF VELECSTATUS
+    if (device_mutex_.try_lock())
+    {
+      is_waiting_device = is_waiting_device_;
+      device_received = device_received_;
+      device = device_;
+      device_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] device lock not acquired");
+    }
+
+    if (is_waiting_device && device_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->deviceNameHandle(device);
+        }
+        else
+        {
+          class_cb_->deviceNameHandle(device);
+        }
+        if (device_mutex_.try_lock())
+        {
+          device_received_ = false;
+          is_waiting_device_ = false;
+          device_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+
+    //END of device name
+    if (logevents_mutex_.try_lock())
+    {
+      is_waiting_logevents = is_waiting_logevents_;
+      logevents_received = logevents_received_;
+      logevents = logevents_;
+      logevents_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] logevents lock not acquired");
+    }
+
+    if (is_waiting_logevents && logevents_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->logeventsHandle(logevents);
+        }
+        else
+        {
+          class_cb_->logeventsHandle(logevents);
+        }
+        if (logevents_mutex_.try_lock())
+        {
+          logevents_received_ = false;
+          is_waiting_logevents_ = false;
+          logevents_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of logevents
+    if (patternstatus_mutex_.try_lock())
+    {
+      is_waiting_patternstatus = is_waiting_patternstatus_;
+      patternstatus_received = patternstatus_received_;
+      patternstatus = patternstatus_;
+      patternstatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] patternstatus lock not acquired");
+    }
+
+    if (is_waiting_patternstatus && patternstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->patternHandle(patternstatus);
+        }
+        else
+        {
+          class_cb_->patternHandle(patternstatus);
+        }
+        if (patternstatus_mutex_.try_lock())
+        {
+          patternstatus_received_ = false;
+          is_waiting_patternstatus_ = false;
+          patternstatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of pattern status
+    if (hvstatus_mutex_.try_lock())
+    {
+      is_waiting_hvstatus = is_waiting_hvstatus_;
+      hvstatus_received = hvstatus_received_;
+      hvstatus = hvstatus_;
+      hvstatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] hvstatus lock not acquired");
+    }
+
+    if (is_waiting_hvstatus && hvstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->hvHandle(hvstatus);
+        }
+        else
+        {
+          class_cb_->hvHandle(hvstatus);
+        }
+        if (hvstatus_mutex_.try_lock())
+        {
+          hvstatus_received_ = false;
+          is_waiting_hvstatus_ = false;
+          hvstatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of hvstatus
+
+    if (intervalstatus_mutex_.try_lock())
+    {
+      is_waiting_intervalstatus = is_waiting_intervalstatus_;
+      intervalstatus_received = intervalstatus_received_;
+      intervalstatus = intervalstatus_;
+      intervalstatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] intervalstatus lock not acquired");
+    }
+
+    if (is_waiting_intervalstatus && intervalstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->intervalHandle(intervalstatus);
+        }
+        else
+        {
+          class_cb_->intervalHandle(intervalstatus);
+        }
+        if (intervalstatus_mutex_.try_lock())
+        {
+          intervalstatus_received_ = false;
+          is_waiting_intervalstatus_ = false;
+          intervalstatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of intervalstatus
+
+    if (rtcstatus_mutex_.try_lock())
+    {
+      is_waiting_rtcstatus = is_waiting_rtcstatus_;
+      rtcstatus_received = rtcstatus_received_;
+      rtcstatus = rtcstatus_;
+      rtcstatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] rtcstatus lock not acquired");
+    }
+
+    if (is_waiting_rtcstatus && rtcstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->rtcHandle(rtcstatus);
+        }
+        else
+        {
+          class_cb_->rtcHandle(rtcstatus);
+        }
+        if (rtcstatus_mutex_.try_lock())
+        {
+          rtcstatus_received_ = false;
+          is_waiting_rtcstatus_ = false;
+          rtcstatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of rtc
+    
+    if (buzzerstatus_mutex_.try_lock())
+    {
+      is_waiting_buzzerstatus = is_waiting_buzzerstatus_;
+      buzzerstatus_received = buzzerstatus_received_;
+      buzzerstatus = buzzerstatus_;
+      buzzerstatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] buzzer lock not acquired");
+    }
+
+    if (is_waiting_buzzerstatus && buzzerstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->buzzerHandle(buzzerstatus);
+        }
+        else
+        {
+          class_cb_->buzzerHandle(buzzerstatus);
+        }
+        if (buzzerstatus_mutex_.try_lock())
+        {
+          buzzerstatus_received_ = false;
+          is_waiting_buzzerstatus_ = false;
+          buzzerstatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of buzzer
+    if (frequencystatus_mutex_.try_lock())
+    {
+      is_waiting_frequencystatus = is_waiting_frequencystatus_;
+      frequencystatus_received = frequencystatus_received_;
+      frequencystatus = frequencystatus_;
+      frequencystatus_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] frequency lock not acquired");
+    }
+
+    if (is_waiting_frequencystatus && frequencystatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->frequencyHandle(frequencystatus);
+        }
+        else
+        {
+          class_cb_->frequencyHandle(frequencystatus);
+        }
+        if (frequencystatus_mutex_.try_lock())
+        {
+          frequencystatus_received_ = false;
+          is_waiting_frequencystatus_ = false;
+          frequencystatus_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of frequency
+
+    if (sdfunction_mutex_.try_lock())
+    {
+      is_waiting_sdfunctionstatus = is_waiting_sdfunction_;
+      sdfunctionstatus_received = sdfunction_received_;
+      sdfunctionstatus = sdfunctionstatus_;
+      sdfunction_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] sdfunction lock not acquired");
+    }
+
+    if (is_waiting_sdfunctionstatus && sdfunctionstatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->sdfunctionHandle(sdfunctionstatus);
+        }
+        else
+        {
+          class_cb_->sdfunctionHandle(sdfunctionstatus);
+        }
+        if (sdfunction_mutex_.try_lock())
+        {
+          sdfunction_received_ = false;
+          is_waiting_sdfunction_ = false;
+          sdfunction_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of sdfunction
+
+    if (sduname_mutex_.try_lock())
+    {
+      is_waiting_sdunamestatus = is_waiting_sduname_;
+      sdunamestatus_received = sduname_received_;
+      sdunamestatus = sdunamestatus_;
+      sduname_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] sduname lock not acquired");
+    }
+
+    if (is_waiting_sdunamestatus && sdunamestatus_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->sdunameHandle(sdunamestatus);
+        }
+        else
+        {
+          class_cb_->sdunameHandle(sdunamestatus);
+        }
+        if (sduname_mutex_.try_lock())
+        {
+          sduname_received_ = false;
+          is_waiting_sduname_ = false;
+          sduname_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    //END of sduname
+
+    // HARDWARE
+    // if (hardware_ != "")
+    // {
+    //   hardware = hardware_;
+    //   class_cb_->hardwareHandle(hardware);
+    // }
+
+    if (hardware_mutex_.try_lock())
+    {
+      is_waiting_hardware = is_waiting_hardware_;
+      hardware_received = hardware_received_;
+      hardware = hardware_;
+      hardware_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] hardware lock not acquired");
+    }
+
+    if (is_waiting_hardware && hardware_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->hardwareHandle(hardware);
+        }
+        else
+        {
+          class_cb_->hardwareHandle(hardware);
+        }
+        if (hardware_mutex_.try_lock())
+        {
+          hardware_received_ = false;
+          is_waiting_hardware_ = false;
+          hardware_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+
+    // END OF HARDWARE
+
+    // TIC
+    // if (tic_ != "")
+    // {
+    //   tic = tic_;
+    //   class_cb_->ticHandle(tic);
+    // }
+    if (tic_mutex_.try_lock())
+    {
+      is_waiting_tic = is_waiting_tic_;
+      tic_received = tic_received_;
+      tic = tic_;
+      tic_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] tic lock not acquired");
+    }
+
+    if (is_waiting_tic && tic_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->ticHandle(tic);
+        }
+        else
+        {
+          class_cb_->ticHandle(tic);
+        }
+        if (tic_mutex_.try_lock())
+        {
+          tic_received_ = false;
+          is_waiting_tic_ = false;
+          tic_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    // END OF TIC
+
+    // BATTERY
+    if (battery_mutex_.try_lock())
+    {
+      is_waiting_battery = is_waiting_battery_;
+      battery_received = battery_received_;
+      battery = battery_;
+      battery_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] battery lock not acquired");
+    }
+
+    if (is_waiting_battery && battery_received)
+    {
+      if (class_cb_ != nullptr)
+      {
+        if (language_ == "python")
+        {
+          class PyThreadStateLock PyThreadLock; // fix segmentation fault
+          class_cb_->batteryHandle(battery);
+        }
+        else
+        {
+          class_cb_->batteryHandle(battery);
+        }
+        if (battery_mutex_.try_lock())
+        {
+          battery_received_ = false;
+          is_waiting_battery_ = false;
+          battery_mutex_.unlock();
+        }
+      }
+      else
+      {
+        log_error_stream_("[waitingThreadFunction_] callback is null");
+      }
+    }
+    // END OF BATTERY
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 0.5 secs
+
+    if (waiting_mutex_.try_lock())
+    {
+      waiting = waiting_;
+      waiting_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[waitingThreadFunction_] waiting lock not acquired");
+    }
+  } while (waiting);
+}
+
+void ClassCore::lockErrorThreadFunction_()
+{
+  error_waiting_ = true;
+
+  bool error_waiting = true;
+
+  log_stream_("[lockErrorThreadFunction_] Thread for taking into account lock errors started");
+
+  do
+  {
+    int lock_error_number = 0;
+
+    if (lock_error_mutex_.try_lock())
+    {
+      lock_error_number = lock_error_number_;
+      lock_error_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[lockErrorThreadFunction_] error lock not acquired");
+    }
+
+    if (lock_error_number >= lock_error_number_max_)
+    {
+      log_error_stream_("[lockErrorThreadFunction_] Number of errors when getting lock has overcome the limit. Check processes in your computer.");
+    }
+
+    if (lock_error_mutex_.try_lock())
+    {
+      lock_error_number_ = 0;
+      lock_error_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[lockErrorThreadFunction_] error lock not acquired");
+    }
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 1secs
+
+    if (lock_error_mutex_.try_lock())
+    {
+      error_waiting = error_waiting_;
+      lock_error_mutex_.unlock();
+    }
+    else
+    {
+      log_warn_stream_("[lockErrorThreadFunction_] error lock not acquired");
+    }
+  } while (error_waiting);
+}
+
+ClassError::Error ClassCore::setCallback(ClassCallback *callback, std::string language)
+{
+  if (class_cb_ != nullptr)
+  {
+    delete class_cb_;
+    class_cb_ = nullptr;
+  }
+
+  if (callback)
+  {
+    class_cb_ = callback;
+  }
+  else
+  {
+    return ClassError::ERROR_CB_NULL;
+  }
+
+  language_ = language;
+
+  return ClassError::CLASS_NO_ERROR;
+}
+
+std::pair<double, bool> ClassCore::trunc_n(double value, std::size_t digits_after_decimal = 0)
+{
+  static constexpr std::intmax_t maxv = std::numeric_limits<std::intmax_t>::max();
+  static constexpr std::intmax_t minv = std::numeric_limits<std::intmax_t>::min();
+
+  unsigned long long multiplier = 1;
+  for (std::size_t i = 0; i < digits_after_decimal; ++i)
+    multiplier *= 10;
+
+  const auto scaled_value = value * multiplier;
+
+  const bool did_trunc = scaled_value != scaled_value + 0.5 && scaled_value != scaled_value - 0.5;
+
+  if (scaled_value >= minv && scaled_value <= maxv)
+    return {double(std::intmax_t(scaled_value)) / multiplier, did_trunc};
+  else
+    return {std::trunc(scaled_value) / multiplier, did_trunc};
+}
+
+void ClassCore::increaseLockError()
+{
+  lock_error_mutex_.lock();
+  lock_error_number_ += 1;
+  lock_error_mutex_.unlock();
+}
+ClassError::Error ClassCore::sendCustomCmd(const std::string &cmd)
+{
+  return sendMsg(cmd);
+}
+
+/*LOGGING functions*/
+
+void ClassCore::log_(const std::string &text)
+{
+  LOG_INFO << "[ClassCore]: " << text;
+}
+
+void ClassCore::log_(std::ostream &text)
+{
+  LOG_INFO << "[ClassCore]: " << text.rdbuf();
+}
+
+void ClassCore::log_(const std::stringstream &text)
+{
+  LOG_INFO << "[ClassCore]: " << text.str();
+}
+
+// see http://www.cplusplus.com/forum/unices/36461/
+// see https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute
+
+#ifdef __unix__
+const std::string bold_red("\033[1;31m");
+const std::string bold_yellow("\033[1;33m");
+const std::string reset("\033[0m");
+#endif
+
+void ClassCore::log_err_(const std::string &text)
+{
+#ifdef __unix__
+  LOG_ERROR << bold_red << "[ClassCore]: " << text << reset;
+#elif defined(_WIN32) || defined(WIN32)
+  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+  SetConsoleTextAttribute(hConsole, 12);
+  LOG_ERROR << "[ClassCore]: " << text;
+  SetConsoleTextAttribute(hConsole, 7);
+#endif
+}
+
+void ClassCore::log_err_(std::ostream &text)
+{
+#ifdef __unix__
+  LOG_ERROR << bold_red << "[ClassCore]: " << text.rdbuf() << reset;
+#elif defined(_WIN32) || defined(WIN32)
+  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+  SetConsoleTextAttribute(hConsole, 12);
+  LOG_ERROR << "[ClassCore]: " << text.rdbuf();
+  SetConsoleTextAttribute(hConsole, 7);
+#endif
+}
+
+void ClassCore::log_warn_(const std::string &text)
+{
+#ifdef __unix__
+  LOG_WARNING << bold_yellow << "[ClassCore]: " << text << reset;
+#elif defined(_WIN32) || defined(WIN32)
+  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+  SetConsoleTextAttribute(hConsole, 14);
+  LOG_WARNING << "[ClassCore]: " << text;
+  SetConsoleTextAttribute(hConsole, 7);
+#endif
+}
+
+void ClassCore::log_warn_(std::ostream &text)
+{
+#ifdef __unix__
+  LOG_WARNING << bold_yellow << "[ClassCore]: " << text.rdbuf() << reset;
+#elif defined(_WIN32) || defined(WIN32)
+  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+  SetConsoleTextAttribute(hConsole, 14);
+  LOG_WARNING << "[ClassCore]: " << text.rdbuf();
+  SetConsoleTextAttribute(hConsole, 7);
+#endif
+}