text: import ctypes, pygame, sys
  from pygame.locals import *
  from ctypes import *
  import numpy as np
  #import matplotlib.pyplot as plt
  from sklearn import preprocessing
  
  # Contains lightstone device readings
  class lightstone_info(Structure):
      _fields_ = [("hrv", c_float), ("scl", c_float)]
  
  
  class lightstone:
      def __init__(self):
          # Open DLL
          self.dll = ctypes.CDLL("/home/me/liblightstone/build/lib/liblightstone.so")
  
          # Define argument types & return types for all methods
          self._create = self.dll.lightstone_create
          self._create.argtypes = []
          self._create.restype = c_void_p
  
          self._delete = self.dll.lightstone_delete
          self._delete.argtypes = [c_void_p]
  
          self._count = self.dll.lightstone_get_count
          self._count.restype = c_int
          self._count.argtypes = [c_void_p]
  
          self._open = self.dll.lightstone_open
          self._open.restype = c_int
          self._open.argtypes = [c_void_p, c_uint]
  
          self._get_info = self.dll.lightstone_get_info
          self._get_info.restype = lightstone_info
          self._get_info.argtypes = [c_void_p]
  
          self._valid = self.dll.lightstone_valid
          self._valid.restype = c_int
          self._valid.argtypes = [c_void_p]
  
          self._close = self.dll.lightstone_close
          self._close.restype = c_int
          self._close.argtypes = [c_void_p]
  
          # Call lightstone_create
          self.ptr = self._create()
  
      # Destructor, calls lightstone_delete
      def __del__(self):
          self._delete(self.ptr)
  
          # Return count of lightstone devices
  
      def count(self):
          return self._count(self.ptr)
  
      # Open given lightstone device
      def open(self, idx):
          return self._open(self.ptr, idx)
  
      # Return device readings
      def get(self):
          val = self._get()
          while val.hrv == 0.0:
              val = self._get()
          return val
  
      def _get(self):
          return self._get_info(self.ptr)
  
      # Returns > 0 if device is initalized, 0 otherwise
      def valid(self):
          return self._valid(self.ptr)
  
      # Close opened lightstone device
      def close(self):
          return self._close(self.ptr)
  
  
  WID = 1300
  HGT = 480
  
  pygame.init()
  fpsClock = pygame.time.Clock()
  surface = pygame.display.set_mode((WID, HGT))
  pygame.display.set_caption('Lightstone')
  
  
  red = pygame.Color(255, 0, 0)
  blue = pygame.Color(0, 0, 255)
  orange = pygame.Color(255, 128, 0)
  white = pygame.Color(255, 255, 255)
  black = pygame.Color(0, 0, 0)
  grey = pygame.Color(200, 200, 200)
  
  stone = lightstone()
  res = stone.open(0)
  if res != 0:
      print("Failure: " + str(res))
  
  x = 1
  
  #surface.fill(black)
  
  font = pygame.font.Font('freesansbold.ttf', 16)
  
  rate = 0
  avgbeat = 0
  
  #print([xx[0] for xx in hearts[i - 3:i + 3]])
  
  x = 0
  irate = irate2 = 0
  rate2 = 0
  FWD = 14
  REV = 14
  
  ss, hh = list(), list()
  bufferLen = 128
  
  # fig, (a,b) = plt.subplots(2, 1, sharex=True)
  
  #sScaler = preprocessing.MinMaxScaler(feature_range=(-1,+1))
  sScaler = preprocessing.StandardScaler()
  hScaler = preprocessing.StandardScaler()
  
  iter = 0
  while True:
  
  
      if (len(ss) > bufferLen):
          ss.pop(0)
          hh.pop(0)
  
      val = stone.get()
  
      # Skin Conductivity Level (galvanic skin response, GSR)
      #   Good values are 1.0 and above.
      #   You may require lotion on your fingers
      #   or warming your hands up if <1.0.
      #      http://support.wilddivine.com/knowledgebase/iom-driver-and-sensor-troubleshooting-guide/
      scl = val.scl
  
      # HRV is based on the fact that not only does heart rate change but regularity of the beat. Greater regularity (reduced HRV) is an indicator of stress.
      # https://www.whoop.com/thelocker/heart-rate-variability-hrv/
      h = val.hrv
      #print(scl, h)
  
      ss.append(scl)
      hh.append(h)
  
      if (iter%(bufferLen*16)==0):
          print("reset")
          sScaler._reset()
          hScaler._reset()
      sScaler.partial_fit([[scl]])
      hScaler.partial_fit([[h]])
      # if (iter%bufferLen==0):
      #     sScaler.fit(_ss)
      # else:
      #     sScaler.partial_fit(_ss)
  
      sss = sScaler.transform(np.array([ss]).transpose()).transpose()[0]
      hhh = hScaler.transform([[h]])[0][0]
      #print(h,hhh)
  
  
  
  
      # if (iter % 8 == 0):
      #     X = list()
      #     for i in range(len(scls)): X.append(iter + i)
      #
      #     a.clear()
      #     a.plot(X, hrvs)
      #     b.clear()
      #     b.plot(X, scls)
      #
      #     plt.draw()
      #     plt.pause(0.01)
  
  
      #ax.set(xlabel='time (s)', ylabel='voltage (mV)',
          #       title='About as simple as it gets, folks')
          #ax.grid()
  
          #plt.xlabel("")
          #plt.ylabel("")
          #plt.title("")
          #plt.legend()
  
  
      pygame.draw.rect(surface, pygame.Color(max(0,min(255, int((hhh*0.5+1)/2 * 255))), 0, 0), (0, 0, WID, HGT))
      #pygame.draw.rect(surface, pygame.Color(0,min(255,int(scl*128)),0), (WID/4, HGT/4, WID*1/2, HGT*1/2))
  
      sssn = len(sss)
      cw, ch = WID/float(sssn), HGT/10.0
      for i in range(sssn):
          cx = i * cw
          cy = ((-sss[i]*0.5)+1)/2 * HGT
          #cy = sss[i] * HGT
          ssss = (int(cx-cw/2.0),int(cy-ch/2.0),cw+1,ch+1)
          pygame.draw.rect(surface, white, ssss)
  
  
      for e in pygame.event.get():
         if e.type == QUIT:
             pygame.quit()
             sys.exit()
  
      pygame.display.update()
      fpsClock.tick(30)
      iter=iter+1
  