class PygameController:
    comms = None

    def __init__(self, serial_name, baud_rate):
        self.comms = Communication(serial_name, baud_rate)

    def run(self):
        # 1. make sure data sending is stopped by ending streaming
        self.comms.send_message("stop")
        self.comms.clear()

        # 2. start streaming orientation data
        input("Ready to start? Hit enter to begin.\n")
        self.comms.send_message("start")

        # 3. Forever collect orientation and send to PyGame until user exits
        print("Use <CTRL+C> to exit the program.\n")
        while True:
            message = self.comms.receive_message()
            if (message != None):
                command = None
                message = int(message)
                # if message == 0:
                #   command = "FLAT"
                # if message == 1:
                #   command = "UP"
                if message == 2:
                    command = "FIRE"
                elif message == 3:
                    command = "LEFT"
                elif message == 4:
                    command = "RIGHT"

                if command is not None:
                    mySocket.send(command.encode("UTF-8"))
def collect_samples():

    num_samples = 500  # 10 seconds of data @ 50Hz
    times = CircularList([], num_samples)
    ax = CircularList([], num_samples)
    ay = CircularList([], num_samples)
    az = CircularList([], num_samples)

    comms = Communication("/dev/cu.ag-ESP32SPP", 115200)
    try:
        comms.clear()  # just in case any junk is in the pipes
        # wait for user to start walking before starting to collect data
        input("Ready to collect data? Press [ENTER] to begin.\n")
        sleep(3)
        comms.send_message("wearable")  # begin sending data

        sample = 0
        while (sample < num_samples):
            message = comms.receive_message()
            if (message != None):
                try:
                    (m1, m2, m3, m4) = message.split(',')
                except ValueError:  # if corrupted data, skip the sample
                    continue

                # add the new values to the circular lists
                times.add(int(m1))
                ax.add(int(m2))
                ay.add(int(m3))
                az.add(int(m4))
                sample += 1
                print("Collected {:d} samples".format(sample))

        # a single ndarray for all samples for easy file I/O
        data = np.column_stack([times, ax, ay, az])

    except (Exception, KeyboardInterrupt) as e:
        print(e)  # exiting the program due to exception
    finally:
        comms.send_message("sleep")  # stop sending data
        comms.close()

    return data
def collect_samples():
  num_samples = 500 # 10 seconds of data @ 50Hz
  times = CircularList([], num_samples)
  ppg = CircularList([], num_samples)

  comms = Communication("/dev/cu.usbserial-1420", 115200)
  try:
    comms.clear() # just in case any junk is in the pipes
    # wait for user and then count down
    input("Ready to collect data? Press [ENTER] to begin.\n")
    print("Start measuring in...")
    for k in range(3,0,-1):
      print(k)
      sleep(1)
    print("Begin!")
    comms.send_message("wearable") # begin sending data

    sample = 0
    while(sample < num_samples):
      message = comms.receive_message()
      if(message != None):
        try:
          (m1, _, _, _, m2) = message.split(',')
        except ValueError: # if corrupted data, skip the sample
          continue

        # add the new values to the circular lists
        times.add(int(m1))
        ppg.add(int(m2))
        sample += 1
        print("Collected {:d} samples".format(sample))

    # a single ndarray for all samples for easy file I/O
    data = np.column_stack([times, ppg])

  except(Exception, KeyboardInterrupt) as e:
    print(e) # exiting the program due to exception
  finally:
    comms.send_message("sleep") # stop sending data
    comms.close()

  return data
 fs = 50                         # sampling rate
 num_samples = 500               # 10 seconds of data @ 50Hz
 process_time = 1                # compute the heart beat every second
 MAX_HEART_RATE  = 200
 hr = HRMonitor(num_samples, 50)
 comms = Communication("/dev/cu.ag-ESP32SPP", 115200)
 comms.clear()                   # just in case any junk is in the pipes
 comms.send_message("wearable")  # begin sending data
 print("Ready!")
 hr_plot = CircularList([], num_samples)
 t = CircularList([], num_samples)
 # Live data
 try:
     previous_time = time()
     while(True):
         message = comms.receive_message()
         if (message != None):
             try:
                 (m1, _, _, _, m2) = message.split(',')
             except ValueError:  # if corrupted data, skip the sample
                 continue
             # Collect data in the pedometer
             hr.add(int(m1)/1e3, int(m2))
             t.add(int(m1)/1e3)
             t_plot = np.array(t)
             # if enough time has elapsed, process the data and plot it
             current_time = time()
             if (current_time - previous_time > process_time):
                 previous_time = current_time
                 try:
                     heart_rate, peaks, filtered = hr.process()
