<jupyter_start><jupyter_text>Breathing/ECG actuation notebook modified to send data to 2 actuators  This notebook uses a breathing actuation sequence that computes triggers inflation deflation based upon an average inhale/exhale duration and a scale factor<jupyter_code>from pythonosc import dispatcher, osc_server
from pythonosc.udp_client import SimpleUDPClient
import biofeatures
import threading
import numpy as np
def data_handler(unused_addr, args, data1, data2, data3, data4, data5, data6): #BITalino ServerBIT format
# def data_handler(unused_addr, args, values): #R-IoT Node-RED format

    global ecg_data, resp_data
    global osc_client
    
    B = args[0]
    HR = args[1]
    
    # print(data5) # breathing
    # print(data6) # ECG
    
    # BITalino ServerBIT
    ecg_data.append(float(data6))
    resp_data.append(float(data5))

    # R-IoT Node-RED
#     resp_data.append(np.float(values.split("\t")[5]))
#     ecg_data.append(np.random.rand(1)[0])

        
    if B.is_warmed_up:
        B.set_data(resp_data[-B.buffer_length:])
    
    if HR.is_warmed_up:
        HR.set_data(ecg_data[-HR.buffer_length:])
    

def warmup(B, resp_data, HR, ecg_data):
    """Function to launch once the system has warmed up.
    Sets the  data initially and launches a recursive update of features.
    Parameters
    ----------
    B: object containing breathing data and features
    resp_data: respiration signal data to set
    HR: object containing ECG data and features
    ecg_data: ecg signal data to set
    """
    
    if not B.is_warmed_up:
        print("Breathing WARMUP")
        B.set_data(resp_data[-B.buffer_length:])
        B.is_warmed_up = True
        B.update_loop()
        
    if not HR.is_warmed_up:
        print("ECG WARMUP")
        HR.set_data(ecg_data[-HR.buffer_length:])
        HR.is_warmed_up = True
        HR.update_loop()
        
    breathing_factor = 1.2
    
    timer_actuation = threading.Timer(0.1, breathing_actuation, [B, breathing_factor, True] )
    timer_actuation.start()<jupyter_output><empty_output><jupyter_text>Different actuation sequences<jupyter_code># computes inhale and exhale interval duration as average duration of breath * breathing_factor 

def breathing_actuation(B, breathing_factor, inflate):
    global osc_client, actuation_flag
    
    if not actuation_flag:
        return
    
    if inflate:
        print("inhale: ", B.features['avg_inhale'])
        osc_client.send_message("/actuator/inflate", 100.0)
        timer_exhale = threading.Timer(B.features['avg_inhale'] * breathing_factor, breathing_actuation, [B, breathing_factor, False])
        timer_exhale.start()

    else:
        print("exhale: ", B.features['avg_exhale'])
        osc_client.send_message("/actuator/inflate", -100.0)
        timer_inhale = threading.Timer(B.features['avg_exhale'] * breathing_factor, breathing_actuation, [B, breathing_factor, True])
        timer_inhale.start()
# specify inhale and exhale interval duration in seconds

def breathing_intervals(inhale_duration, exhale_duration, inflate):
    global osc_client, actuation_flag
    
    if not actuation_flag:
        return
    
    if inflate:
        print("inhale: ", inhale_duration)
        osc_client.send_message("/actuator/inflate", 100.0)
        timer_exhale = threading.Timer(inhale_duration, breathing_intervals, [inhale_duration, exhale_duration, False])
        timer_exhale.start()

    else:
        print("exhale: ", exhale_duration)
        osc_client.send_message("/actuator/inflate", -100.0)
        timer_inhale = threading.Timer(exhale_duration, breathing_intervals, [inhale_duration, exhale_duration, True])
        timer_inhale.start()
# specify duration of one breath in seconds and inhale/exhale ratio in percentage

def breathing_ratio(breath_duration, in_ex_ratio, inflate):
    global osc_client, actuation_flag
    
    if not actuation_flag:
        return
    
    if inflate:
        interval_length = breath_duration * in_ex_ratio
        print("inhale: ", interval_length)
        osc_client.send_message("/actuator/inflate", 100.0)
        timer_exhale = threading.Timer(interval_length, breathing_ratio, [breath_duration, in_ex_ratio, False])
        timer_exhale.start()

    else:
        interval_length = breath_duration * (1 - in_ex_ratio)
        print("exhale: ", interval_length)
        osc_client.send_message("/actuator/inflate", -100.0)
        timer_inhale = threading.Timer(interval_length, breathing_ratio, [breath_duration, in_ex_ratio, True])
        timer_inhale.start()
# computes degree of inflation/deflation based on ECG features

# TODO: how to compute the inflation/deflation value?

def heartrate_actuation(HR, timer_interval):
    global osc_client, actuation_flag
    
    if not actuation_flag:
        return
    
    current_trend = HR.current_trends["hr_mean"]
    #current_trend = HR.current_trends["rmssd"]
    #current_trend = HR.current_trends["LF/HF ratio"]
    
    current_feature = HR.features[-1]["hr_mean"]
    #current_feature = HR.features[-1]["rmssd"]
    #current_feature = HR.features[-1]["LF/HF ratio"]
    
    if current_trend > 0:
        print("inhale: ", B.features['avg_inhale'])
        
        # Single device
        #osc_client.send_message("/actuator/inflate", current_trend * 1000/current_feature)