Exemple #5
0
class Processing():
    __num_samples = 250  # 2 seconds of data @ 50Hz
    __refresh_time = 0.5  # update the plot every 0.1s (10 FPS)

    # Thresholds for idle detection
    __ax_idle_threshold = 1935
    __ay_idle_threshold = 1918
    __az_idle_threshold = 2430
    """
    Initializes the processing object and sets the field variables 
    @:param transformation_method: the type of transformation that will be plotted and computed 
    @:param port_name: The Serial port name used for Serial communication 
    """
    def __init__(self, transformation_method, port_name):
        self.comms = Communication(port_name, 115200)

        __transform_dict = {
            "average acceleration": self.__average_value,
            "sample difference": self.__sample_difference,
            "L2 norm": self.__l2_norm_calculation,
            "L1 norm": self.__l1_norm_calculation,
            "Maximum acceleration:": self.__max_acceleration
        }

        # This will keep track of the transformation method to be called
        self.__transformation_method = __transform_dict[transformation_method]
        self.transform_x = CircularList([], self.__num_samples)
        self.transform_y = CircularList([], self.__num_samples)
        self.transform_z = CircularList([], self.__num_samples)

        # These will contain the acceleration values read from the accelerometer
        self.times = CircularList([], self.__num_samples)
        self.ax = CircularList([], self.__num_samples)
        self.ay = CircularList([], self.__num_samples)
        self.az = CircularList([], self.__num_samples)

        # Set up the plotting
        fig = plt.figure()
        self.ax1 = fig.add_subplot(311)
        self.ax2 = fig.add_subplot(312)
        self.ax3 = fig.add_subplot(313)
        self.graph_type = transformation_method

        # times to keep track of when
        self.__last_idlecheck_time = 0
        self.__idle_state = False
        self.__last_active_time = 0

    """
    Updates the plot, displaying the acceeleration values as well as the 
    transformation values
    """

    def __plot(self):
        # Clears the plots and resets them
        self.ax1.cla()
        self.ax2.cla()
        self.ax3.cla()

        # Sets the title of the plot
        self.ax1.set_title("X acceleration (Red) and " + self.graph_type +
                           " (Blue)")
        self.ax2.set_title("Y acceleration (Red) and " + self.graph_type +
                           " (Blue)")
        self.ax3.set_title("X acceleration (Red) and " + self.graph_type +
                           " (Blue)")

        # Plots the acceleration values along with the transformation
        self.ax1.plot(self.transform_x, 'b', self.ax, 'r')
        self.ax2.plot(self.transform_y, 'b', self.ay, 'r')
        self.ax3.plot(self.transform_z, 'b', self.az, 'r')
        plt.show(block=False)
        plt.pause(0.0001)

    """
    Determines whether the device is inactive or not 
    @:param average_x: The average x-acceleration
    @:param average_y: The average y-acceleration
    @:param average_z: The average z-acceleration
    @:return: a boolean representing whether the device is inactive or not 
    """

    def __is_inactive(self, average_x, average_y, average_z):
        return average_x <= self.__ax_idle_threshold and average_y <= self.__ay_idle_threshold and average_z <= self.__az_idle_threshold

    """
    Computes the average value for an axis
    @:param acceleration_list: The acceleration over 5 seconds in either x,y,z direction
    @:return: A scalar which is the average from the acceleration list 
    """

    def __average_value(self, acceleration_list):
        return np.average(np.array(acceleration_list))

    """
    Computes the difference between each adjacent indexes
    @:param acceleration_list: The acceleration over 5 seconds in either x,y,z direction
    @:return: a numpy array containing the differences between each point
    """

    def __sample_difference(self, acceleration_list):
        np.diff(np.array(acceleration_list))
        return np.diff(np.array(acceleration_list))

    """
    Computes the euclidean distance for the acceleration list
    @:param x_acceleration: one sample of the x-acceleration
    @:param y_acceleration: one sample of the y-acceleration
    @:param z_acceleration: one sample of the z-acceleration
    @:return: A scalar which is the square root of the sum of each number in the 
    """

    def __l2_norm_calculation(self, x_acceleration, y_acceleration,
                              z_acceleration):
        return np.linalg.norm(
            np.array([x_acceleration, y_acceleration, z_acceleration]))

    """
    Computes the L1 norm for the acceleration lsit
    @:param x_acceleration: one sample of the x-acceleration
    @:param y_acceleration: one sample of the y-acceleration
    @:param z_acceleration: one sample of the z-acceleration
    @:return: The sum of the absolute value of each acceleration value 
    """

    def __l1_norm_calculation(self, x_acceleration, y_acceleration,
                              z_acceleration):
        return np.linalg.norm(np.array(
            [x_acceleration, y_acceleration, z_acceleration]),
                              ord=1)

    """
    Finds the max acceleration in one of the accelerations 
    @:param: acceleration_list: The acceleration over 5 seconds in either x,y,z direction
    @:return: The maximum value in the acceleration list 
    """

    def __max_acceleration(self, acceleration_list):
        return int(np.max(np.array(acceleration_list)))

    """
    Records the acceleration and times from the accelerometer 
    @:param time: the time from the Serial monitor
    @:param x_acceleration: the x-acceleration
    @:param y_acceleration: the y-acceleration
    @:param z_acceleration: the z-acceleration
    """

    def __record_acceleration(self, time, x_acceleration, y_acceleration,
                              z_acceleration):
        # add the new values to the circular lists
        self.times.add(int(time))
        self.ax.add(int(x_acceleration))
        self.ay.add(int(y_acceleration))
        self.az.add(int(z_acceleration))

    """
    Records the transformation value based on what transformation type the 
    instance uses 
    @:param x_acceleration: one sample of the x-acceleration
    @:param y_acceleration: one sample of the y-acceleration
    @:param z_acceleration: one sample of the z-acceleration
    """

    def __record_transformation(self, x_acceleration, y_acceleration,
                                z_acceleration):
        # These if statements are used because some of these methods have different parameters and return types
        if self.__transformation_method == self.__l1_norm_calculation or self.__transformation_method == self.__l2_norm_calculation:
            norm_number = self.__transformation_method()
            self.transform_x.add(norm_number)
            self.transform_y.add(norm_number)
            self.transform_z.add(norm_number)
        # sets each transformation method to the sample difference array
        elif self.__transformation_method == self.__sample_difference:
            self.transform_x = self.__transformation_method(self.ax)
            self.transform_y = self.__transformation_method(self.ay)
            self.transform_z = self.__transformation_method(self.az)
        # This will either call average_value or maximum_acceleration
        else:
            self.transform_x.add(self.__transformation_method(self.ax))
            self.transform_y.add(self.__transformation_method(self.ay))
            self.transform_z.add(self.__transformation_method(self.az))

    """
    Checks if the device has been idle for 5 seconds or if it's been active for 1 
    second. This will either cause the motor to buzz or another message displaying that the person has
    been active.
    @:param current_time: The current time the program is at 
    """

    def __check_idle(self, current_time):
        # If it's been 5 seconds since the last time the person has been inactive
        if current_time - self.__last_idlecheck_time >= 5:
            # get the average acceleration over 5 seconds
            average_x = self.__average_value(self.ax)
            average_y = self.__average_value(self.ay)
            average_z = self.__average_value(self.az)
            print(average_x, ",", average_y, ",", average_z)
            self.__last_idlecheck_time = current_time
            # if the device has been idle for 5 seconds, buzz the motor
            if self.__is_inactive(average_x, average_y, average_z):
                self.__idle_state = True
                self.comms.send_message("Buzz motor")
            else:
                self.__idle_state = False
        # If the person has been inactive but has become active for 1 second
        if self.__idle_state and current_time - self.__last_active_time >= 1:
            self.__last_active_time = current_time
            # get the average values for the last 1 second
            average_x = self.__average_value(self.ax[200:])
            average_y = self.__average_value(self.ay[200:])
            average_z = self.__average_value(self.az[200:])
            if not self.__is_inactive(average_x, average_y, average_z):
                print("Active accelerations: ", average_x, average_y,
                      average_z)
                self.__last_idlecheck_time = current_time  # this ensures that the person must be inactive for 5 seconds after their activity
                self.comms.send_message("Keep it up!")

    """
    Runs all the processing for the Serial communication, including plotting the 
    acceleration values and checking whether the device has been inactive or not. 
    """

    def run(self):
        self.comms.clear()  # just in case any junk is in the pipes
        self.comms.send_message("wearable")  # begin sending data
        try:
            previous_time = 0
            while (True):
                message = self.comms.receive_message()
                if (message != None):
                    try:
                        (m1, m2, m3, m4) = message.split(',')
                    except ValueError:  # if corrupted data, skip the sample
                        continue
                    # Record the acceleration and transformation
                    self.__record_acceleration(m1, m2, m3, m4)
                    self.__record_transformation(m2, m3, m4)
                    current_time = time()
                    if current_time - previous_time > self.__refresh_time:
                        previous_time = current_time
                        self.__plot()
                    self.__check_idle(current_time)

        except (Exception, KeyboardInterrupt) as e:
            print(e)  # Exiting the program due to exception
        finally:
            print("Closing Connection")
            self.comms.send_message("sleep")  # stop sending data
            self.comms.close()
            sleep(1)
Exemple #6
0
class PygameController:
    comms = None
    filename = "./scores/topscores.csv"

    def __init__(self, serial_name, baud_rate):
        self.comms = Communication(serial_name, baud_rate)

    # Save data to file
    def save_data(self, filename, data):
        np.savetxt(filename, data, delimiter=",")

    # Load data from file
    def load_data(self, filename):
        return np.genfromtxt(filename, delimiter=",")

    """
  Receives a message from the server 
  @:return: the message sent from the server 
  """

    def receive_from_server(self):
        game_status = None
        # Receive game status
        try:
            game_status, _ = mySocket.recvfrom(1024)
            game_status = game_status.decode('utf-8')
        except:
            pass

        return game_status

    """
  Orders the top three scores with the score from 
  the last round
  @:param top_scores: the last top three scores
  @:param score: the score from the last round 
  @:return: the new top three scores 
  """

    def order_top_scores(self, top_scores, score):
        top_scores.append(
            score)  # add the score from last round to the top scores
        top_scores.sort(
            reverse=True)  # sort all the scores from greatest to least
        return top_scores[:len(
            top_scores
        ) - 1]  # return the new top scores with the lowest score removed

    """
  Updates the top three scores of the user after their game is zero
  @:param score: The score the user got in their last game 
  @:return: message send to Arduino which contains the top three scores 
  """

    def update_top_scores(self, score):

        # Load the top three scores from the file and sort them
        # from greatest to least
        top_scores = list(self.load_data(self.filename))
        top_scores.sort(reverse=True)

        # Update the top three scores and save them to the file
        top_scores = self.order_top_scores(top_scores, score)
        self.save_data(self.filename, np.array(top_scores))

        # This is the message that will be sent to the MCU to be displayed on the LED
        message = "Top Scores:" + "," + "#1: " + str(int(
            top_scores[0])) + "," + "#2: " + str(int(top_scores[1]))
        message = message + "," + "#3: " + str(int(top_scores[2]))
        return message

    """
  Method that only terminates once the game is 
  ready to start 
  """

    def wait_until_start(self):
        # Wait until the user starts the game again
        while True:
            game_status = self.receive_from_server()
            if game_status == "START":
                print("Game started")
                self.comms.send_message("start")
                break

    """
  Checks the current game status, which is sent by the server 
  @:param game_status: a string which represents the game status 
  """

    def check_game_status(self, game_status):

        # If the game ends, we stall sending data until a new game starts
        if game_status is not None and "GAME OVER" in game_status:

            # make sure data sending is stopped by ending streaming once game is over
            self.comms.send_message("stop")
            self.comms.clear()

            print("GAME OVER...")

            score = game_status.split(",")[
                1]  # Get the score from the last round
            message = self.update_top_scores(
                int(score))  # Update the top three scores
            print(message)
            self.comms.send_message(
                message)  # send the top three score to the MCU

            self.wait_until_start()

        # If the player has been hit, buzz the motor
        elif game_status == "BUZZ":
            print("Player hit!")
            self.comms.send_message("buzz")

    def run(self):
        # 1. make sure data sending is stopped by ending streaming
        self.comms.send_message("stop")
        self.comms.clear()

        # 2. start streaming orientation data
        input("Ready to start? Hit enter to begin.\n")
        mySocket.send("CONTROLLER ON".encode('utf-8'))

        # 3. Forever collect orientation and send to PyGame until user exits
        print("Waiting for game to start...")
        self.wait_until_start()

        print("Use <CTRL+C> to exit the program.\n")
        while True:
            message = self.comms.receive_message()
            if (message != None):
                command = None
                message = int(message)
                if message == -1:
                    self.comms.send_message("LOW BATTERY")
                    command = "QUIT"
                elif message == 0:
                    command = "FLAT"
                elif message == 1:
                    command = "UP"
                elif message == 2:
                    command = "FIRE"
                elif message == 3:
                    command = "LEFT"
                elif message == 4:
                    command = "RIGHT"
                elif message == 5:
                    command = "FIRE LEFT"
                elif message == 6:
                    command = "FIRE RIGHT"
                elif message == 10:
                    command = "PAUSE"
                elif message == 11:
                    command = "PLAY"
                if command is not None:
                    mySocket.send(command.encode("UTF-8"))
                print(command)

            # Check the current game status
            game_status = self.receive_from_server()

            self.check_game_status(game_status)