#         osc_client.send_message("/actuator/inflate", 100.0)       
        
        # Two device        
        osc_client.send_message("/actuator/dev1/inflate", 100.0)
        osc_client.send_message("/actuator/dev2/inflate", 100.0)
        
        timer_exhale = threading.Timer(timer_interval, heartrate_actuation, [HR, timer_interval])
        timer_exhale.start()

    else:
        print("exhale: ", B.features['avg_exhale'])
        
        # Single device
        #osc_client.send_message("/actuator/inflate", current_trend * 1000/current_feature)
#         osc_client.send_message("/actuator/inflate", -100.0)

        # Two device
        osc_client.send_message("/actuator/dev1/inflate", -100.0)
        osc_client.send_message("/actuator/dev2/inflate", -100.0)
        
        timer_inhale = threading.Timer(timer_interval, heartrate_actuation, [HR, timer_interval])
        timer_inhale.start()<jupyter_output><empty_output><jupyter_text>Set up configuration: IP (BITalino, R-IoT or Node-RED), port and OSC address pattern<jupyter_code># Definitions
bitalino_ip = "127.0.0.1"
bitalino_port = 8000

riot_ip = "192.168.0.102"
riot_port = 31000 

# riot_ip = "192.168.1.40"
# riot_port = 12000 

actuator_ip = '192.168.0.100'
actuator_port = 12000

riot_address = "/raw/"
bitalino_address = "/0/bitalino"


# Configuration 
config_address = bitalino_address
config_ip = bitalino_ip

osc_client = SimpleUDPClient(actuator_ip, actuator_port) 

actuation_flag = True

ecg_data = []
resp_data = []

B = biofeatures.breathing(data = np.ones(10), buffer_length=2000, srate=100)
HRV = biofeatures.hrv(data = np.ones(10), buffer_length=2000, srate=100)

my_dispatcher = dispatcher.Dispatcher()
my_dispatcher.map(config_address, data_handler, B, HRV)

server = osc_server.ThreadingOSCUDPServer((config_ip, config_port), my_dispatcher)
print("Serving on {}".format(server.server_address))

# Warmup
warmup_t = 10
timer_warmup = threading.Timer(warmup_t, warmup, [B,resp_data,HRV,ecg_data])
timer_warmup.start()

try: 
    server.serve_forever()
except KeyboardInterrupt:
    B.update_data_flag = False
    HRV.update_data_flag = False
    actuation_flag = False
    server.server_close()
except:
    raise
%matplotlib inline
import matplotlib.pyplot as plt

plt.plot(resp_data)
# actuator_ip = '192.168.0.110' 
actuator_ip = ['192.168.0.110','192.168.0.220'] 
actuator_port = 12000

osc_client = [SimpleUDPClient(actuator_ip[0], actuator_port), SimpleUDPClient(actuator_ip[1], actuator_port)] 
val = 50
osc_client[0].send_message("/actuator/inflate", val)
osc_client[1].send_message("/actuator/inflate", val)

server.server_close()
osc_client[0]
osc_client[1]
osc_client1 = SimpleUDPClient('192.168.0.220', actuator_port)
val = 0.0
osc_client1.send_message("/actuator/inflate", float(val))
osc_client2 = SimpleUDPClient('192.168.0.110', actuator_port)
val = 100.0
osc_client1.send_message("/actuator/inflate", float(val))<jupyter_output><empty_output><jupyter_text>Arduino<jupyter_code>from pythonosc import dispatcher, osc_server
from pythonosc.udp_client import SimpleUDPClient
import biofeatures
import threading
import numpy as np
actuator_port = 12000
osc_client1 = SimpleUDPClient('127.0.0.1', actuator_port)
osc_client2 = SimpleUDPClient('127.0.0.1', actuator_port)
val = 0.0

for i in range(0,10):
    osc_client1.send_message("/actuator/inflate", float(val))
    osc_client2.send_message("/actuator/inflate", float(val))
val = 100.0

for i in range(0,10):
    osc_client1.send_message("/actuator/inflate", float(val))
    osc_client2.send_message("/actuator/inflate", float(val))
val = -100.0

for i in range(0,10):
    osc_client1.send_message("/actuator/inflate", float(val))
    osc_client2.send_message("/actuator/inflate", float(val))
from pythonosc import dispatcher, osc_server
from pythonosc.udp_client import SimpleUDPClient
import biofeatures
import threading
import numpy as np

actuator_port = 12000
osc_client = SimpleUDPClient('192.168.0.220', actuator_port)


for i in range(0,10):
    osc_client.send_message("/actuator/inflate", 80.0)

# actuator_port = 12000
# osc_client = SimpleUDPClient('192.168.0.110', actuator_port)
# osc_client.send_message("/actuator/inflate", 100.0)

actuator_port = 12000
osc_client1 = SimpleUDPClient('192.168.0.110', actuator_port)
osc_client1.send_message("/actuator/inflate", 0.0)

val = 0.0
actuator_port = 12000
osc_client1 = SimpleUDPClient('192.168.0.220', actuator_port)
    
for i in range(0,10):
    osc_client1.send_message("/actuator/inflate", float(val))

from pythonosc import dispatcher, osc_server
from pythonosc.udp_client import SimpleUDPClient
import biofeatures
import threading
import numpy as np
actuator_port = 12000
osc_client1 = SimpleUDPClient('127.0.0.1', actuator_port)
osc_client2 = SimpleUDPClient('127.0.0.1', actuator_port)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10965774980330449, 'fraction_numerical': 0.037077104642014164, 'mean_word_length': 4.004429133858268, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 17, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '20036', 'license': 'ISC', 'n_tokens_mistral': 3781, 'n_tokens_neox': 3497, 'n_words': 673, 'path': 'notebooks/TwoDevice_HR_Resp_OSC.ipynb', 'repo_name': 'malfarasplux/biofeatures', 'size': '10168'}